PageSourceSearch

https://www.museum-joanneum.at/typo3temp/assets/compressed/selecti…b20a66f76faa1eb783890f827affe.js?1790764415

js museum-joanneum.at collected 2026-10-01 10:12:18 UTC 115,299 bytes, 4,200 lines download raw bytes

vendor: 6,500 bytes, lines 1-247
1/**
2 * sifter.js
3 * Copyright (c) 2013–2020 Brian Reavis & contributors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under
10 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 * ANY KIND, either express or implied. See the License for the specific language
12 * governing permissions and limitations under the License.
13 *
14 * @author Brian Reavis <[email protected]>
15 */
16
17(function(root, factory) {
18	if (typeof define === 'function' && define.amd) {
19		define('sifter', factory);
20	} else if (typeof exports === 'object') {
21		module.exports = factory();
22	} else {
23		root.Sifter = factory();
24	}
25}(this, function() {
26
27	/**
28	 * Textually searches arrays and hashes of objects
29	 * by property (or multiple properties). Designed
30	 * specifically for autocomplete.
31	 *
32	 * @constructor
33	 * @param {array|object} items
34	 * @param {object} items
35	 */
36	var Sifter = function(items, settings) {
37		this.items = items;
38		this.settings = settings || {diacritics: true};
39	};
40
41	/**
42	 * Splits a search string into an array of individual
43	 * regexps to be used to match results.
44	 *
45	 * @param {string} query
46	 * @returns {array}
47	 */
48	Sifter.prototype.tokenize = function(query, respect_word_boundaries) {
49		query = trim(String(query || '').toLowerCase());
50		if (!query || !query.length) return [];
51
52		var i, n, regex, letter;
53		var tokens = [];
54		var words = query.split(/ +/);
55
56		for (i = 0, n = words.length; i < n; i++) {
57			regex = escape_regex(words[i]);
58			if (this.settings.diacritics) {
59				for (letter in DIACRITICS) {
60					if (DIACRITICS.hasOwnProperty(letter)) {
61						regex = regex.replace(new RegExp(letter, 'g'), DIACRITICS[letter]);
62					}
63				}
64			}
65			if (respect_word_boundaries) regex = "\\b"+regex
66			tokens.push({
67				string : words[i],
68				regex  : new RegExp(regex, 'i')
69			});
70		}
71
72		return tokens;
73	};
74
75	/**
76	 * Iterates over arrays and hashes.
77	 *
78	 * ```
79	 * this.iterator(this.items, function(item, id) {
80	 *    // invoked for each item
81	 * });
82	 * ```
83	 *
84	 * @param {array|object} object
85	 */
86	Sifter.prototype.iterator = function(object, callback) {
87		var iterator;
88		if (is_array(object)) {
89			iterator = Array.prototype.forEach || function(callback) {
90				for (var i = 0, n = this.length; i < n; i++) {
91					callback(this[i], i, this);
92				}
93			};
94		} else {
95			iterator = function(callback) {
96				for (var key in this) {
97					if (this.hasOwnProperty(key)) {
98						callback(this[key], key, this);
99					}
100				}
101			};
102		}
103
104		iterator.apply(object, [callback]);
105	};
106
107	/**
108	 * Returns a function to be used to score individual results.
109	 *
110	 * Good matches will have a higher score than poor matches.
111	 * If an item is not a match, 0 will be returned by the function.
112	 *
113	 * @param {object|string} search
114	 * @param {object} options (optional)
115	 * @returns {function}
116	 */
117	Sifter.prototype.getScoreFunction = function(search, options) {
118		var self, fields, tokens, token_count, nesting;
119
120		self        = this;
121		search      = self.prepareSearch(search, options);
122		tokens      = search.tokens;
123		fields      = search.options.fields;
124		token_count = tokens.length;
125		nesting     = search.options.nesting;
126
127		/**
128		 * Calculates how close of a match the
129		 * given value is against a search token.
130		 *
131		 * @param {mixed} value
132		 * @param {object} token
133		 * @return {number}
134		 */
135		var scoreValue = function(value, token) {
136			var score, pos;
137
138			if (!value) return 0;
139			value = String(value || '');
140			pos = value.search(token.regex);
141			if (pos === -1) return 0;
142			score = token.string.length / value.length;
143			if (pos === 0) score += 0.5;
144			return score;
145		};
146
147		/**
148		 * Calculates the score of an object
149		 * against the search query.
150		 *
151		 * @param {object} token
152		 * @param {object} data
153		 * @return {number}
154		 */
155		var scoreObject = (function() {
156			var field_count = fields.length;
157			if (!field_count) {
158				return function() { return 0; };
159			}
160			if (field_count === 1) {
161				return function(token, data) {
162					return scoreValue(getattr(data, fields[0], nesting), token);
163				};
164			}
165			return function(token, data) {
166				for (var i = 0, sum = 0; i < field_count; i++) {
167					sum += scoreValue(getattr(data, fields[i], nesting), token);
168				}
169				return sum / field_count;
170			};
171		})();
172
173		if (!token_count) {
174			return function() { return 0; };
175		}
176		if (token_count === 1) {
177			return function(data) {
178				return scoreObject(tokens[0], data);
179			};
180		}
181
182		if (search.options.conjunction === 'and') {
183			return function(data) {
184				var score;
185				for (var i = 0, sum = 0; i < token_count; i++) {
186					score = scoreObject(tokens[i], data);
187					if (score <= 0) return 0;
188					sum += score;
189				}
190				return sum / token_count;
191			};
192		} else {
193			return function(data) {
194				for (var i = 0, sum = 0; i < token_count; i++) {
195					sum += scoreObject(tokens[i], data);
196				}
197				return sum / token_count;
198			};
199		}
200	};
201
202	/**
203	 * Returns a function that can be used to compare two
204	 * results, for sorting purposes. If no sorting should
205	 * be performed, `null` will be returned.
206	 *
207	 * @param {string|object} search
208	 * @param {object} options
209	 * @return function(a,b)
210	 */
211	Sifter.prototype.getSortFunction = function(search, options) {
212		var i, n, self, field, fields, fields_count, multiplier, multipliers, get_field, implicit_score, sort;
213
214		self   = this;
215		search = self.prepareSearch(search, options);
216		sort   = (!search.query && options.sort_empty) || options.sort;
217
218		/**
219		 * Fetches the specified sort field value
220		 * from a search result item.
221		 *
222		 * @param  {string} name
223		 * @param  {object} result
224		 * @return {mixed}
225		 */
226		get_field = function(name, result) {
227			if (name === '$score') return result.score;
228			return getattr(self.items[result.id], name, options.nesting);
229		};
230
231		// parse options
232		fields = [];
233		if (sort) {
234			for (i = 0, n = sort.length; i < n; i++) {
235				if (search.query || sort[i].field !== '$score') {
236					fields.push(sort[i]);
237				}
238			}
239		}
240
241		// the "$score" field is implied to be the primary
242		// sort field, unless it's manually specified
243		if (search.query) {
244			implicit_score = true;
245			for (i = 0, n = fields.length; i < n; i++) {
246				if (fields[i].field === '$score') {
247					implicit_score = false;
vendor: 2,197 bytes, lines 248-338
248					break;
249				}
250			}
251			if (implicit_score) {
252				fields.unshift({field: '$score', direction: 'desc'});
253			}
254		} else {
255			for (i = 0, n = fields.length; i < n; i++) {
256				if (fields[i].field === '$score') {
257					fields.splice(i, 1);
258					break;
259				}
260			}
261		}
262
263		multipliers = [];
264		for (i = 0, n = fields.length; i < n; i++) {
265			multipliers.push(fields[i].direction === 'desc' ? -1 : 1);
266		}
267
268		// build function
269		fields_count = fields.length;
270		if (!fields_count) {
271			return null;
272		} else if (fields_count === 1) {
273			field = fields[0].field;
274			multiplier = multipliers[0];
275			return function(a, b) {
276				return multiplier * cmp(
277					get_field(field, a),
278					get_field(field, b)
279				);
280			};
281		} else {
282			return function(a, b) {
283				var i, result, a_value, b_value, field;
284				for (i = 0; i < fields_count; i++) {
285					field = fields[i].field;
286					result = multipliers[i] * cmp(
287						get_field(field, a),
288						get_field(field, b)
289					);
290					if (result) return result;
291				}
292				return 0;
293			};
294		}
295	};
296
297	/**
298	 * Parses a search query and returns an object
299	 * with tokens and fields ready to be populated
300	 * with results.
301	 *
302	 * @param {string} query
303	 * @param {object} options
304	 * @returns {object}
305	 */
306	Sifter.prototype.prepareSearch = function(query, options) {
307		if (typeof query === 'object') return query;
308
309		options = extend({}, options);
310
311		var option_fields     = options.fields;
312		var option_sort       = options.sort;
313		var option_sort_empty = options.sort_empty;
314
315		if (option_fields && !is_array(option_fields)) options.fields = [option_fields];
316		if (option_sort && !is_array(option_sort)) options.sort = [option_sort];
317		if (option_sort_empty && !is_array(option_sort_empty)) options.sort_empty = [option_sort_empty];
318
319		return {
320			options : options,
321			query   : String(query || '').toLowerCase(),
322			tokens  : this.tokenize(query, options.respect_word_boundaries),
323			total   : 0,
324			items   : []
325		};
326	};
327
328	/**
329	 * Searches through all items and returns a sorted array of matches.
330	 *
331	 * The `options` parameter can contain:
332	 *
333	 *   - fields {string|array}
334	 *   - sort {array}
335	 *   - score {function}
336	 *   - filter {bool}
337	 *   - limit {integer}
338	 *
vendor: 10,301 bytes, lines 339-682
339	 * Returns an object containing:
340	 *
341	 *   - options {object}
342	 *   - query {string}
343	 *   - tokens {array}
344	 *   - total {int}
345	 *   - items {array}
346	 *
347	 * @param {string} query
348	 * @param {object} options
349	 * @returns {object}
350	 */
351	Sifter.prototype.search = function(query, options) {
352		var self = this, value, score, search, calculateScore;
353		var fn_sort;
354		var fn_score;
355
356		search  = this.prepareSearch(query, options);
357		options = search.options;
358		query   = search.query;
359
360		// generate result scoring function
361		fn_score = options.score || self.getScoreFunction(search);
362
363		// perform search and sort
364		if (query.length) {
365			self.iterator(self.items, function(item, id) {
366				score = fn_score(item);
367				if (options.filter === false || score > 0) {
368					search.items.push({'score': score, 'id': id});
369				}
370			});
371		} else {
372			self.iterator(self.items, function(item, id) {
373				search.items.push({'score': 1, 'id': id});
374			});
375		}
376
377		fn_sort = self.getSortFunction(search, options);
378		if (fn_sort) search.items.sort(fn_sort);
379
380		// apply limits
381		search.total = search.items.length;
382		if (typeof options.limit === 'number') {
383			search.items = search.items.slice(0, options.limit);
384		}
385
386		return search;
387	};
388
389	// utilities
390	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
391
392	var cmp = function(a, b) {
393		if (typeof a === 'number' && typeof b === 'number') {
394			return a > b ? 1 : (a < b ? -1 : 0);
395		}
396		a = asciifold(String(a || ''));
397		b = asciifold(String(b || ''));
398		if (a > b) return 1;
399		if (b > a) return -1;
400		return 0;
401	};
402
403	var extend = function(a, b) {
404		var i, n, k, object;
405		for (i = 1, n = arguments.length; i < n; i++) {
406			object = arguments[i];
407			if (!object) continue;
408			for (k in object) {
409				if (object.hasOwnProperty(k)) {
410					a[k] = object[k];
411				}
412			}
413		}
414		return a;
415	};
416
417	/**
418	 * A property getter resolving dot-notation
419	 * @param  {Object}  obj     The root object to fetch property on
420	 * @param  {String}  name    The optionally dotted property name to fetch
421	 * @param  {Boolean} nesting Handle nesting or not
422	 * @return {Object}          The resolved property value
423	 */
424	var getattr = function(obj, name, nesting) {
425		if (!obj || !name) return;
426		if (!nesting) return obj[name];
427		var names = name.split(".");
428		while(names.length && (obj = obj[names.shift()]));
429		return obj;
430	};
431
432	var trim = function(str) {
433		return (str + '').replace(/^\s+|\s+$|/g, '');
434	};
435
436	var escape_regex = function(str) {
437		return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
438	};
439
440	var is_array = Array.isArray || (typeof $ !== 'undefined' && $.isArray) || function(object) {
441		return Object.prototype.toString.call(object) === '[object Array]';
442	};
443
444	var DIACRITICS = {
445		'a': '[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]',
446		'b': '[b␢βΒB฿𐌁ᛒ]',
447		'c': '[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄCc]',
448		'd': '[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅDdð]',
449		'e': '[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇEeɘǝƏƐε]',
450		'f': '[fƑƒḞḟ]',
451		'g': '[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]',
452		'h': '[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]',
453		'i': '[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪIi]',
454		'j': '[jȷĴĵɈɉʝɟʲ]',
455		'k': '[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]',
456		'l': '[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟLl]',
457		'n': '[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴNnŊŋ]',
458		'o': '[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]',
459		'p': '[pṔṕṖṗⱣᵽƤƥᵱ]',
460		'q': '[qꝖꝗʠɊɋꝘꝙq̃]',
461		'r': '[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]',
462		's': '[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]',
463		't': '[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]',
464		'u': '[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]',
465		'v': '[vṼṽṾṿƲʋꝞꝟⱱʋ]',
466		'w': '[wẂẃẀẁŴŵẄẅẆẇẈẉ]',
467		'x': '[xẌẍẊẋχ]',
468		'y': '[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]',
469		'z': '[zŹźẐẑŽžŻżẒẓẔẕƵƶ]'
470	};
471
472	var asciifold = (function() {
473		var i, n, k, chunk;
474		var foreignletters = '';
475		var lookup = {};
476		for (k in DIACRITICS) {
477			if (DIACRITICS.hasOwnProperty(k)) {
478				chunk = DIACRITICS[k].substring(2, DIACRITICS[k].length - 1);
479				foreignletters += chunk;
480				for (i = 0, n = chunk.length; i < n; i++) {
481					lookup[chunk.charAt(i)] = k;
482				}
483			}
484		}
485		var regexp = new RegExp('[' +  foreignletters + ']', 'g');
486		return function(str) {
487			return str.replace(regexp, function(foreignletter) {
488				return lookup[foreignletter];
489			}).toLowerCase();
490		};
491	})();
492
493
494	// export
495	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
496
497	return Sifter;
498}));
499
500
501/**
502 * microplugin.js
503 * Copyright (c) 2013 Brian Reavis & contributors
504 *
505 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
506 * file except in compliance with the License. You may obtain a copy of the License at:
507 * http://www.apache.org/licenses/LICENSE-2.0
508 *
509 * Unless required by applicable law or agreed to in writing, software distributed under
510 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
511 * ANY KIND, either express or implied. See the License for the specific language
512 * governing permissions and limitations under the License.
513 *
514 * @author Brian Reavis <[email protected]>
515 */
516
517(function(root, factory) {
518	if (typeof define === 'function' && define.amd) {
519		define('microplugin', factory);
520	} else if (typeof exports === 'object') {
521		module.exports = factory();
522	} else {
523		root.MicroPlugin = factory();
524	}
525}(this, function() {
526	var MicroPlugin = {};
527
528	MicroPlugin.mixin = function(Interface) {
529		Interface.plugins = {};
530
531		/**
532		 * Initializes the listed plugins (with options).
533		 * Acceptable formats:
534		 *
535		 * List (without options):
536		 *   ['a', 'b', 'c']
537		 *
538		 * List (with options):
539		 *   [{'name': 'a', options: {}}, {'name': 'b', options: {}}]
540		 *
541		 * Hash (with options):
542		 *   {'a': { ... }, 'b': { ... }, 'c': { ... }}
543		 *
544		 * @param {mixed} plugins
545		 */
546		Interface.prototype.initializePlugins = function(plugins) {
547			var i, n, key;
548			var self  = this;
549			var queue = [];
550
551			self.plugins = {
552				names     : [],
553				settings  : {},
554				requested : {},
555				loaded    : {}
556			};
557
558			if (utils.isArray(plugins)) {
559				for (i = 0, n = plugins.length; i < n; i++) {
560					if (typeof plugins[i] === 'string') {
561						queue.push(plugins[i]);
562					} else {
563						self.plugins.settings[plugins[i].name] = plugins[i].options;
564						queue.push(plugins[i].name);
565					}
566				}
567			} else if (plugins) {
568				for (key in plugins) {
569					if (plugins.hasOwnProperty(key)) {
570						self.plugins.settings[key] = plugins[key];
571						queue.push(key);
572					}
573				}
574			}
575
576			while (queue.length) {
577				self.require(queue.shift());
578			}
579		};
580
581		Interface.prototype.loadPlugin = function(name) {
582			var self    = this;
583			var plugins = self.plugins;
584			var plugin  = Interface.plugins[name];
585
586			if (!Interface.plugins.hasOwnProperty(name)) {
587				throw new Error('Unable to find "' +  name + '" plugin');
588			}
589
590			plugins.requested[name] = true;
591			plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
592			plugins.names.push(name);
593		};
594
595		/**
596		 * Initializes a plugin.
597		 *
598		 * @param {string} name
599		 */
600		Interface.prototype.require = function(name) {
601			var self = this;
602			var plugins = self.plugins;
603
604			if (!self.plugins.loaded.hasOwnProperty(name)) {
605				if (plugins.requested[name]) {
606					throw new Error('Plugin has circular dependency ("' + name + '")');
607				}
608				self.loadPlugin(name);
609			}
610
611			return plugins.loaded[name];
612		};
613
614		/**
615		 * Registers a plugin.
616		 *
617		 * @param {string} name
618		 * @param {function} fn
619		 */
620		Interface.define = function(name, fn) {
621			Interface.plugins[name] = {
622				'name' : name,
623				'fn'   : fn
624			};
625		};
626	};
627
628	var utils = {
629		isArray: Array.isArray || function(vArg) {
630			return Object.prototype.toString.call(vArg) === '[object Array]';
631		}
632	};
633
634	return MicroPlugin;
635}));
636
637/**
638 * selectize.js (v0.13.6)
639 * Copyright (c) 2013–2015 Brian Reavis & contributors
640 * Copyright (c) 2020-2022 Selectize Team & contributors
641 *
642 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
643 * file except in compliance with the License. You may obtain a copy of the License at:
644 * http://www.apache.org/licenses/LICENSE-2.0
645 *
646 * Unless required by applicable law or agreed to in writing, software distributed under
647 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
648 * ANY KIND, either express or implied. See the License for the specific language
649 * governing permissions and limitations under the License.
650 *
651 * @author Brian Reavis <[email protected]>
652 * @author Ris Adams <[email protected]>
653 */
654
655/*jshint curly:false */
656/*jshint browser:true */
657
658(function(root, factory) {
659	if (typeof define === 'function' && define.amd) {
660		define('selectize', ['jquery','sifter','microplugin'], factory);
661	} else if (typeof module === 'object' && typeof module.exports === 'object') {
662		module.exports = factory(require('jquery'), require('sifter'), require('microplugin'));
663	} else {
664		root.Selectize = factory(root.jQuery, root.Sifter, root.MicroPlugin);
665	}
666}(this, function($, Sifter, MicroPlugin) {
667	'use strict';
668
669	var highlight = function($element, pattern) {
670		if (typeof pattern === 'string' && !pattern.length) return;
671		var regex = (typeof pattern === 'string') ? new RegExp(pattern, 'i') : pattern;
672
673		var highlight = function(node) {
674			var skip = 0;
675			// Wrap matching part of text node with highlighting <span>, e.g.
676			// Soccer  ->  <span class="highlight">Soc</span>cer  for regex = /soc/i
677			if (node.nodeType === 3) {
678				var pos = node.data.search(regex);
679				if (pos >= 0 && node.data.length > 0) {
680					var match = node.data.match(regex);
681					var spannode = document.createElement('span');
682					spannode.className = 'highlight';
vendor: 5,314 bytes, lines 683-885
683					var middlebit = node.splitText(pos);
684					var endbit = middlebit.splitText(match[0].length);
685					var middleclone = middlebit.cloneNode(true);
686					spannode.appendChild(middleclone);
687					middlebit.parentNode.replaceChild(spannode, middlebit);
688					skip = 1;
689				}
690			}
691				// Recurse element node, looking for child text nodes to highlight, unless element
692			// is childless, <script>, <style>, or already highlighted: <span class="highlight">
693			else if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && ( node.className !== 'highlight' || node.tagName !== 'SPAN' )) {
694				for (var i = 0; i < node.childNodes.length; ++i) {
695					i += highlight(node.childNodes[i]);
696				}
697			}
698			return skip;
699		};
700
701		return $element.each(function() {
702			highlight(this);
703		});
704	};
705
706	/**
707	 * removeHighlight fn copied from highlight v5 and
708	 * edited to remove with() and pass js strict mode
709	 */
710	$.fn.removeHighlight = function() {
711		return this.find("span.highlight").each(function() {
712			this.parentNode.firstChild.nodeName;
713			var parent = this.parentNode;
714			parent.replaceChild(this.firstChild, this);
715			parent.normalize();
716		}).end();
717	};
718
719
720	var MicroEvent = function() {};
721	MicroEvent.prototype = {
722		on: function(event, fct){
723			this._events = this._events || {};
724			this._events[event] = this._events[event] || [];
725			this._events[event].push(fct);
726		},
727		off: function(event, fct){
728			var n = arguments.length;
729			if (n === 0) return delete this._events;
730			if (n === 1) return delete this._events[event];
731
732			this._events = this._events || {};
733			if (event in this._events === false) return;
734			this._events[event].splice(this._events[event].indexOf(fct), 1);
735		},
736		trigger: function(event /* , args... */){
737			this._events = this._events || {};
738			if (event in this._events === false) return;
739			for (var i = 0; i < this._events[event].length; i++){
740				this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
741			}
742		}
743	};
744
745	/**
746	 * Mixin will delegate all MicroEvent.js function in the destination object.
747	 *
748	 * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
749	 *
750	 * @param {object} the object which will support MicroEvent
751	 */
752	MicroEvent.mixin = function(destObject){
753		var props = ['on', 'off', 'trigger'];
754		for (var i = 0; i < props.length; i++){
755			destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
756		}
757	};
758
759	function uaDetect(platform, re) {
760		if (navigator.userAgentData) {
761			return platform === navigator.userAgentData.platform;
762		}
763
764		return re.test(navigator.userAgent);
765	}
766
767	var IS_MAC        = uaDetect("macOS", /Mac/);
768
769	var KEY_A         = 65;
770	var KEY_COMMA     = 188;
771	var KEY_RETURN    = 13;
772	var KEY_ESC       = 27;
773	var KEY_LEFT      = 37;
774	var KEY_UP        = 38;
775	var KEY_P         = 80;
776	var KEY_RIGHT     = 39;
777	var KEY_DOWN      = 40;
778	var KEY_N         = 78;
779	var KEY_BACKSPACE = 8;
780	var KEY_DELETE    = 46;
781	var KEY_SHIFT     = 16;
782	var KEY_CMD       = IS_MAC ? 91 : 17;
783	var KEY_CTRL      = IS_MAC ? 18 : 17;
784	var KEY_TAB       = 9;
785
786	var TAG_SELECT    = 1;
787	var TAG_INPUT     = 2;
788
789	// for now, android support in general is too spotty to support validity
790	var SUPPORTS_VALIDITY_API = !uaDetect("Android", /android/i) && !!document.createElement('input').validity;
791
792
793	var isset = function(object) {
794		return typeof object !== 'undefined';
795	};
796
797	/**
798	 * Converts a scalar to its best string representation
799	 * for hash keys and HTML attribute values.
800	 *
801	 * Transformations:
802	 *   'str'     -> 'str'
803	 *   null      -> ''
804	 *   undefined -> ''
805	 *   true      -> '1'
806	 *   false     -> '0'
807	 *   0         -> '0'
808	 *   1         -> '1'
809	 *
810	 * @param {string} value
811	 * @returns {string|null}
812	 */
813	var hash_key = function(value) {
814		if (typeof value === 'undefined' || value === null) return null;
815		if (typeof value === 'boolean') return value ? '1' : '0';
816		return value + '';
817	};
818
819	/**
820	 * Escapes a string for use within HTML.
821	 *
822	 * @param {string} str
823	 * @returns {string}
824	 */
825	var escape_html = function(str) {
826		return (str + '')
827			.replace(/&/g, '&amp;')
828			.replace(/</g, '&lt;')
829			.replace(/>/g, '&gt;')
830			.replace(/"/g, '&quot;');
831	};
832
833	/**
834	 * Escapes "$" characters in replacement strings.
835	 *
836	 * @param {string} str
837	 * @returns {string}
838	 */
839	var escape_replace = function(str) {
840		return (str + '').replace(/\$/g, '$$$$');
841	};
842
843	var hook = {};
844
845	/**
846	 * Wraps `method` on `self` so that `fn`
847	 * is invoked before the original method.
848	 *
849	 * @param {object} self
850	 * @param {string} method
851	 * @param {function} fn
852	 */
853	hook.before = function(self, method, fn) {
854		var original = self[method];
855		self[method] = function() {
856			fn.apply(self, arguments);
857			return original.apply(self, arguments);
858		};
859	};
860
861	/**
862	 * Wraps `method` on `self` so that `fn`
863	 * is invoked after the original method.
864	 *
865	 * @param {object} self
866	 * @param {string} method
867	 * @param {function} fn
868	 */
869	hook.after = function(self, method, fn) {
870		var original = self[method];
871		self[method] = function() {
872			var result = original.apply(self, arguments);
873			fn.apply(self, arguments);
874			return result;
875		};
876	};
877
878	/**
879	 * Wraps `fn` so that it can only be invoked once.
880	 *
881	 * @param {function} fn
882	 * @returns {function}
883	 */
884	var once = function(fn) {
885		var called = false;
vendor: 1,944 bytes, lines 886-968
886		return function() {
887			if (called) return;
888			called = true;
889			fn.apply(this, arguments);
890		};
891	};
892
893	/**
894	 * Wraps `fn` so that it can only be called once
895	 * every `delay` milliseconds (invoked on the falling edge).
896	 *
897	 * @param {function} fn
898	 * @param {int} delay
899	 * @returns {function}
900	 */
901	var debounce = function(fn, delay) {
902		var timeout;
903		return function() {
904			var self = this;
905			var args = arguments;
906			window.clearTimeout(timeout);
907			timeout = window.setTimeout(function() {
908				fn.apply(self, args);
909			}, delay);
910		};
911	};
912
913	/**
914	 * Debounce all fired events types listed in `types`
915	 * while executing the provided `fn`.
916	 *
917	 * @param {object} self
918	 * @param {array} types
919	 * @param {function} fn
920	 */
921	var debounce_events = function(self, types, fn) {
922		var type;
923		var trigger = self.trigger;
924		var event_args = {};
925
926		// override trigger method
927		self.trigger = function() {
928			var type = arguments[0];
929			if (types.indexOf(type) !== -1) {
930				event_args[type] = arguments;
931			} else {
932				return trigger.apply(self, arguments);
933			}
934		};
935
936		// invoke provided function
937		fn.apply(self, []);
938		self.trigger = trigger;
939
940		// trigger queued events
941		for (type in event_args) {
942			if (event_args.hasOwnProperty(type)) {
943				trigger.apply(self, event_args[type]);
944			}
945		}
946	};
947
948	/**
949	 * A workaround for http://bugs.jquery.com/ticket/6696
950	 *
951	 * @param {object} $parent - Parent element to listen on.
952	 * @param {string} event - Event name.
953	 * @param {string} selector - Descendant selector to filter by.
954	 * @param {function} fn - Event handler.
955	 */
956	var watchChildEvent = function($parent, event, selector, fn) {
957		$parent.on(event, selector, function(e) {
958			var child = e.target;
959			while (child && child.parentNode !== $parent[0]) {
960				child = child.parentNode;
961			}
962			e.currentTarget = child;
963			return fn.apply(this, [e]);
964		});
965	};
966
967	/**
968	 * Determines the current selection within a text input control.
vendor: 8,672 bytes, lines 969-1285
969	 * Returns an object containing:
970	 *   - start
971	 *   - length
972	 *
973	 * @param {object} input
974	 * @returns {object}
975	 */
976	var getSelection = function(input) {
977		var result = {};
978		if(input === undefined) {
979			console.warn('WARN getSelection cannot locate input control');
980			return result;
981		}
982		if ('selectionStart' in input) {
983			result.start = input.selectionStart;
984			result.length = input.selectionEnd - result.start;
985		} else if (document.selection) {
986			input.focus();
987			var sel = document.selection.createRange();
988			var selLen = document.selection.createRange().text.length;
989			sel.moveStart('character', -input.value.length);
990			result.start = sel.text.length - selLen;
991			result.length = selLen;
992		}
993		return result;
994	};
995
996	/**
997	 * Copies CSS properties from one element to another.
998	 *
999	 * @param {object} $from
1000	 * @param {object} $to
1001	 * @param {array} properties
1002	 */
1003	var transferStyles = function($from, $to, properties) {
1004		var i, n, styles = {};
1005		if (properties) {
1006			for (i = 0, n = properties.length; i < n; i++) {
1007				styles[properties[i]] = $from.css(properties[i]);
1008			}
1009		} else {
1010			styles = $from.css();
1011		}
1012		$to.css(styles);
1013	};
1014
1015	/**
1016	 * Measures the width of a string within a
1017	 * parent element (in pixels).
1018	 *
1019	 * @param {string} str
1020	 * @param {object} $parent
1021	 * @returns {int}
1022	 */
1023	var measureString = function(str, $parent) {
1024		if (!str) {
1025			return 0;
1026		}
1027
1028		if (!Selectize.$testInput) {
1029			Selectize.$testInput = $('<span />').css({
1030				position: 'absolute',
1031				width: 'auto',
1032				padding: 0,
1033				whiteSpace: 'pre'
1034			});
1035
1036			$('<div />').css({
1037				position: 'absolute',
1038				width: 0,
1039				height: 0,
1040				overflow: 'hidden'
1041			}).append(Selectize.$testInput).appendTo('body');
1042		}
1043
1044		Selectize.$testInput.text(str);
1045
1046		transferStyles($parent, Selectize.$testInput, [
1047			'letterSpacing',
1048			'fontSize',
1049			'fontFamily',
1050			'fontWeight',
1051			'textTransform'
1052		]);
1053
1054		return Selectize.$testInput.width();
1055	};
1056
1057	/**
1058	 * Sets up an input to grow horizontally as the user
1059	 * types. If the value is changed manually, you can
1060	 * trigger the "update" handler to resize:
1061	 *
1062	 * $input.trigger('update');
1063	 *
1064	 * @param {object} $input
1065	 */
1066	var autoGrow = function($input) {
1067		var currentWidth = null;
1068
1069		var update = function(e, options) {
1070			var value, keyCode, printable, width;
1071			var placeholder, placeholderWidth;
1072			var shift, character, selection;
1073			e = e || window.event || {};
1074			options = options || {};
1075
1076			if (e.metaKey || e.altKey) return;
1077			if (!options.force && $input.data('grow') === false) return;
1078
1079			value = $input.val();
1080			if (e.type && e.type.toLowerCase() === 'keydown') {
1081				keyCode = e.keyCode;
1082				printable = (
1083					(keyCode >= 48 && keyCode <= 57)  || // 0-9
1084					(keyCode >= 65 && keyCode <= 90)   || // a-z
1085					(keyCode >= 96 && keyCode <= 111)  || // numpad 0-9, numeric operators
1086					(keyCode >= 186 && keyCode <= 222) || // semicolon, equal, comma, dash, etc.
1087					keyCode === 32 // space
1088				);
1089
1090				if (keyCode === KEY_DELETE || keyCode === KEY_BACKSPACE) {
1091					selection = getSelection($input[0]);
1092					if (selection.length) {
1093						value = value.substring(0, selection.start) + value.substring(selection.start + selection.length);
1094					} else if (keyCode === KEY_BACKSPACE && selection.start) {
1095						value = value.substring(0, selection.start - 1) + value.substring(selection.start + 1);
1096					} else if (keyCode === KEY_DELETE && typeof selection.start !== 'undefined') {
1097						value = value.substring(0, selection.start) + value.substring(selection.start + 1);
1098					}
1099				} else if (printable) {
1100					shift = e.shiftKey;
1101					character = String.fromCharCode(e.keyCode);
1102					if (shift) character = character.toUpperCase();
1103					else character = character.toLowerCase();
1104					value += character;
1105				}
1106			}
1107
1108			placeholder = $input.attr('placeholder');
1109			if (placeholder) {
1110				placeholderWidth = measureString(placeholder, $input) + 4;
1111			} else {
1112				placeholderWidth = 0;
1113			}
1114
1115			width = Math.max(measureString(value, $input), placeholderWidth) + 4;
1116			if (width !== currentWidth) {
1117				currentWidth = width;
1118				$input.width(width);
1119				$input.triggerHandler('resize');
1120			}
1121		};
1122
1123		$input.on('keydown keyup update blur', update);
1124		update();
1125	};
1126
1127	var domToString = function(d) {
1128		var tmp = document.createElement('div');
1129
1130		tmp.appendChild(d.cloneNode(true));
1131
1132		return tmp.innerHTML;
1133	};
1134
1135	var logError = function(message, options){
1136		if(!options) options = {};
1137		var component = "Selectize";
1138
1139		console.error(component + ": " + message)
1140
1141		if(options.explanation){
1142			// console.group is undefined in <IE11
1143			if(console.group) console.group();
1144			console.error(options.explanation);
1145			if(console.group) console.groupEnd();
1146		}
1147	}
1148
1149
1150	var Selectize = function($input, settings) {
1151		var key, i, n, dir, input, self = this;
1152		input = $input[0];
1153		input.selectize = self;
1154
1155		// detect rtl environment
1156		var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null);
1157		dir = computedStyle ? computedStyle.getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction;
1158		dir = dir || $input.parents('[dir]:first').attr('dir') || '';
1159
1160		// setup default state
1161		$.extend(self, {
1162			order            : 0,
1163			settings         : settings,
1164			$input           : $input,
1165			tabIndex         : $input.attr('tabindex') || '',
1166			tagType          : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT,
1167			rtl              : /rtl/i.test(dir),
1168
1169			eventNS          : '.selectize' + (++Selectize.count),
1170			highlightedValue : null,
1171			isBlurring       : false,
1172			isOpen           : false,
1173			isDisabled       : false,
1174			isRequired       : $input.is('[required]'),
1175			isInvalid        : false,
1176			isLocked         : false,
1177			isFocused        : false,
1178			isInputHidden    : false,
1179			isSetup          : false,
1180			isShiftDown      : false,
1181			isCmdDown        : false,
1182			isCtrlDown       : false,
1183			ignoreFocus      : false,
1184			ignoreBlur       : false,
1185			ignoreHover      : false,
1186			hasOptions       : false,
1187			currentResults   : null,
1188			lastValue        : '',
1189			lastValidValue   : '',
1190			caretPos         : 0,
1191			loading          : 0,
1192			loadedSearches   : {},
1193
1194			$activeOption    : null,
1195			$activeItems     : [],
1196
1197			optgroups        : {},
1198			options          : {},
1199			userOptions      : {},
1200			items            : [],
1201			renderCache      : {},
1202			onSearchChange   : settings.loadThrottle === null ? self.onSearchChange : debounce(self.onSearchChange, settings.loadThrottle)
1203		});
1204
1205		// search system
1206		self.sifter = new Sifter(this.options, {diacritics: settings.diacritics});
1207
1208		// build options table
1209		if (self.settings.options) {
1210			for (i = 0, n = self.settings.options.length; i < n; i++) {
1211				self.registerOption(self.settings.options[i]);
1212			}
1213			delete self.settings.options;
1214		}
1215
1216		// build optgroup table
1217		if (self.settings.optgroups) {
1218			for (i = 0, n = self.settings.optgroups.length; i < n; i++) {
1219				self.registerOptionGroup(self.settings.optgroups[i]);
1220			}
1221			delete self.settings.optgroups;
1222		}
1223
1224		// option-dependent defaults
1225		self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi');
1226		if (typeof self.settings.hideSelected !== 'boolean') {
1227			self.settings.hideSelected = self.settings.mode === 'multi';
1228		}
1229
1230		self.initializePlugins(self.settings.plugins);
1231		self.setupCallbacks();
1232		self.setupTemplates();
1233		self.setup();
1234	};
1235
1236	// mixins
1237	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1238
1239	MicroEvent.mixin(Selectize);
1240
1241	if(typeof MicroPlugin !== "undefined"){
1242		MicroPlugin.mixin(Selectize);
1243	}else{
1244		logError("Dependency MicroPlugin is missing",
1245			{explanation:
1246					"Make sure you either: (1) are using the \"standalone\" "+
1247					"version of Selectize, or (2) require MicroPlugin before you "+
1248					"load Selectize."}
1249		);
1250	}
1251
1252
1253	// methods
1254	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1255
1256	$.extend(Selectize.prototype, {
1257
1258		/**
1259		 * Creates all elements and sets up event bindings.
1260		 */
1261		setup: function() {
1262			var self      = this;
1263			var settings  = self.settings;
1264			var eventNS   = self.eventNS;
1265			var $window   = $(window);
1266			var $document = $(document);
1267			var $input    = self.$input;
1268
1269			var $wrapper;
1270			var $control;
1271			var $control_input;
1272			var $dropdown;
1273			var $dropdown_content;
1274			var $dropdown_parent;
1275			var inputMode;
1276			var timeout_blur;
1277			var timeout_focus;
1278			var classes;
1279			var classes_plugins;
1280			var inputId;
1281
1282			inputMode         = self.settings.mode;
1283			classes           = $input.attr('class') || '';
1284
1285			$wrapper          = $('<div>
vendor: 18,776 bytes, lines 1285-1952
1285').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode);
1286			$control          = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper);
1287			$control_input    = $('<input type="text" autocomplete="new-password" autofill="no" />').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex);
1288			$dropdown_parent  = $(settings.dropdownParent || $wrapper);
1289			$dropdown         = $('<div>').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent);
1290			$dropdown_content = $('<div>').addClass(settings.dropdownContentClass).attr('tabindex', '-1').appendTo($dropdown);
1291
1292			if(inputId = $input.attr('id')) {
1293				$control_input.attr('id', inputId + '-selectized');
1294				$("label[for='"+inputId+"']").attr('for', inputId + '-selectized');
1295			}
1296
1297			if(self.settings.copyClassesToDropdown) {
1298				$dropdown.addClass(classes);
1299			}
1300
1301			$wrapper.css({
1302				width: $input[0].style.width
1303			});
1304
1305			if (self.plugins.names.length) {
1306				classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
1307				$wrapper.addClass(classes_plugins);
1308				$dropdown.addClass(classes_plugins);
1309			}
1310
1311			if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) {
1312				$input.attr('multiple', 'multiple');
1313			}
1314
1315			if (self.settings.placeholder) {
1316				$control_input.attr('placeholder', settings.placeholder);
1317			}
1318
1319			// if splitOn was not passed in, construct it from the delimiter to allow pasting universally
1320			if (!self.settings.splitOn && self.settings.delimiter) {
1321				var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
1322				self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*');
1323			}
1324
1325			if ($input.attr('autocorrect')) {
1326				$control_input.attr('autocorrect', $input.attr('autocorrect'));
1327			}
1328
1329			if ($input.attr('autocapitalize')) {
1330				$control_input.attr('autocapitalize', $input.attr('autocapitalize'));
1331			}
1332			if ($input.is('input')) {
1333				$control_input[0].type = $input[0].type;
1334			}
1335
1336			self.$wrapper          = $wrapper;
1337			self.$control          = $control;
1338			self.$control_input    = $control_input;
1339			self.$dropdown         = $dropdown;
1340			self.$dropdown_content = $dropdown_content;
1341
1342			$dropdown.on('mouseenter mousedown click', '[data-disabled]>[data-selectable]', function(e) { e.stopImmediatePropagation(); });
1343			$dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); });
1344			$dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
1345			watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); });
1346			autoGrow($control_input);
1347
1348			$control.on({
1349				mousedown : function() { return self.onMouseDown.apply(self, arguments); },
1350				click     : function() { return self.onClick.apply(self, arguments); }
1351			});
1352
1353			$control_input.on({
1354				mousedown : function(e) { e.stopPropagation(); },
1355				keydown   : function() { return self.onKeyDown.apply(self, arguments); },
1356				keypress  : function() { return self.onKeyPress.apply(self, arguments); },
1357				input     : function() { return self.onInput.apply(self, arguments); },
1358				resize    : function() { self.positionDropdown.apply(self, []); },
1359				blur      : function() { return self.onBlur.apply(self, arguments); },
1360				focus     : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); },
1361				paste     : function() { return self.onPaste.apply(self, arguments); }
1362			});
1363
1364			$document.on('keydown' + eventNS, function(e) {
1365				self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey'];
1366				self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey'];
1367				self.isShiftDown = e.shiftKey;
1368			});
1369
1370			$document.on('keyup' + eventNS, function(e) {
1371				if (e.keyCode === KEY_CTRL) self.isCtrlDown = false;
1372				if (e.keyCode === KEY_SHIFT) self.isShiftDown = false;
1373				if (e.keyCode === KEY_CMD) self.isCmdDown = false;
1374			});
1375
1376			$document.on('mousedown' + eventNS, function(e) {
1377				if (self.isFocused) {
1378					// prevent events on the dropdown scrollbar from causing the control to blur
1379					if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) {
1380						return false;
1381					}
1382					// blur on click outside
1383					if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
1384						self.blur(e.target);
1385					}
1386				}
1387			});
1388
1389			$window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() {
1390				if (self.isOpen) {
1391					self.positionDropdown.apply(self, arguments);
1392				}
1393			});
1394			$window.on('mousemove' + eventNS, function() {
1395				self.ignoreHover = false;
1396			});
1397
1398			// store original children and tab index so that they can be
1399			// restored when the destroy() method is called.
1400			this.revertSettings = {
1401				$children : $input.children().detach(),
1402				tabindex  : $input.attr('tabindex')
1403			};
1404
1405			$input.attr('tabindex', -1).hide().after(self.$wrapper);
1406
1407			if (Array.isArray(settings.items)) {
1408				self.lastValidValue = settings.items;
1409				self.setValue(settings.items);
1410				delete settings.items;
1411			}
1412
1413			// feature detect for the validation API
1414			if (SUPPORTS_VALIDITY_API) {
1415				$input.on('invalid' + eventNS, function(e) {
1416					e.preventDefault();
1417					self.isInvalid = true;
1418					self.refreshState();
1419				});
1420			}
1421
1422			self.updateOriginalInput();
1423			self.refreshItems();
1424			self.refreshState();
1425			self.updatePlaceholder();
1426			self.isSetup = true;
1427
1428			if ($input.is(':disabled')) {
1429				self.disable();
1430			}
1431
1432			self.on('change', this.onChange);
1433
1434			$input.data('selectize', self);
1435			$input.addClass('selectized');
1436			self.trigger('initialize');
1437
1438			// preload options
1439			if (settings.preload === true) {
1440				self.onSearchChange('');
1441			}
1442
1443		},
1444
1445		/**
1446		 * Sets up default rendering functions.
1447		 */
1448		setupTemplates: function() {
1449			var self = this;
1450			var field_label = self.settings.labelField;
1451			var field_value = self.settings.valueField;
1452			var field_optgroup = self.settings.optgroupLabelField;
1453
1454			var templates = {
1455				'optgroup': function(data) {
1456					return '<div class="optgroup">' + data.html + '</div>';
1457				},
1458				'optgroup_header': function(data, escape) {
1459					return '<div class="optgroup-header">' + escape(data[field_optgroup]) + '</div>';
1460				},
1461				'option': function(data, escape) {
1462					return '<div class="option '+( data[field_value] === '' ? 'selectize-dropdown-emptyoptionlabel' : '')+'">' + escape(data[field_label]) + '</div>';
1463				},
1464				'item': function(data, escape) {
1465					return '<div class="item">' + escape(data[field_label]) + '</div>';
1466				},
1467				'option_create': function(data, escape) {
1468					return '<div class="create">Add <strong>' + escape(data.input) + '</strong>&#x2026;</div>';
1469				}
1470			};
1471
1472			self.settings.render = $.extend({}, templates, self.settings.render);
1473		},
1474
1475		/**
1476		 * Maps fired events to callbacks provided
1477		 * in the settings used when creating the control.
1478		 */
1479		setupCallbacks: function() {
1480			var key, fn, callbacks = {
1481				'initialize'      : 'onInitialize',
1482				'change'          : 'onChange',
1483				'item_add'        : 'onItemAdd',
1484				'item_remove'     : 'onItemRemove',
1485				'clear'           : 'onClear',
1486				'option_add'      : 'onOptionAdd',
1487				'option_remove'   : 'onOptionRemove',
1488				'option_clear'    : 'onOptionClear',
1489				'optgroup_add'    : 'onOptionGroupAdd',
1490				'optgroup_remove' : 'onOptionGroupRemove',
1491				'optgroup_clear'  : 'onOptionGroupClear',
1492				'dropdown_open'   : 'onDropdownOpen',
1493				'dropdown_close'  : 'onDropdownClose',
1494				'type'            : 'onType',
1495				'load'            : 'onLoad',
1496				'focus'           : 'onFocus',
1497				'blur'            : 'onBlur',
1498				'dropdown_item_activate'        : 'onDropdownItemActivate',
1499				'dropdown_item_deactivate'      : 'onDropdownItemDeactivate'
1500			};
1501
1502			for (key in callbacks) {
1503				if (callbacks.hasOwnProperty(key)) {
1504					fn = this.settings[callbacks[key]];
1505					if (fn) this.on(key, fn);
1506				}
1507			}
1508		},
1509
1510		/**
1511		 * Triggered when the main control element
1512		 * has a click event.
1513		 *
1514		 * @param {object} e
1515		 * @return {boolean}
1516		 */
1517		onClick: function(e) {
1518			var self = this;
1519
1520			// necessary for mobile webkit devices (manual focus triggering
1521			// is ignored unless invoked within a click event)
1522			// also necessary to reopen a dropdown that has been closed by
1523			// closeAfterSelect
1524			if (!self.isFocused || !self.isOpen) {
1525				self.focus();
1526				e.preventDefault();
1527			}
1528		},
1529
1530		/**
1531		 * Triggered when the main control element
1532		 * has a mouse down event.
1533		 *
1534		 * @param {object} e
1535		 * @return {boolean}
1536		 */
1537		onMouseDown: function(e) {
1538			var self = this;
1539			var defaultPrevented = e.isDefaultPrevented();
1540			var $target = $(e.target);
1541
1542			if (self.isFocused) {
1543				// retain focus by preventing native handling. if the
1544				// event target is the input it should not be modified.
1545				// otherwise, text selection within the input won't work.
1546				if (e.target !== self.$control_input[0]) {
1547					if (self.settings.mode === 'single') {
1548						// toggle dropdown
1549						self.isOpen ? self.close() : self.open();
1550					} else if (!defaultPrevented) {
1551						self.setActiveItem(null);
1552					}
1553					return false;
1554				}
1555			} else {
1556				// give control focus
1557				if (!defaultPrevented) {
1558					window.setTimeout(function() {
1559						self.focus();
1560					}, 0);
1561				}
1562			}
1563		},
1564
1565		/**
1566		 * Triggered when the value of the control has been changed.
1567		 * This should propagate the event to the original DOM
1568		 * input / select element.
1569		 */
1570		onChange: function() {
1571			var self = this;
1572			if (self.getValue() !== "") {
1573				self.lastValidValue = self.getValue();
1574			}
1575			this.$input.trigger('input');
1576			this.$input.trigger('change');
1577		},
1578
1579		/**
1580		 * Triggered on <input> paste.
1581		 *
1582		 * @param {object} e
1583		 * @returns {boolean}
1584		 */
1585		onPaste: function(e) {
1586			var self = this;
1587
1588			if (self.isFull() || self.isInputHidden || self.isLocked) {
1589				e.preventDefault();
1590				return;
1591			}
1592
1593			// If a regex or string is included, this will split the pasted
1594			// input and create Items for each separate value
1595			if (self.settings.splitOn) {
1596
1597				// Wait for pasted text to be recognized in value
1598				setTimeout(function() {
1599					var pastedText = self.$control_input.val();
1600					if(!pastedText.match(self.settings.splitOn)){ return }
1601
1602					var splitInput = pastedText
1603						.trim()
1604						.split(self.settings.splitOn);
1605					for (var i = 0, n = splitInput.length; i < n; i++) {
1606						self.createItem(splitInput[i]);
1607					}
1608				}, 0);
1609			}
1610		},
1611
1612		/**
1613		 * Triggered on <input> keypress.
1614		 *
1615		 * @param {object} e
1616		 * @returns {boolean}
1617		 */
1618		onKeyPress: function(e) {
1619			if (this.isLocked) return e && e.preventDefault();
1620			var character = String.fromCharCode(e.keyCode || e.which);
1621			if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) {
1622				this.createItem();
1623				e.preventDefault();
1624				return false;
1625			}
1626		},
1627
1628		/**
1629		 * Triggered on <input> keydown.
1630		 *
1631		 * @param {object} e
1632		 * @returns {boolean}
1633		 */
1634		onKeyDown: function(e) {
1635			var isInput = e.target === this.$control_input[0];
1636			var self = this;
1637
1638			if (self.isLocked) {
1639				if (e.keyCode !== KEY_TAB) {
1640					e.preventDefault();
1641				}
1642				return;
1643			}
1644
1645			switch (e.keyCode) {
1646				case KEY_A:
1647					if (self.isCmdDown) {
1648						self.selectAll();
1649						return;
1650					}
1651					break;
1652				case KEY_ESC:
1653					if (self.isOpen) {
1654						e.preventDefault();
1655						e.stopPropagation();
1656						self.close();
1657					}
1658					return;
1659				case KEY_N:
1660					if (!e.ctrlKey || e.altKey) break;
1661				case KEY_DOWN:
1662					if (!self.isOpen && self.hasOptions) {
1663						self.open();
1664					} else if (self.$activeOption) {
1665						self.ignoreHover = true;
1666						var $next = self.getAdjacentOption(self.$activeOption, 1);
1667						if ($next.length) self.setActiveOption($next, true, true);
1668					}
1669					e.preventDefault();
1670					return;
1671				case KEY_P:
1672					if (!e.ctrlKey || e.altKey) break;
1673				case KEY_UP:
1674					if (self.$activeOption) {
1675						self.ignoreHover = true;
1676						var $prev = self.getAdjacentOption(self.$activeOption, -1);
1677						if ($prev.length) self.setActiveOption($prev, true, true);
1678					}
1679					e.preventDefault();
1680					return;
1681				case KEY_RETURN:
1682					if (self.isOpen && self.$activeOption) {
1683						self.onOptionSelect({currentTarget: self.$activeOption});
1684						e.preventDefault();
1685					}
1686					return;
1687				case KEY_LEFT:
1688					self.advanceSelection(-1, e);
1689					return;
1690				case KEY_RIGHT:
1691					self.advanceSelection(1, e);
1692					return;
1693				case KEY_TAB:
1694					if (self.settings.selectOnTab && self.isOpen && self.$activeOption) {
1695						self.onOptionSelect({currentTarget: self.$activeOption});
1696
1697						// Default behaviour is to jump to the next field, we only want this
1698						// if the current field doesn't accept any more entries
1699						if (!self.isFull()) {
1700							e.preventDefault();
1701						}
1702					}
1703					if (self.settings.create && self.createItem() && self.settings.showAddOptionOnCreate) {
1704						e.preventDefault();
1705					}
1706					return;
1707				case KEY_BACKSPACE:
1708				case KEY_DELETE:
1709					self.deleteSelection(e);
1710					return;
1711			}
1712
1713			if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) {
1714				e.preventDefault();
1715				return;
1716			}
1717		},
1718
1719		/**
1720		 * Triggered on <input> input.
1721		 *
1722		 * @param {object} e
1723		 * @returns {boolean}
1724		 */
1725		onInput: function(e) {
1726			var self = this;
1727
1728			var value = self.$control_input.val() || '';
1729			if (self.lastValue !== value) {
1730				self.lastValue = value;
1731				self.onSearchChange(value);
1732				self.refreshOptions();
1733				self.trigger('type', value);
1734			}
1735		},
1736
1737		/**
1738		 * Invokes the user-provide option provider / loader.
1739		 *
1740		 * Note: this function is debounced in the Selectize
1741		 * constructor (by `settings.loadThrottle` milliseconds)
1742		 *
1743		 * @param {string} value
1744		 */
1745		onSearchChange: function(value) {
1746			var self = this;
1747			var fn = self.settings.load;
1748			if (!fn) return;
1749			if (self.loadedSearches.hasOwnProperty(value)) return;
1750			self.loadedSearches[value] = true;
1751			self.load(function(callback) {
1752				fn.apply(self, [value, callback]);
1753			});
1754		},
1755
1756		/**
1757		 * Triggered on <input> focus.
1758		 *
1759		 * @param {object} e (optional)
1760		 * @returns {boolean}
1761		 */
1762		onFocus: function(e) {
1763			var self = this;
1764			var wasFocused = self.isFocused;
1765
1766			if (self.isDisabled) {
1767				self.blur();
1768				e && e.preventDefault();
1769				return false;
1770			}
1771
1772			if (self.ignoreFocus) return;
1773			self.isFocused = true;
1774			if (self.settings.preload === 'focus') self.onSearchChange('');
1775
1776			if (!wasFocused) self.trigger('focus');
1777
1778			if (!self.$activeItems.length) {
1779				self.showInput();
1780				self.setActiveItem(null);
1781				self.refreshOptions(!!self.settings.openOnFocus);
1782			}
1783
1784			self.refreshState();
1785		},
1786
1787		/**
1788		 * Triggered on <input> blur.
1789		 *
1790		 * @param {object} e
1791		 * @param {Element} dest
1792		 */
1793		onBlur: function(e, dest) {
1794			var self = this;
1795			if (!self.isFocused) return;
1796			self.isFocused = false;
1797
1798			if (self.ignoreFocus) {
1799				return;
1800			} else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) {
1801				// necessary to prevent IE closing the dropdown when the scrollbar is clicked
1802				self.ignoreBlur = true;
1803				self.onFocus(e);
1804				return;
1805			}
1806
1807			var deactivate = function() {
1808				self.close();
1809				self.setTextboxValue('');
1810				self.setActiveItem(null);
1811				self.setActiveOption(null);
1812				self.setCaret(self.items.length);
1813				self.refreshState();
1814
1815				// IE11 bug: element still marked as active
1816				dest && dest.focus && dest.focus();
1817
1818				self.isBlurring = false;
1819				self.ignoreFocus = false;
1820				self.trigger('blur');
1821			};
1822
1823			self.isBlurring = true;
1824			self.ignoreFocus = true;
1825			if (self.settings.create && self.settings.createOnBlur) {
1826				self.createItem(null, false, deactivate);
1827			} else {
1828				deactivate();
1829			}
1830		},
1831
1832		/**
1833		 * Triggered when the user rolls over
1834		 * an option in the autocomplete dropdown menu.
1835		 *
1836		 * @param {object} e
1837		 * @returns {boolean}
1838		 */
1839		onOptionHover: function(e) {
1840			if (this.ignoreHover) return;
1841			this.setActiveOption(e.currentTarget, false);
1842		},
1843
1844		/**
1845		 * Triggered when the user clicks on an option
1846		 * in the autocomplete dropdown menu.
1847		 *
1848		 * @param {object} e
1849		 * @returns {boolean}
1850		 */
1851		onOptionSelect: function(e) {
1852			var value, $target, $option, self = this;
1853
1854			if (e.preventDefault) {
1855				e.preventDefault();
1856				e.stopPropagation();
1857			}
1858
1859			$target = $(e.currentTarget);
1860			if ($target.hasClass('create')) {
1861				self.createItem(null, function() {
1862					if (self.settings.closeAfterSelect) {
1863						self.close();
1864					}
1865				});
1866			} else {
1867				value = $target.attr('data-value');
1868				if (typeof value !== 'undefined') {
1869					self.lastQuery = null;
1870					self.setTextboxValue('');
1871					self.addItem(value);
1872					if (self.settings.closeAfterSelect) {
1873						self.close();
1874					} else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) {
1875						self.setActiveOption(self.getOption(value));
1876					}
1877				}
1878			}
1879		},
1880
1881		/**
1882		 * Triggered when the user clicks on an item
1883		 * that has been selected.
1884		 *
1885		 * @param {object} e
1886		 * @returns {boolean}
1887		 */
1888		onItemSelect: function(e) {
1889			var self = this;
1890
1891			if (self.isLocked) return;
1892			if (self.settings.mode === 'multi') {
1893				e.preventDefault();
1894				self.setActiveItem(e.currentTarget, e);
1895			}
1896		},
1897
1898		/**
1899		 * Invokes the provided method that provides
1900		 * results to a callback---which are then added
1901		 * as options to the control.
1902		 *
1903		 * @param {function} fn
1904		 */
1905		load: function(fn) {
1906			var self = this;
1907			var $wrapper = self.$wrapper.addClass(self.settings.loadingClass);
1908
1909			self.loading++;
1910			fn.apply(self, [function(results) {
1911				self.loading = Math.max(self.loading - 1, 0);
1912				if (results && results.length) {
1913					self.addOption(results);
1914					self.refreshOptions(self.isFocused && !self.isInputHidden);
1915				}
1916				if (!self.loading) {
1917					$wrapper.removeClass(self.settings.loadingClass);
1918				}
1919				self.trigger('load', results);
1920			}]);
1921		},
1922
1923		/**
1924		 * Gets the value of input field of the control.
1925		 *
1926		 * @returns {string} value
1927		 */
1928		getTextboxValue: function() {
1929			var $input = this.$control_input;
1930			return $input.val();
1931		},
1932
1933		/**
1934		 * Sets the input field of the control to the specified value.
1935		 *
1936		 * @param {string} value
1937		 */
1938		setTextboxValue: function(value) {
1939			var $input = this.$control_input;
1940			var changed = $input.val() !== value;
1941			if (changed) {
1942				$input.val(value).triggerHandler('update');
1943				this.lastValue = value;
1944			}
1945		},
1946
1947		/**
1948		 * Returns the value of the control. If multiple items
1949		 * can be selected (e.g. <select multiple>), this returns
1950		 * an array. If only one item can be selected, this
1951		 * returns a string.
1952		 *
vendor: 27,022 bytes, lines 1953-2940
1953		 * @returns {mixed}
1954		 */
1955		getValue: function() {
1956			if (this.tagType === TAG_SELECT && this.$input.attr('multiple')) {
1957				return this.items;
1958			} else {
1959				return this.items.join(this.settings.delimiter);
1960			}
1961		},
1962
1963		/**
1964		 * Resets the selected items to the given value.
1965		 *
1966		 * @param {mixed} value
1967		 */
1968		setValue: function(value, silent) {
1969			var events = silent ? [] : ['change'];
1970
1971			debounce_events(this, events, function() {
1972				this.clear(silent);
1973				this.addItems(value, silent);
1974			});
1975		},
1976
1977		/**
1978		 * Resets the number of max items to the given value
1979		 *
1980		 * @param {number} value
1981		 */
1982		setMaxItems: function(value){
1983			if(value === 0) value = null; //reset to unlimited items.
1984			this.settings.maxItems = value;
1985			this.settings.mode = this.settings.mode || (this.settings.maxItems === 1 ? 'single' : 'multi');
1986			this.refreshState();
1987		},
1988
1989		/**
1990		 * Sets the selected item.
1991		 *
1992		 * @param {object} $item
1993		 * @param {object} e (optional)
1994		 */
1995		setActiveItem: function($item, e) {
1996			var self = this;
1997			var eventName;
1998			var i, idx, begin, end, item, swap;
1999			var $last;
2000
2001			if (self.settings.mode === 'single') return;
2002			$item = $($item);
2003
2004			// clear the active selection
2005			if (!$item.length) {
2006				$(self.$activeItems).removeClass('active');
2007				self.$activeItems = [];
2008				if (self.isFocused) {
2009					self.showInput();
2010				}
2011				return;
2012			}
2013
2014			// modify selection
2015			eventName = e && e.type.toLowerCase();
2016
2017			if (eventName === 'mousedown' && self.isShiftDown && self.$activeItems.length) {
2018				$last = self.$control.children('.active:last');
2019				begin = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$last[0]]);
2020				end   = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$item[0]]);
2021				if (begin > end) {
2022					swap  = begin;
2023					begin = end;
2024					end   = swap;
2025				}
2026				for (i = begin; i <= end; i++) {
2027					item = self.$control[0].childNodes[i];
2028					if (self.$activeItems.indexOf(item) === -1) {
2029						$(item).addClass('active');
2030						self.$activeItems.push(item);
2031					}
2032				}
2033				e.preventDefault();
2034			} else if ((eventName === 'mousedown' && self.isCtrlDown) || (eventName === 'keydown' && this.isShiftDown)) {
2035				if ($item.hasClass('active')) {
2036					idx = self.$activeItems.indexOf($item[0]);
2037					self.$activeItems.splice(idx, 1);
2038					$item.removeClass('active');
2039				} else {
2040					self.$activeItems.push($item.addClass('active')[0]);
2041				}
2042			} else {
2043				$(self.$activeItems).removeClass('active');
2044				self.$activeItems = [$item.addClass('active')[0]];
2045			}
2046
2047			// ensure control has focus
2048			self.hideInput();
2049			if (!this.isFocused) {
2050				self.focus();
2051			}
2052		},
2053
2054		/**
2055		 * Sets the selected item in the dropdown menu
2056		 * of available options.
2057		 *
2058		 * @param {object} $object
2059		 * @param {boolean} scroll
2060		 * @param {boolean} animate
2061		 */
2062		setActiveOption: function($option, scroll, animate) {
2063			var height_menu, height_item, y;
2064			var scroll_top, scroll_bottom;
2065			var self = this;
2066
2067			if (self.$activeOption) {
2068				self.$activeOption.removeClass('active');
2069				self.trigger('dropdown_item_deactivate', self.$activeOption.attr('data-value'));
2070			}
2071			self.$activeOption = null;
2072
2073			$option = $($option);
2074			if (!$option.length) return;
2075
2076			self.$activeOption = $option.addClass('active');
2077			if (self.isOpen) self.trigger('dropdown_item_activate', self.$activeOption.attr('data-value'));
2078
2079			if (scroll || !isset(scroll)) {
2080
2081				height_menu   = self.$dropdown_content.height();
2082				height_item   = self.$activeOption.outerHeight(true);
2083				scroll        = self.$dropdown_content.scrollTop() || 0;
2084				y             = self.$activeOption.offset().top - self.$dropdown_content.offset().top + scroll;
2085				scroll_top    = y;
2086				scroll_bottom = y - height_menu + height_item;
2087
2088				if (y + height_item > height_menu + scroll) {
2089					self.$dropdown_content.stop().animate({scrollTop: scroll_bottom}, animate ? self.settings.scrollDuration : 0);
2090				} else if (y < scroll) {
2091					self.$dropdown_content.stop().animate({scrollTop: scroll_top}, animate ? self.settings.scrollDuration : 0);
2092				}
2093
2094			}
2095		},
2096
2097		/**
2098		 * Selects all items (CTRL + A).
2099		 */
2100		selectAll: function() {
2101			var self = this;
2102			if (self.settings.mode === 'single') return;
2103
2104			self.$activeItems = Array.prototype.slice.apply(self.$control.children(':not(input)').addClass('active'));
2105			if (self.$activeItems.length) {
2106				self.hideInput();
2107				self.close();
2108			}
2109			self.focus();
2110		},
2111
2112		/**
2113		 * Hides the input element out of view, while
2114		 * retaining its focus.
2115		 */
2116		hideInput: function() {
2117			var self = this;
2118
2119			self.setTextboxValue('');
2120			self.$control_input.css({opacity: 0, position: 'absolute', left: self.rtl ? 10000 : -10000});
2121			self.isInputHidden = true;
2122		},
2123
2124		/**
2125		 * Restores input visibility.
2126		 */
2127		showInput: function() {
2128			this.$control_input.css({opacity: 1, position: 'relative', left: 0});
2129			this.isInputHidden = false;
2130		},
2131
2132		/**
2133		 * Gives the control focus.
2134		 */
2135		focus: function() {
2136			var self = this;
2137			if (self.isDisabled) return self;
2138
2139			self.ignoreFocus = true;
2140			self.$control_input[0].focus();
2141			window.setTimeout(function() {
2142				self.ignoreFocus = false;
2143				self.onFocus();
2144			}, 0);
2145			return self;
2146		},
2147
2148		/**
2149		 * Forces the control out of focus.
2150		 *
2151		 * @param {Element} dest
2152		 */
2153		blur: function(dest) {
2154			this.$control_input[0].blur();
2155			this.onBlur(null, dest);
2156			return this;
2157		},
2158
2159		/**
2160		 * Returns a function that scores an object
2161		 * to show how good of a match it is to the
2162		 * provided query.
2163		 *
2164		 * @param {string} query
2165		 * @param {object} options
2166		 * @return {function}
2167		 */
2168		getScoreFunction: function(query) {
2169			return this.sifter.getScoreFunction(query, this.getSearchOptions());
2170		},
2171
2172		/**
2173		 * Returns search options for sifter (the system
2174		 * for scoring and sorting results).
2175		 *
2176		 * @see https://github.com/brianreavis/sifter.js
2177		 * @return {object}
2178		 */
2179		getSearchOptions: function() {
2180			var settings = this.settings;
2181			var sort = settings.sortField;
2182			if (typeof sort === 'string') {
2183				sort = [{field: sort}];
2184			}
2185
2186			return {
2187				fields      : settings.searchField,
2188				conjunction : settings.searchConjunction,
2189				sort        : sort,
2190				nesting     : settings.nesting
2191			};
2192		},
2193
2194		/**
2195		 * Searches through available options and returns
2196		 * a sorted array of matches.
2197		 *
2198		 * Returns an object containing:
2199		 *
2200		 *   - query {string}
2201		 *   - tokens {array}
2202		 *   - total {int}
2203		 *   - items {array}
2204		 *
2205		 * @param {string} query
2206		 * @returns {object}
2207		 */
2208		search: function(query) {
2209			var i, value, score, result, calculateScore;
2210			var self     = this;
2211			var settings = self.settings;
2212			var options  = this.getSearchOptions();
2213
2214			// validate user-provided result scoring function
2215			if (settings.score) {
2216				calculateScore = self.settings.score.apply(this, [query]);
2217				if (typeof calculateScore !== 'function') {
2218					throw new Error('Selectize "score" setting must be a function that returns a function');
2219				}
2220			}
2221
2222			// perform search
2223			if (query !== self.lastQuery) {
2224				self.lastQuery = query;
2225				result = self.sifter.search(query, $.extend(options, {score: calculateScore}));
2226				self.currentResults = result;
2227			} else {
2228				result = $.extend(true, {}, self.currentResults);
2229			}
2230
2231			// filter out selected items
2232			if (settings.hideSelected) {
2233				for (i = result.items.length - 1; i >= 0; i--) {
2234					if (self.items.indexOf(hash_key(result.items[i].id)) !== -1) {
2235						result.items.splice(i, 1);
2236					}
2237				}
2238			}
2239
2240			return result;
2241		},
2242
2243		/**
2244		 * Refreshes the list of available options shown
2245		 * in the autocomplete dropdown menu.
2246		 *
2247		 * @param {boolean} triggerDropdown
2248		 */
2249		refreshOptions: function(triggerDropdown) {
2250			var i, j, k, n, groups, groups_order, option, option_html, optgroup, optgroups, html, html_children, has_create_option;
2251			var $active, $active_before, $create;
2252
2253			if (typeof triggerDropdown === 'undefined') {
2254				triggerDropdown = true;
2255			}
2256
2257			var self              = this;
2258			var query             = (self.$control_input.val()).trim();
2259			var results           = self.search(query);
2260			var $dropdown_content = self.$dropdown_content;
2261			var active_before     = self.$activeOption && hash_key(self.$activeOption.attr('data-value'));
2262
2263			// build markup
2264			n = results.items.length;
2265			if (typeof self.settings.maxOptions === 'number') {
2266				n = Math.min(n, self.settings.maxOptions);
2267			}
2268
2269			// render and group available options individually
2270			groups = {};
2271			groups_order = [];
2272
2273			for (i = 0; i < n; i++) {
2274				option      = self.options[results.items[i].id];
2275				option_html = self.render('option', option);
2276				optgroup    = option[self.settings.optgroupField] || '';
2277				optgroups   = Array.isArray(optgroup) ? optgroup : [optgroup];
2278
2279				for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
2280					optgroup = optgroups[j];
2281					if (!self.optgroups.hasOwnProperty(optgroup)) {
2282						optgroup = '';
2283					}
2284					if (!groups.hasOwnProperty(optgroup)) {
2285						groups[optgroup] = document.createDocumentFragment();
2286						groups_order.push(optgroup);
2287					}
2288					groups[optgroup].appendChild(option_html);
2289				}
2290			}
2291
2292			// sort optgroups
2293			if (this.settings.lockOptgroupOrder) {
2294				groups_order.sort(function(a, b) {
2295					var a_order = self.optgroups[a] && self.optgroups[a].$order || 0;
2296					var b_order = self.optgroups[b] && self.optgroups[b].$order || 0;
2297					return a_order - b_order;
2298				});
2299			}
2300
2301			// render optgroup headers & join groups
2302			html = document.createDocumentFragment();
2303			for (i = 0, n = groups_order.length; i < n; i++) {
2304				optgroup = groups_order[i];
2305				if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].childNodes.length) {
2306					// render the optgroup header and options within it,
2307					// then pass it to the wrapper template
2308					html_children = document.createDocumentFragment();
2309					html_children.appendChild(self.render('optgroup_header', self.optgroups[optgroup]));
2310					html_children.appendChild(groups[optgroup]);
2311
2312					html.appendChild(self.render('optgroup', $.extend({}, self.optgroups[optgroup], {
2313						html: domToString(html_children),
2314						dom:  html_children
2315					})));
2316				} else {
2317					html.appendChild(groups[optgroup]);
2318				}
2319			}
2320
2321			$dropdown_content.html(html);
2322
2323			// highlight matching terms inline
2324			if (self.settings.highlight) {
2325				$dropdown_content.removeHighlight();
2326				if (results.query.length && results.tokens.length) {
2327					for (i = 0, n = results.tokens.length; i < n; i++) {
2328						highlight($dropdown_content, results.tokens[i].regex);
2329					}
2330				}
2331			}
2332
2333			// add "selected" class to selected options
2334			if (!self.settings.hideSelected) {
2335				// clear selection on all previously selected elements first
2336				self.$dropdown.find('.selected').removeClass('selected');
2337
2338				for (i = 0, n = self.items.length; i < n; i++) {
2339					self.getOption(self.items[i]).addClass('selected');
2340				}
2341			}
2342
2343			// add create option
2344			has_create_option = self.canCreate(query);
2345			if (has_create_option) {
2346				if(self.settings.showAddOptionOnCreate) {
2347					$dropdown_content.prepend(self.render('option_create', {input: query}));
2348					$create = $($dropdown_content[0].childNodes[0]);
2349				}
2350			}
2351
2352			// activate
2353			self.hasOptions = results.items.length > 0 || ( has_create_option && self.settings.showAddOptionOnCreate );
2354			if (self.hasOptions) {
2355				if (results.items.length > 0) {
2356					$active_before = active_before && self.getOption(active_before);
2357					if (results.query !== "" && $active_before && $active_before.length) {
2358						$active = $active_before;
2359					} else if (self.settings.mode === 'single' && self.items.length) {
2360						$active = self.getOption(self.items[0]);
2361					}
2362					if (!$active || !$active.length) {
2363						if ($create && !self.settings.addPrecedence) {
2364							$active = self.getAdjacentOption($create, 1);
2365						} else {
2366							$active = $dropdown_content.find('[data-selectable]:first');
2367						}
2368					}
2369				} else {
2370					$active = $create;
2371				}
2372				self.setActiveOption($active);
2373				if (triggerDropdown && !self.isOpen) { self.open(); }
2374			} else {
2375				self.setActiveOption(null);
2376				if (triggerDropdown && self.isOpen) { self.close(); }
2377			}
2378		},
2379
2380		/**
2381		 * Adds an available option. If it already exists,
2382		 * nothing will happen. Note: this does not refresh
2383		 * the options list dropdown (use `refreshOptions`
2384		 * for that).
2385		 *
2386		 * Usage:
2387		 *
2388		 *   this.addOption(data)
2389		 *
2390		 * @param {object|array} data
2391		 */
2392		addOption: function(data) {
2393			var i, n, value, self = this;
2394
2395			if (Array.isArray(data)) {
2396				for (i = 0, n = data.length; i < n; i++) {
2397					self.addOption(data[i]);
2398				}
2399				return;
2400			}
2401
2402			if (value = self.registerOption(data)) {
2403				self.userOptions[value] = true;
2404				self.lastQuery = null;
2405				self.trigger('option_add', value, data);
2406			}
2407		},
2408
2409		/**
2410		 * Registers an option to the pool of options.
2411		 *
2412		 * @param {object} data
2413		 * @return {boolean|string}
2414		 */
2415		registerOption: function(data) {
2416			var key = hash_key(data[this.settings.valueField]);
2417			if (typeof key === 'undefined' || key === null || this.options.hasOwnProperty(key)) return false;
2418			data.$order = data.$order || ++this.order;
2419			this.options[key] = data;
2420			return key;
2421		},
2422
2423		/**
2424		 * Registers an option group to the pool of option groups.
2425		 *
2426		 * @param {object} data
2427		 * @return {boolean|string}
2428		 */
2429		registerOptionGroup: function(data) {
2430			var key = hash_key(data[this.settings.optgroupValueField]);
2431			if (!key) return false;
2432
2433			data.$order = data.$order || ++this.order;
2434			this.optgroups[key] = data;
2435			return key;
2436		},
2437
2438		/**
2439		 * Registers a new optgroup for options
2440		 * to be bucketed into.
2441		 *
2442		 * @param {string} id
2443		 * @param {object} data
2444		 */
2445		addOptionGroup: function(id, data) {
2446			data[this.settings.optgroupValueField] = id;
2447			if (id = this.registerOptionGroup(data)) {
2448				this.trigger('optgroup_add', id, data);
2449			}
2450		},
2451
2452		/**
2453		 * Removes an existing option group.
2454		 *
2455		 * @param {string} id
2456		 */
2457		removeOptionGroup: function(id) {
2458			if (this.optgroups.hasOwnProperty(id)) {
2459				delete this.optgroups[id];
2460				this.renderCache = {};
2461				this.trigger('optgroup_remove', id);
2462			}
2463		},
2464
2465		/**
2466		 * Clears all existing option groups.
2467		 */
2468		clearOptionGroups: function() {
2469			this.optgroups = {};
2470			this.renderCache = {};
2471			this.trigger('optgroup_clear');
2472		},
2473
2474		/**
2475		 * Updates an option available for selection. If
2476		 * it is visible in the selected items or options
2477		 * dropdown, it will be re-rendered automatically.
2478		 *
2479		 * @param {string} value
2480		 * @param {object} data
2481		 */
2482		updateOption: function(value, data) {
2483			var self = this;
2484			var $item, $item_new;
2485			var value_new, index_item, cache_items, cache_options, order_old;
2486
2487			value     = hash_key(value);
2488			value_new = hash_key(data[self.settings.valueField]);
2489
2490			// sanity checks
2491			if (value === null) return;
2492			if (!self.options.hasOwnProperty(value)) return;
2493			if (typeof value_new !== 'string') throw new Error('Value must be set in option data');
2494
2495			order_old = self.options[value].$order;
2496
2497			// update references
2498			if (value_new !== value) {
2499				delete self.options[value];
2500				index_item = self.items.indexOf(value);
2501				if (index_item !== -1) {
2502					self.items.splice(index_item, 1, value_new);
2503				}
2504			}
2505			data.$order = data.$order || order_old;
2506			self.options[value_new] = data;
2507
2508			// invalidate render cache
2509			cache_items = self.renderCache['item'];
2510			cache_options = self.renderCache['option'];
2511
2512			if (cache_items) {
2513				delete cache_items[value];
2514				delete cache_items[value_new];
2515			}
2516			if (cache_options) {
2517				delete cache_options[value];
2518				delete cache_options[value_new];
2519			}
2520
2521			// update the item if it's selected
2522			if (self.items.indexOf(value_new) !== -1) {
2523				$item = self.getItem(value);
2524				$item_new = $(self.render('item', data));
2525				if ($item.hasClass('active')) $item_new.addClass('active');
2526				$item.replaceWith($item_new);
2527			}
2528
2529			// invalidate last query because we might have updated the sortField
2530			self.lastQuery = null;
2531
2532			// update dropdown contents
2533			if (self.isOpen) {
2534				self.refreshOptions(false);
2535			}
2536		},
2537
2538		/**
2539		 * Removes a single option.
2540		 *
2541		 * @param {string} value
2542		 * @param {boolean} silent
2543		 */
2544		removeOption: function(value, silent) {
2545			var self = this;
2546			value = hash_key(value);
2547
2548			var cache_items = self.renderCache['item'];
2549			var cache_options = self.renderCache['option'];
2550			if (cache_items) delete cache_items[value];
2551			if (cache_options) delete cache_options[value];
2552
2553			delete self.userOptions[value];
2554			delete self.options[value];
2555			self.lastQuery = null;
2556			self.trigger('option_remove', value);
2557			self.removeItem(value, silent);
2558		},
2559
2560		/**
2561		 * Clears all options.
2562		 *
2563		 * @param {boolean} silent
2564		 */
2565		clearOptions: function(silent) {
2566			var self = this;
2567
2568			self.loadedSearches = {};
2569			self.userOptions = {};
2570			self.renderCache = {};
2571			var options = self.options;
2572			$.each(self.options, function(key, value) {
2573				if(self.items.indexOf(key) == -1) {
2574					delete options[key];
2575				}
2576			});
2577			self.options = self.sifter.items = options;
2578			self.lastQuery = null;
2579			self.trigger('option_clear');
2580			self.clear(silent);
2581		},
2582
2583		/**
2584		 * Returns the jQuery element of the option
2585		 * matching the given value.
2586		 *
2587		 * @param {string} value
2588		 * @returns {object}
2589		 */
2590		getOption: function(value) {
2591			return this.getElementWithValue(value, this.$dropdown_content.find('[data-selectable]'));
2592		},
2593
2594		/**
2595		 * Returns the jQuery element of the first
2596		 * selectable option.
2597		 *
2598		 * @return {object}
2599		 */
2600		getFirstOption: function() {
2601			var $options = this.$dropdown.find('[data-selectable]');
2602			return $options.length > 0 ? $options.eq(0) : $();
2603		},
2604
2605		/**
2606		 * Returns the jQuery element of the next or
2607		 * previous selectable option.
2608		 *
2609		 * @param {object} $option
2610		 * @param {int} direction  can be 1 for next or -1 for previous
2611		 * @return {object}
2612		 */
2613		getAdjacentOption: function($option, direction) {
2614			var $options = this.$dropdown.find('[data-selectable]');
2615			var index    = $options.index($option) + direction;
2616
2617			return index >= 0 && index < $options.length ? $options.eq(index) : $();
2618		},
2619
2620		/**
2621		 * Finds the first element with a "data-value" attribute
2622		 * that matches the given value.
2623		 *
2624		 * @param {mixed} value
2625		 * @param {object} $els
2626		 * @return {object}
2627		 */
2628		getElementWithValue: function(value, $els) {
2629			value = hash_key(value);
2630
2631			if (typeof value !== 'undefined' && value !== null) {
2632				for (var i = 0, n = $els.length; i < n; i++) {
2633					if ($els[i].getAttribute('data-value') === value) {
2634						return $($els[i]);
2635					}
2636				}
2637			}
2638
2639			return $();
2640		},
2641
2642		/**
2643		 * Finds the first element with a "textContent" property
2644		 * that matches the given textContent value.
2645		 *
2646		 * @param {mixed} textContent
2647		 * @param {boolean} ignoreCase
2648		 * @param {object} $els
2649		 * @return {object}
2650		 */
2651		getElementWithTextContent: function(textContent, ignoreCase ,$els) {
2652			textContent = hash_key(textContent);
2653
2654			if (typeof textContent !== 'undefined' && textContent !== null) {
2655				for (var i = 0, n = $els.length; i < n; i++) {
2656					var eleTextContent = $els[i].textContent
2657					if (ignoreCase == true) {
2658						eleTextContent = (eleTextContent !== null) ? eleTextContent.toLowerCase() : null;
2659						textContent = textContent.toLowerCase();
2660					}
2661					if (eleTextContent === textContent) {
2662						return $($els[i]);
2663					}
2664				}
2665			}
2666
2667			return $();
2668		},
2669
2670		/**
2671		 * Returns the jQuery element of the item
2672		 * matching the given value.
2673		 *
2674		 * @param {string} value
2675		 * @returns {object}
2676		 */
2677		getItem: function(value) {
2678			return this.getElementWithValue(value, this.$control.children());
2679		},
2680
2681		/**
2682		 * Returns the jQuery element of the item
2683		 * matching the given textContent.
2684		 *
2685		 * @param {string} value
2686		 * @param {boolean} ignoreCase
2687		 * @returns {object}
2688		 */
2689		getFirstItemMatchedByTextContent: function(textContent, ignoreCase) {
2690			ignoreCase = (ignoreCase !== null && ignoreCase === true) ? true : false;
2691			return this.getElementWithTextContent(textContent, ignoreCase, this.$dropdown_content.find('[data-selectable]'));
2692		},
2693
2694		/**
2695		 * "Selects" multiple items at once. Adds them to the list
2696		 * at the current caret position.
2697		 *
2698		 * @param {string} value
2699		 * @param {boolean} silent
2700		 */
2701		addItems: function(values, silent) {
2702			this.buffer = document.createDocumentFragment();
2703
2704			var childNodes = this.$control[0].childNodes;
2705			for (var i = 0; i < childNodes.length; i++) {
2706				this.buffer.appendChild(childNodes[i]);
2707			}
2708
2709			var items = Array.isArray(values) ? values : [values];
2710			for (var i = 0, n = items.length; i < n; i++) {
2711				this.isPending = (i < n - 1);
2712				this.addItem(items[i], silent);
2713			}
2714
2715			var control = this.$control[0];
2716			control.insertBefore(this.buffer, control.firstChild);
2717
2718			this.buffer = null;
2719		},
2720
2721		/**
2722		 * "Selects" an item. Adds it to the list
2723		 * at the current caret position.
2724		 *
2725		 * @param {string} value
2726		 * @param {boolean} silent
2727		 */
2728		addItem: function(value, silent) {
2729			var events = silent ? [] : ['change'];
2730
2731			debounce_events(this, events, function() {
2732				var $item, $option, $options;
2733				var self = this;
2734				var inputMode = self.settings.mode;
2735				var i, active, value_next, wasFull;
2736				value = hash_key(value);
2737
2738				if (self.items.indexOf(value) !== -1) {
2739					if (inputMode === 'single') self.close();
2740					return;
2741				}
2742
2743				if (!self.options.hasOwnProperty(value)) return;
2744				if (inputMode === 'single') self.clear(silent);
2745				if (inputMode === 'multi' && self.isFull()) return;
2746
2747				$item = $(self.render('item', self.options[value]));
2748				wasFull = self.isFull();
2749				self.items.splice(self.caretPos, 0, value);
2750				self.insertAtCaret($item);
2751				if (!self.isPending || (!wasFull && self.isFull())) {
2752					self.refreshState();
2753				}
2754
2755				if (self.isSetup) {
2756					$options = self.$dropdown_content.find('[data-selectable]');
2757
2758					// update menu / remove the option (if this is not one item being added as part of series)
2759					if (!self.isPending) {
2760						$option = self.getOption(value);
2761						value_next = self.getAdjacentOption($option, 1).attr('data-value');
2762						self.refreshOptions(self.isFocused && inputMode !== 'single');
2763						if (value_next) {
2764							self.setActiveOption(self.getOption(value_next));
2765						}
2766					}
2767
2768					// hide the menu if the maximum number of items have been selected or no options are left
2769					if (!$options.length || self.isFull()) {
2770						self.close();
2771					} else if (!self.isPending) {
2772						self.positionDropdown();
2773					}
2774
2775					self.updatePlaceholder();
2776					self.trigger('item_add', value, $item);
2777
2778					if (!self.isPending) {
2779						self.updateOriginalInput({silent: silent});
2780					}
2781				}
2782			});
2783		},
2784
2785		/**
2786		 * Removes the selected item matching
2787		 * the provided value.
2788		 *
2789		 * @param {string} value
2790		 */
2791		removeItem: function(value, silent) {
2792			var self = this;
2793			var $item, i, idx;
2794
2795			$item = (value instanceof $) ? value : self.getItem(value);
2796			value = hash_key($item.attr('data-value'));
2797			i = self.items.indexOf(value);
2798
2799			if (i !== -1) {
2800				self.trigger('item_before_remove', value, $item);
2801				$item.remove();
2802				if ($item.hasClass('active')) {
2803					idx = self.$activeItems.indexOf($item[0]);
2804					self.$activeItems.splice(idx, 1);
2805				}
2806
2807				self.items.splice(i, 1);
2808				self.lastQuery = null;
2809				if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
2810					self.removeOption(value, silent);
2811				}
2812
2813				if (i < self.caretPos) {
2814					self.setCaret(self.caretPos - 1);
2815				}
2816
2817				self.refreshState();
2818				self.updatePlaceholder();
2819				self.updateOriginalInput({silent: silent});
2820				self.positionDropdown();
2821				self.trigger('item_remove', value, $item);
2822			}
2823		},
2824
2825		/**
2826		 * Invokes the `create` method provided in the
2827		 * selectize options that should provide the data
2828		 * for the new item, given the user input.
2829		 *
2830		 * Once this completes, it will be added
2831		 * to the item list.
2832		 *
2833		 * @param {string} value
2834		 * @param {boolean} [triggerDropdown]
2835		 * @param {function} [callback]
2836		 * @return {boolean}
2837		 */
2838		createItem: function(input, triggerDropdown) {
2839			var self  = this;
2840			var caret = self.caretPos;
2841			input = input || (self.$control_input.val() || '').trim();
2842
2843			var callback = arguments[arguments.length - 1];
2844			if (typeof callback !== 'function') callback = function() {};
2845
2846			if (typeof triggerDropdown !== 'boolean') {
2847				triggerDropdown = true;
2848			}
2849
2850			if (!self.canCreate(input)) {
2851				callback();
2852				return false;
2853			}
2854
2855			self.lock();
2856
2857			var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) {
2858				var data = {};
2859				data[self.settings.labelField] = input;
2860				var key = input;
2861				if ( self.settings.formatValueToKey && typeof self.settings.formatValueToKey === 'function' ) {
2862					key = self.settings.formatValueToKey.apply(this, [key]);
2863					if (key === null || typeof key === 'undefined' || typeof key === 'object' || typeof key === 'function') {
2864						throw new Error('Selectize "formatValueToKey" setting must be a function that returns a value other than object or function.');
2865					}
2866				}
2867				data[self.settings.valueField] = key;
2868				return data;
2869			};
2870
2871			var create = once(function(data) {
2872				self.unlock();
2873
2874				if (!data || typeof data !== 'object') return callback();
2875				var value = hash_key(data[self.settings.valueField]);
2876				if (typeof value !== 'string') return callback();
2877
2878				self.setTextboxValue('');
2879				self.addOption(data);
2880				self.setCaret(caret);
2881				self.addItem(value);
2882				self.refreshOptions(triggerDropdown && self.settings.mode !== 'single');
2883				callback(data);
2884			});
2885
2886			var output = setup.apply(this, [input, create]);
2887			if (typeof output !== 'undefined') {
2888				create(output);
2889			}
2890
2891			return true;
2892		},
2893
2894		/**
2895		 * Re-renders the selected item lists.
2896		 */
2897		refreshItems: function() {
2898			this.lastQuery = null;
2899
2900			if (this.isSetup) {
2901				this.addItem(this.items);
2902			}
2903
2904			this.refreshState();
2905			this.updateOriginalInput();
2906		},
2907
2908		/**
2909		 * Updates all state-dependent attributes
2910		 * and CSS classes.
2911		 */
2912		refreshState: function() {
2913			this.refreshValidityState();
2914			this.refreshClasses();
2915		},
2916
2917		/**
2918		 * Update the `required` attribute of both input and control input.
2919		 *
2920		 * The `required` property needs to be activated on the control input
2921		 * for the error to be displayed at the right place. `required` also
2922		 * needs to be temporarily deactivated on the input since the input is
2923		 * hidden and can't show errors.
2924		 */
2925		refreshValidityState: function() {
2926			if (!this.isRequired) return false;
2927
2928			var invalid = !this.items.length;
2929
2930			this.isInvalid = invalid;
2931			this.$control_input.prop('required', invalid);
2932			this.$input.prop('required', !invalid);
2933		},
2934
2935		/**
2936		 * Updates all state-dependent CSS classes.
2937		 */
2938		refreshClasses: function() {
2939			var self     = this;
2940			var isFull   = self.isFull();
vendor: 18,191 bytes, lines 2941-3611
2941			var isLocked = self.isLocked;
2942
2943			self.$wrapper
2944				.toggleClass('rtl', self.rtl);
2945
2946			self.$control
2947				.toggleClass('focus', self.isFocused)
2948				.toggleClass('disabled', self.isDisabled)
2949				.toggleClass('required', self.isRequired)
2950				.toggleClass('invalid', self.isInvalid)
2951				.toggleClass('locked', isLocked)
2952				.toggleClass('full', isFull).toggleClass('not-full', !isFull)
2953				.toggleClass('input-active', self.isFocused && !self.isInputHidden)
2954				.toggleClass('dropdown-active', self.isOpen)
2955				.toggleClass('has-options', !$.isEmptyObject(self.options))
2956				.toggleClass('has-items', self.items.length > 0);
2957
2958			self.$control_input.data('grow', !isFull && !isLocked);
2959		},
2960
2961		/**
2962		 * Determines whether or not more items can be added
2963		 * to the control without exceeding the user-defined maximum.
2964		 *
2965		 * @returns {boolean}
2966		 */
2967		isFull: function() {
2968			return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
2969		},
2970
2971		/**
2972		 * Refreshes the original <select> or <input>
2973		 * element to reflect the current state.
2974		 */
2975		updateOriginalInput: function(opts) {
2976			var i, n, options, label, self = this;
2977			opts = opts || {};
2978
2979			if (self.tagType === TAG_SELECT) {
2980				options = [];
2981				for (i = 0, n = self.items.length; i < n; i++) {
2982					label = self.options[self.items[i]][self.settings.labelField] || '';
2983					options.push('<option value="' + escape_html(self.items[i]) + '" selected="selected">' + escape_html(label) + '</option>');
2984				}
2985				if (!options.length && !this.$input.attr('multiple')) {
2986					options.push('<option value="" selected="selected"></option>');
2987				}
2988				self.$input.html(options.join(''));
2989			} else {
2990				self.$input.val(self.getValue());
2991				self.$input.attr('value',self.$input.val());
2992			}
2993
2994			if (self.isSetup) {
2995				if (!opts.silent) {
2996					self.trigger('change', self.$input.val());
2997				}
2998			}
2999		},
3000
3001		/**
3002		 * Shows/hide the input placeholder depending
3003		 * on if there items in the list already.
3004		 */
3005		updatePlaceholder: function() {
3006			if (!this.settings.placeholder) return;
3007			var $input = this.$control_input;
3008
3009			if (this.items.length) {
3010				$input.removeAttr('placeholder');
3011			} else {
3012				$input.attr('placeholder', this.settings.placeholder);
3013			}
3014			$input.triggerHandler('update', {force: true});
3015		},
3016
3017		/**
3018		 * Shows the autocomplete dropdown containing
3019		 * the available options.
3020		 */
3021		open: function() {
3022			var self = this;
3023
3024			if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return;
3025			self.focus();
3026			self.isOpen = true;
3027			self.refreshState();
3028			self.$dropdown.css({visibility: 'hidden', display: 'block'});
3029			self.positionDropdown();
3030			self.$dropdown.css({visibility: 'visible'});
3031			self.trigger('dropdown_open', self.$dropdown);
3032		},
3033
3034		/**
3035		 * Closes the autocomplete dropdown menu.
3036		 */
3037		close: function() {
3038			var self = this;
3039			var trigger = self.isOpen;
3040
3041			if (self.settings.mode === 'single' && self.items.length) {
3042				self.hideInput();
3043
3044				// Do not trigger blur while inside a blur event,
3045				// this fixes some weird tabbing behavior in FF and IE.
3046				// See #1164
3047				if (self.isBlurring) {
3048					self.$control_input.blur(); // close keyboard on iOS
3049				}
3050			}
3051
3052			self.isOpen = false;
3053			self.$dropdown.hide();
3054			self.setActiveOption(null);
3055			self.refreshState();
3056
3057			if (trigger) self.trigger('dropdown_close', self.$dropdown);
3058		},
3059
3060		/**
3061		 * Calculates and applies the appropriate
3062		 * position of the dropdown.
3063		 */
3064		positionDropdown: function() {
3065			var $control = this.$control;
3066			var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
3067			offset.top += $control.outerHeight(true);
3068
3069			this.$dropdown.css({
3070				width : $control[0].getBoundingClientRect().width,
3071				top   : offset.top,
3072				left  : offset.left
3073			});
3074		},
3075
3076		/**
3077		 * Resets / clears all selected items
3078		 * from the control.
3079		 *
3080		 * @param {boolean} silent
3081		 */
3082		clear: function(silent) {
3083			var self = this;
3084
3085			if (!self.items.length) return;
3086			self.$control.children(':not(input)').remove();
3087			self.items = [];
3088			self.lastQuery = null;
3089			self.setCaret(0);
3090			self.setActiveItem(null);
3091			self.updatePlaceholder();
3092			self.updateOriginalInput({silent: silent});
3093			self.refreshState();
3094			self.showInput();
3095			self.trigger('clear');
3096		},
3097
3098		/**
3099		 * A helper method for inserting an element
3100		 * at the current caret position.
3101		 *
3102		 * @param {object} $el
3103		 */
3104		insertAtCaret: function($el) {
3105			var caret = Math.min(this.caretPos, this.items.length);
3106			var el = $el[0];
3107			var target = this.buffer || this.$control[0];
3108
3109			if (caret === 0) {
3110				target.insertBefore(el, target.firstChild);
3111			} else {
3112				target.insertBefore(el, target.childNodes[caret]);
3113			}
3114
3115			this.setCaret(caret + 1);
3116		},
3117
3118		/**
3119		 * Removes the current selected item(s).
3120		 *
3121		 * @param {object} e (optional)
3122		 * @returns {boolean}
3123		 */
3124		deleteSelection: function(e) {
3125			var i, n, direction, selection, values, caret, option_select, $option_select, $tail;
3126			var self = this;
3127
3128			direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1;
3129			selection = getSelection(self.$control_input[0]);
3130
3131			if (self.$activeOption && !self.settings.hideSelected) {
3132				if (typeof self.settings.deselectBehavior === 'string' && self.settings.deselectBehavior === 'top') {
3133					option_select = self.getFirstOption().attr('data-value');
3134				} else {
3135					option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value');
3136				}
3137			}
3138
3139			// determine items that will be removed
3140			values = [];
3141
3142			if (self.$activeItems.length) {
3143				$tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first'));
3144				caret = self.$control.children(':not(input)').index($tail);
3145				if (direction > 0) { caret++; }
3146
3147				for (i = 0, n = self.$activeItems.length; i < n; i++) {
3148					values.push($(self.$activeItems[i]).attr('data-value'));
3149				}
3150				if (e) {
3151					e.preventDefault();
3152					e.stopPropagation();
3153				}
3154			} else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
3155				if (direction < 0 && selection.start === 0 && selection.length === 0) {
3156					values.push(self.items[self.caretPos - 1]);
3157				} else if (direction > 0 && selection.start === self.$control_input.val().length) {
3158					values.push(self.items[self.caretPos]);
3159				}
3160			}
3161
3162			// allow the callback to abort
3163			if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) {
3164				return false;
3165			}
3166
3167			// perform removal
3168			if (typeof caret !== 'undefined') {
3169				self.setCaret(caret);
3170			}
3171			while (values.length) {
3172				self.removeItem(values.pop());
3173			}
3174
3175			self.showInput();
3176			self.positionDropdown();
3177			self.refreshOptions(true);
3178
3179			// select previous option
3180			if (option_select) {
3181				$option_select = self.getOption(option_select);
3182				if ($option_select.length) {
3183					self.setActiveOption($option_select);
3184				}
3185			}
3186
3187			return true;
3188		},
3189
3190		/**
3191		 * Selects the previous / next item (depending
3192		 * on the `direction` argument).
3193		 *
3194		 * > 0 - right
3195		 * < 0 - left
3196		 *
3197		 * @param {int} direction
3198		 * @param {object} e (optional)
3199		 */
3200		advanceSelection: function(direction, e) {
3201			var tail, selection, idx, valueLength, cursorAtEdge, $tail;
3202			var self = this;
3203
3204			if (direction === 0) return;
3205			if (self.rtl) direction *= -1;
3206
3207			tail = direction > 0 ? 'last' : 'first';
3208			selection = getSelection(self.$control_input[0]);
3209
3210			if (self.isFocused && !self.isInputHidden) {
3211				valueLength = self.$control_input.val().length;
3212				cursorAtEdge = direction < 0
3213					? selection.start === 0 && selection.length === 0
3214					: selection.start === valueLength;
3215
3216				if (cursorAtEdge && !valueLength) {
3217					self.advanceCaret(direction, e);
3218				}
3219			} else {
3220				$tail = self.$control.children('.active:' + tail);
3221				if ($tail.length) {
3222					idx = self.$control.children(':not(input)').index($tail);
3223					self.setActiveItem(null);
3224					self.setCaret(direction > 0 ? idx + 1 : idx);
3225				}
3226			}
3227		},
3228
3229		/**
3230		 * Moves the caret left / right.
3231		 *
3232		 * @param {int} direction
3233		 * @param {object} e (optional)
3234		 */
3235		advanceCaret: function(direction, e) {
3236			var self = this, fn, $adj;
3237
3238			if (direction === 0) return;
3239
3240			fn = direction > 0 ? 'next' : 'prev';
3241			if (self.isShiftDown) {
3242				$adj = self.$control_input[fn]();
3243				if ($adj.length) {
3244					self.hideInput();
3245					self.setActiveItem($adj);
3246					e && e.preventDefault();
3247				}
3248			} else {
3249				self.setCaret(self.caretPos + direction);
3250			}
3251		},
3252
3253		/**
3254		 * Moves the caret to the specified index.
3255		 *
3256		 * @param {int} i
3257		 */
3258		setCaret: function(i) {
3259			var self = this;
3260
3261			if (self.settings.mode === 'single') {
3262				i = self.items.length;
3263			} else {
3264				i = Math.max(0, Math.min(self.items.length, i));
3265			}
3266
3267			if(!self.isPending) {
3268				// the input must be moved by leaving it in place and moving the
3269				// siblings, due to the fact that focus cannot be restored once lost
3270				// on mobile webkit devices
3271				var j, n, fn, $children, $child;
3272				$children = self.$control.children(':not(input)');
3273				for (j = 0, n = $children.length; j < n; j++) {
3274					$child = $($children[j]).detach();
3275					if (j <  i) {
3276						self.$control_input.before($child);
3277					} else {
3278						self.$control.append($child);
3279					}
3280				}
3281			}
3282
3283			self.caretPos = i;
3284		},
3285
3286		/**
3287		 * Disables user input on the control. Used while
3288		 * items are being asynchronously created.
3289		 */
3290		lock: function() {
3291			this.close();
3292			this.isLocked = true;
3293			this.refreshState();
3294		},
3295
3296		/**
3297		 * Re-enables user input on the control.
3298		 */
3299		unlock: function() {
3300			this.isLocked = false;
3301			this.refreshState();
3302		},
3303
3304		/**
3305		 * Disables user input on the control completely.
3306		 * While disabled, it cannot receive focus.
3307		 */
3308		disable: function() {
3309			var self = this;
3310			self.$input.prop('disabled', true);
3311			self.$control_input.prop('disabled', true).prop('tabindex', -1);
3312			self.isDisabled = true;
3313			self.lock();
3314		},
3315
3316		/**
3317		 * Enables the control so that it can respond
3318		 * to focus and user input.
3319		 */
3320		enable: function() {
3321			var self = this;
3322			self.$input.prop('disabled', false);
3323			self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex);
3324			self.isDisabled = false;
3325			self.unlock();
3326		},
3327
3328		/**
3329		 * Completely destroys the control and
3330		 * unbinds all event listeners so that it can
3331		 * be garbage collected.
3332		 */
3333		destroy: function() {
3334			var self = this;
3335			var eventNS = self.eventNS;
3336			var revertSettings = self.revertSettings;
3337
3338			self.trigger('destroy');
3339			self.off();
3340			self.$wrapper.remove();
3341			self.$dropdown.remove();
3342
3343			self.$input
3344				.html('')
3345				.append(revertSettings.$children)
3346				.removeAttr('tabindex')
3347				.removeClass('selectized')
3348				.attr({tabindex: revertSettings.tabindex})
3349				.show();
3350
3351			self.$control_input.removeData('grow');
3352			self.$input.removeData('selectize');
3353
3354			if (--Selectize.count == 0 && Selectize.$testInput) {
3355				Selectize.$testInput.remove();
3356				Selectize.$testInput = undefined;
3357			}
3358
3359			$(window).off(eventNS);
3360			$(document).off(eventNS);
3361			$(document.body).off(eventNS);
3362
3363			delete self.$input[0].selectize;
3364		},
3365
3366		/**
3367		 * A helper method for rendering "item" and
3368		 * "option" templates, given the data.
3369		 *
3370		 * @param {string} templateName
3371		 * @param {object} data
3372		 * @returns {string}
3373		 */
3374		render: function(templateName, data) {
3375			var value, id, label;
3376			var html = '';
3377			var cache = false;
3378			var self = this;
3379			var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
3380
3381			if (templateName === 'option' || templateName === 'item') {
3382				value = hash_key(data[self.settings.valueField]);
3383				cache = !!value;
3384			}
3385
3386			// pull markup from cache if it exists
3387			if (cache) {
3388				if (!isset(self.renderCache[templateName])) {
3389					self.renderCache[templateName] = {};
3390				}
3391				if (self.renderCache[templateName].hasOwnProperty(value)) {
3392					return self.renderCache[templateName][value];
3393				}
3394			}
3395
3396			// render markup
3397			html = $(self.settings.render[templateName].apply(this, [data, escape_html]));
3398
3399			// add mandatory attributes
3400			if (templateName === 'option' || templateName === 'option_create') {
3401				if (!data[self.settings.disabledField]) {
3402					html.attr('data-selectable', '');
3403				}
3404			}
3405			else if (templateName === 'optgroup') {
3406				id = data[self.settings.optgroupValueField] || '';
3407				html.attr('data-group', id);
3408				if(data[self.settings.disabledField]) {
3409					html.attr('data-disabled', '');
3410				}
3411			}
3412			if (templateName === 'option' || templateName === 'item') {
3413				html.attr('data-value', value || '');
3414			}
3415
3416			// update cache
3417			if (cache) {
3418				self.renderCache[templateName][value] = html[0];
3419			}
3420
3421			return html[0];
3422		},
3423
3424		/**
3425		 * Clears the render cache for a template. If
3426		 * no template is given, clears all render
3427		 * caches.
3428		 *
3429		 * @param {string} templateName
3430		 */
3431		clearCache: function(templateName) {
3432			var self = this;
3433			if (typeof templateName === 'undefined') {
3434				self.renderCache = {};
3435			} else {
3436				delete self.renderCache[templateName];
3437			}
3438		},
3439
3440		/**
3441		 * Determines whether or not to display the
3442		 * create item prompt, given a user input.
3443		 *
3444		 * @param {string} input
3445		 * @return {boolean}
3446		 */
3447		canCreate: function(input) {
3448			var self = this;
3449			if (!self.settings.create) return false;
3450			var filter = self.settings.createFilter;
3451			return input.length
3452				&& (typeof filter !== 'function' || filter.apply(self, [input]))
3453				&& (typeof filter !== 'string' || new RegExp(filter).test(input))
3454				&& (!(filter instanceof RegExp) || filter.test(input));
3455		}
3456
3457	});
3458
3459
3460	Selectize.count = 0;
3461	Selectize.defaults = {
3462		options: [],
3463		optgroups: [],
3464
3465		plugins: [],
3466		delimiter: ',',
3467		splitOn: null, // regexp or string for splitting up values from a paste command
3468		persist: true,
3469		diacritics: true,
3470		create: false,
3471		showAddOptionOnCreate: true,
3472		createOnBlur: false,
3473		createFilter: null,
3474		highlight: true,
3475		openOnFocus: true,
3476		maxOptions: 1000,
3477		maxItems: null,
3478		hideSelected: null,
3479		addPrecedence: false,
3480		selectOnTab: true,
3481		preload: false,
3482		allowEmptyOption: false,
3483		showEmptyOptionInDropdown: false,
3484		emptyOptionLabel: '--',
3485		closeAfterSelect: false,
3486
3487		scrollDuration: 60,
3488		deselectBehavior: 'previous', //top, previous
3489		loadThrottle: 300,
3490		loadingClass: 'loading',
3491
3492		dataAttr: 'data-data',
3493		optgroupField: 'optgroup',
3494		valueField: 'value',
3495		labelField: 'text',
3496		disabledField: 'disabled',
3497		optgroupLabelField: 'label',
3498		optgroupValueField: 'value',
3499		lockOptgroupOrder: false,
3500
3501		sortField: '$order',
3502		searchField: ['text'],
3503		searchConjunction: 'and',
3504
3505		mode: null,
3506		wrapperClass: 'selectize-control',
3507		inputClass: 'selectize-input',
3508		dropdownClass: 'selectize-dropdown',
3509		dropdownContentClass: 'selectize-dropdown-content',
3510
3511		dropdownParent: null,
3512
3513		copyClassesToDropdown: true,
3514
3515		/*
3516		load                 : null, // function(query, callback) { ... }
3517		score                : null, // function(search) { ... }
3518		formatValueToKey     : null, // function(key) { ... }
3519		onInitialize         : null, // function() { ... }
3520		onChange             : null, // function(value) { ... }
3521		onItemAdd            : null, // function(value, $item) { ... }
3522		onItemRemove         : null, // function(value) { ... }
3523		onClear              : null, // function() { ... }
3524		onOptionAdd          : null, // function(value, data) { ... }
3525		onOptionRemove       : null, // function(value) { ... }
3526		onOptionClear        : null, // function() { ... }
3527		onOptionGroupAdd     : null, // function(id, data) { ... }
3528		onOptionGroupRemove  : null, // function(id) { ... }
3529		onOptionGroupClear   : null, // function() { ... }
3530		onDropdownOpen       : null, // function($dropdown) { ... }
3531		onDropdownClose      : null, // function($dropdown) { ... }
3532		onType               : null, // function(str) { ... }
3533		onDelete             : null, // function(values) { ... }
3534		*/
3535
3536		render: {
3537			/*
3538			item: null,
3539			optgroup: null,
3540			optgroup_header: null,
3541			option: null,
3542			option_create: null
3543			*/
3544		}
3545	};
3546
3547
3548	$.fn.selectize = function(settings_user) {
3549		var defaults             = $.fn.selectize.defaults;
3550		var settings             = $.extend({}, defaults, settings_user);
3551		var attr_data            = settings.dataAttr;
3552		var field_label          = settings.labelField;
3553		var field_value          = settings.valueField;
3554		var field_disabled       = settings.disabledField;
3555		var field_optgroup       = settings.optgroupField;
3556		var field_optgroup_label = settings.optgroupLabelField;
3557		var field_optgroup_value = settings.optgroupValueField;
3558
3559		/**
3560		 * Initializes selectize from a <input type="text"> element.
3561		 *
3562		 * @param {object} $input
3563		 * @param {object} settings_element
3564		 */
3565		var init_textbox = function($input, settings_element) {
3566			var i, n, values, option;
3567
3568			var data_raw = $input.attr(attr_data);
3569
3570			if (!data_raw) {
3571				var value = $.trim($input.val() || '');
3572				if (!settings.allowEmptyOption && !value.length) return;
3573				values = value.split(settings.delimiter);
3574				for (i = 0, n = values.length; i < n; i++) {
3575					option = {};
3576					option[field_label] = values[i];
3577					option[field_value] = values[i];
3578					settings_element.options.push(option);
3579				}
3580				settings_element.items = values;
3581			} else {
3582				settings_element.options = JSON.parse(data_raw);
3583				for (i = 0, n = settings_element.options.length; i < n; i++) {
3584					settings_element.items.push(settings_element.options[i][field_value]);
3585				}
3586			}
3587		};
3588
3589		/**
3590		 * Initializes selectize from a <select> element.
3591		 *
3592		 * @param {object} $input
3593		 * @param {object} settings_element
3594		 */
3595		var init_select = function($input, settings_element) {
3596			var i, n, tagName, $children, order = 0;
3597			var options = settings_element.options;
3598			var optionsMap = {};
3599
3600			var readData = function($el) {
3601				var data = attr_data && $el.attr(attr_data);
3602				if (typeof data === 'string' && data.length) {
3603					return JSON.parse(data);
3604				}
3605				return null;
3606			};
3607
3608			var addOption = function($option, group) {
3609				$option = $($option);
3610
3611				var value = hash_key($option.val());
vendor: 6,028 bytes, lines 3612-3814
3612				if (!value && !settings.allowEmptyOption) return;
3613
3614				// if the option already exists, it's probably been
3615				// duplicated in another optgroup. in this case, push
3616				// the current group to the "optgroup" property on the
3617				// existing option so that it's rendered in both places.
3618				if (optionsMap.hasOwnProperty(value)) {
3619					if (group) {
3620						var arr = optionsMap[value][field_optgroup];
3621						if (!arr) {
3622							optionsMap[value][field_optgroup] = group;
3623						} else if (!$.isArray(arr)) {
3624							optionsMap[value][field_optgroup] = [arr, group];
3625						} else {
3626							arr.push(group);
3627						}
3628					}
3629					return;
3630				}
3631
3632				var option             = readData($option) || {};
3633				option[field_label]    = option[field_label] || $option.text();
3634				option[field_value]    = option[field_value] || value;
3635				option[field_disabled] = option[field_disabled] || $option.prop('disabled');
3636				option[field_optgroup] = option[field_optgroup] || group;
3637
3638				optionsMap[value] = option;
3639				options.push(option);
3640
3641				if ($option.is(':selected')) {
3642					settings_element.items.push(value);
3643				}
3644			};
3645
3646			var addGroup = function($optgroup) {
3647				var i, n, id, optgroup, $options;
3648
3649				$optgroup = $($optgroup);
3650				id = $optgroup.attr('label');
3651
3652				if (id) {
3653					optgroup = readData($optgroup) || {};
3654					optgroup[field_optgroup_label] = id;
3655					optgroup[field_optgroup_value] = id;
3656					optgroup[field_disabled] = $optgroup.prop('disabled');
3657					settings_element.optgroups.push(optgroup);
3658				}
3659
3660				$options = $('option', $optgroup);
3661				for (i = 0, n = $options.length; i < n; i++) {
3662					addOption($options[i], id);
3663				}
3664			};
3665
3666			settings_element.maxItems = $input.attr('multiple') ? null : 1;
3667
3668			$children = $input.children();
3669			for (i = 0, n = $children.length; i < n; i++) {
3670				tagName = $children[i].tagName.toLowerCase();
3671				if (tagName === 'optgroup') {
3672					addGroup($children[i]);
3673				} else if (tagName === 'option') {
3674					addOption($children[i]);
3675				}
3676			}
3677		};
3678
3679		return this.each(function() {
3680			if (this.selectize) return;
3681
3682			var instance;
3683			var $input = $(this);
3684			var tag_name = this.tagName.toLowerCase();
3685			var placeholder = $input.attr('placeholder') || $input.attr('data-placeholder');
3686			if (!placeholder && !settings.allowEmptyOption) {
3687				placeholder = $input.children('option[value=""]').text();
3688			}
3689			if (settings.allowEmptyOption && settings.showEmptyOptionInDropdown && !$input.children('option[value=""]').length) {
3690				var input_html = $input.html();
3691				$input.html('<option value="">'+settings.emptyOptionLabel+'</option>'+input_html);
3692			}
3693
3694			var settings_element = {
3695				'placeholder' : placeholder,
3696				'options'     : [],
3697				'optgroups'   : [],
3698				'items'       : []
3699			};
3700
3701			if (tag_name === 'select') {
3702				init_select($input, settings_element);
3703			} else {
3704				init_textbox($input, settings_element);
3705			}
3706
3707			instance = new Selectize($input, $.extend(true, {}, defaults, settings_element, settings_user));
3708			instance.settings_user = settings_user;
3709		});
3710	};
3711
3712	$.fn.selectize.defaults = Selectize.defaults;
3713	$.fn.selectize.support = {
3714		validity: SUPPORTS_VALIDITY_API
3715	};
3716
3717
3718	Selectize.define("auto_position", function () {
3719		var self = this;
3720
3721		const POSITION = {
3722			top: 'top',
3723			bottom: 'bottom',
3724		};
3725
3726		self.positionDropdown = (function() {
3727			return function() {
3728				const $control = this.$control;
3729				const offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
3730				offset.top += $control.outerHeight(true);
3731
3732				const dropdownHeight = this.$dropdown.prop('scrollHeight') + 5; // 5 - padding value;
3733				const controlPosTop = this.$control.get(0).getBoundingClientRect().top;
3734				const wrapperHeight = this.$wrapper.height();
3735				const position = controlPosTop + dropdownHeight + wrapperHeight  > window.innerHeight ? POSITION.top : POSITION.bottom;
3736				const styles = {
3737					width: $control.outerWidth(),
3738					left: offset.left
3739				};
3740
3741				if (position === POSITION.top) {
3742					Object.assign(styles, {bottom: offset.top, top: 'unset', margin: '0 0 5px 0'});
3743					this.$dropdown.addClass('selectize-position-top');
3744				} else {
3745					Object.assign(styles, {top: offset.top, bottom: 'unset', margin: '5px 0 0 0'});
3746					this.$dropdown.removeClass('selectize-position-top');
3747				}
3748
3749				this.$dropdown.css(styles);
3750			}
3751		}());
3752	});
3753
3754
3755	Selectize.define('auto_select_on_type', function(options) {
3756		var self = this;
3757
3758		self.onBlur = (function() {
3759			var originalBlur = self.onBlur;
3760			return function(e) {
3761				var $matchedItem = self.getFirstItemMatchedByTextContent(self.lastValue, true);
3762				if (typeof $matchedItem.attr('data-value') !== 'undefined' && self.getValue() !== $matchedItem.attr('data-value'))
3763				{
3764					self.setValue($matchedItem.attr('data-value'));
3765				}
3766				return originalBlur.apply(this, arguments);
3767			}
3768		}());
3769	});
3770
3771
3772	Selectize.define("autofill_disable", function (options) {
3773		var self = this;
3774
3775		self.setup = (function () {
3776			var original = self.setup;
3777			return function () {
3778				original.apply(self, arguments);
3779
3780				// https://stackoverflow.com/questions/30053167/autocomplete-off-vs-false
3781				self.$control_input.attr({ autocomplete: "new-password", autofill: "no" });
3782			};
3783		})();
3784	});
3785
3786
3787	Selectize.define('drag_drop', function(options) {
3788		if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');
3789		if (this.settings.mode !== 'multi') return;
3790		var self = this;
3791
3792		self.lock = (function() {
3793			var original = self.lock;
3794			return function() {
3795				var sortable = self.$control.data('sortable');
3796				if (sortable) sortable.disable();
3797				return original.apply(self, arguments);
3798			};
3799		})();
3800
3801		self.unlock = (function() {
3802			var original = self.unlock;
3803			return function() {
3804				var sortable = self.$control.data('sortable');
3805				if (sortable) sortable.enable();
3806				return original.apply(self, arguments);
3807			};
3808		})();
3809
3810		self.setup = (function() {
3811			var original = self.setup;
3812			return function() {
3813				original.apply(this, arguments);
3814
vendor: 7,364 bytes, lines 3815-4068
3815				var $control = self.$control.sortable({
3816					items: '[data-value]',
3817					forcePlaceholderSize: true,
3818					disabled: self.isLocked,
3819					start: function(e, ui) {
3820						ui.placeholder.css('width', ui.helper.css('width'));
3821						// $control.css({overflow: 'visible'});
3822						$control.addClass('dragging');
3823					},
3824					stop: function() {
3825						// $control.css({overflow: 'hidden'});
3826						$control.removeClass('dragging');
3827						var active = self.$activeItems ? self.$activeItems.slice() : null;
3828						var values = [];
3829						$control.children('[data-value]').each(function() {
3830							values.push($(this).attr('data-value'));
3831						});
3832						self.isFocused = false;
3833						self.setValue(values);
3834						self.isFocused = true;
3835						self.setActiveItem(active);
3836						self.positionDropdown();
3837					}
3838				});
3839			};
3840		})();
3841
3842	});
3843
3844
3845	Selectize.define('dropdown_header', function(options) {
3846		var self = this;
3847
3848		options = $.extend({
3849			title         : 'Untitled',
3850			headerClass   : 'selectize-dropdown-header',
3851			titleRowClass : 'selectize-dropdown-header-title',
3852			labelClass    : 'selectize-dropdown-header-label',
3853			closeClass    : 'selectize-dropdown-header-close',
3854
3855			html: function(data) {
3856				return (
3857					'<div class="' + data.headerClass + '">' +
3858					'<div class="' + data.titleRowClass + '">' +
3859					'<span class="' + data.labelClass + '">' + data.title + '</span>' +
3860					'<a href="javascript:void(0)" class="' + data.closeClass + '">&#xd7;</a>' +
3861					'</div>' +
3862					'</div>'
3863				);
3864			}
3865		}, options);
3866
3867		self.setup = (function() {
3868			var original = self.setup;
3869			return function() {
3870				original.apply(self, arguments);
3871				self.$dropdown_header = $(options.html(options));
3872				self.$dropdown.prepend(self.$dropdown_header);
3873			};
3874		})();
3875
3876	});
3877
3878
3879	Selectize.define('optgroup_columns', function(options) {
3880		var self = this;
3881
3882		options = $.extend({
3883			equalizeWidth  : true,
3884			equalizeHeight : true
3885		}, options);
3886
3887		this.getAdjacentOption = function($option, direction) {
3888			var $options = $option.closest('[data-group]').find('[data-selectable]');
3889			var index    = $options.index($option) + direction;
3890
3891			return index >= 0 && index < $options.length ? $options.eq(index) : $();
3892		};
3893
3894		this.onKeyDown = (function() {
3895			var original = self.onKeyDown;
3896			return function(e) {
3897				var index, $option, $options, $optgroup;
3898
3899				if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) {
3900					self.ignoreHover = true;
3901					$optgroup = this.$activeOption.closest('[data-group]');
3902					index = $optgroup.find('[data-selectable]').index(this.$activeOption);
3903
3904					if(e.keyCode === KEY_LEFT) {
3905						$optgroup = $optgroup.prev('[data-group]');
3906					} else {
3907						$optgroup = $optgroup.next('[data-group]');
3908					}
3909
3910					$options = $optgroup.find('[data-selectable]');
3911					$option  = $options.eq(Math.min($options.length - 1, index));
3912					if ($option.length) {
3913						this.setActiveOption($option);
3914					}
3915					return;
3916				}
3917
3918				return original.apply(this, arguments);
3919			};
3920		})();
3921
3922		var getScrollbarWidth = function() {
3923			var div;
3924			var width = getScrollbarWidth.width;
3925			var doc = document;
3926
3927			if (typeof width === 'undefined') {
3928				div = doc.createElement('div');
3929				div.innerHTML = '<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>';
3930				div = div.firstChild;
3931				doc.body.appendChild(div);
3932				width = getScrollbarWidth.width = div.offsetWidth - div.clientWidth;
3933				doc.body.removeChild(div);
3934			}
3935			return width;
3936		};
3937
3938		var equalizeSizes = function() {
3939			var i, n, height_max, width, width_last, width_parent, $optgroups;
3940
3941			$optgroups = $('[data-group]', self.$dropdown_content);
3942			n = $optgroups.length;
3943			if (!n || !self.$dropdown_content.width()) return;
3944
3945			if (options.equalizeHeight) {
3946				height_max = 0;
3947				for (i = 0; i < n; i++) {
3948					height_max = Math.max(height_max, $optgroups.eq(i).height());
3949				}
3950				$optgroups.css({height: height_max});
3951			}
3952
3953			if (options.equalizeWidth) {
3954				width_parent = self.$dropdown_content.innerWidth() - getScrollbarWidth();
3955				width = Math.round(width_parent / n);
3956				$optgroups.css({width: width});
3957				if (n > 1) {
3958					width_last = width_parent - width * (n - 1);
3959					$optgroups.eq(n - 1).css({width: width_last});
3960				}
3961			}
3962		};
3963
3964		if (options.equalizeHeight || options.equalizeWidth) {
3965			hook.after(this, 'positionDropdown', equalizeSizes);
3966			hook.after(this, 'refreshOptions', equalizeSizes);
3967		}
3968
3969
3970	});
3971
3972
3973	Selectize.define('remove_button', function(options) {
3974		options = $.extend({
3975			label     : '&#xd7;',
3976			title     : 'Remove',
3977			className : 'remove',
3978			append    : true
3979		}, options);
3980
3981		var singleClose = function(thisRef, options) {
3982
3983			options.className = 'remove-single';
3984
3985			var self = thisRef;
3986			var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
3987
3988			/**
3989			 * Appends an element as a child (with raw HTML).
3990			 *
3991			 * @param {string} html_container
3992			 * @param {string} html_element
3993			 * @return {string}
3994			 */
3995			var append = function(html_container, html_element) {
3996				return $('<span>').append(html_container)
3997					.append(html_element);
3998			};
3999
4000			thisRef.setup = (function() {
4001				var original = self.setup;
4002				return function() {
4003					// override the item rendering method to add the button to each
4004					if (options.append) {
4005						var id = $(self.$input.context).attr('id');
4006						var selectizer = $('#'+id);
4007
4008						var render_item = self.settings.render.item;
4009						self.settings.render.item = function(data) {
4010							return append(render_item.apply(thisRef, arguments), html);
4011						};
4012					}
4013
4014					original.apply(thisRef, arguments);
4015
4016					// add event listener
4017					thisRef.$control.on('click', '.' + options.className, function(e) {
4018						e.preventDefault();
4019						if (self.isLocked) return;
4020
4021						self.clear();
4022					});
4023
4024				};
4025			})();
4026		};
4027
4028		var multiClose = function(thisRef, options) {
4029
4030			var self = thisRef;
4031			var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
4032
4033			/**
4034			 * Appends an element as a child (with raw HTML).
4035			 *
4036			 * @param {string} html_container
4037			 * @param {string} html_element
4038			 * @return {string}
4039			 */
4040			var append = function(html_container, html_element) {
4041				var pos = html_container.search(/(<\/[^>]+>\s*)$/);
4042				return html_container.substring(0, pos) + html_element + html_container.substring(pos);
4043			};
4044
4045			thisRef.setup = (function() {
4046				var original = self.setup;
4047				return function() {
4048					// override the item rendering method to add the button to each
4049					if (options.append) {
4050						var render_item = self.settings.render.item;
4051						self.settings.render.item = function(data) {
4052							return append(render_item.apply(thisRef, arguments), html);
4053						};
4054					}
4055
4056					original.apply(thisRef, arguments);
4057
4058					// add event listener
4059					thisRef.$control.on('click', '.' + options.className, function(e) {
4060						e.preventDefault();
4061						if (self.isLocked) return;
4062
4063						var $item = $(e.currentTarget).parent();
4064						self.setActiveItem($item);
4065						if (self.deleteSelection()) {
4066							self.setCaret(self.items.length);
4067						}
4068						return false;
vendor: 2,990 bytes, lines 4069-4200
4069					});
4070
4071				};
4072			})();
4073		};
4074
4075		if (this.settings.mode === 'single') {
4076			singleClose(this, options);
4077			return;
4078		} else {
4079			multiClose(this, options);
4080		}
4081	});
4082
4083
4084	Selectize.define('restore_on_backspace', function(options) {
4085		var self = this;
4086
4087		options.text = options.text || function(option) {
4088			return option[this.settings.labelField];
4089		};
4090
4091		this.onKeyDown = (function() {
4092			var original = self.onKeyDown;
4093			return function(e) {
4094				var index, option;
4095				if (e.keyCode === KEY_BACKSPACE && this.$control_input.val() === '' && !this.$activeItems.length) {
4096					index = this.caretPos - 1;
4097					if (index >= 0 && index < this.items.length) {
4098						option = this.options[this.items[index]];
4099						if (this.deleteSelection(e)) {
4100							this.setTextboxValue(options.text.apply(this, [option]));
4101							this.refreshOptions(true);
4102						}
4103						e.preventDefault();
4104						return;
4105					}
4106				}
4107				return original.apply(this, arguments);
4108			};
4109		})();
4110	});
4111
4112
4113	Selectize.define('select_on_focus', function(options) {
4114		var self = this;
4115
4116		self.on('focus', function() {
4117			var originalFocus = self.onFocus;
4118			return function(e) {
4119				var value = self.getItem(self.getValue()).text();
4120				self.clear();
4121				self.setTextboxValue(value);
4122				self.$control_input.select();
4123				setTimeout( function () {
4124					if (self.settings.selectOnTab) {
4125						self.setActiveOption(self.getFirstItemMatchedByTextContent(value));
4126					}
4127					self.settings.score = null;
4128				},0);
4129				return originalFocus.apply(this, arguments);
4130			};
4131		}());
4132
4133		self.onBlur = (function() {
4134			var originalBlur = self.onBlur;
4135			return function(e) {
4136				if (self.getValue() === "" && self.lastValidValue !== self.getValue()) {
4137					self.setValue(self.lastValidValue);
4138				}
4139				setTimeout( function () {
4140					self.settings.score = function() {
4141						return function() {
4142							return 1;
4143						};
4144					};
4145				}, 0 );
4146				return originalBlur.apply(this, arguments);
4147			}
4148		}());
4149		self.settings.score = function() {
4150			return function() { return 1; };
4151		};
4152
4153	});
4154
4155
4156	Selectize.define('tag_limit', function (options) {
4157		const self = this
4158		options.tagLimit = options.tagLimit
4159		this.onBlur = (function (e) {
4160			const original = self.onBlur
4161
4162			return function (e) {
4163				original.apply(this, e);
4164				if (!e)
4165					return
4166				const $control = this.$control
4167				const $items = $control.find('.item')
4168				const limit = options.tagLimit
4169				if (limit === undefined || $items.length <= limit)
4170					return
4171
4172				$items.toArray().forEach(function(item, index) {
4173					if (index < limit)
4174						return
4175					$(item).hide()
4176				});
4177
4178				$control.append('<span><b>' + ($items.length - limit) + '</b></span>')
4179			};
4180		})()
4181
4182		this.onFocus = (function (e) {
4183			const original = self.onFocus
4184
4185			return function (e) {
4186				original.apply(this, e);
4187				if (!e)
4188					return
4189				const $control = this.$control
4190				const $items = $control.find('.item')
4191				$items.show()
4192				$control.find('span').remove()
4193
4194			};
4195		})()
4196	});
4197
4198
4199	return Selectize;
4200}));

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