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