PageSourceSearch

https://www.emvco.com/wp-content/themes/twentyseventeen-child/assets/js/selectize.min.js?ver=7.1.2

js emvco.com collected 2026-09-24 08:27:58 UTC 110,325 bytes, 3,894 lines download raw bytes

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

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.