src/ztfy/myams/resources/js/ext/jquery-select2-3.5.4.js
changeset 210 a4497eed4ff7
equal deleted inserted replaced
209:1bde2a1c1902 210:a4497eed4ff7
       
     1 /*
       
     2 Copyright 2012 Igor Vaynberg
       
     3 
       
     4 Version: 3.5.4 Timestamp: Sun Aug 30 13:30:32 EDT 2015
       
     5 
       
     6 This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
       
     7 General Public License version 2 (the "GPL License"). You may choose either license to govern your
       
     8 use of this software only upon the condition that you accept all of the terms of either the Apache
       
     9 License or the GPL License.
       
    10 
       
    11 You 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 
       
    16 Unless required by applicable law or agreed to in writing, software distributed under the
       
    17 Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
       
    18 CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
       
    19 the 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 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":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038A":"\u0399","\u03AA":"\u0399","\u038C":"\u039F","\u038E":"\u03A5","\u03AB":"\u03A5","\u038F":"\u03A9","\u03AC":"\u03B1","\u03AD":"\u03B5","\u03AE":"\u03B7","\u03AF":"\u03B9","\u03CA":"\u03B9","\u0390":"\u03B9","\u03CC":"\u03BF","\u03CD":"\u03C5","\u03CB":"\u03C5","\u03B0":"\u03C5","\u03C9":"\u03C9","\u03C2":"\u03C3"};
       
   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(document.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, transforming 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, transform) {
       
   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] = transform(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             $(document.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 containing 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();
       
   701 
       
   702             this.liveRegion = $('.select2-hidden-accessible');
       
   703             if (this.liveRegion.length == 0) {
       
   704                 this.liveRegion = $("<span>", {
       
   705                         role: "status",
       
   706                         "aria-live": "polite"
       
   707                     })
       
   708                     .addClass("select2-hidden-accessible")
       
   709                     .appendTo(document.body);
       
   710             }
       
   711 
       
   712             this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
       
   713             this.containerEventName= this.containerId
       
   714                 .replace(/([.])/g, '_')
       
   715                 .replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
       
   716             this.container.attr("id", this.containerId);
       
   717 
       
   718             this.container.attr("title", opts.element.attr("title"));
       
   719 
       
   720             this.body = $(document.body);
       
   721 
       
   722             syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
       
   723 
       
   724             this.container.attr("style", opts.element.attr("style"));
       
   725             this.container.css(evaluate(opts.containerCss, this.opts.element));
       
   726             this.container.addClass(evaluate(opts.containerCssClass, this.opts.element));
       
   727 
       
   728             this.elementTabIndex = this.opts.element.attr("tabindex");
       
   729 
       
   730             // swap container for the element
       
   731             this.opts.element
       
   732                 .data("select2", this)
       
   733                 .attr("tabindex", "-1")
       
   734                 .before(this.container)
       
   735                 .on("click.select2", killEvent); // do not leak click events
       
   736 
       
   737             this.container.data("select2", this);
       
   738 
       
   739             this.dropdown = this.container.find(".select2-drop");
       
   740 
       
   741             syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
       
   742 
       
   743             this.dropdown.addClass(evaluate(opts.dropdownCssClass, this.opts.element));
       
   744             this.dropdown.data("select2", this);
       
   745             this.dropdown.on("click", killEvent);
       
   746 
       
   747             this.results = results = this.container.find(resultsSelector);
       
   748             this.search = search = this.container.find("input.select2-input");
       
   749 
       
   750             this.queryCount = 0;
       
   751             this.resultsPage = 0;
       
   752             this.context = null;
       
   753 
       
   754             // initialize the container
       
   755             this.initContainer();
       
   756 
       
   757             this.container.on("click", killEvent);
       
   758 
       
   759             installFilteredMouseMove(this.results);
       
   760 
       
   761             this.dropdown.on("mousemove-filtered", resultsSelector, this.bind(this.highlightUnderEvent));
       
   762             this.dropdown.on("touchstart touchmove touchend", resultsSelector, this.bind(function (event) {
       
   763                 this._touchEvent = true;
       
   764                 this.highlightUnderEvent(event);
       
   765             }));
       
   766             this.dropdown.on("touchmove", resultsSelector, this.bind(this.touchMoved));
       
   767             this.dropdown.on("touchstart touchend", resultsSelector, this.bind(this.clearTouchMoved));
       
   768 
       
   769             // Waiting for a click event on touch devices to select option and hide dropdown
       
   770             // otherwise click will be triggered on an underlying element
       
   771             this.dropdown.on('click', this.bind(function (event) {
       
   772                 if (this._touchEvent) {
       
   773                     this._touchEvent = false;
       
   774                     this.selectHighlighted();
       
   775                 }
       
   776             }));
       
   777 
       
   778             installDebouncedScroll(80, this.results);
       
   779             this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
       
   780 
       
   781             // do not propagate change event from the search field out of the component
       
   782             $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
       
   783             $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
       
   784 
       
   785             // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
       
   786             if ($.fn.mousewheel) {
       
   787                 results.mousewheel(function (e, delta, deltaX, deltaY) {
       
   788                     var top = results.scrollTop();
       
   789                     if (deltaY > 0 && top - deltaY <= 0) {
       
   790                         results.scrollTop(0);
       
   791                         killEvent(e);
       
   792                     } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
       
   793                         results.scrollTop(results.get(0).scrollHeight - results.height());
       
   794                         killEvent(e);
       
   795                     }
       
   796                 });
       
   797             }
       
   798 
       
   799             installKeyUpChangeEvent(search);
       
   800             search.on("keyup-change input paste", this.bind(this.updateResults));
       
   801             search.on("focus", function () { search.addClass("select2-focused"); });
       
   802             search.on("blur", function () { search.removeClass("select2-focused");});
       
   803 
       
   804             this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
       
   805                 if ($(e.target).closest(".select2-result-selectable").length > 0) {
       
   806                     this.highlightUnderEvent(e);
       
   807                     this.selectHighlighted(e);
       
   808                 }
       
   809             }));
       
   810 
       
   811             // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
       
   812             // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
       
   813             // dom it will trigger the popup close, which is not what we want
       
   814             // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal.
       
   815             this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); });
       
   816 
       
   817             this.lastSearchTerm = undefined;
       
   818 
       
   819             if ($.isFunction(this.opts.initSelection)) {
       
   820                 // initialize selection based on the current value of the source element
       
   821                 this.initSelection();
       
   822 
       
   823                 // if the user has provided a function that can set selection based on the value of the source element
       
   824                 // we monitor the change event on the element and trigger it, allowing for two way synchronization
       
   825                 this.monitorSource();
       
   826             }
       
   827 
       
   828             if (opts.maximumInputLength !== null) {
       
   829                 this.search.attr("maxlength", opts.maximumInputLength);
       
   830             }
       
   831 
       
   832             var disabled = opts.element.prop("disabled");
       
   833             if (disabled === undefined) disabled = false;
       
   834             this.enable(!disabled);
       
   835 
       
   836             var readonly = opts.element.prop("readonly");
       
   837             if (readonly === undefined) readonly = false;
       
   838             this.readonly(readonly);
       
   839 
       
   840             // Calculate size of scrollbar
       
   841             scrollBarDimensions = scrollBarDimensions || measureScrollbar();
       
   842 
       
   843             this.autofocus = opts.element.prop("autofocus");
       
   844             opts.element.prop("autofocus", false);
       
   845             if (this.autofocus) this.focus();
       
   846 
       
   847             this.search.attr("placeholder", opts.searchInputPlaceholder);
       
   848         },
       
   849 
       
   850         // abstract
       
   851         destroy: function () {
       
   852             var element=this.opts.element, select2 = element.data("select2"), self = this;
       
   853 
       
   854             this.close();
       
   855 
       
   856             if (element.length && element[0].detachEvent && self._sync) {
       
   857                 element.each(function () {
       
   858                     if (self._sync) {
       
   859                         this.detachEvent("onpropertychange", self._sync);
       
   860                     }
       
   861                 });
       
   862             }
       
   863             if (this.propertyObserver) {
       
   864                 this.propertyObserver.disconnect();
       
   865                 this.propertyObserver = null;
       
   866             }
       
   867             this._sync = null;
       
   868 
       
   869             if (select2 !== undefined) {
       
   870                 select2.container.remove();
       
   871                 select2.liveRegion.remove();
       
   872                 select2.dropdown.remove();
       
   873                 element.removeData("select2")
       
   874                     .off(".select2");
       
   875                 if (!element.is("input[type='hidden']")) {
       
   876                     element
       
   877                         .show()
       
   878                         .prop("autofocus", this.autofocus || false);
       
   879                     if (this.elementTabIndex) {
       
   880                         element.attr({tabindex: this.elementTabIndex});
       
   881                     } else {
       
   882                         element.removeAttr("tabindex");
       
   883                     }
       
   884                     element.show();
       
   885                 } else {
       
   886                     element.css("display", "");
       
   887                 }
       
   888             }
       
   889 
       
   890             cleanupJQueryElements.call(this,
       
   891                 "container",
       
   892                 "liveRegion",
       
   893                 "dropdown",
       
   894                 "results",
       
   895                 "search"
       
   896             );
       
   897         },
       
   898 
       
   899         // abstract
       
   900         optionToData: function(element) {
       
   901             if (element.is("option")) {
       
   902                 return {
       
   903                     id:element.prop("value"),
       
   904                     text:element.text(),
       
   905                     element: element.get(),
       
   906                     css: element.attr("class"),
       
   907                     disabled: element.prop("disabled"),
       
   908                     locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true)
       
   909                 };
       
   910             } else if (element.is("optgroup")) {
       
   911                 return {
       
   912                     text:element.attr("label"),
       
   913                     children:[],
       
   914                     element: element.get(),
       
   915                     css: element.attr("class")
       
   916                 };
       
   917             }
       
   918         },
       
   919 
       
   920         // abstract
       
   921         prepareOpts: function (opts) {
       
   922             var element, select, idKey, ajaxUrl, self = this;
       
   923 
       
   924             element = opts.element;
       
   925 
       
   926             if (element.get(0).tagName.toLowerCase() === "select") {
       
   927                 this.select = select = opts.element;
       
   928             }
       
   929 
       
   930             if (select) {
       
   931                 // these options are not allowed when attached to a select because they are picked up off the element itself
       
   932                 $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
       
   933                     if (this in opts) {
       
   934                         throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
       
   935                     }
       
   936                 });
       
   937             }
       
   938 
       
   939             opts.debug = opts.debug || $.fn.select2.defaults.debug;
       
   940 
       
   941             // Warnings for options renamed/removed in Select2 4.0.0
       
   942             // Only when it's enabled through debug mode
       
   943             if (opts.debug && console && console.warn) {
       
   944                 // id was removed
       
   945                 if (opts.id != null) {
       
   946                     console.warn(
       
   947                         'Select2: The `id` option has been removed in Select2 4.0.0, ' +
       
   948                         'consider renaming your `id` property or mapping the property before your data makes it to Select2. ' +
       
   949                         'You can read more at https://select2.github.io/announcements-4.0.html#changed-id'
       
   950                     );
       
   951                 }
       
   952 
       
   953                 // text was removed
       
   954                 if (opts.text != null) {
       
   955                     console.warn(
       
   956                         'Select2: The `text` option has been removed in Select2 4.0.0, ' +
       
   957                         'consider renaming your `text` property or mapping the property before your data makes it to Select2. ' +
       
   958                         'You can read more at https://select2.github.io/announcements-4.0.html#changed-id'
       
   959                     );
       
   960                 }
       
   961 
       
   962                 // sortResults was renamed to results
       
   963                 if (opts.sortResults != null) {
       
   964                     console.warn(
       
   965                         'Select2: the `sortResults` option has been renamed to `sorter` in Select2 4.0.0. '
       
   966                     );
       
   967                 }
       
   968 
       
   969                 // selectOnBlur was renamed to selectOnClose
       
   970                 if (opts.selectOnBlur != null) {
       
   971                     console.warn(
       
   972                         'Select2: The `selectOnBlur` option has been renamed to `selectOnClose` in Select2 4.0.0.'
       
   973                     );
       
   974                 }
       
   975 
       
   976                 // ajax.results was renamed to ajax.processResults
       
   977                 if (opts.ajax != null && opts.ajax.results != null) {
       
   978                     console.warn(
       
   979                         'Select2: The `ajax.results` option has been renamed to `ajax.processResults` in Select2 4.0.0.'
       
   980                     );
       
   981                 }
       
   982 
       
   983                 // format* options were renamed to language.*
       
   984                 if (opts.formatNoResults != null) {
       
   985                     console.warn(
       
   986                         'Select2: The `formatNoResults` option has been renamed to `language.noResults` in Select2 4.0.0.'
       
   987                     );
       
   988                 }
       
   989                 if (opts.formatSearching != null) {
       
   990                     console.warn(
       
   991                         'Select2: The `formatSearching` option has been renamed to `language.searching` in Select2 4.0.0.'
       
   992                     );
       
   993                 }
       
   994                 if (opts.formatInputTooShort != null) {
       
   995                     console.warn(
       
   996                         'Select2: The `formatInputTooShort` option has been renamed to `language.inputTooShort` in Select2 4.0.0.'
       
   997                     );
       
   998                 }
       
   999                 if (opts.formatInputTooLong != null) {
       
  1000                     console.warn(
       
  1001                         'Select2: The `formatInputTooLong` option has been renamed to `language.inputTooLong` in Select2 4.0.0.'
       
  1002                     );
       
  1003                 }
       
  1004                 if (opts.formatLoading != null) {
       
  1005                     console.warn(
       
  1006                         'Select2: The `formatLoading` option has been renamed to `language.loadingMore` in Select2 4.0.0.'
       
  1007                     );
       
  1008                 }
       
  1009                 if (opts.formatSelectionTooBig != null) {
       
  1010                     console.warn(
       
  1011                         'Select2: The `formatSelectionTooBig` option has been renamed to `language.maximumSelected` in Select2 4.0.0.'
       
  1012                     );
       
  1013                 }
       
  1014 
       
  1015                 if (opts.element.data('select2Tags')) {
       
  1016                     console.warn(
       
  1017                         'Select2: The `data-select2-tags` attribute has been renamed to `data-tags` in Select2 4.0.0.'
       
  1018                     );
       
  1019                 }
       
  1020             }
       
  1021 
       
  1022             // Aliasing options renamed in Select2 4.0.0
       
  1023 
       
  1024             // data-select2-tags -> data-tags
       
  1025             if (opts.element.data('tags') != null) {
       
  1026                 var elemTags = opts.element.data('tags');
       
  1027 
       
  1028                 // data-tags should actually be a boolean
       
  1029                 if (!$.isArray(elemTags)) {
       
  1030                     elemTags = [];
       
  1031                 }
       
  1032 
       
  1033                 opts.element.data('select2Tags', elemTags);
       
  1034             }
       
  1035 
       
  1036             // sortResults -> sorter
       
  1037             if (opts.sorter != null) {
       
  1038                 opts.sortResults = opts.sorter;
       
  1039             }
       
  1040 
       
  1041             // selectOnBlur -> selectOnClose
       
  1042             if (opts.selectOnClose != null) {
       
  1043                 opts.selectOnBlur = opts.selectOnClose;
       
  1044             }
       
  1045 
       
  1046             // ajax.results -> ajax.processResults
       
  1047             if (opts.ajax != null) {
       
  1048                 if ($.isFunction(opts.ajax.processResults)) {
       
  1049                     opts.ajax.results = opts.ajax.processResults;
       
  1050                 }
       
  1051             }
       
  1052 
       
  1053             // Formatters/language options
       
  1054             if (opts.language != null) {
       
  1055                 var lang = opts.language;
       
  1056 
       
  1057                 // formatNoMatches -> language.noMatches
       
  1058                 if ($.isFunction(lang.noMatches)) {
       
  1059                     opts.formatNoMatches = lang.noMatches;
       
  1060                 }
       
  1061 
       
  1062                 // formatSearching -> language.searching
       
  1063                 if ($.isFunction(lang.searching)) {
       
  1064                     opts.formatSearching = lang.searching;
       
  1065                 }
       
  1066 
       
  1067                 // formatInputTooShort -> language.inputTooShort
       
  1068                 if ($.isFunction(lang.inputTooShort)) {
       
  1069                     opts.formatInputTooShort = lang.inputTooShort;
       
  1070                 }
       
  1071 
       
  1072                 // formatInputTooLong -> language.inputTooLong
       
  1073                 if ($.isFunction(lang.inputTooLong)) {
       
  1074                     opts.formatInputTooLong = lang.inputTooLong;
       
  1075                 }
       
  1076 
       
  1077                 // formatLoading -> language.loadingMore
       
  1078                 if ($.isFunction(lang.loadingMore)) {
       
  1079                     opts.formatLoading = lang.loadingMore;
       
  1080                 }
       
  1081 
       
  1082                 // formatSelectionTooBig -> language.maximumSelected
       
  1083                 if ($.isFunction(lang.maximumSelected)) {
       
  1084                     opts.formatSelectionTooBig = lang.maximumSelected;
       
  1085                 }
       
  1086             }
       
  1087 
       
  1088             opts = $.extend({}, {
       
  1089                 populateResults: function(container, results, query) {
       
  1090                     var populate, id=this.opts.id, liveRegion=this.liveRegion;
       
  1091 
       
  1092                     populate=function(results, container, depth) {
       
  1093 
       
  1094                         var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
       
  1095 
       
  1096                         results = opts.sortResults(results, container, query);
       
  1097 
       
  1098                         // collect the created nodes for bulk append
       
  1099                         var nodes = [];
       
  1100                         for (i = 0, l = results.length; i < l; i = i + 1) {
       
  1101 
       
  1102                             result=results[i];
       
  1103 
       
  1104                             disabled = (result.disabled === true);
       
  1105                             selectable = (!disabled) && (id(result) !== undefined);
       
  1106 
       
  1107                             compound=result.children && result.children.length > 0;
       
  1108 
       
  1109                             node=$("<li></li>");
       
  1110                             node.addClass("select2-results-dept-"+depth);
       
  1111                             node.addClass("select2-result");
       
  1112                             node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
       
  1113                             if (disabled) { node.addClass("select2-disabled"); }
       
  1114                             if (compound) { node.addClass("select2-result-with-children"); }
       
  1115                             node.addClass(self.opts.formatResultCssClass(result));
       
  1116                             node.attr("role", "presentation");
       
  1117 
       
  1118                             label=$(document.createElement("div"));
       
  1119                             label.addClass("select2-result-label");
       
  1120                             label.attr("id", "select2-result-label-" + nextUid());
       
  1121                             label.attr("role", "option");
       
  1122 
       
  1123                             formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
       
  1124                             if (formatted!==undefined) {
       
  1125                                 label.html(formatted);
       
  1126                                 node.append(label);
       
  1127                             }
       
  1128 
       
  1129 
       
  1130                             if (compound) {
       
  1131                                 innerContainer=$("<ul></ul>");
       
  1132                                 innerContainer.addClass("select2-result-sub");
       
  1133                                 populate(result.children, innerContainer, depth+1);
       
  1134                                 node.append(innerContainer);
       
  1135                             }
       
  1136 
       
  1137                             node.data("select2-data", result);
       
  1138                             nodes.push(node[0]);
       
  1139                         }
       
  1140 
       
  1141                         // bulk append the created nodes
       
  1142                         container.append(nodes);
       
  1143                         liveRegion.text(opts.formatMatches(results.length));
       
  1144                     };
       
  1145 
       
  1146                     populate(results, container, 0);
       
  1147                 }
       
  1148             }, $.fn.select2.defaults, opts);
       
  1149 
       
  1150             if (typeof(opts.id) !== "function") {
       
  1151                 idKey = opts.id;
       
  1152                 opts.id = function (e) { return e[idKey]; };
       
  1153             }
       
  1154 
       
  1155             if ($.isArray(opts.element.data("select2Tags"))) {
       
  1156                 if ("tags" in opts) {
       
  1157                     throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
       
  1158                 }
       
  1159                 opts.tags=opts.element.data("select2Tags");
       
  1160             }
       
  1161 
       
  1162             if (select) {
       
  1163                 opts.query = this.bind(function (query) {
       
  1164                     var data = { results: [], more: false },
       
  1165                         term = query.term,
       
  1166                         children, placeholderOption, process;
       
  1167 
       
  1168                     process=function(element, collection) {
       
  1169                         var group;
       
  1170                         if (element.is("option")) {
       
  1171                             if (query.matcher(term, element.text(), element)) {
       
  1172                                 collection.push(self.optionToData(element));
       
  1173                             }
       
  1174                         } else if (element.is("optgroup")) {
       
  1175                             group=self.optionToData(element);
       
  1176                             element.children().each2(function(i, elm) { process(elm, group.children); });
       
  1177                             if (group.children.length>0) {
       
  1178                                 collection.push(group);
       
  1179                             }
       
  1180                         }
       
  1181                     };
       
  1182 
       
  1183                     children=element.children();
       
  1184 
       
  1185                     // ignore the placeholder option if there is one
       
  1186                     if (this.getPlaceholder() !== undefined && children.length > 0) {
       
  1187                         placeholderOption = this.getPlaceholderOption();
       
  1188                         if (placeholderOption) {
       
  1189                             children=children.not(placeholderOption);
       
  1190                         }
       
  1191                     }
       
  1192 
       
  1193                     children.each2(function(i, elm) { process(elm, data.results); });
       
  1194 
       
  1195                     query.callback(data);
       
  1196                 });
       
  1197                 // this is needed because inside val() we construct choices from options and their id is hardcoded
       
  1198                 opts.id=function(e) { return e.id; };
       
  1199             } else {
       
  1200                 if (!("query" in opts)) {
       
  1201                     if ("ajax" in opts) {
       
  1202                         ajaxUrl = opts.element.data("ajax-url");
       
  1203                         if (ajaxUrl && ajaxUrl.length > 0) {
       
  1204                             opts.ajax.url = ajaxUrl;
       
  1205                         }
       
  1206                         opts.query = ajax.call(opts.element, opts.ajax);
       
  1207                     } else if ("data" in opts) {
       
  1208                         opts.query = local(opts.data);
       
  1209                     } else if ("tags" in opts) {
       
  1210                         opts.query = tags(opts.tags);
       
  1211                         if (opts.createSearchChoice === undefined) {
       
  1212                             opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
       
  1213                         }
       
  1214                         if (opts.initSelection === undefined) {
       
  1215                             opts.initSelection = function (element, callback) {
       
  1216                                 var data = [];
       
  1217                                 $(splitVal(element.val(), opts.separator, opts.transformVal)).each(function () {
       
  1218                                     var obj = { id: this, text: this },
       
  1219                                         tags = opts.tags;
       
  1220                                     if ($.isFunction(tags)) tags=tags();
       
  1221                                     $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
       
  1222                                     data.push(obj);
       
  1223                                 });
       
  1224 
       
  1225                                 callback(data);
       
  1226                             };
       
  1227                         }
       
  1228                     }
       
  1229                 }
       
  1230             }
       
  1231             if (typeof(opts.query) !== "function") {
       
  1232                 throw "query function not defined for Select2 " + opts.element.attr("id");
       
  1233             }
       
  1234 
       
  1235             if (opts.createSearchChoicePosition === 'top') {
       
  1236                 opts.createSearchChoicePosition = function(list, item) { list.unshift(item); };
       
  1237             }
       
  1238             else if (opts.createSearchChoicePosition === 'bottom') {
       
  1239                 opts.createSearchChoicePosition = function(list, item) { list.push(item); };
       
  1240             }
       
  1241             else if (typeof(opts.createSearchChoicePosition) !== "function")  {
       
  1242                 throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
       
  1243             }
       
  1244 
       
  1245             return opts;
       
  1246         },
       
  1247 
       
  1248         /**
       
  1249          * Monitor the original element for changes and update select2 accordingly
       
  1250          */
       
  1251         // abstract
       
  1252         monitorSource: function () {
       
  1253             var el = this.opts.element, observer, self = this;
       
  1254 
       
  1255             el.on("change.select2", this.bind(function (e) {
       
  1256                 if (this.opts.element.data("select2-change-triggered") !== true) {
       
  1257                     this.initSelection();
       
  1258                 }
       
  1259             }));
       
  1260 
       
  1261             this._sync = this.bind(function () {
       
  1262 
       
  1263                 // sync enabled state
       
  1264                 var disabled = el.prop("disabled");
       
  1265                 if (disabled === undefined) disabled = false;
       
  1266                 this.enable(!disabled);
       
  1267 
       
  1268                 var readonly = el.prop("readonly");
       
  1269                 if (readonly === undefined) readonly = false;
       
  1270                 this.readonly(readonly);
       
  1271 
       
  1272                 if (this.container) {
       
  1273                     syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
       
  1274                     this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element));
       
  1275                 }
       
  1276 
       
  1277                 if (this.dropdown) {
       
  1278                     syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
       
  1279                     this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element));
       
  1280                 }
       
  1281 
       
  1282             });
       
  1283 
       
  1284             // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener)
       
  1285             if (el.length && el[0].attachEvent) {
       
  1286                 el.each(function() {
       
  1287                     this.attachEvent("onpropertychange", self._sync);
       
  1288                 });
       
  1289             }
       
  1290 
       
  1291             // safari, chrome, firefox, IE11
       
  1292             observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver;
       
  1293             if (observer !== undefined) {
       
  1294                 if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
       
  1295                 this.propertyObserver = new observer(function (mutations) {
       
  1296                     $.each(mutations, self._sync);
       
  1297                 });
       
  1298                 this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
       
  1299             }
       
  1300         },
       
  1301 
       
  1302         // abstract
       
  1303         triggerSelect: function(data) {
       
  1304             var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data });
       
  1305             this.opts.element.trigger(evt);
       
  1306             return !evt.isDefaultPrevented();
       
  1307         },
       
  1308 
       
  1309         /**
       
  1310          * Triggers the change event on the source element
       
  1311          */
       
  1312         // abstract
       
  1313         triggerChange: function (details) {
       
  1314 
       
  1315             details = details || {};
       
  1316             details= $.extend({}, details, { type: "change", val: this.val() });
       
  1317             // prevents recursive triggering
       
  1318             this.opts.element.data("select2-change-triggered", true);
       
  1319             this.opts.element.trigger(details);
       
  1320             this.opts.element.data("select2-change-triggered", false);
       
  1321 
       
  1322             // some validation frameworks ignore the change event and listen instead to keyup, click for selects
       
  1323             // so here we trigger the click event manually
       
  1324             this.opts.element.click();
       
  1325 
       
  1326             // ValidationEngine ignores the change event and listens instead to blur
       
  1327             // so here we trigger the blur event manually if so desired
       
  1328             if (this.opts.blurOnChange)
       
  1329                 this.opts.element.blur();
       
  1330         },
       
  1331 
       
  1332         //abstract
       
  1333         isInterfaceEnabled: function()
       
  1334         {
       
  1335             return this.enabledInterface === true;
       
  1336         },
       
  1337 
       
  1338         // abstract
       
  1339         enableInterface: function() {
       
  1340             var enabled = this._enabled && !this._readonly,
       
  1341                 disabled = !enabled;
       
  1342 
       
  1343             if (enabled === this.enabledInterface) return false;
       
  1344 
       
  1345             this.container.toggleClass("select2-container-disabled", disabled);
       
  1346             this.close();
       
  1347             this.enabledInterface = enabled;
       
  1348 
       
  1349             return true;
       
  1350         },
       
  1351 
       
  1352         // abstract
       
  1353         enable: function(enabled) {
       
  1354             if (enabled === undefined) enabled = true;
       
  1355             if (this._enabled === enabled) return;
       
  1356             this._enabled = enabled;
       
  1357 
       
  1358             this.opts.element.prop("disabled", !enabled);
       
  1359             this.enableInterface();
       
  1360         },
       
  1361 
       
  1362         // abstract
       
  1363         disable: function() {
       
  1364             this.enable(false);
       
  1365         },
       
  1366 
       
  1367         // abstract
       
  1368         readonly: function(enabled) {
       
  1369             if (enabled === undefined) enabled = false;
       
  1370             if (this._readonly === enabled) return;
       
  1371             this._readonly = enabled;
       
  1372 
       
  1373             this.opts.element.prop("readonly", enabled);
       
  1374             this.enableInterface();
       
  1375         },
       
  1376 
       
  1377         // abstract
       
  1378         opened: function () {
       
  1379             return (this.container) ? this.container.hasClass("select2-dropdown-open") : false;
       
  1380         },
       
  1381 
       
  1382         // abstract
       
  1383         positionDropdown: function() {
       
  1384             var $dropdown = this.dropdown,
       
  1385                 container = this.container,
       
  1386                 offset = container.offset(),
       
  1387                 height = container.outerHeight(false),
       
  1388                 width = container.outerWidth(false),
       
  1389                 dropHeight = $dropdown.outerHeight(false),
       
  1390                 $window = $(window),
       
  1391                 windowWidth = $window.width(),
       
  1392                 windowHeight = $window.height(),
       
  1393                 viewPortRight = $window.scrollLeft() + windowWidth,
       
  1394                 viewportBottom = $window.scrollTop() + windowHeight,
       
  1395                 dropTop = offset.top + height,
       
  1396                 dropLeft = offset.left,
       
  1397                 enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
       
  1398                 enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(),
       
  1399                 dropWidth = $dropdown.outerWidth(false),
       
  1400                 enoughRoomOnRight = function() {
       
  1401                     return dropLeft + dropWidth <= viewPortRight;
       
  1402                 },
       
  1403                 enoughRoomOnLeft = function() {
       
  1404                     return offset.left + viewPortRight + container.outerWidth(false)  > dropWidth;
       
  1405                 },
       
  1406                 aboveNow = $dropdown.hasClass("select2-drop-above"),
       
  1407                 bodyOffset,
       
  1408                 above,
       
  1409                 changeDirection,
       
  1410                 css,
       
  1411                 resultsListNode;
       
  1412 
       
  1413             // always prefer the current above/below alignment, unless there is not enough room
       
  1414             if (aboveNow) {
       
  1415                 above = true;
       
  1416                 if (!enoughRoomAbove && enoughRoomBelow) {
       
  1417                     changeDirection = true;
       
  1418                     above = false;
       
  1419                 }
       
  1420             } else {
       
  1421                 above = false;
       
  1422                 if (!enoughRoomBelow && enoughRoomAbove) {
       
  1423                     changeDirection = true;
       
  1424                     above = true;
       
  1425                 }
       
  1426             }
       
  1427 
       
  1428             //if we are changing direction we need to get positions when dropdown is hidden;
       
  1429             if (changeDirection) {
       
  1430                 $dropdown.hide();
       
  1431                 offset = this.container.offset();
       
  1432                 height = this.container.outerHeight(false);
       
  1433                 width = this.container.outerWidth(false);
       
  1434                 dropHeight = $dropdown.outerHeight(false);
       
  1435                 viewPortRight = $window.scrollLeft() + windowWidth;
       
  1436                 viewportBottom = $window.scrollTop() + windowHeight;
       
  1437                 dropTop = offset.top + height;
       
  1438                 dropLeft = offset.left;
       
  1439                 dropWidth = $dropdown.outerWidth(false);
       
  1440                 $dropdown.show();
       
  1441 
       
  1442                 // fix so the cursor does not move to the left within the search-textbox in IE
       
  1443                 this.focusSearch();
       
  1444             }
       
  1445 
       
  1446             if (this.opts.dropdownAutoWidth) {
       
  1447                 resultsListNode = $('.select2-results', $dropdown)[0];
       
  1448                 $dropdown.addClass('select2-drop-auto-width');
       
  1449                 $dropdown.css('width', '');
       
  1450                 // Add scrollbar width to dropdown if vertical scrollbar is present
       
  1451                 dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
       
  1452                 dropWidth > width ? width = dropWidth : dropWidth = width;
       
  1453                 dropHeight = $dropdown.outerHeight(false);
       
  1454             }
       
  1455             else {
       
  1456                 this.container.removeClass('select2-drop-auto-width');
       
  1457             }
       
  1458 
       
  1459             //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
       
  1460             //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove);
       
  1461 
       
  1462             // fix positioning when body has an offset and is not position: static
       
  1463             if (this.body.css('position') !== 'static') {
       
  1464                 bodyOffset = this.body.offset();
       
  1465                 dropTop -= bodyOffset.top;
       
  1466                 dropLeft -= bodyOffset.left;
       
  1467             }
       
  1468 
       
  1469             if (!enoughRoomOnRight() && enoughRoomOnLeft()) {
       
  1470                 dropLeft = offset.left + this.container.outerWidth(false) - dropWidth;
       
  1471             }
       
  1472 
       
  1473             css =  {
       
  1474                 left: dropLeft,
       
  1475                 width: width
       
  1476             };
       
  1477 
       
  1478             if (above) {
       
  1479                 this.container.addClass("select2-drop-above");
       
  1480                 $dropdown.addClass("select2-drop-above");
       
  1481                 dropHeight = $dropdown.outerHeight(false);
       
  1482                 css.top = offset.top - dropHeight;
       
  1483                 css.bottom = 'auto';
       
  1484             }
       
  1485             else {
       
  1486                 css.top = dropTop;
       
  1487                 css.bottom = 'auto';
       
  1488                 this.container.removeClass("select2-drop-above");
       
  1489                 $dropdown.removeClass("select2-drop-above");
       
  1490             }
       
  1491             css = $.extend(css, evaluate(this.opts.dropdownCss, this.opts.element));
       
  1492 
       
  1493             $dropdown.css(css);
       
  1494         },
       
  1495 
       
  1496         // abstract
       
  1497         shouldOpen: function() {
       
  1498             var event;
       
  1499 
       
  1500             if (this.opened()) return false;
       
  1501 
       
  1502             if (this._enabled === false || this._readonly === true) return false;
       
  1503 
       
  1504             event = $.Event("select2-opening");
       
  1505             this.opts.element.trigger(event);
       
  1506             return !event.isDefaultPrevented();
       
  1507         },
       
  1508 
       
  1509         // abstract
       
  1510         clearDropdownAlignmentPreference: function() {
       
  1511             // clear the classes used to figure out the preference of where the dropdown should be opened
       
  1512             this.container.removeClass("select2-drop-above");
       
  1513             this.dropdown.removeClass("select2-drop-above");
       
  1514         },
       
  1515 
       
  1516         /**
       
  1517          * Opens the dropdown
       
  1518          *
       
  1519          * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
       
  1520          * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
       
  1521          */
       
  1522         // abstract
       
  1523         open: function () {
       
  1524 
       
  1525             if (!this.shouldOpen()) return false;
       
  1526 
       
  1527             this.opening();
       
  1528 
       
  1529             // Only bind the document mousemove when the dropdown is visible
       
  1530             $document.on("mousemove.select2Event", function (e) {
       
  1531                 lastMousePosition.x = e.pageX;
       
  1532                 lastMousePosition.y = e.pageY;
       
  1533             });
       
  1534 
       
  1535             return true;
       
  1536         },
       
  1537 
       
  1538         /**
       
  1539          * Performs the opening of the dropdown
       
  1540          */
       
  1541         // abstract
       
  1542         opening: function() {
       
  1543             var cid = this.containerEventName,
       
  1544                 scroll = "scroll." + cid,
       
  1545                 resize = "resize."+cid,
       
  1546                 orient = "orientationchange."+cid,
       
  1547                 mask;
       
  1548 
       
  1549             this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
       
  1550 
       
  1551             this.clearDropdownAlignmentPreference();
       
  1552 
       
  1553             if(this.dropdown[0] !== this.body.children().last()[0]) {
       
  1554                 this.dropdown.detach().appendTo(this.body);
       
  1555             }
       
  1556 
       
  1557             // create the dropdown mask if doesn't already exist
       
  1558             mask = $("#select2-drop-mask");
       
  1559             if (mask.length === 0) {
       
  1560                 mask = $(document.createElement("div"));
       
  1561                 mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
       
  1562                 mask.hide();
       
  1563                 mask.appendTo(this.body);
       
  1564                 mask.on("mousedown touchstart click", function (e) {
       
  1565                     // Prevent IE from generating a click event on the body
       
  1566                     reinsertElement(mask);
       
  1567 
       
  1568                     var dropdown = $("#select2-drop"), self;
       
  1569                     if (dropdown.length > 0) {
       
  1570                         self=dropdown.data("select2");
       
  1571                         if (self.opts.selectOnBlur) {
       
  1572                             self.selectHighlighted({noFocus: true});
       
  1573                         }
       
  1574                         self.close();
       
  1575                         e.preventDefault();
       
  1576                         e.stopPropagation();
       
  1577                     }
       
  1578                 });
       
  1579             }
       
  1580 
       
  1581             // ensure the mask is always right before the dropdown
       
  1582             if (this.dropdown.prev()[0] !== mask[0]) {
       
  1583                 this.dropdown.before(mask);
       
  1584             }
       
  1585 
       
  1586             // move the global id to the correct dropdown
       
  1587             $("#select2-drop").removeAttr("id");
       
  1588             this.dropdown.attr("id", "select2-drop");
       
  1589 
       
  1590             // show the elements
       
  1591             mask.show();
       
  1592 
       
  1593             this.positionDropdown();
       
  1594             this.dropdown.show();
       
  1595             this.positionDropdown();
       
  1596 
       
  1597             this.dropdown.addClass("select2-drop-active");
       
  1598 
       
  1599             // attach listeners to events that can change the position of the container and thus require
       
  1600             // the position of the dropdown to be updated as well so it does not come unglued from the container
       
  1601             var that = this;
       
  1602             this.container.parents().add(window).each(function () {
       
  1603                 $(this).on(resize+" "+scroll+" "+orient, function (e) {
       
  1604                     if (that.opened()) that.positionDropdown();
       
  1605                 });
       
  1606             });
       
  1607 
       
  1608 
       
  1609         },
       
  1610 
       
  1611         // abstract
       
  1612         close: function () {
       
  1613             if (!this.opened()) return;
       
  1614 
       
  1615             var cid = this.containerEventName,
       
  1616                 scroll = "scroll." + cid,
       
  1617                 resize = "resize."+cid,
       
  1618                 orient = "orientationchange."+cid;
       
  1619 
       
  1620             // unbind event listeners
       
  1621             this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
       
  1622 
       
  1623             this.clearDropdownAlignmentPreference();
       
  1624 
       
  1625             $("#select2-drop-mask").hide();
       
  1626             this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
       
  1627             this.dropdown.hide();
       
  1628             this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
       
  1629             this.results.empty();
       
  1630 
       
  1631             // Now that the dropdown is closed, unbind the global document mousemove event
       
  1632             $document.off("mousemove.select2Event");
       
  1633 
       
  1634             this.clearSearch();
       
  1635             this.search.removeClass("select2-active");
       
  1636 
       
  1637             // Remove the aria active descendant for highlighted element
       
  1638             this.search.removeAttr("aria-activedescendant");
       
  1639             this.opts.element.trigger($.Event("select2-close"));
       
  1640         },
       
  1641 
       
  1642         /**
       
  1643          * Opens control, sets input value, and updates results.
       
  1644          */
       
  1645         // abstract
       
  1646         externalSearch: function (term) {
       
  1647             this.open();
       
  1648             this.search.val(term);
       
  1649             this.updateResults(false);
       
  1650         },
       
  1651 
       
  1652         // abstract
       
  1653         clearSearch: function () {
       
  1654 
       
  1655         },
       
  1656 
       
  1657         /**
       
  1658          * @return {Boolean} Whether or not search value was changed.
       
  1659          * @private
       
  1660          */
       
  1661         prefillNextSearchTerm: function () {
       
  1662             // initializes search's value with nextSearchTerm (if defined by user)
       
  1663             // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
       
  1664             if(this.search.val() !== "") {
       
  1665                 return false;
       
  1666             }
       
  1667 
       
  1668             var nextSearchTerm = this.opts.nextSearchTerm(this.data(), this.lastSearchTerm);
       
  1669             if(nextSearchTerm !== undefined){
       
  1670                 this.search.val(nextSearchTerm);
       
  1671                 this.search.select();
       
  1672                 return true;
       
  1673             }
       
  1674 
       
  1675             return false;
       
  1676         },
       
  1677 
       
  1678         //abstract
       
  1679         getMaximumSelectionSize: function() {
       
  1680             return evaluate(this.opts.maximumSelectionSize, this.opts.element);
       
  1681         },
       
  1682 
       
  1683         // abstract
       
  1684         ensureHighlightVisible: function () {
       
  1685             var results = this.results, children, index, child, hb, rb, y, more, topOffset;
       
  1686 
       
  1687             index = this.highlight();
       
  1688 
       
  1689             if (index < 0) return;
       
  1690 
       
  1691             if (index == 0) {
       
  1692 
       
  1693                 // if the first element is highlighted scroll all the way to the top,
       
  1694                 // that way any unselectable headers above it will also be scrolled
       
  1695                 // into view
       
  1696 
       
  1697                 results.scrollTop(0);
       
  1698                 return;
       
  1699             }
       
  1700 
       
  1701             children = this.findHighlightableChoices().find('.select2-result-label');
       
  1702 
       
  1703             child = $(children[index]);
       
  1704 
       
  1705             topOffset = (child.offset() || {}).top || 0;
       
  1706 
       
  1707             hb = topOffset + child.outerHeight(true);
       
  1708 
       
  1709             // if this is the last child lets also make sure select2-more-results is visible
       
  1710             if (index === children.length - 1) {
       
  1711                 more = results.find("li.select2-more-results");
       
  1712                 if (more.length > 0) {
       
  1713                     hb = more.offset().top + more.outerHeight(true);
       
  1714                 }
       
  1715             }
       
  1716 
       
  1717             rb = results.offset().top + results.outerHeight(false);
       
  1718             if (hb > rb) {
       
  1719                 results.scrollTop(results.scrollTop() + (hb - rb));
       
  1720             }
       
  1721             y = topOffset - results.offset().top;
       
  1722 
       
  1723             // make sure the top of the element is visible
       
  1724             if (y < 0 && child.css('display') != 'none' ) {
       
  1725                 results.scrollTop(results.scrollTop() + y); // y is negative
       
  1726             }
       
  1727         },
       
  1728 
       
  1729         // abstract
       
  1730         findHighlightableChoices: function() {
       
  1731             return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)");
       
  1732         },
       
  1733 
       
  1734         // abstract
       
  1735         moveHighlight: function (delta) {
       
  1736             var choices = this.findHighlightableChoices(),
       
  1737                 index = this.highlight();
       
  1738 
       
  1739             while (index > -1 && index < choices.length) {
       
  1740                 index += delta;
       
  1741                 var choice = $(choices[index]);
       
  1742                 if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
       
  1743                     this.highlight(index);
       
  1744                     break;
       
  1745                 }
       
  1746             }
       
  1747         },
       
  1748 
       
  1749         // abstract
       
  1750         highlight: function (index) {
       
  1751             var choices = this.findHighlightableChoices(),
       
  1752                 choice,
       
  1753                 data;
       
  1754 
       
  1755             if (arguments.length === 0) {
       
  1756                 return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
       
  1757             }
       
  1758 
       
  1759             if (index >= choices.length) index = choices.length - 1;
       
  1760             if (index < 0) index = 0;
       
  1761 
       
  1762             this.removeHighlight();
       
  1763 
       
  1764             choice = $(choices[index]);
       
  1765             choice.addClass("select2-highlighted");
       
  1766 
       
  1767             // ensure assistive technology can determine the active choice
       
  1768             this.search.attr("aria-activedescendant", choice.find(".select2-result-label").attr("id"));
       
  1769 
       
  1770             this.ensureHighlightVisible();
       
  1771 
       
  1772             this.liveRegion.text(choice.text());
       
  1773 
       
  1774             data = choice.data("select2-data");
       
  1775             if (data) {
       
  1776                 this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
       
  1777             }
       
  1778         },
       
  1779 
       
  1780         removeHighlight: function() {
       
  1781             this.results.find(".select2-highlighted").removeClass("select2-highlighted");
       
  1782         },
       
  1783 
       
  1784         touchMoved: function() {
       
  1785             this._touchMoved = true;
       
  1786         },
       
  1787 
       
  1788         clearTouchMoved: function() {
       
  1789           this._touchMoved = false;
       
  1790         },
       
  1791 
       
  1792         // abstract
       
  1793         countSelectableResults: function() {
       
  1794             return this.findHighlightableChoices().length;
       
  1795         },
       
  1796 
       
  1797         // abstract
       
  1798         highlightUnderEvent: function (event) {
       
  1799             var el = $(event.target).closest(".select2-result-selectable");
       
  1800             if (el.length > 0 && !el.is(".select2-highlighted")) {
       
  1801                 var choices = this.findHighlightableChoices();
       
  1802                 this.highlight(choices.index(el));
       
  1803             } else if (el.length == 0) {
       
  1804                 // if we are over an unselectable item remove all highlights
       
  1805                 this.removeHighlight();
       
  1806             }
       
  1807         },
       
  1808 
       
  1809         // abstract
       
  1810         loadMoreIfNeeded: function () {
       
  1811             var results = this.results,
       
  1812                 more = results.find("li.select2-more-results"),
       
  1813                 below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
       
  1814                 page = this.resultsPage + 1,
       
  1815                 self=this,
       
  1816                 term=this.search.val(),
       
  1817                 context=this.context;
       
  1818 
       
  1819             if (more.length === 0) return;
       
  1820             below = more.offset().top - results.offset().top - results.height();
       
  1821 
       
  1822             if (below <= this.opts.loadMorePadding) {
       
  1823                 more.addClass("select2-active");
       
  1824                 this.opts.query({
       
  1825                         element: this.opts.element,
       
  1826                         term: term,
       
  1827                         page: page,
       
  1828                         context: context,
       
  1829                         matcher: this.opts.matcher,
       
  1830                         callback: this.bind(function (data) {
       
  1831 
       
  1832                     // ignore a response if the select2 has been closed before it was received
       
  1833                     if (!self.opened()) return;
       
  1834 
       
  1835 
       
  1836                     self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
       
  1837                     self.postprocessResults(data, false, false);
       
  1838 
       
  1839                     if (data.more===true) {
       
  1840                         more.detach().appendTo(results).html(self.opts.escapeMarkup(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)));
       
  1841                         window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
       
  1842                     } else {
       
  1843                         more.remove();
       
  1844                     }
       
  1845                     self.positionDropdown();
       
  1846                     self.resultsPage = page;
       
  1847                     self.context = data.context;
       
  1848                     this.opts.element.trigger({ type: "select2-loaded", items: data });
       
  1849                 })});
       
  1850             }
       
  1851         },
       
  1852 
       
  1853         /**
       
  1854          * Default tokenizer function which does nothing
       
  1855          */
       
  1856         tokenize: function() {
       
  1857 
       
  1858         },
       
  1859 
       
  1860         /**
       
  1861          * @param initial whether or not this is the call to this method right after the dropdown has been opened
       
  1862          */
       
  1863         // abstract
       
  1864         updateResults: function (initial) {
       
  1865             var search = this.search,
       
  1866                 results = this.results,
       
  1867                 opts = this.opts,
       
  1868                 data,
       
  1869                 self = this,
       
  1870                 input,
       
  1871                 term = search.val(),
       
  1872                 lastTerm = $.data(this.container, "select2-last-term"),
       
  1873                 // sequence number used to drop out-of-order responses
       
  1874                 queryNumber;
       
  1875 
       
  1876             // prevent duplicate queries against the same term
       
  1877             if (initial !== true && lastTerm && equal(term, lastTerm)) return;
       
  1878 
       
  1879             $.data(this.container, "select2-last-term", term);
       
  1880 
       
  1881             // if the search is currently hidden we do not alter the results
       
  1882             if (initial !== true && (this.showSearchInput === false || !this.opened())) {
       
  1883                 return;
       
  1884             }
       
  1885 
       
  1886             function postRender() {
       
  1887                 search.removeClass("select2-active");
       
  1888                 self.positionDropdown();
       
  1889                 if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) {
       
  1890                     self.liveRegion.text(results.text());
       
  1891                 }
       
  1892                 else {
       
  1893                     self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable:not(".select2-selected")').length));
       
  1894                 }
       
  1895             }
       
  1896 
       
  1897             function render(html) {
       
  1898                 results.html(html);
       
  1899                 postRender();
       
  1900             }
       
  1901 
       
  1902             queryNumber = ++this.queryCount;
       
  1903 
       
  1904             var maxSelSize = this.getMaximumSelectionSize();
       
  1905             if (maxSelSize >=1) {
       
  1906                 data = this.data();
       
  1907                 if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
       
  1908                     render("<li class='select2-selection-limit'>" + evaluate(opts.formatSelectionTooBig, opts.element, maxSelSize) + "</li>");
       
  1909                     return;
       
  1910                 }
       
  1911             }
       
  1912 
       
  1913             if (search.val().length < opts.minimumInputLength) {
       
  1914                 if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
       
  1915                     render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooShort, opts.element, search.val(), opts.minimumInputLength) + "</li>");
       
  1916                 } else {
       
  1917                     render("");
       
  1918                 }
       
  1919                 if (initial && this.showSearch) this.showSearch(true);
       
  1920                 return;
       
  1921             }
       
  1922 
       
  1923             if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
       
  1924                 if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
       
  1925                     render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooLong, opts.element, search.val(), opts.maximumInputLength) + "</li>");
       
  1926                 } else {
       
  1927                     render("");
       
  1928                 }
       
  1929                 return;
       
  1930             }
       
  1931 
       
  1932             if (opts.formatSearching && this.findHighlightableChoices().length === 0) {
       
  1933                 render("<li class='select2-searching'>" + evaluate(opts.formatSearching, opts.element) + "</li>");
       
  1934             }
       
  1935 
       
  1936             search.addClass("select2-active");
       
  1937 
       
  1938             this.removeHighlight();
       
  1939 
       
  1940             // give the tokenizer a chance to pre-process the input
       
  1941             input = this.tokenize();
       
  1942             if (input != undefined && input != null) {
       
  1943                 search.val(input);
       
  1944             }
       
  1945 
       
  1946             this.resultsPage = 1;
       
  1947 
       
  1948             opts.query({
       
  1949                 element: opts.element,
       
  1950                     term: search.val(),
       
  1951                     page: this.resultsPage,
       
  1952                     context: null,
       
  1953                     matcher: opts.matcher,
       
  1954                     callback: this.bind(function (data) {
       
  1955                 var def; // default choice
       
  1956 
       
  1957                 // ignore old responses
       
  1958                 if (queryNumber != this.queryCount) {
       
  1959                   return;
       
  1960                 }
       
  1961 
       
  1962                 // ignore a response if the select2 has been closed before it was received
       
  1963                 if (!this.opened()) {
       
  1964                     this.search.removeClass("select2-active");
       
  1965                     return;
       
  1966                 }
       
  1967 
       
  1968                 // handle ajax error
       
  1969                 if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) {
       
  1970                     render("<li class='select2-ajax-error'>" + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + "</li>");
       
  1971                     return;
       
  1972                 }
       
  1973 
       
  1974                 // save context, if any
       
  1975                 this.context = (data.context===undefined) ? null : data.context;
       
  1976                 // create a default choice and prepend it to the list
       
  1977                 if (this.opts.createSearchChoice && search.val() !== "") {
       
  1978                     def = this.opts.createSearchChoice.call(self, search.val(), data.results);
       
  1979                     if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
       
  1980                         if ($(data.results).filter(
       
  1981                             function () {
       
  1982                                 return equal(self.id(this), self.id(def));
       
  1983                             }).length === 0) {
       
  1984                             this.opts.createSearchChoicePosition(data.results, def);
       
  1985                         }
       
  1986                     }
       
  1987                 }
       
  1988 
       
  1989                 if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
       
  1990                     render("<li class='select2-no-results'>" + evaluate(opts.formatNoMatches, opts.element, search.val()) + "</li>");
       
  1991                     if(this.showSearch){
       
  1992                         this.showSearch(search.val());
       
  1993                     }
       
  1994                     return;
       
  1995                 }
       
  1996 
       
  1997                 results.empty();
       
  1998                 self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
       
  1999 
       
  2000                 if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
       
  2001                     results.append("<li class='select2-more-results'>" + opts.escapeMarkup(evaluate(opts.formatLoadMore, opts.element, this.resultsPage)) + "</li>");
       
  2002                     window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
       
  2003                 }
       
  2004 
       
  2005                 this.postprocessResults(data, initial);
       
  2006 
       
  2007                 postRender();
       
  2008 
       
  2009                 this.opts.element.trigger({ type: "select2-loaded", items: data });
       
  2010             })});
       
  2011         },
       
  2012 
       
  2013         // abstract
       
  2014         cancel: function () {
       
  2015             this.close();
       
  2016         },
       
  2017 
       
  2018         // abstract
       
  2019         blur: function () {
       
  2020             // if selectOnBlur == true, select the currently highlighted option
       
  2021             if (this.opts.selectOnBlur)
       
  2022                 this.selectHighlighted({noFocus: true});
       
  2023 
       
  2024             this.close();
       
  2025             this.container.removeClass("select2-container-active");
       
  2026             // synonymous to .is(':focus'), which is available in jquery >= 1.6
       
  2027             if (this.search[0] === document.activeElement) { this.search.blur(); }
       
  2028             this.clearSearch();
       
  2029             this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
       
  2030         },
       
  2031 
       
  2032         // abstract
       
  2033         focusSearch: function () {
       
  2034             focus(this.search);
       
  2035         },
       
  2036 
       
  2037         // abstract
       
  2038         selectHighlighted: function (options) {
       
  2039             if (this._touchMoved) {
       
  2040               this.clearTouchMoved();
       
  2041               return;
       
  2042             }
       
  2043             var index=this.highlight(),
       
  2044                 highlighted=this.results.find(".select2-highlighted"),
       
  2045                 data = highlighted.closest('.select2-result').data("select2-data");
       
  2046 
       
  2047             if (data) {
       
  2048                 this.highlight(index);
       
  2049                 this.onSelect(data, options);
       
  2050             } else if (options && options.noFocus) {
       
  2051                 this.close();
       
  2052             }
       
  2053         },
       
  2054 
       
  2055         // abstract
       
  2056         getPlaceholder: function () {
       
  2057             var placeholderOption;
       
  2058             return this.opts.element.attr("placeholder") ||
       
  2059                 this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
       
  2060                 this.opts.element.data("placeholder") ||
       
  2061                 this.opts.placeholder ||
       
  2062                 ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined);
       
  2063         },
       
  2064 
       
  2065         // abstract
       
  2066         getPlaceholderOption: function() {
       
  2067             if (this.select) {
       
  2068                 var firstOption = this.select.children('option').first();
       
  2069                 if (this.opts.placeholderOption !== undefined ) {
       
  2070                     //Determine the placeholder option based on the specified placeholderOption setting
       
  2071                     return (this.opts.placeholderOption === "first" && firstOption) ||
       
  2072                            (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select));
       
  2073                 } else if ($.trim(firstOption.text()) === "" && firstOption.val() === "") {
       
  2074                     //No explicit placeholder option specified, use the first if it's blank
       
  2075                     return firstOption;
       
  2076                 }
       
  2077             }
       
  2078         },
       
  2079 
       
  2080         /**
       
  2081          * Get the desired width for the container element.  This is
       
  2082          * derived first from option `width` passed to select2, then
       
  2083          * the inline 'style' on the original element, and finally
       
  2084          * falls back to the jQuery calculated element width.
       
  2085          */
       
  2086         // abstract
       
  2087         initContainerWidth: function () {
       
  2088             function resolveContainerWidth() {
       
  2089                 var style, attrs, matches, i, l, attr;
       
  2090 
       
  2091                 if (this.opts.width === "off") {
       
  2092                     return null;
       
  2093                 } else if (this.opts.width === "element"){
       
  2094                     return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
       
  2095                 } else if (this.opts.width === "copy" || this.opts.width === "resolve") {
       
  2096                     // check if there is inline style on the element that contains width
       
  2097                     style = this.opts.element.attr('style');
       
  2098                     if (typeof(style) === "string") {
       
  2099                         attrs = style.split(';');
       
  2100                         for (i = 0, l = attrs.length; i < l; i = i + 1) {
       
  2101                             attr = attrs[i].replace(/\s/g, '');
       
  2102                             matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
       
  2103                             if (matches !== null && matches.length >= 1)
       
  2104                                 return matches[1];
       
  2105                         }
       
  2106                     }
       
  2107 
       
  2108                     if (this.opts.width === "resolve") {
       
  2109                         // next check if css('width') can resolve a width that is percent based, this is sometimes possible
       
  2110                         // when attached to input type=hidden or elements hidden via css
       
  2111                         style = this.opts.element.css('width');
       
  2112                         if (style.indexOf("%") > 0) return style;
       
  2113 
       
  2114                         // finally, fallback on the calculated width of the element
       
  2115                         return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
       
  2116                     }
       
  2117 
       
  2118                     return null;
       
  2119                 } else if ($.isFunction(this.opts.width)) {
       
  2120                     return this.opts.width();
       
  2121                 } else {
       
  2122                     return this.opts.width;
       
  2123                }
       
  2124             };
       
  2125 
       
  2126             var width = resolveContainerWidth.call(this);
       
  2127             if (width !== null) {
       
  2128                 this.container.css("width", width);
       
  2129             }
       
  2130         }
       
  2131     });
       
  2132 
       
  2133     SingleSelect2 = clazz(AbstractSelect2, {
       
  2134 
       
  2135         // single
       
  2136 
       
  2137         createContainer: function () {
       
  2138             var container = $(document.createElement("div")).attr({
       
  2139                 "class": "select2-container"
       
  2140             }).html([
       
  2141                 "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>",
       
  2142                 "   <span class='select2-chosen'>&#160;</span><abbr class='select2-search-choice-close'></abbr>",
       
  2143                 "   <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>",
       
  2144                 "</a>",
       
  2145                 "<label for='' class='select2-offscreen'></label>",
       
  2146                 "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />",
       
  2147                 "<div class='select2-drop select2-display-none'>",
       
  2148                 "   <div class='select2-search'>",
       
  2149                 "       <label for='' class='select2-offscreen'></label>",
       
  2150                 "       <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'",
       
  2151                 "       aria-autocomplete='list' />",
       
  2152                 "   </div>",
       
  2153                 "   <ul class='select2-results' role='listbox'>",
       
  2154                 "   </ul>",
       
  2155                 "</div>"].join(""));
       
  2156             return container;
       
  2157         },
       
  2158 
       
  2159         // single
       
  2160         enableInterface: function() {
       
  2161             if (this.parent.enableInterface.apply(this, arguments)) {
       
  2162                 this.focusser.prop("disabled", !this.isInterfaceEnabled());
       
  2163             }
       
  2164         },
       
  2165 
       
  2166         // single
       
  2167         opening: function () {
       
  2168             var el, range, len;
       
  2169 
       
  2170             if (this.opts.minimumResultsForSearch >= 0) {
       
  2171                 this.showSearch(true);
       
  2172             }
       
  2173 
       
  2174             this.parent.opening.apply(this, arguments);
       
  2175 
       
  2176             if (this.showSearchInput !== false) {
       
  2177                 // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
       
  2178                 // all other browsers handle this just fine
       
  2179 
       
  2180                 this.search.val(this.focusser.val());
       
  2181             }
       
  2182             if (this.opts.shouldFocusInput(this)) {
       
  2183                 this.search.focus();
       
  2184                 // move the cursor to the end after focussing, otherwise it will be at the beginning and
       
  2185                 // new text will appear *before* focusser.val()
       
  2186                 el = this.search.get(0);
       
  2187                 if (el.createTextRange) {
       
  2188                     range = el.createTextRange();
       
  2189                     range.collapse(false);
       
  2190                     range.select();
       
  2191                 } else if (el.setSelectionRange) {
       
  2192                     len = this.search.val().length;
       
  2193                     el.setSelectionRange(len, len);
       
  2194                 }
       
  2195             }
       
  2196 
       
  2197             this.prefillNextSearchTerm();
       
  2198 
       
  2199             this.focusser.prop("disabled", true).val("");
       
  2200             this.updateResults(true);
       
  2201             this.opts.element.trigger($.Event("select2-open"));
       
  2202         },
       
  2203 
       
  2204         // single
       
  2205         close: function () {
       
  2206             if (!this.opened()) return;
       
  2207             this.parent.close.apply(this, arguments);
       
  2208 
       
  2209             this.focusser.prop("disabled", false);
       
  2210 
       
  2211             if (this.opts.shouldFocusInput(this)) {
       
  2212                 this.focusser.focus();
       
  2213             }
       
  2214         },
       
  2215 
       
  2216         // single
       
  2217         focus: function () {
       
  2218             if (this.opened()) {
       
  2219                 this.close();
       
  2220             } else {
       
  2221                 this.focusser.prop("disabled", false);
       
  2222                 if (this.opts.shouldFocusInput(this)) {
       
  2223                     this.focusser.focus();
       
  2224                 }
       
  2225             }
       
  2226         },
       
  2227 
       
  2228         // single
       
  2229         isFocused: function () {
       
  2230             return this.container.hasClass("select2-container-active");
       
  2231         },
       
  2232 
       
  2233         // single
       
  2234         cancel: function () {
       
  2235             this.parent.cancel.apply(this, arguments);
       
  2236             this.focusser.prop("disabled", false);
       
  2237 
       
  2238             if (this.opts.shouldFocusInput(this)) {
       
  2239                 this.focusser.focus();
       
  2240             }
       
  2241         },
       
  2242 
       
  2243         // single
       
  2244         destroy: function() {
       
  2245             $("label[for='" + this.focusser.attr('id') + "']")
       
  2246                 .attr('for', this.opts.element.attr("id"));
       
  2247             this.parent.destroy.apply(this, arguments);
       
  2248 
       
  2249             cleanupJQueryElements.call(this,
       
  2250                 "selection",
       
  2251                 "focusser"
       
  2252             );
       
  2253         },
       
  2254 
       
  2255         // single
       
  2256         initContainer: function () {
       
  2257 
       
  2258             var selection,
       
  2259                 container = this.container,
       
  2260                 dropdown = this.dropdown,
       
  2261                 idSuffix = nextUid(),
       
  2262                 elementLabel;
       
  2263 
       
  2264             if (this.opts.minimumResultsForSearch < 0) {
       
  2265                 this.showSearch(false);
       
  2266             } else {
       
  2267                 this.showSearch(true);
       
  2268             }
       
  2269 
       
  2270             this.selection = selection = container.find(".select2-choice");
       
  2271 
       
  2272             this.focusser = container.find(".select2-focusser");
       
  2273 
       
  2274             // add aria associations
       
  2275             selection.find(".select2-chosen").attr("id", "select2-chosen-"+idSuffix);
       
  2276             this.focusser.attr("aria-labelledby", "select2-chosen-"+idSuffix);
       
  2277             this.results.attr("id", "select2-results-"+idSuffix);
       
  2278             this.search.attr("aria-owns", "select2-results-"+idSuffix);
       
  2279 
       
  2280             // rewrite labels from original element to focusser
       
  2281             this.focusser.attr("id", "s2id_autogen"+idSuffix);
       
  2282 
       
  2283             elementLabel = $("label[for='" + this.opts.element.attr("id") + "']");
       
  2284             this.opts.element.on('focus.select2', this.bind(function () { this.focus(); }));
       
  2285 
       
  2286             this.focusser.prev()
       
  2287                 .text(elementLabel.text())
       
  2288                 .attr('for', this.focusser.attr('id'));
       
  2289 
       
  2290             // Ensure the original element retains an accessible name
       
  2291             var originalTitle = this.opts.element.attr("title");
       
  2292             this.opts.element.attr("title", (originalTitle || elementLabel.text()));
       
  2293 
       
  2294             this.focusser.attr("tabindex", this.elementTabIndex);
       
  2295 
       
  2296             // write label for search field using the label from the focusser element
       
  2297             this.search.attr("id", this.focusser.attr('id') + '_search');
       
  2298 
       
  2299             this.search.prev()
       
  2300                 .text($("label[for='" + this.focusser.attr('id') + "']").text())
       
  2301                 .attr('for', this.search.attr('id'));
       
  2302 
       
  2303             this.search.on("keydown", this.bind(function (e) {
       
  2304                 if (!this.isInterfaceEnabled()) return;
       
  2305 
       
  2306                 // filter 229 keyCodes (input method editor is processing key input)
       
  2307                 if (229 == e.keyCode) return;
       
  2308 
       
  2309                 if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
       
  2310                     // prevent the page from scrolling
       
  2311                     killEvent(e);
       
  2312                     return;
       
  2313                 }
       
  2314 
       
  2315                 switch (e.which) {
       
  2316                     case KEY.UP:
       
  2317                     case KEY.DOWN:
       
  2318                         this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
       
  2319                         killEvent(e);
       
  2320                         return;
       
  2321                     case KEY.ENTER:
       
  2322                         this.selectHighlighted();
       
  2323                         killEvent(e);
       
  2324                         return;
       
  2325                     case KEY.TAB:
       
  2326                         this.selectHighlighted({noFocus: true});
       
  2327                         return;
       
  2328                     case KEY.ESC:
       
  2329                         this.cancel(e);
       
  2330                         killEvent(e);
       
  2331                         return;
       
  2332                 }
       
  2333             }));
       
  2334 
       
  2335             this.search.on("blur", this.bind(function(e) {
       
  2336                 // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
       
  2337                 // without this the search field loses focus which is annoying
       
  2338                 if (document.activeElement === this.body.get(0)) {
       
  2339                     window.setTimeout(this.bind(function() {
       
  2340                         if (this.opened() && this.results && this.results.length > 1) {
       
  2341                             this.search.focus();
       
  2342                         }
       
  2343                     }), 0);
       
  2344                 }
       
  2345             }));
       
  2346 
       
  2347             this.focusser.on("keydown", this.bind(function (e) {
       
  2348                 if (!this.isInterfaceEnabled()) return;
       
  2349 
       
  2350                 if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
       
  2351                     return;
       
  2352                 }
       
  2353 
       
  2354                 if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
       
  2355                     killEvent(e);
       
  2356                     return;
       
  2357                 }
       
  2358 
       
  2359                 if (e.which == KEY.DOWN || e.which == KEY.UP
       
  2360                     || (e.which == KEY.ENTER && this.opts.openOnEnter)) {
       
  2361 
       
  2362                     if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
       
  2363 
       
  2364                     this.open();
       
  2365                     killEvent(e);
       
  2366                     return;
       
  2367                 }
       
  2368 
       
  2369                 if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) {
       
  2370                     if (this.opts.allowClear) {
       
  2371                         this.clear();
       
  2372                     }
       
  2373                     killEvent(e);
       
  2374                     return;
       
  2375                 }
       
  2376             }));
       
  2377 
       
  2378 
       
  2379             installKeyUpChangeEvent(this.focusser);
       
  2380             this.focusser.on("keyup-change input", this.bind(function(e) {
       
  2381                 if (this.opts.minimumResultsForSearch >= 0) {
       
  2382                     e.stopPropagation();
       
  2383                     if (this.opened()) return;
       
  2384                     this.open();
       
  2385                 }
       
  2386             }));
       
  2387 
       
  2388             selection.on("mousedown touchstart", "abbr", this.bind(function (e) {
       
  2389                 if (!this.isInterfaceEnabled()) {
       
  2390                     return;
       
  2391                 }
       
  2392 
       
  2393                 this.clear();
       
  2394                 killEventImmediately(e);
       
  2395                 this.close();
       
  2396 
       
  2397                 if (this.selection) {
       
  2398                     this.selection.focus();
       
  2399                 }
       
  2400             }));
       
  2401 
       
  2402             selection.on("mousedown touchstart", this.bind(function (e) {
       
  2403                 // Prevent IE from generating a click event on the body
       
  2404                 reinsertElement(selection);
       
  2405 
       
  2406                 if (!this.container.hasClass("select2-container-active")) {
       
  2407                     this.opts.element.trigger($.Event("select2-focus"));
       
  2408                 }
       
  2409 
       
  2410                 if (this.opened()) {
       
  2411                     this.close();
       
  2412                 } else if (this.isInterfaceEnabled()) {
       
  2413                     this.open();
       
  2414                 }
       
  2415 
       
  2416                 killEvent(e);
       
  2417             }));
       
  2418 
       
  2419             dropdown.on("mousedown touchstart", this.bind(function() {
       
  2420                 if (this.opts.shouldFocusInput(this)) {
       
  2421                     this.search.focus();
       
  2422                 }
       
  2423             }));
       
  2424 
       
  2425             selection.on("focus", this.bind(function(e) {
       
  2426                 killEvent(e);
       
  2427             }));
       
  2428 
       
  2429             this.focusser.on("focus", this.bind(function(){
       
  2430                 if (!this.container.hasClass("select2-container-active")) {
       
  2431                     this.opts.element.trigger($.Event("select2-focus"));
       
  2432                 }
       
  2433                 this.container.addClass("select2-container-active");
       
  2434             })).on("blur", this.bind(function() {
       
  2435                 if (!this.opened()) {
       
  2436                     this.container.removeClass("select2-container-active");
       
  2437                     this.opts.element.trigger($.Event("select2-blur"));
       
  2438                 }
       
  2439             }));
       
  2440             this.search.on("focus", this.bind(function(){
       
  2441                 if (!this.container.hasClass("select2-container-active")) {
       
  2442                     this.opts.element.trigger($.Event("select2-focus"));
       
  2443                 }
       
  2444                 this.container.addClass("select2-container-active");
       
  2445             }));
       
  2446 
       
  2447             this.initContainerWidth();
       
  2448             this.opts.element.hide();
       
  2449             this.setPlaceholder();
       
  2450 
       
  2451         },
       
  2452 
       
  2453         // single
       
  2454         clear: function(triggerChange) {
       
  2455             var data=this.selection.data("select2-data");
       
  2456             if (data) { // guard against queued quick consecutive clicks
       
  2457                 var evt = $.Event("select2-clearing");
       
  2458                 this.opts.element.trigger(evt);
       
  2459                 if (evt.isDefaultPrevented()) {
       
  2460                     return;
       
  2461                 }
       
  2462                 var placeholderOption = this.getPlaceholderOption();
       
  2463                 this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
       
  2464                 this.selection.find(".select2-chosen").empty();
       
  2465                 this.selection.removeData("select2-data");
       
  2466                 this.setPlaceholder();
       
  2467 
       
  2468                 if (triggerChange !== false){
       
  2469                     this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
       
  2470                     this.triggerChange({removed:data});
       
  2471                 }
       
  2472             }
       
  2473         },
       
  2474 
       
  2475         /**
       
  2476          * Sets selection based on source element's value
       
  2477          */
       
  2478         // single
       
  2479         initSelection: function () {
       
  2480             var selected;
       
  2481             if (this.isPlaceholderOptionSelected()) {
       
  2482                 this.updateSelection(null);
       
  2483                 this.close();
       
  2484                 this.setPlaceholder();
       
  2485             } else {
       
  2486                 var self = this;
       
  2487                 this.opts.initSelection.call(null, this.opts.element, function(selected){
       
  2488                     if (selected !== undefined && selected !== null) {
       
  2489                         self.updateSelection(selected);
       
  2490                         self.close();
       
  2491                         self.setPlaceholder();
       
  2492                         self.lastSearchTerm = self.search.val();
       
  2493                     }
       
  2494                 });
       
  2495             }
       
  2496         },
       
  2497 
       
  2498         isPlaceholderOptionSelected: function() {
       
  2499             var placeholderOption;
       
  2500             if (this.getPlaceholder() === undefined) return false; // no placeholder specified so no option should be considered
       
  2501             return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected"))
       
  2502                 || (this.opts.element.val() === "")
       
  2503                 || (this.opts.element.val() === undefined)
       
  2504                 || (this.opts.element.val() === null);
       
  2505         },
       
  2506 
       
  2507         // single
       
  2508         prepareOpts: function () {
       
  2509             var opts = this.parent.prepareOpts.apply(this, arguments),
       
  2510                 self=this;
       
  2511 
       
  2512             if (opts.element.get(0).tagName.toLowerCase() === "select") {
       
  2513                 // install the selection initializer
       
  2514                 opts.initSelection = function (element, callback) {
       
  2515                     var selected = element.find("option").filter(function() { return this.selected && !this.disabled });
       
  2516                     // a single select box always has a value, no need to null check 'selected'
       
  2517                     callback(self.optionToData(selected));
       
  2518                 };
       
  2519             } else if ("data" in opts) {
       
  2520                 // install default initSelection when applied to hidden input and data is local
       
  2521                 opts.initSelection = opts.initSelection || function (element, callback) {
       
  2522                     var id = element.val();
       
  2523                     //search in data by id, storing the actual matching item
       
  2524                     var match = null;
       
  2525                     opts.query({
       
  2526                         matcher: function(term, text, el){
       
  2527                             var is_match = equal(id, opts.id(el));
       
  2528                             if (is_match) {
       
  2529                                 match = el;
       
  2530                             }
       
  2531                             return is_match;
       
  2532                         },
       
  2533                         callback: !$.isFunction(callback) ? $.noop : function() {
       
  2534                             callback(match);
       
  2535                         }
       
  2536                     });
       
  2537                 };
       
  2538             }
       
  2539 
       
  2540             return opts;
       
  2541         },
       
  2542 
       
  2543         // single
       
  2544         getPlaceholder: function() {
       
  2545             // if a placeholder is specified on a single select without a valid placeholder option ignore it
       
  2546             if (this.select) {
       
  2547                 if (this.getPlaceholderOption() === undefined) {
       
  2548                     return undefined;
       
  2549                 }
       
  2550             }
       
  2551 
       
  2552             return this.parent.getPlaceholder.apply(this, arguments);
       
  2553         },
       
  2554 
       
  2555         // single
       
  2556         setPlaceholder: function () {
       
  2557             var placeholder = this.getPlaceholder();
       
  2558 
       
  2559             if (this.isPlaceholderOptionSelected() && placeholder !== undefined) {
       
  2560 
       
  2561                 // check for a placeholder option if attached to a select
       
  2562                 if (this.select && this.getPlaceholderOption() === undefined) return;
       
  2563 
       
  2564                 this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder));
       
  2565 
       
  2566                 this.selection.addClass("select2-default");
       
  2567 
       
  2568                 this.container.removeClass("select2-allowclear");
       
  2569             }
       
  2570         },
       
  2571 
       
  2572         // single
       
  2573         postprocessResults: function (data, initial, noHighlightUpdate) {
       
  2574             var selected = 0, self = this, showSearchInput = true;
       
  2575 
       
  2576             // find the selected element in the result list
       
  2577 
       
  2578             this.findHighlightableChoices().each2(function (i, elm) {
       
  2579                 if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
       
  2580                     selected = i;
       
  2581                     return false;
       
  2582                 }
       
  2583             });
       
  2584 
       
  2585             // and highlight it
       
  2586             if (noHighlightUpdate !== false) {
       
  2587                 if (initial === true && selected >= 0) {
       
  2588                     this.highlight(selected);
       
  2589                 } else {
       
  2590                     this.highlight(0);
       
  2591                 }
       
  2592             }
       
  2593 
       
  2594             // hide the search box if this is the first we got the results and there are enough of them for search
       
  2595 
       
  2596             if (initial === true) {
       
  2597                 var min = this.opts.minimumResultsForSearch;
       
  2598                 if (min >= 0) {
       
  2599                     this.showSearch(countResults(data.results) >= min);
       
  2600                 }
       
  2601             }
       
  2602         },
       
  2603 
       
  2604         // single
       
  2605         showSearch: function(showSearchInput) {
       
  2606             if (this.showSearchInput === showSearchInput) return;
       
  2607 
       
  2608             this.showSearchInput = showSearchInput;
       
  2609 
       
  2610             this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
       
  2611             this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
       
  2612             //add "select2-with-searchbox" to the container if search box is shown
       
  2613             $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
       
  2614         },
       
  2615 
       
  2616         // single
       
  2617         onSelect: function (data, options) {
       
  2618 
       
  2619             if (!this.triggerSelect(data)) { return; }
       
  2620 
       
  2621             var old = this.opts.element.val(),
       
  2622                 oldData = this.data();
       
  2623 
       
  2624             this.opts.element.val(this.id(data));
       
  2625             this.updateSelection(data);
       
  2626 
       
  2627             this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
       
  2628 
       
  2629             this.lastSearchTerm = this.search.val();
       
  2630             this.close();
       
  2631 
       
  2632             if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) {
       
  2633                 this.focusser.focus();
       
  2634             }
       
  2635 
       
  2636             if (!equal(old, this.id(data))) {
       
  2637                 this.triggerChange({ added: data, removed: oldData });
       
  2638             }
       
  2639         },
       
  2640 
       
  2641         // single
       
  2642         updateSelection: function (data) {
       
  2643 
       
  2644             var container=this.selection.find(".select2-chosen"), formatted, cssClass;
       
  2645 
       
  2646             this.selection.data("select2-data", data);
       
  2647 
       
  2648             container.empty();
       
  2649             if (data !== null) {
       
  2650                 formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
       
  2651             }
       
  2652             if (formatted !== undefined) {
       
  2653                 container.append(formatted);
       
  2654             }
       
  2655             cssClass=this.opts.formatSelectionCssClass(data, container);
       
  2656             if (cssClass !== undefined) {
       
  2657                 container.addClass(cssClass);
       
  2658             }
       
  2659 
       
  2660             this.selection.removeClass("select2-default");
       
  2661 
       
  2662             if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
       
  2663                 this.container.addClass("select2-allowclear");
       
  2664             }
       
  2665         },
       
  2666 
       
  2667         // single
       
  2668         val: function () {
       
  2669             var val,
       
  2670                 triggerChange = false,
       
  2671                 data = null,
       
  2672                 self = this,
       
  2673                 oldData = this.data();
       
  2674 
       
  2675             if (arguments.length === 0) {
       
  2676                 return this.opts.element.val();
       
  2677             }
       
  2678 
       
  2679             val = arguments[0];
       
  2680 
       
  2681             if (arguments.length > 1) {
       
  2682                 triggerChange = arguments[1];
       
  2683 
       
  2684                 if (this.opts.debug && console && console.warn) {
       
  2685                     console.warn(
       
  2686                         'Select2: The second option to `select2("val")` is not supported in Select2 4.0.0. ' +
       
  2687                         'The `change` event will always be triggered in 4.0.0.'
       
  2688                     );
       
  2689                 }
       
  2690             }
       
  2691 
       
  2692             if (this.select) {
       
  2693                 if (this.opts.debug && console && console.warn) {
       
  2694                     console.warn(
       
  2695                         'Select2: Setting the value on a <select> using `select2("val")` is no longer supported in 4.0.0. ' +
       
  2696                         'You can use the `.val(newValue).trigger("change")` method provided by jQuery instead.'
       
  2697                     );
       
  2698                 }
       
  2699 
       
  2700                 this.select
       
  2701                     .val(val)
       
  2702                     .find("option").filter(function() { return this.selected }).each2(function (i, elm) {
       
  2703                         data = self.optionToData(elm);
       
  2704                         return false;
       
  2705                     });
       
  2706                 this.updateSelection(data);
       
  2707                 this.setPlaceholder();
       
  2708                 if (triggerChange) {
       
  2709                     this.triggerChange({added: data, removed:oldData});
       
  2710                 }
       
  2711             } else {
       
  2712                 // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
       
  2713                 if (!val && val !== 0) {
       
  2714                     this.clear(triggerChange);
       
  2715                     return;
       
  2716                 }
       
  2717                 if (this.opts.initSelection === undefined) {
       
  2718                     throw new Error("cannot call val() if initSelection() is not defined");
       
  2719                 }
       
  2720                 this.opts.element.val(val);
       
  2721                 this.opts.initSelection(this.opts.element, function(data){
       
  2722                     self.opts.element.val(!data ? "" : self.id(data));
       
  2723                     self.updateSelection(data);
       
  2724                     self.setPlaceholder();
       
  2725                     if (triggerChange) {
       
  2726                         self.triggerChange({added: data, removed:oldData});
       
  2727                     }
       
  2728                 });
       
  2729             }
       
  2730         },
       
  2731 
       
  2732         // single
       
  2733         clearSearch: function () {
       
  2734             this.search.val("");
       
  2735             this.focusser.val("");
       
  2736         },
       
  2737 
       
  2738         // single
       
  2739         data: function(value) {
       
  2740             var data,
       
  2741                 triggerChange = false;
       
  2742 
       
  2743             if (arguments.length === 0) {
       
  2744                 data = this.selection.data("select2-data");
       
  2745                 if (data == undefined) data = null;
       
  2746                 return data;
       
  2747             } else {
       
  2748                 if (this.opts.debug && console && console.warn) {
       
  2749                     console.warn(
       
  2750                         'Select2: The `select2("data")` method can no longer set selected values in 4.0.0, ' +
       
  2751                         'consider using the `.val()` method instead.'
       
  2752                     );
       
  2753                 }
       
  2754 
       
  2755                 if (arguments.length > 1) {
       
  2756                     triggerChange = arguments[1];
       
  2757                 }
       
  2758                 if (!value) {
       
  2759                     this.clear(triggerChange);
       
  2760                 } else {
       
  2761                     data = this.data();
       
  2762                     this.opts.element.val(!value ? "" : this.id(value));
       
  2763                     this.updateSelection(value);
       
  2764                     if (triggerChange) {
       
  2765                         this.triggerChange({added: value, removed:data});
       
  2766                     }
       
  2767                 }
       
  2768             }
       
  2769         }
       
  2770     });
       
  2771 
       
  2772     MultiSelect2 = clazz(AbstractSelect2, {
       
  2773 
       
  2774         // multi
       
  2775         createContainer: function () {
       
  2776             var container = $(document.createElement("div")).attr({
       
  2777                 "class": "select2-container select2-container-multi"
       
  2778             }).html([
       
  2779                 "<ul class='select2-choices'>",
       
  2780                 "  <li class='select2-search-field'>",
       
  2781                 "    <label for='' class='select2-offscreen'></label>",
       
  2782                 "    <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
       
  2783                 "  </li>",
       
  2784                 "</ul>",
       
  2785                 "<div class='select2-drop select2-drop-multi select2-display-none'>",
       
  2786                 "   <ul class='select2-results'>",
       
  2787                 "   </ul>",
       
  2788                 "</div>"].join(""));
       
  2789             return container;
       
  2790         },
       
  2791 
       
  2792         // multi
       
  2793         prepareOpts: function () {
       
  2794             var opts = this.parent.prepareOpts.apply(this, arguments),
       
  2795                 self=this;
       
  2796 
       
  2797             // TODO validate placeholder is a string if specified
       
  2798             if (opts.element.get(0).tagName.toLowerCase() === "select") {
       
  2799                 // install the selection initializer
       
  2800                 opts.initSelection = function (element, callback) {
       
  2801 
       
  2802                     var data = [];
       
  2803 
       
  2804                     element.find("option").filter(function() { return this.selected && !this.disabled }).each2(function (i, elm) {
       
  2805                         data.push(self.optionToData(elm));
       
  2806                     });
       
  2807                     callback(data);
       
  2808                 };
       
  2809             } else if ("data" in opts) {
       
  2810                 // install default initSelection when applied to hidden input and data is local
       
  2811                 opts.initSelection = opts.initSelection || function (element, callback) {
       
  2812                     var ids = splitVal(element.val(), opts.separator, opts.transformVal);
       
  2813                     //search in data by array of ids, storing matching items in a list
       
  2814                     var matches = [];
       
  2815                     opts.query({
       
  2816                         matcher: function(term, text, el){
       
  2817                             var is_match = $.grep(ids, function(id) {
       
  2818                                 return equal(id, opts.id(el));
       
  2819                             }).length;
       
  2820                             if (is_match) {
       
  2821                                 matches.push(el);
       
  2822                             }
       
  2823                             return is_match;
       
  2824                         },
       
  2825                         callback: !$.isFunction(callback) ? $.noop : function() {
       
  2826                             // reorder matches based on the order they appear in the ids array because right now
       
  2827                             // they are in the order in which they appear in data array
       
  2828                             var ordered = [];
       
  2829                             for (var i = 0; i < ids.length; i++) {
       
  2830                                 var id = ids[i];
       
  2831                                 for (var j = 0; j < matches.length; j++) {
       
  2832                                     var match = matches[j];
       
  2833                                     if (equal(id, opts.id(match))) {
       
  2834                                         ordered.push(match);
       
  2835                                         matches.splice(j, 1);
       
  2836                                         break;
       
  2837                                     }
       
  2838                                 }
       
  2839                             }
       
  2840                             callback(ordered);
       
  2841                         }
       
  2842                     });
       
  2843                 };
       
  2844             }
       
  2845 
       
  2846             return opts;
       
  2847         },
       
  2848 
       
  2849         // multi
       
  2850         selectChoice: function (choice) {
       
  2851 
       
  2852             var selected = this.container.find(".select2-search-choice-focus");
       
  2853             if (selected.length && choice && choice[0] == selected[0]) {
       
  2854 
       
  2855             } else {
       
  2856                 if (selected.length) {
       
  2857                     this.opts.element.trigger("choice-deselected", selected);
       
  2858                 }
       
  2859                 selected.removeClass("select2-search-choice-focus");
       
  2860                 if (choice && choice.length) {
       
  2861                     this.close();
       
  2862                     choice.addClass("select2-search-choice-focus");
       
  2863                     this.opts.element.trigger("choice-selected", choice);
       
  2864                 }
       
  2865             }
       
  2866         },
       
  2867 
       
  2868         // multi
       
  2869         destroy: function() {
       
  2870             $("label[for='" + this.search.attr('id') + "']")
       
  2871                 .attr('for', this.opts.element.attr("id"));
       
  2872             this.parent.destroy.apply(this, arguments);
       
  2873 
       
  2874             cleanupJQueryElements.call(this,
       
  2875                 "searchContainer",
       
  2876                 "selection"
       
  2877             );
       
  2878         },
       
  2879 
       
  2880         // multi
       
  2881         initContainer: function () {
       
  2882 
       
  2883             var selector = ".select2-choices", selection;
       
  2884 
       
  2885             this.searchContainer = this.container.find(".select2-search-field");
       
  2886             this.selection = selection = this.container.find(selector);
       
  2887 
       
  2888             var _this = this;
       
  2889             this.selection.on("click", ".select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)", function (e) {
       
  2890                 _this.search[0].focus();
       
  2891                 _this.selectChoice($(this));
       
  2892             });
       
  2893 
       
  2894             // rewrite labels from original element to focusser
       
  2895             this.search.attr("id", "s2id_autogen"+nextUid());
       
  2896 
       
  2897             this.search.prev()
       
  2898                 .text($("label[for='" + this.opts.element.attr("id") + "']").text())
       
  2899                 .attr('for', this.search.attr('id'));
       
  2900             this.opts.element.on('focus.select2', this.bind(function () { this.focus(); }));
       
  2901 
       
  2902             this.search.on("input paste", this.bind(function() {
       
  2903                 if (this.search.attr('placeholder') && this.search.val().length == 0) return;
       
  2904                 if (!this.isInterfaceEnabled()) return;
       
  2905                 if (!this.opened()) {
       
  2906                     this.open();
       
  2907                 }
       
  2908             }));
       
  2909 
       
  2910             this.search.attr("tabindex", this.elementTabIndex);
       
  2911 
       
  2912             this.keydowns = 0;
       
  2913             this.search.on("keydown", this.bind(function (e) {
       
  2914                 if (!this.isInterfaceEnabled()) return;
       
  2915 
       
  2916                 ++this.keydowns;
       
  2917                 var selected = selection.find(".select2-search-choice-focus");
       
  2918                 var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
       
  2919                 var next = selected.next(".select2-search-choice:not(.select2-locked)");
       
  2920                 var pos = getCursorInfo(this.search);
       
  2921 
       
  2922                 if (selected.length &&
       
  2923                     (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
       
  2924                     var selectedChoice = selected;
       
  2925                     if (e.which == KEY.LEFT && prev.length) {
       
  2926                         selectedChoice = prev;
       
  2927                     }
       
  2928                     else if (e.which == KEY.RIGHT) {
       
  2929                         selectedChoice = next.length ? next : null;
       
  2930                     }
       
  2931                     else if (e.which === KEY.BACKSPACE) {
       
  2932                         if (this.unselect(selected.first())) {
       
  2933                             this.search.width(10);
       
  2934                             selectedChoice = prev.length ? prev : next;
       
  2935                         }
       
  2936                     } else if (e.which == KEY.DELETE) {
       
  2937                         if (this.unselect(selected.first())) {
       
  2938                             this.search.width(10);
       
  2939                             selectedChoice = next.length ? next : null;
       
  2940                         }
       
  2941                     } else if (e.which == KEY.ENTER) {
       
  2942                         selectedChoice = null;
       
  2943                     }
       
  2944 
       
  2945                     this.selectChoice(selectedChoice);
       
  2946                     killEvent(e);
       
  2947                     if (!selectedChoice || !selectedChoice.length) {
       
  2948                         this.open();
       
  2949                     }
       
  2950                     return;
       
  2951                 } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
       
  2952                     || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
       
  2953 
       
  2954                     this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
       
  2955                     killEvent(e);
       
  2956                     return;
       
  2957                 } else {
       
  2958                     this.selectChoice(null);
       
  2959                 }
       
  2960 
       
  2961                 if (this.opened()) {
       
  2962                     switch (e.which) {
       
  2963                     case KEY.UP:
       
  2964                     case KEY.DOWN:
       
  2965                         this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
       
  2966                         killEvent(e);
       
  2967                         return;
       
  2968                     case KEY.ENTER:
       
  2969                         this.selectHighlighted();
       
  2970                         killEvent(e);
       
  2971                         return;
       
  2972                     case KEY.TAB:
       
  2973                         this.selectHighlighted({noFocus:true});
       
  2974                         this.close();
       
  2975                         return;
       
  2976                     case KEY.ESC:
       
  2977                         this.cancel(e);
       
  2978                         killEvent(e);
       
  2979                         return;
       
  2980                     }
       
  2981                 }
       
  2982 
       
  2983                 if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
       
  2984                  || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
       
  2985                     return;
       
  2986                 }
       
  2987 
       
  2988                 if (e.which === KEY.ENTER) {
       
  2989                     if (this.opts.openOnEnter === false) {
       
  2990                         return;
       
  2991                     } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
       
  2992                         return;
       
  2993                     }
       
  2994                 }
       
  2995 
       
  2996                 this.open();
       
  2997 
       
  2998                 if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
       
  2999                     // prevent the page from scrolling
       
  3000                     killEvent(e);
       
  3001                 }
       
  3002 
       
  3003                 if (e.which === KEY.ENTER) {
       
  3004                     // prevent form from being submitted
       
  3005                     killEvent(e);
       
  3006                 }
       
  3007 
       
  3008             }));
       
  3009 
       
  3010             this.search.on("keyup", this.bind(function (e) {
       
  3011                 this.keydowns = 0;
       
  3012                 this.resizeSearch();
       
  3013             })
       
  3014             );
       
  3015 
       
  3016             this.search.on("blur", this.bind(function(e) {
       
  3017                 this.container.removeClass("select2-container-active");
       
  3018                 this.search.removeClass("select2-focused");
       
  3019                 this.selectChoice(null);
       
  3020                 if (!this.opened()) this.clearSearch();
       
  3021                 e.stopImmediatePropagation();
       
  3022                 this.opts.element.trigger($.Event("select2-blur"));
       
  3023             }));
       
  3024 
       
  3025             this.container.on("click", selector, this.bind(function (e) {
       
  3026                 if (!this.isInterfaceEnabled()) return;
       
  3027                 if ($(e.target).closest(".select2-search-choice").length > 0) {
       
  3028                     // clicked inside a select2 search choice, do not open
       
  3029                     return;
       
  3030                 }
       
  3031                 this.selectChoice(null);
       
  3032                 this.clearPlaceholder();
       
  3033                 if (!this.container.hasClass("select2-container-active")) {
       
  3034                     this.opts.element.trigger($.Event("select2-focus"));
       
  3035                 }
       
  3036                 this.open();
       
  3037                 this.focusSearch();
       
  3038                 e.preventDefault();
       
  3039             }));
       
  3040 
       
  3041             this.container.on("focus", selector, this.bind(function () {
       
  3042                 if (!this.isInterfaceEnabled()) return;
       
  3043                 if (!this.container.hasClass("select2-container-active")) {
       
  3044                     this.opts.element.trigger($.Event("select2-focus"));
       
  3045                 }
       
  3046                 this.container.addClass("select2-container-active");
       
  3047                 this.dropdown.addClass("select2-drop-active");
       
  3048                 this.clearPlaceholder();
       
  3049             }));
       
  3050 
       
  3051             this.initContainerWidth();
       
  3052             this.opts.element.hide();
       
  3053 
       
  3054             // set the placeholder if necessary
       
  3055             this.clearSearch();
       
  3056         },
       
  3057 
       
  3058         // multi
       
  3059         enableInterface: function() {
       
  3060             if (this.parent.enableInterface.apply(this, arguments)) {
       
  3061                 this.search.prop("disabled", !this.isInterfaceEnabled());
       
  3062             }
       
  3063         },
       
  3064 
       
  3065         // multi
       
  3066         initSelection: function () {
       
  3067             var data;
       
  3068             if (this.opts.element.val() === "" && this.opts.element.text() === "") {
       
  3069                 this.updateSelection([]);
       
  3070                 this.close();
       
  3071                 // set the placeholder if necessary
       
  3072                 this.clearSearch();
       
  3073             }
       
  3074             if (this.select || this.opts.element.val() !== "") {
       
  3075                 var self = this;
       
  3076                 this.opts.initSelection.call(null, this.opts.element, function(data){
       
  3077                     if (data !== undefined && data !== null) {
       
  3078                         self.updateSelection(data);
       
  3079                         self.close();
       
  3080                         // set the placeholder if necessary
       
  3081                         self.clearSearch();
       
  3082                     }
       
  3083                 });
       
  3084             }
       
  3085         },
       
  3086 
       
  3087         // multi
       
  3088         clearSearch: function () {
       
  3089             var placeholder = this.getPlaceholder(),
       
  3090                 maxWidth = this.getMaxSearchWidth();
       
  3091 
       
  3092             if (placeholder !== undefined  && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
       
  3093                 this.search.val(placeholder).addClass("select2-default");
       
  3094                 // stretch the search box to full width of the container so as much of the placeholder is visible as possible
       
  3095                 // 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
       
  3096                 this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
       
  3097             } else {
       
  3098                 this.search.val("").width(10);
       
  3099             }
       
  3100         },
       
  3101 
       
  3102         // multi
       
  3103         clearPlaceholder: function () {
       
  3104             if (this.search.hasClass("select2-default")) {
       
  3105                 this.search.val("").removeClass("select2-default");
       
  3106             }
       
  3107         },
       
  3108 
       
  3109         // multi
       
  3110         opening: function () {
       
  3111             this.clearPlaceholder(); // should be done before super so placeholder is not used to search
       
  3112             this.resizeSearch();
       
  3113 
       
  3114             this.parent.opening.apply(this, arguments);
       
  3115 
       
  3116             this.focusSearch();
       
  3117 
       
  3118             this.prefillNextSearchTerm();
       
  3119             this.updateResults(true);
       
  3120 
       
  3121             if (this.opts.shouldFocusInput(this)) {
       
  3122                 this.search.focus();
       
  3123             }
       
  3124             this.opts.element.trigger($.Event("select2-open"));
       
  3125         },
       
  3126 
       
  3127         // multi
       
  3128         close: function () {
       
  3129             if (!this.opened()) return;
       
  3130             this.parent.close.apply(this, arguments);
       
  3131         },
       
  3132 
       
  3133         // multi
       
  3134         focus: function () {
       
  3135             this.close();
       
  3136             this.search.focus();
       
  3137         },
       
  3138 
       
  3139         // multi
       
  3140         isFocused: function () {
       
  3141             return this.search.hasClass("select2-focused");
       
  3142         },
       
  3143 
       
  3144         // multi
       
  3145         updateSelection: function (data) {
       
  3146             var ids = {}, filtered = [], self = this;
       
  3147 
       
  3148             // filter out duplicates
       
  3149             $(data).each(function () {
       
  3150                 if (!(self.id(this) in ids)) {
       
  3151                     ids[self.id(this)] = 0;
       
  3152                     filtered.push(this);
       
  3153                 }
       
  3154             });
       
  3155 
       
  3156             this.selection.find(".select2-search-choice").remove();
       
  3157             this.addSelectedChoice(filtered);
       
  3158             self.postprocessResults();
       
  3159         },
       
  3160 
       
  3161         // multi
       
  3162         tokenize: function() {
       
  3163             var input = this.search.val();
       
  3164             input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts);
       
  3165             if (input != null && input != undefined) {
       
  3166                 this.search.val(input);
       
  3167                 if (input.length > 0) {
       
  3168                     this.open();
       
  3169                 }
       
  3170             }
       
  3171 
       
  3172         },
       
  3173 
       
  3174         // multi
       
  3175         onSelect: function (data, options) {
       
  3176 
       
  3177             if (!this.triggerSelect(data) || data.text === "") { return; }
       
  3178 
       
  3179             this.addSelectedChoice(data);
       
  3180 
       
  3181             this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
       
  3182 
       
  3183             // keep track of the search's value before it gets cleared
       
  3184             this.lastSearchTerm = this.search.val();
       
  3185 
       
  3186             this.clearSearch();
       
  3187             this.updateResults();
       
  3188 
       
  3189             if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true);
       
  3190 
       
  3191             if (this.opts.closeOnSelect) {
       
  3192                 this.close();
       
  3193                 this.search.width(10);
       
  3194             } else {
       
  3195                 if (this.countSelectableResults()>0) {
       
  3196                     this.search.width(10);
       
  3197                     this.resizeSearch();
       
  3198                     if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) {
       
  3199                         // if we reached max selection size repaint the results so choices
       
  3200                         // are replaced with the max selection reached message
       
  3201                         this.updateResults(true);
       
  3202                     } else {
       
  3203                         // initializes search's value with nextSearchTerm and update search result
       
  3204                         if (this.prefillNextSearchTerm()) {
       
  3205                             this.updateResults();
       
  3206                         }
       
  3207                     }
       
  3208                     this.positionDropdown();
       
  3209                 } else {
       
  3210                     // if nothing left to select close
       
  3211                     this.close();
       
  3212                     this.search.width(10);
       
  3213                 }
       
  3214             }
       
  3215 
       
  3216             // since its not possible to select an element that has already been
       
  3217             // added we do not need to check if this is a new element before firing change
       
  3218             this.triggerChange({ added: data });
       
  3219 
       
  3220             if (!options || !options.noFocus)
       
  3221                 this.focusSearch();
       
  3222         },
       
  3223 
       
  3224         // multi
       
  3225         cancel: function () {
       
  3226             this.close();
       
  3227             this.focusSearch();
       
  3228         },
       
  3229 
       
  3230         addSelectedChoice: function (data) {
       
  3231             var val = this.getVal(), self = this;
       
  3232             $(data).each(function () {
       
  3233                 val.push(self.createChoice(this));
       
  3234             });
       
  3235             this.setVal(val);
       
  3236         },
       
  3237 
       
  3238         createChoice: function (data) {
       
  3239             var enableChoice = !data.locked,
       
  3240                 enabledItem = $(
       
  3241                     "<li class='select2-search-choice'>" +
       
  3242                     "    <div></div>" +
       
  3243                     "    <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" +
       
  3244                     "</li>"),
       
  3245                 disabledItem = $(
       
  3246                     "<li class='select2-search-choice select2-locked'>" +
       
  3247                     "<div></div>" +
       
  3248                     "</li>");
       
  3249             var choice = enableChoice ? enabledItem : disabledItem,
       
  3250                 id = this.id(data),
       
  3251                 formatted,
       
  3252                 cssClass;
       
  3253 
       
  3254             formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup);
       
  3255             if (formatted != undefined) {
       
  3256                 choice.find("div").replaceWith($("<div></div>").html(formatted));
       
  3257             }
       
  3258             cssClass=this.opts.formatSelectionCssClass(data, choice.find("div"));
       
  3259             if (cssClass != undefined) {
       
  3260                 choice.addClass(cssClass);
       
  3261             }
       
  3262 
       
  3263             if(enableChoice){
       
  3264               choice.find(".select2-search-choice-close")
       
  3265                   .on("mousedown", killEvent)
       
  3266                   .on("click dblclick", this.bind(function (e) {
       
  3267                   if (!this.isInterfaceEnabled()) return;
       
  3268 
       
  3269                   this.unselect($(e.target));
       
  3270                   this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
       
  3271                   killEvent(e);
       
  3272                   this.close();
       
  3273                   this.focusSearch();
       
  3274               })).on("focus", this.bind(function () {
       
  3275                   if (!this.isInterfaceEnabled()) return;
       
  3276                   this.container.addClass("select2-container-active");
       
  3277                   this.dropdown.addClass("select2-drop-active");
       
  3278               }));
       
  3279             }
       
  3280 
       
  3281             choice.data("select2-data", data);
       
  3282             choice.insertBefore(this.searchContainer);
       
  3283 
       
  3284             return id;
       
  3285         },
       
  3286 
       
  3287         // multi
       
  3288         unselect: function (selected) {
       
  3289             var val = this.getVal(),
       
  3290                 data,
       
  3291                 index;
       
  3292             selected = selected.closest(".select2-search-choice");
       
  3293 
       
  3294             if (selected.length === 0) {
       
  3295                 throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
       
  3296             }
       
  3297 
       
  3298             data = selected.data("select2-data");
       
  3299 
       
  3300             if (!data) {
       
  3301                 // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
       
  3302                 // and invoked on an element already removed
       
  3303                 return;
       
  3304             }
       
  3305 
       
  3306             var evt = $.Event("select2-removing");
       
  3307             evt.val = this.id(data);
       
  3308             evt.choice = data;
       
  3309             this.opts.element.trigger(evt);
       
  3310 
       
  3311             if (evt.isDefaultPrevented()) {
       
  3312                 return false;
       
  3313             }
       
  3314 
       
  3315             while((index = indexOf(this.id(data), val)) >= 0) {
       
  3316                 val.splice(index, 1);
       
  3317                 this.setVal(val);
       
  3318                 if (this.select) this.postprocessResults();
       
  3319             }
       
  3320 
       
  3321             selected.remove();
       
  3322 
       
  3323             this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
       
  3324             this.triggerChange({ removed: data });
       
  3325 
       
  3326             return true;
       
  3327         },
       
  3328 
       
  3329         // multi
       
  3330         postprocessResults: function (data, initial, noHighlightUpdate) {
       
  3331             var val = this.getVal(),
       
  3332                 choices = this.results.find(".select2-result"),
       
  3333                 compound = this.results.find(".select2-result-with-children"),
       
  3334                 self = this;
       
  3335 
       
  3336             choices.each2(function (i, choice) {
       
  3337                 var id = self.id(choice.data("select2-data"));
       
  3338                 if (indexOf(id, val) >= 0) {
       
  3339                     choice.addClass("select2-selected");
       
  3340                     // mark all children of the selected parent as selected
       
  3341                     choice.find(".select2-result-selectable").addClass("select2-selected");
       
  3342                 }
       
  3343             });
       
  3344 
       
  3345             compound.each2(function(i, choice) {
       
  3346                 // hide an optgroup if it doesn't have any selectable children
       
  3347                 if (!choice.is('.select2-result-selectable')
       
  3348                     && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) {
       
  3349                     choice.addClass("select2-selected");
       
  3350                 }
       
  3351             });
       
  3352 
       
  3353             if (this.highlight() == -1 && noHighlightUpdate !== false && this.opts.closeOnSelect === true){
       
  3354                 self.highlight(0);
       
  3355             }
       
  3356 
       
  3357             //If all results are chosen render formatNoMatches
       
  3358             if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
       
  3359                 if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) {
       
  3360                     if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) {
       
  3361                         this.results.append("<li class='select2-no-results'>" + evaluate(self.opts.formatNoMatches, self.opts.element, self.search.val()) + "</li>");
       
  3362                     }
       
  3363                 }
       
  3364             }
       
  3365 
       
  3366         },
       
  3367 
       
  3368         // multi
       
  3369         getMaxSearchWidth: function() {
       
  3370             return this.selection.width() - getSideBorderPadding(this.search);
       
  3371         },
       
  3372 
       
  3373         // multi
       
  3374         resizeSearch: function () {
       
  3375             var minimumWidth, left, maxWidth, containerLeft, searchWidth,
       
  3376                 sideBorderPadding = getSideBorderPadding(this.search);
       
  3377 
       
  3378             minimumWidth = measureTextWidth(this.search) + 10;
       
  3379 
       
  3380             left = this.search.offset().left;
       
  3381 
       
  3382             maxWidth = this.selection.width();
       
  3383             containerLeft = this.selection.offset().left;
       
  3384 
       
  3385             searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
       
  3386 
       
  3387             if (searchWidth < minimumWidth) {
       
  3388                 searchWidth = maxWidth - sideBorderPadding;
       
  3389             }
       
  3390 
       
  3391             if (searchWidth < 40) {
       
  3392                 searchWidth = maxWidth - sideBorderPadding;
       
  3393             }
       
  3394 
       
  3395             if (searchWidth <= 0) {
       
  3396               searchWidth = minimumWidth;
       
  3397             }
       
  3398 
       
  3399             this.search.width(Math.floor(searchWidth));
       
  3400         },
       
  3401 
       
  3402         // multi
       
  3403         getVal: function () {
       
  3404             var val;
       
  3405             if (this.select) {
       
  3406                 val = this.select.val();
       
  3407                 return val === null ? [] : val;
       
  3408             } else {
       
  3409                 val = this.opts.element.val();
       
  3410                 return splitVal(val, this.opts.separator, this.opts.transformVal);
       
  3411             }
       
  3412         },
       
  3413 
       
  3414         // multi
       
  3415         setVal: function (val) {
       
  3416             if (this.select) {
       
  3417                 this.select.val(val);
       
  3418             } else {
       
  3419                 var unique = [], valMap = {};
       
  3420                 // filter out duplicates
       
  3421                 $(val).each(function () {
       
  3422                     if (!(this in valMap)) {
       
  3423                         unique.push(this);
       
  3424                         valMap[this] = 0;
       
  3425                     }
       
  3426                 });
       
  3427                 this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
       
  3428             }
       
  3429         },
       
  3430 
       
  3431         // multi
       
  3432         buildChangeDetails: function (old, current) {
       
  3433             var current = current.slice(0),
       
  3434                 old = old.slice(0);
       
  3435 
       
  3436             // remove intersection from each array
       
  3437             for (var i = 0; i < current.length; i++) {
       
  3438                 for (var j = 0; j < old.length; j++) {
       
  3439                     if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
       
  3440                         current.splice(i, 1);
       
  3441                         i--;
       
  3442                         old.splice(j, 1);
       
  3443                         break;
       
  3444                     }
       
  3445                 }
       
  3446             }
       
  3447 
       
  3448             return {added: current, removed: old};
       
  3449         },
       
  3450 
       
  3451 
       
  3452         // multi
       
  3453         val: function (val, triggerChange) {
       
  3454             var oldData, self=this;
       
  3455 
       
  3456             if (arguments.length === 0) {
       
  3457                 return this.getVal();
       
  3458             }
       
  3459 
       
  3460             oldData=this.data();
       
  3461             if (!oldData.length) oldData=[];
       
  3462 
       
  3463             // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
       
  3464             if (!val && val !== 0) {
       
  3465                 this.opts.element.val("");
       
  3466                 this.updateSelection([]);
       
  3467                 this.clearSearch();
       
  3468                 if (triggerChange) {
       
  3469                     this.triggerChange({added: this.data(), removed: oldData});
       
  3470                 }
       
  3471                 return;
       
  3472             }
       
  3473 
       
  3474             // val is a list of ids
       
  3475             this.setVal(val);
       
  3476 
       
  3477             if (this.select) {
       
  3478                 this.opts.initSelection(this.select, this.bind(this.updateSelection));
       
  3479                 if (triggerChange) {
       
  3480                     this.triggerChange(this.buildChangeDetails(oldData, this.data()));
       
  3481                 }
       
  3482             } else {
       
  3483                 if (this.opts.initSelection === undefined) {
       
  3484                     throw new Error("val() cannot be called if initSelection() is not defined");
       
  3485                 }
       
  3486 
       
  3487                 this.opts.initSelection(this.opts.element, function(data){
       
  3488                     var ids=$.map(data, self.id);
       
  3489                     self.setVal(ids);
       
  3490                     self.updateSelection(data);
       
  3491                     self.clearSearch();
       
  3492                     if (triggerChange) {
       
  3493                         self.triggerChange(self.buildChangeDetails(oldData, self.data()));
       
  3494                     }
       
  3495                 });
       
  3496             }
       
  3497             this.clearSearch();
       
  3498         },
       
  3499 
       
  3500         // multi
       
  3501         onSortStart: function() {
       
  3502             if (this.select) {
       
  3503                 throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
       
  3504             }
       
  3505 
       
  3506             // collapse search field into 0 width so its container can be collapsed as well
       
  3507             this.search.width(0);
       
  3508             // hide the container
       
  3509             this.searchContainer.hide();
       
  3510         },
       
  3511 
       
  3512         // multi
       
  3513         onSortEnd:function() {
       
  3514 
       
  3515             var val=[], self=this;
       
  3516 
       
  3517             // show search and move it to the end of the list
       
  3518             this.searchContainer.show();
       
  3519             // make sure the search container is the last item in the list
       
  3520             this.searchContainer.appendTo(this.searchContainer.parent());
       
  3521             // since we collapsed the width in dragStarted, we resize it here
       
  3522             this.resizeSearch();
       
  3523 
       
  3524             // update selection
       
  3525             this.selection.find(".select2-search-choice").each(function() {
       
  3526                 val.push(self.opts.id($(this).data("select2-data")));
       
  3527             });
       
  3528             this.setVal(val);
       
  3529             this.triggerChange();
       
  3530         },
       
  3531 
       
  3532         // multi
       
  3533         data: function(values, triggerChange) {
       
  3534             var self=this, ids, old;
       
  3535             if (arguments.length === 0) {
       
  3536                  return this.selection
       
  3537                      .children(".select2-search-choice")
       
  3538                      .map(function() { return $(this).data("select2-data"); })
       
  3539                      .get();
       
  3540             } else {
       
  3541                 old = this.data();
       
  3542                 if (!values) { values = []; }
       
  3543                 ids = $.map(values, function(e) { return self.opts.id(e); });
       
  3544                 this.setVal(ids);
       
  3545                 this.updateSelection(values);
       
  3546                 this.clearSearch();
       
  3547                 if (triggerChange) {
       
  3548                     this.triggerChange(this.buildChangeDetails(old, this.data()));
       
  3549                 }
       
  3550             }
       
  3551         }
       
  3552     });
       
  3553 
       
  3554     $.fn.select2 = function () {
       
  3555 
       
  3556         var args = Array.prototype.slice.call(arguments, 0),
       
  3557             opts,
       
  3558             select2,
       
  3559             method, value, multiple,
       
  3560             allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"],
       
  3561             valueMethods = ["opened", "isFocused", "container", "dropdown"],
       
  3562             propertyMethods = ["val", "data"],
       
  3563             methodsMap = { search: "externalSearch" };
       
  3564 
       
  3565         this.each(function () {
       
  3566             if (args.length === 0 || typeof(args[0]) === "object") {
       
  3567                 opts = args.length === 0 ? {} : $.extend({}, args[0]);
       
  3568                 opts.element = $(this);
       
  3569 
       
  3570                 if (opts.element.get(0).tagName.toLowerCase() === "select") {
       
  3571                     multiple = opts.element.prop("multiple");
       
  3572                 } else {
       
  3573                     multiple = opts.multiple || false;
       
  3574                     if ("tags" in opts) {opts.multiple = multiple = true;}
       
  3575                 }
       
  3576 
       
  3577                 select2 = multiple ? new window.Select2["class"].multi() : new window.Select2["class"].single();
       
  3578                 select2.init(opts);
       
  3579             } else if (typeof(args[0]) === "string") {
       
  3580 
       
  3581                 if (indexOf(args[0], allowedMethods) < 0) {
       
  3582                     throw "Unknown method: " + args[0];
       
  3583                 }
       
  3584 
       
  3585                 value = undefined;
       
  3586                 select2 = $(this).data("select2");
       
  3587                 if (select2 === undefined) return;
       
  3588 
       
  3589                 method=args[0];
       
  3590 
       
  3591                 if (method === "container") {
       
  3592                     value = select2.container;
       
  3593                 } else if (method === "dropdown") {
       
  3594                     value = select2.dropdown;
       
  3595                 } else {
       
  3596                     if (methodsMap[method]) method = methodsMap[method];
       
  3597 
       
  3598                     value = select2[method].apply(select2, args.slice(1));
       
  3599                 }
       
  3600                 if (indexOf(args[0], valueMethods) >= 0
       
  3601                     || (indexOf(args[0], propertyMethods) >= 0 && args.length == 1)) {
       
  3602                     return false; // abort the iteration, ready to return first matched value
       
  3603                 }
       
  3604             } else {
       
  3605                 throw "Invalid arguments to select2 plugin: " + args;
       
  3606             }
       
  3607         });
       
  3608         return (value === undefined) ? this : value;
       
  3609     };
       
  3610 
       
  3611     // plugin defaults, accessible to users
       
  3612     $.fn.select2.defaults = {
       
  3613         debug: false,
       
  3614         width: "copy",
       
  3615         loadMorePadding: 0,
       
  3616         closeOnSelect: true,
       
  3617         openOnEnter: true,
       
  3618         containerCss: {},
       
  3619         dropdownCss: {},
       
  3620         containerCssClass: "",
       
  3621         dropdownCssClass: "",
       
  3622         formatResult: function(result, container, query, escapeMarkup) {
       
  3623             var markup=[];
       
  3624             markMatch(this.text(result), query.term, markup, escapeMarkup);
       
  3625             return markup.join("");
       
  3626         },
       
  3627         transformVal: function(val) {
       
  3628             return $.trim(val);
       
  3629         },
       
  3630         formatSelection: function (data, container, escapeMarkup) {
       
  3631             return data ? escapeMarkup(this.text(data)) : undefined;
       
  3632         },
       
  3633         sortResults: function (results, container, query) {
       
  3634             return results;
       
  3635         },
       
  3636         formatResultCssClass: function(data) {return data.css;},
       
  3637         formatSelectionCssClass: function(data, container) {return undefined;},
       
  3638         minimumResultsForSearch: 0,
       
  3639         minimumInputLength: 0,
       
  3640         maximumInputLength: null,
       
  3641         maximumSelectionSize: 0,
       
  3642         id: function (e) { return e == undefined ? null : e.id; },
       
  3643         text: function (e) {
       
  3644           if (e && this.data && this.data.text) {
       
  3645             if ($.isFunction(this.data.text)) {
       
  3646               return this.data.text(e);
       
  3647             } else {
       
  3648               return e[this.data.text];
       
  3649             }
       
  3650           } else {
       
  3651             return e.text;
       
  3652           }
       
  3653         },
       
  3654         matcher: function(term, text) {
       
  3655             return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
       
  3656         },
       
  3657         separator: ",",
       
  3658         tokenSeparators: [],
       
  3659         tokenizer: defaultTokenizer,
       
  3660         escapeMarkup: defaultEscapeMarkup,
       
  3661         blurOnChange: false,
       
  3662         selectOnBlur: false,
       
  3663         adaptContainerCssClass: function(c) { return c; },
       
  3664         adaptDropdownCssClass: function(c) { return null; },
       
  3665         nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; },
       
  3666         searchInputPlaceholder: '',
       
  3667         createSearchChoicePosition: 'top',
       
  3668         shouldFocusInput: function (instance) {
       
  3669             // Attempt to detect touch devices
       
  3670             var supportsTouchEvents = (('ontouchstart' in window) ||
       
  3671                                        (navigator.msMaxTouchPoints > 0));
       
  3672 
       
  3673             // Only devices which support touch events should be special cased
       
  3674             if (!supportsTouchEvents) {
       
  3675                 return true;
       
  3676             }
       
  3677 
       
  3678             // Never focus the input if search is disabled
       
  3679             if (instance.opts.minimumResultsForSearch < 0) {
       
  3680                 return false;
       
  3681             }
       
  3682 
       
  3683             return true;
       
  3684         }
       
  3685     };
       
  3686 
       
  3687     $.fn.select2.locales = [];
       
  3688 
       
  3689     $.fn.select2.locales['en'] = {
       
  3690          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."; },
       
  3691          formatNoMatches: function () { return "No matches found"; },
       
  3692          formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; },
       
  3693          formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); },
       
  3694          formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
       
  3695          formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
       
  3696          formatLoadMore: function (pageNumber) { return "Loading more results…"; },
       
  3697          formatSearching: function () { return "Searching…"; }
       
  3698     };
       
  3699 
       
  3700     $.extend($.fn.select2.defaults, $.fn.select2.locales['en']);
       
  3701 
       
  3702     $.fn.select2.ajaxDefaults = {
       
  3703         transport: $.ajax,
       
  3704         params: {
       
  3705             type: "GET",
       
  3706             cache: false,
       
  3707             dataType: "json"
       
  3708         }
       
  3709     };
       
  3710 
       
  3711     // exports
       
  3712     window.Select2 = {
       
  3713         query: {
       
  3714             ajax: ajax,
       
  3715             local: local,
       
  3716             tags: tags
       
  3717         }, util: {
       
  3718             debounce: debounce,
       
  3719             markMatch: markMatch,
       
  3720             escapeMarkup: defaultEscapeMarkup,
       
  3721             stripDiacritics: stripDiacritics
       
  3722         }, "class": {
       
  3723             "abstract": AbstractSelect2,
       
  3724             "single": SingleSelect2,
       
  3725             "multi": MultiSelect2
       
  3726         }
       
  3727     };
       
  3728 
       
  3729 }(jQuery));