PageSourceSearch

https://balancingmomlife.com/wp-content/plugins/studiocart-pro/public/js/selectize.js?ver=2.8.3

js balancingmomlife.com collected 2026-10-05 07:14:17 UTC 106,428 bytes, 3,891 lines download raw bytes

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

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.