ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

selectize.js (100362B)


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