selectize-rails 0.6.14 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -25,20 +25,27 @@ In your `application.css`, include the following:
25
25
 
26
26
  ```css
27
27
  *= require selectize
28
+ *= require selectize.default
28
29
  ```
29
30
 
31
+ ### Themes
32
+
33
+ To include additional theme's you can replace the `selectize.default` for on of the [theme files](https://github.com/brianreavis/selectize.js/tree/master/dist/css)
34
+
35
+
30
36
  ## Examples
31
37
 
32
38
  See the [demo page of Brian Reavis](http://brianreavis.github.io/selectize.js/) for examples how to use the plugin
33
39
 
34
40
  ## Changes
35
41
 
36
- | Version | Notes |
37
- | -------:| ----------------------------------------------------------------------------------- |
38
- | 0.6.14 | Update to v0.6.14 of selectize.js |
39
- | 0.6.4 | Update to v0.6.4 of selectize.js |
40
- | 0.6.1 | Update and set gem version equal to selectize.js version |
41
- | 0.1.0 | Initial release |
42
+ | Version | Notes |
43
+ | -------:| ----------------------------------------------------------- |
44
+ | 0.7.0 | Update to v0.7.0 of selectize.js |
45
+ | 0.6.14 | Update to v0.6.14 of selectize.js |
46
+ | 0.6.4 | Update to v0.6.4 of selectize.js |
47
+ | 0.6.1 | Update and set gem version equal to selectize.js version |
48
+ | 0.1.0 | Initial release |
42
49
 
43
50
  ## License
44
51
 
@@ -1,5 +1,5 @@
1
1
  module Selectize
2
2
  module Rails
3
- VERSION = "0.6.14"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
  end
@@ -6,9 +6,9 @@ git clone https://github.com/brianreavis/selectize.js.git tmp_vendor
6
6
 
7
7
  # Copy files
8
8
  echo "Copying selectize.js"
9
- cp tmp_vendor/selectize.js vendor/assets/javascripts/selectize.js
10
- echo "Copying selectize.css"
11
- cp tmp_vendor/selectize.css vendor/assets/stylesheets/selectize.css
9
+ cp tmp_vendor/dist/js/selectize.js vendor/assets/javascripts/selectize.js
10
+ echo "Copying css files"
11
+ cp tmp_vendor/dist/css/*.css vendor/assets/stylesheets/
12
12
 
13
13
  # Delete vendor repo
14
14
  echo "Removing cloned vendor repo"
@@ -1,30 +1,35 @@
1
- /*! selectize.js - v0.6.14 | https://github.com/brianreavis/selectize.js | Apache License (v2) */
2
-
3
- (function(factory) {
4
- if (typeof exports === 'object') {
5
- factory(require('jquery'));
6
- } else if (typeof define === 'function' && define.amd) {
7
- define(['jquery'], factory);
1
+ /**
2
+ * selectize.js (v0.7.0)
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
+ /*jshint curly:false */
18
+ /*jshint browser:true */
19
+
20
+ (function(root, factory) {
21
+ if (typeof define === 'function' && define.amd) {
22
+ define(['sifter','microplugin'], factory);
8
23
  } else {
9
- factory(jQuery);
24
+ root.Selectize = factory(root.Sifter, root.MicroPlugin);
10
25
  }
11
- }(function ($) {
12
- "use strict";
13
-
14
- /* --- file: "src/contrib/highlight.js" --- */
15
-
16
- /**
17
- * highlight v3 | MIT license | Johann Burkard <jb@eaio.com>
18
- * Highlights arbitrary terms in a node.
19
- *
20
- * - Modified by Marshal <beatgates@gmail.com> 2011-6-24 (added regex)
21
- * - Modified by Brian Reavis <brian@thirdroute.com> 2012-8-27 (cleanup)
22
- */
26
+ }(this, function(Sifter, MicroPlugin) {
27
+ 'use strict';
23
28
 
24
29
  var highlight = function($element, pattern) {
25
30
  if (typeof pattern === 'string' && !pattern.length) return;
26
31
  var regex = (typeof pattern === 'string') ? new RegExp(pattern, 'i') : pattern;
27
-
32
+
28
33
  var highlight = function(node) {
29
34
  var skip = 0;
30
35
  if (node.nodeType === 3) {
@@ -47,32 +52,12 @@
47
52
  }
48
53
  return skip;
49
54
  };
50
-
55
+
51
56
  return $element.each(function() {
52
57
  highlight(this);
53
58
  });
54
59
  };
55
-
56
- var unhighlight = function($element) {
57
- return $element.find('span.highlight').each(function() {
58
- var parent = this.parentNode;
59
- parent.replaceChild(parent.firstChild, parent);
60
- parent.normalize();
61
- }).end();
62
- };
63
-
64
- /* --- file: "src/contrib/microevent.js" --- */
65
-
66
- /**
67
- * MicroEvent - to make any js object an event emitter
68
- *
69
- * - pure javascript - server compatible, browser compatible
70
- * - dont rely on the browser doms
71
- * - super simple - you get it immediatly, no mistery, no magic involved
72
- *
73
- * @author Jerome Etienne (https://github.com/jeromeetienne)
74
- */
75
-
60
+
76
61
  var MicroEvent = function() {};
77
62
  MicroEvent.prototype = {
78
63
  on: function(event, fct){
@@ -81,6 +66,10 @@
81
66
  this._events[event].push(fct);
82
67
  },
83
68
  off: function(event, fct){
69
+ var n = arguments.length;
70
+ if (n === 0) return delete this._events;
71
+ if (n === 1) return delete this._events[event];
72
+
84
73
  this._events = this._events || {};
85
74
  if (event in this._events === false) return;
86
75
  this._events[event].splice(this._events[event].indexOf(fct), 1);
@@ -93,41 +82,23 @@
93
82
  }
94
83
  }
95
84
  };
96
-
85
+
97
86
  /**
98
- * Mixin will delegate all MicroEvent.js function in the destination object.
99
- *
100
- * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
101
- *
102
- * @param {object} the object which will support MicroEvent
103
- */
87
+ * Mixin will delegate all MicroEvent.js function in the destination object.
88
+ *
89
+ * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent
90
+ *
91
+ * @param {object} the object which will support MicroEvent
92
+ */
104
93
  MicroEvent.mixin = function(destObject){
105
94
  var props = ['on', 'off', 'trigger'];
106
95
  for (var i = 0; i < props.length; i++){
107
96
  destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
108
97
  }
109
98
  };
110
-
111
- /* --- file: "src/constants.js" --- */
112
-
113
- /**
114
- * selectize - A highly customizable select control with autocomplete.
115
- * Copyright (c) 2013 Brian Reavis & contributors
116
- *
117
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
118
- * file except in compliance with the License. You may obtain a copy of the License at:
119
- * http://www.apache.org/licenses/LICENSE-2.0
120
- *
121
- * Unless required by applicable law or agreed to in writing, software distributed under
122
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
123
- * ANY KIND, either express or implied. See the License for the specific language
124
- * governing permissions and limitations under the License.
125
- *
126
- * @author Brian Reavis <brian@thirdroute.com>
127
- */
128
-
99
+
129
100
  var IS_MAC = /Mac/.test(navigator.userAgent);
130
-
101
+
131
102
  var KEY_A = 65;
132
103
  var KEY_COMMA = 188;
133
104
  var KEY_RETURN = 13;
@@ -142,141 +113,42 @@
142
113
  var KEY_CMD = IS_MAC ? 91 : 17;
143
114
  var KEY_CTRL = IS_MAC ? 18 : 17;
144
115
  var KEY_TAB = 9;
145
-
116
+
146
117
  var TAG_SELECT = 1;
147
118
  var TAG_INPUT = 2;
148
-
149
- var DIACRITICS = {
150
- 'a': '[aÀÁÂÃÄÅàáâãäå]',
151
- 'c': '[cÇç]',
152
- 'e': '[eÈÉÊËèéêë]',
153
- 'i': '[iÌÍÎÏìíîï]',
154
- 'n': '[nÑñ]',
155
- 'o': '[oÒÓÔÕÕÖØòóôõöø]',
156
- 's': '[sŠš]',
157
- 'u': '[uÙÚÛÜùúûü]',
158
- 'y': '[yŸÿý]',
159
- 'z': '[zŽž]'
160
- };
161
-
162
- /* --- file: "src/plugins.js" --- */
163
-
164
- var Plugins = {};
165
-
166
- Plugins.mixin = function(Interface, interfaceName) {
167
- Interface.plugins = {};
168
-
169
- /**
170
- * Initializes the provided functions.
171
- * Acceptable formats:
172
- *
173
- * List (without options):
174
- * ['a', 'b', 'c']
175
- *
176
- * List (with options)
177
- * {'a': { ... }, 'b': { ... }, 'c': { ... }}
178
- *
179
- * @param {mixed} plugins
180
- */
181
- Interface.prototype.loadPlugins = function(plugins) {
182
- var i, n, key;
183
- this.plugins = [];
184
- this.pluginSettings = {};
185
-
186
- if ($.isArray(plugins)) {
187
- for (i = 0, n = plugins.length; i < n; i++) {
188
- this.loadPlugin(plugins[i]);
189
- }
190
- } else if (plugins) {
191
- this.pluginSettings = $.extend({}, plugins);
192
- for (key in plugins) {
193
- if (plugins.hasOwnProperty(key)) {
194
- this.loadPlugin(key);
195
- }
196
- }
197
- }
198
- };
199
-
200
- /**
201
- * Initializes a plugin.
202
- *
203
- * @param {string} name
204
- */
205
- Interface.prototype.loadPlugin = function(name) {
206
- var plugin, i, n;
207
-
208
- if (this.plugins.indexOf(name) !== -1) return;
209
- if (!Interface.plugins.hasOwnProperty(name)) {
210
- throw new Error(interfaceName + ' unable to find "' + name + '" plugin');
211
- }
212
-
213
- plugin = Interface.plugins[name];
214
-
215
- // initialize plugin and dependencies
216
- this.plugins.push(name);
217
- for (i = 0, n = plugin.dependencies.length; i < n; i++) {
218
- this.loadPlugin(plugin.dependencies[i]);
219
- }
220
- plugin.fn.apply(this, [this.pluginSettings[name] || {}]);
221
- };
222
-
223
- /**
224
- * Registers a plugin.
225
- *
226
- * @param {string} name
227
- * @param {array} dependencies (optional)
228
- * @param {function} fn
229
- */
230
- Interface.registerPlugin = function(name) {
231
- var args = arguments;
232
- Interface.plugins[name] = {
233
- 'name' : name,
234
- 'fn' : args[args.length - 1],
235
- 'dependencies' : args.length === 3 ? args[1] : []
236
- };
237
- };
238
- };
239
-
240
- /* --- file: "src/utils.js" --- */
241
-
242
- /**
243
- * Determines if the provided value has been defined.
244
- *
245
- * @param {mixed} object
246
- * @returns {boolean}
247
- */
119
+
248
120
  var isset = function(object) {
249
121
  return typeof object !== 'undefined';
250
122
  };
251
-
123
+
252
124
  /**
253
- * Converts a scalar to its best string representation
254
- * for hash keys and HTML attribute values.
255
- *
256
- * Transformations:
257
- * 'str' -> 'str'
258
- * null -> ''
259
- * undefined -> ''
260
- * true -> '1'
261
- * false -> '0'
262
- * 0 -> '0'
263
- * 1 -> '1'
264
- *
265
- * @param {string} value
266
- * @returns {string}
267
- */
125
+ * Converts a scalar to its best string representation
126
+ * for hash keys and HTML attribute values.
127
+ *
128
+ * Transformations:
129
+ * 'str' -> 'str'
130
+ * null -> ''
131
+ * undefined -> ''
132
+ * true -> '1'
133
+ * false -> '0'
134
+ * 0 -> '0'
135
+ * 1 -> '1'
136
+ *
137
+ * @param {string} value
138
+ * @returns {string}
139
+ */
268
140
  var hash_key = function(value) {
269
141
  if (typeof value === 'undefined' || value === null) return '';
270
142
  if (typeof value === 'boolean') return value ? '1' : '0';
271
143
  return value + '';
272
144
  };
273
-
145
+
274
146
  /**
275
- * Escapes a string for use within HTML.
276
- *
277
- * @param {string} str
278
- * @returns {string}
279
- */
147
+ * Escapes a string for use within HTML.
148
+ *
149
+ * @param {string} str
150
+ * @returns {string}
151
+ */
280
152
  var escape_html = function(str) {
281
153
  return (str + '')
282
154
  .replace(/&/g, '&amp;')
@@ -284,38 +156,28 @@
284
156
  .replace(/>/g, '&gt;')
285
157
  .replace(/"/g, '&quot;');
286
158
  };
287
-
288
- /**
289
- * Escapes a string for use within regular expressions.
290
- *
291
- * @param {string} str
292
- * @returns {string}
293
- */
294
- var escape_regex = function(str) {
295
- return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
296
- };
297
-
159
+
298
160
  /**
299
- * Escapes quotation marks with backslashes. Useful
300
- * for escaping values for use in CSS attribute selectors.
301
- *
302
- * @param {string} str
303
- * @return {string}
304
- */
161
+ * Escapes quotation marks with backslashes. Useful
162
+ * for escaping values for use in CSS attribute selectors.
163
+ *
164
+ * @param {string} str
165
+ * @return {string}
166
+ */
305
167
  var escape_quotes = function(str) {
306
168
  return str.replace(/(['"])/g, '\\$1');
307
169
  };
308
-
170
+
309
171
  var hook = {};
310
-
172
+
311
173
  /**
312
- * Wraps `method` on `self` so that `fn`
313
- * is invoked before the original method.
314
- *
315
- * @param {object} self
316
- * @param {string} method
317
- * @param {function} fn
318
- */
174
+ * Wraps `method` on `self` so that `fn`
175
+ * is invoked before the original method.
176
+ *
177
+ * @param {object} self
178
+ * @param {string} method
179
+ * @param {function} fn
180
+ */
319
181
  hook.before = function(self, method, fn) {
320
182
  var original = self[method];
321
183
  self[method] = function() {
@@ -323,15 +185,15 @@
323
185
  return original.apply(self, arguments);
324
186
  };
325
187
  };
326
-
188
+
327
189
  /**
328
- * Wraps `method` on `self` so that `fn`
329
- * is invoked after the original method.
330
- *
331
- * @param {object} self
332
- * @param {string} method
333
- * @param {function} fn
334
- */
190
+ * Wraps `method` on `self` so that `fn`
191
+ * is invoked after the original method.
192
+ *
193
+ * @param {object} self
194
+ * @param {string} method
195
+ * @param {function} fn
196
+ */
335
197
  hook.after = function(self, method, fn) {
336
198
  var original = self[method];
337
199
  self[method] = function() {
@@ -340,15 +202,15 @@
340
202
  return result;
341
203
  };
342
204
  };
343
-
205
+
344
206
  /**
345
- * Builds a hash table out of an array of
346
- * objects, using the specified `key` within
347
- * each object.
348
- *
349
- * @param {string} key
350
- * @param {mixed} objects
351
- */
207
+ * Builds a hash table out of an array of
208
+ * objects, using the specified `key` within
209
+ * each object.
210
+ *
211
+ * @param {string} key
212
+ * @param {mixed} objects
213
+ */
352
214
  var build_hash_table = function(key, objects) {
353
215
  if (!$.isArray(objects)) return objects;
354
216
  var i, n, table = {};
@@ -359,13 +221,13 @@
359
221
  }
360
222
  return table;
361
223
  };
362
-
224
+
363
225
  /**
364
- * Wraps `fn` so that it can only be invoked once.
365
- *
366
- * @param {function} fn
367
- * @returns {function}
368
- */
226
+ * Wraps `fn` so that it can only be invoked once.
227
+ *
228
+ * @param {function} fn
229
+ * @returns {function}
230
+ */
369
231
  var once = function(fn) {
370
232
  var called = false;
371
233
  return function() {
@@ -374,15 +236,15 @@
374
236
  fn.apply(this, arguments);
375
237
  };
376
238
  };
377
-
239
+
378
240
  /**
379
- * Wraps `fn` so that it can only be called once
380
- * every `delay` milliseconds (invoked on the falling edge).
381
- *
382
- * @param {function} fn
383
- * @param {int} delay
384
- * @returns {function}
385
- */
241
+ * Wraps `fn` so that it can only be called once
242
+ * every `delay` milliseconds (invoked on the falling edge).
243
+ *
244
+ * @param {function} fn
245
+ * @param {int} delay
246
+ * @returns {function}
247
+ */
386
248
  var debounce = function(fn, delay) {
387
249
  var timeout;
388
250
  return function() {
@@ -394,20 +256,20 @@
394
256
  }, delay);
395
257
  };
396
258
  };
397
-
259
+
398
260
  /**
399
- * Debounce all fired events types listed in `types`
400
- * while executing the provided `fn`.
401
- *
402
- * @param {object} self
403
- * @param {array} types
404
- * @param {function} fn
405
- */
261
+ * Debounce all fired events types listed in `types`
262
+ * while executing the provided `fn`.
263
+ *
264
+ * @param {object} self
265
+ * @param {array} types
266
+ * @param {function} fn
267
+ */
406
268
  var debounce_events = function(self, types, fn) {
407
269
  var type;
408
270
  var trigger = self.trigger;
409
271
  var event_args = {};
410
-
272
+
411
273
  // override trigger method
412
274
  self.trigger = function() {
413
275
  var type = arguments[0];
@@ -417,11 +279,11 @@
417
279
  return trigger.apply(self, arguments);
418
280
  }
419
281
  };
420
-
282
+
421
283
  // invoke provided function
422
284
  fn.apply(self, []);
423
285
  self.trigger = trigger;
424
-
286
+
425
287
  // trigger queued events
426
288
  for (type in event_args) {
427
289
  if (event_args.hasOwnProperty(type)) {
@@ -429,15 +291,15 @@
429
291
  }
430
292
  }
431
293
  };
432
-
294
+
433
295
  /**
434
- * A workaround for http://bugs.jquery.com/ticket/6696
435
- *
436
- * @param {object} $parent - Parent element to listen on.
437
- * @param {string} event - Event name.
438
- * @param {string} selector - Descendant selector to filter by.
439
- * @param {function} fn - Event handler.
440
- */
296
+ * A workaround for http://bugs.jquery.com/ticket/6696
297
+ *
298
+ * @param {object} $parent - Parent element to listen on.
299
+ * @param {string} event - Event name.
300
+ * @param {string} selector - Descendant selector to filter by.
301
+ * @param {function} fn - Event handler.
302
+ */
441
303
  var watchChildEvent = function($parent, event, selector, fn) {
442
304
  $parent.on(event, selector, function(e) {
443
305
  var child = e.target;
@@ -448,16 +310,16 @@
448
310
  return fn.apply(this, [e]);
449
311
  });
450
312
  };
451
-
313
+
452
314
  /**
453
- * Determines the current selection within a text input control.
454
- * Returns an object containing:
455
- * - start
456
- * - length
457
- *
458
- * @param {object} input
459
- * @returns {object}
460
- */
315
+ * Determines the current selection within a text input control.
316
+ * Returns an object containing:
317
+ * - start
318
+ * - length
319
+ *
320
+ * @param {object} input
321
+ * @returns {object}
322
+ */
461
323
  var getSelection = function(input) {
462
324
  var result = {};
463
325
  if ('selectionStart' in input) {
@@ -473,14 +335,14 @@
473
335
  }
474
336
  return result;
475
337
  };
476
-
338
+
477
339
  /**
478
- * Copies CSS properties from one element to another.
479
- *
480
- * @param {object} $from
481
- * @param {object} $to
482
- * @param {array} properties
483
- */
340
+ * Copies CSS properties from one element to another.
341
+ *
342
+ * @param {object} $from
343
+ * @param {object} $to
344
+ * @param {array} properties
345
+ */
484
346
  var transferStyles = function($from, $to, properties) {
485
347
  var i, n, styles = {};
486
348
  if (properties) {
@@ -492,15 +354,15 @@
492
354
  }
493
355
  $to.css(styles);
494
356
  };
495
-
357
+
496
358
  /**
497
- * Measures the width of a string within a
498
- * parent element (in pixels).
499
- *
500
- * @param {string} str
501
- * @param {object} $parent
502
- * @returns {int}
503
- */
359
+ * Measures the width of a string within a
360
+ * parent element (in pixels).
361
+ *
362
+ * @param {string} str
363
+ * @param {object} $parent
364
+ * @returns {int}
365
+ */
504
366
  var measureString = function(str, $parent) {
505
367
  var $test = $('<test>').css({
506
368
  position: 'absolute',
@@ -510,7 +372,7 @@
510
372
  padding: 0,
511
373
  whiteSpace: 'nowrap'
512
374
  }).text(str).appendTo('body');
513
-
375
+
514
376
  transferStyles($parent, $test, [
515
377
  'letterSpacing',
516
378
  'fontSize',
@@ -518,31 +380,31 @@
518
380
  'fontWeight',
519
381
  'textTransform'
520
382
  ]);
521
-
383
+
522
384
  var width = $test.width();
523
385
  $test.remove();
524
-
386
+
525
387
  return width;
526
388
  };
527
-
389
+
528
390
  /**
529
- * Sets up an input to grow horizontally as the user
530
- * types. If the value is changed manually, you can
531
- * trigger the "update" handler to resize:
532
- *
533
- * $input.trigger('update');
534
- *
535
- * @param {object} $input
536
- */
391
+ * Sets up an input to grow horizontally as the user
392
+ * types. If the value is changed manually, you can
393
+ * trigger the "update" handler to resize:
394
+ *
395
+ * $input.trigger('update');
396
+ *
397
+ * @param {object} $input
398
+ */
537
399
  var autoGrow = function($input) {
538
400
  var update = function(e) {
539
401
  var value, keyCode, printable, placeholder, width;
540
402
  var shift, character, selection;
541
403
  e = e || window.event || {};
542
-
404
+
543
405
  if (e.metaKey || e.altKey) return;
544
406
  if ($input.data('grow') === false) return;
545
-
407
+
546
408
  value = $input.val();
547
409
  if (e.type && e.type.toLowerCase() === 'keydown') {
548
410
  keyCode = e.keyCode;
@@ -550,9 +412,9 @@
550
412
  (keyCode >= 97 && keyCode <= 122) || // a-z
551
413
  (keyCode >= 65 && keyCode <= 90) || // A-Z
552
414
  (keyCode >= 48 && keyCode <= 57) || // 0-9
553
- keyCode == 32 // space
415
+ keyCode === 32 // space
554
416
  );
555
-
417
+
556
418
  if (keyCode === KEY_DELETE || keyCode === KEY_BACKSPACE) {
557
419
  selection = getSelection($input[0]);
558
420
  if (selection.length) {
@@ -570,51 +432,34 @@
570
432
  value += character;
571
433
  }
572
434
  }
573
-
435
+
574
436
  placeholder = $input.attr('placeholder') || '';
575
437
  if (!value.length && placeholder.length) {
576
438
  value = placeholder;
577
439
  }
578
-
440
+
579
441
  width = measureString(value, $input) + 4;
580
442
  if (width !== $input.width()) {
581
443
  $input.width(width);
582
444
  $input.triggerHandler('resize');
583
445
  }
584
446
  };
585
-
447
+
586
448
  $input.on('keydown keyup update blur', update);
587
449
  update();
588
450
  };
589
-
590
- /* --- file: "src/selectize.js" --- */
591
-
592
- /**
593
- * selectize.js
594
- * Copyright (c) 2013 Brian Reavis & contributors
595
- *
596
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
597
- * file except in compliance with the License. You may obtain a copy of the License at:
598
- * http://www.apache.org/licenses/LICENSE-2.0
599
- *
600
- * Unless required by applicable law or agreed to in writing, software distributed under
601
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
602
- * ANY KIND, either express or implied. See the License for the specific language
603
- * governing permissions and limitations under the License.
604
- *
605
- * @author Brian Reavis <brian@thirdroute.com>
606
- */
607
-
451
+
608
452
  var Selectize = function($input, settings) {
609
453
  var key, i, n, self = this;
610
454
  $input[0].selectize = self;
611
-
455
+
612
456
  // setup default state
613
457
  $.extend(self, {
614
458
  settings : settings,
615
459
  $input : $input,
616
460
  tagType : $input[0].tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT,
617
-
461
+
462
+ eventNS : '.selectize' + (++Selectize.count),
618
463
  highlightedValue : null,
619
464
  isOpen : false,
620
465
  isDisabled : false,
@@ -634,10 +479,10 @@
634
479
  caretPos : 0,
635
480
  loading : 0,
636
481
  loadedSearches : {},
637
-
482
+
638
483
  $activeOption : null,
639
484
  $activeItems : [],
640
-
485
+
641
486
  optgroups : {},
642
487
  options : {},
643
488
  userOptions : {},
@@ -645,43 +490,50 @@
645
490
  renderCache : {},
646
491
  onSearchChange : debounce(self.onSearchChange, settings.loadThrottle)
647
492
  });
648
-
493
+
494
+ // search system
495
+ self.sifter = new Sifter(this.options, {diacritics: settings.diacritics});
496
+
649
497
  // build options table
650
498
  $.extend(self.options, build_hash_table(settings.valueField, settings.options));
651
499
  delete self.settings.options;
652
-
500
+
653
501
  // build optgroup table
654
502
  $.extend(self.optgroups, build_hash_table(settings.optgroupValueField, settings.optgroups));
655
503
  delete self.settings.optgroups;
656
-
504
+
657
505
  // option-dependent defaults
658
506
  self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi');
659
507
  if (typeof self.settings.hideSelected !== 'boolean') {
660
508
  self.settings.hideSelected = self.settings.mode === 'multi';
661
509
  }
662
-
663
- self.loadPlugins(self.settings.plugins);
510
+
511
+ self.initializePlugins(self.settings.plugins);
664
512
  self.setupCallbacks();
665
513
  self.setup();
666
514
  };
667
-
515
+
668
516
  // mixins
669
517
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
670
-
518
+
671
519
  MicroEvent.mixin(Selectize);
672
- Plugins.mixin(Selectize, 'Selectize');
673
-
520
+ MicroPlugin.mixin(Selectize);
521
+
674
522
  // methods
675
523
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
676
-
524
+
677
525
  $.extend(Selectize.prototype, {
678
-
526
+
679
527
  /**
680
528
  * Creates all elements and sets up event bindings.
681
529
  */
682
530
  setup: function() {
683
- var self = this;
684
- var settings = self.settings;
531
+ var self = this;
532
+ var settings = self.settings;
533
+ var eventNS = self.eventNS;
534
+ var $window = $(window);
535
+ var $document = $(document);
536
+
685
537
  var $wrapper;
686
538
  var $control;
687
539
  var $control_input;
@@ -693,43 +545,44 @@
693
545
  var timeout_focus;
694
546
  var tab_index;
695
547
  var classes;
696
-
548
+ var classes_plugins;
549
+
550
+ inputMode = self.settings.mode;
697
551
  tab_index = self.$input.attr('tabindex') || '';
698
552
  classes = self.$input.attr('class') || '';
699
- $wrapper = $('<div>').addClass(settings.theme).addClass(settings.wrapperClass).addClass(classes);
700
- $control = $('<div>').addClass(settings.inputClass).addClass('items').toggleClass('has-options', !$.isEmptyObject(self.options)).appendTo($wrapper);
701
- $control_input = $('<input type="text">').appendTo($control).attr('tabindex',tab_index);
553
+
554
+ $wrapper = $('<div>').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode);
555
+ $control = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper);
556
+ $control_input = $('<input type="text">').appendTo($control).attr('tabindex', tab_index);
702
557
  $dropdown_parent = $(settings.dropdownParent || $wrapper);
703
- $dropdown = $('<div>').addClass(settings.dropdownClass).hide().appendTo($dropdown_parent);
558
+ $dropdown = $('<div>').addClass(settings.dropdownClass).addClass(classes).addClass(inputMode).hide().appendTo($dropdown_parent);
704
559
  $dropdown_content = $('<div>').addClass(settings.dropdownContentClass).appendTo($dropdown);
705
-
560
+
706
561
  $wrapper.css({
707
562
  width: self.$input[0].style.width,
708
563
  display: self.$input.css('display')
709
564
  });
710
-
711
- if (self.plugins.length) {
712
- $wrapper.addClass('plugin-' + self.plugins.join(' plugin-'));
565
+
566
+ if (self.plugins.names.length) {
567
+ classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
568
+ $wrapper.addClass(classes_plugins);
569
+ $dropdown.addClass(classes_plugins);
713
570
  }
714
-
715
- inputMode = self.settings.mode;
716
- $wrapper.toggleClass('single', inputMode === 'single');
717
- $wrapper.toggleClass('multi', inputMode === 'multi');
718
-
571
+
719
572
  if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) {
720
573
  self.$input.attr('multiple', 'multiple');
721
574
  }
722
-
575
+
723
576
  if (self.settings.placeholder) {
724
577
  $control_input.attr('placeholder', settings.placeholder);
725
578
  }
726
-
579
+
727
580
  self.$wrapper = $wrapper;
728
581
  self.$control = $control;
729
582
  self.$control_input = $control_input;
730
583
  self.$dropdown = $dropdown;
731
584
  self.$dropdown_content = $dropdown_content;
732
-
585
+
733
586
  $control.on('mousedown', function(e) {
734
587
  if (!e.isDefaultPrevented()) {
735
588
  window.setTimeout(function() {
@@ -737,7 +590,7 @@
737
590
  }, 0);
738
591
  }
739
592
  });
740
-
593
+
741
594
  // necessary for mobile webkit devices (manual focus triggering
742
595
  // is ignored unless invoked within a click event)
743
596
  $control.on('click', function(e) {
@@ -745,12 +598,12 @@
745
598
  self.focus(true);
746
599
  }
747
600
  });
748
-
601
+
749
602
  $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); });
750
603
  $dropdown.on('mousedown', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
751
604
  watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); });
752
605
  autoGrow($control_input);
753
-
606
+
754
607
  $control_input.on({
755
608
  mousedown : function(e) { e.stopPropagation(); },
756
609
  keydown : function() { return self.onKeyDown.apply(self, arguments); },
@@ -760,73 +613,72 @@
760
613
  blur : function() { return self.onBlur.apply(self, arguments); },
761
614
  focus : function() { return self.onFocus.apply(self, arguments); }
762
615
  });
763
-
764
- $(document).on({
765
- keydown: function(e) {
766
- self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey'];
767
- self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey'];
768
- self.isShiftDown = e.shiftKey;
769
- },
770
- keyup: function(e) {
771
- if (e.keyCode === KEY_CTRL) self.isCtrlDown = false;
772
- if (e.keyCode === KEY_SHIFT) self.isShiftDown = false;
773
- if (e.keyCode === KEY_CMD) self.isCmdDown = false;
774
- },
775
- mousedown: function(e) {
776
- if (self.isFocused) {
777
- // prevent events on the dropdown scrollbar from causing the control to blur
778
- if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) {
779
- var ignoreFocus = self.ignoreFocus;
780
- self.ignoreFocus = true;
781
- window.setTimeout(function() {
782
- self.ignoreFocus = ignoreFocus;
783
- self.focus(false);
784
- }, 0);
785
- return;
786
- }
787
- // blur on click outside
788
- if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
789
- self.blur();
790
- }
616
+
617
+ $document.on('keydown' + eventNS, function(e) {
618
+ self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey'];
619
+ self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey'];
620
+ self.isShiftDown = e.shiftKey;
621
+ });
622
+
623
+ $document.on('keyup' + eventNS, function(e) {
624
+ if (e.keyCode === KEY_CTRL) self.isCtrlDown = false;
625
+ if (e.keyCode === KEY_SHIFT) self.isShiftDown = false;
626
+ if (e.keyCode === KEY_CMD) self.isCmdDown = false;
627
+ });
628
+
629
+ $document.on('mousedown' + eventNS, function(e) {
630
+ if (self.isFocused) {
631
+ // prevent events on the dropdown scrollbar from causing the control to blur
632
+ if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) {
633
+ var ignoreFocus = self.ignoreFocus;
634
+ self.ignoreFocus = true;
635
+ window.setTimeout(function() {
636
+ self.ignoreFocus = ignoreFocus;
637
+ self.focus(false);
638
+ }, 0);
639
+ return;
640
+ }
641
+ // blur on click outside
642
+ if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
643
+ self.blur();
791
644
  }
792
645
  }
793
646
  });
794
-
795
- $(window).on({
796
- 'scroll resize': function() {
797
- if (self.isOpen) {
798
- self.positionDropdown.apply(self, arguments);
799
- }
800
- },
801
- 'mousemove': function() {
802
- self.ignoreHover = false;
647
+
648
+ $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() {
649
+ if (self.isOpen) {
650
+ self.positionDropdown.apply(self, arguments);
803
651
  }
804
652
  });
805
-
653
+ $window.on('mousemove' + eventNS, function() {
654
+ self.ignoreHover = false;
655
+ });
656
+
806
657
  self.$input.attr('tabindex',-1).hide().after(self.$wrapper);
807
-
658
+
808
659
  if ($.isArray(settings.items)) {
809
660
  self.setValue(settings.items);
810
661
  delete settings.items;
811
662
  }
812
-
663
+
813
664
  self.updateOriginalInput();
814
665
  self.refreshItems();
666
+ self.refreshClasses();
815
667
  self.updatePlaceholder();
816
668
  self.isSetup = true;
817
-
669
+
818
670
  if (self.$input.is(':disabled')) {
819
671
  self.disable();
820
672
  }
821
-
673
+
822
674
  self.trigger('initialize');
823
-
675
+
824
676
  // preload options
825
677
  if (settings.preload) {
826
678
  self.onSearchChange('');
827
679
  }
828
680
  },
829
-
681
+
830
682
  /**
831
683
  * Maps fired events to callbacks provided
832
684
  * in the settings used when creating the control.
@@ -845,7 +697,7 @@
845
697
  'dropdown_close' : 'onDropdownClose',
846
698
  'type' : 'onType'
847
699
  };
848
-
700
+
849
701
  for (key in callbacks) {
850
702
  if (callbacks.hasOwnProperty(key)) {
851
703
  fn = this.settings[callbacks[key]];
@@ -853,7 +705,7 @@
853
705
  }
854
706
  }
855
707
  },
856
-
708
+
857
709
  /**
858
710
  * Triggers a callback defined in the user-provided settings.
859
711
  * Events: onItemAdd, onOptionAdd, etc
@@ -867,7 +719,7 @@
867
719
  this.settings[event].apply(this, args);
868
720
  }
869
721
  },
870
-
722
+
871
723
  /**
872
724
  * Triggered on <input> keypress.
873
725
  *
@@ -883,7 +735,7 @@
883
735
  return false;
884
736
  }
885
737
  },
886
-
738
+
887
739
  /**
888
740
  * Triggered on <input> keydown.
889
741
  *
@@ -893,14 +745,14 @@
893
745
  onKeyDown: function(e) {
894
746
  var isInput = e.target === this.$control_input[0];
895
747
  var self = this;
896
-
748
+
897
749
  if (self.isLocked) {
898
750
  if (e.keyCode !== KEY_TAB) {
899
751
  e.preventDefault();
900
752
  }
901
753
  return;
902
754
  }
903
-
755
+
904
756
  switch (e.keyCode) {
905
757
  case KEY_A:
906
758
  if (self.isCmdDown) {
@@ -957,7 +809,7 @@
957
809
  return;
958
810
  }
959
811
  },
960
-
812
+
961
813
  /**
962
814
  * Triggered on <input> keyup.
963
815
  *
@@ -966,7 +818,7 @@
966
818
  */
967
819
  onKeyUp: function(e) {
968
820
  var self = this;
969
-
821
+
970
822
  if (self.isLocked) return e && e.preventDefault();
971
823
  var value = self.$control_input.val() || '';
972
824
  if (self.lastValue !== value) {
@@ -976,7 +828,7 @@
976
828
  self.trigger('type', value);
977
829
  }
978
830
  },
979
-
831
+
980
832
  /**
981
833
  * Invokes the user-provide option provider / loader.
982
834
  *
@@ -995,7 +847,7 @@
995
847
  fn.apply(self, [value, callback]);
996
848
  });
997
849
  },
998
-
850
+
999
851
  /**
1000
852
  * Triggered on <input> focus.
1001
853
  *
@@ -1004,7 +856,7 @@
1004
856
  */
1005
857
  onFocus: function(e) {
1006
858
  var self = this;
1007
-
859
+
1008
860
  self.isInputFocused = true;
1009
861
  self.isFocused = true;
1010
862
  if (self.isDisabled) {
@@ -1012,16 +864,16 @@
1012
864
  e.preventDefault();
1013
865
  return false;
1014
866
  }
1015
-
867
+
1016
868
  if (self.ignoreFocus) return;
1017
869
  if (self.settings.preload === 'focus') self.onSearchChange('');
1018
-
870
+
1019
871
  self.showInput();
1020
872
  self.setActiveItem(null);
1021
873
  self.refreshOptions(!!self.settings.openOnFocus);
1022
874
  self.refreshClasses();
1023
875
  },
1024
-
876
+
1025
877
  /**
1026
878
  * Triggered on <input> blur.
1027
879
  *
@@ -1032,7 +884,7 @@
1032
884
  var self = this;
1033
885
  self.isInputFocused = false;
1034
886
  if (self.ignoreFocus) return;
1035
-
887
+
1036
888
  self.close();
1037
889
  self.setTextboxValue('');
1038
890
  self.setActiveItem(null);
@@ -1041,7 +893,7 @@
1041
893
  self.isFocused = false;
1042
894
  self.refreshClasses();
1043
895
  },
1044
-
896
+
1045
897
  /**
1046
898
  * Triggered when the user rolls over
1047
899
  * an option in the autocomplete dropdown menu.
@@ -1053,7 +905,7 @@
1053
905
  if (this.ignoreHover) return;
1054
906
  this.setActiveOption(e.currentTarget, false);
1055
907
  },
1056
-
908
+
1057
909
  /**
1058
910
  * Triggered when the user clicks on an option
1059
911
  * in the autocomplete dropdown menu.
@@ -1063,11 +915,11 @@
1063
915
  */
1064
916
  onOptionSelect: function(e) {
1065
917
  var value, $target, $option, self = this;
1066
-
918
+
1067
919
  e.preventDefault && e.preventDefault();
1068
920
  e.stopPropagation && e.stopPropagation();
1069
921
  self.focus(false);
1070
-
922
+
1071
923
  $target = $(e.currentTarget);
1072
924
  if ($target.hasClass('create')) {
1073
925
  self.createItem();
@@ -1082,7 +934,7 @@
1082
934
  }
1083
935
  }
1084
936
  },
1085
-
937
+
1086
938
  /**
1087
939
  * Triggered when the user clicks on an item
1088
940
  * that has been selected.
@@ -1092,7 +944,7 @@
1092
944
  */
1093
945
  onItemSelect: function(e) {
1094
946
  var self = this;
1095
-
947
+
1096
948
  if (self.settings.mode === 'multi') {
1097
949
  e.preventDefault();
1098
950
  self.setActiveItem(e.currentTarget, e);
@@ -1100,7 +952,7 @@
1100
952
  self.hideInput();
1101
953
  }
1102
954
  },
1103
-
955
+
1104
956
  /**
1105
957
  * Invokes the provided method that provides
1106
958
  * results to a callback---which are then added
@@ -1111,7 +963,7 @@
1111
963
  load: function(fn) {
1112
964
  var self = this;
1113
965
  var $wrapper = self.$wrapper.addClass('loading');
1114
-
966
+
1115
967
  self.loading++;
1116
968
  fn.apply(self, [function(results) {
1117
969
  self.loading = Math.max(self.loading - 1, 0);
@@ -1126,7 +978,7 @@
1126
978
  self.trigger('load', results);
1127
979
  }]);
1128
980
  },
1129
-
981
+
1130
982
  /**
1131
983
  * Sets the input field of the control to the specified value.
1132
984
  *
@@ -1136,7 +988,7 @@
1136
988
  this.$control_input.val(value).triggerHandler('update');
1137
989
  this.lastValue = value;
1138
990
  },
1139
-
991
+
1140
992
  /**
1141
993
  * Returns the value of the control. If multiple items
1142
994
  * can be selected (e.g. <select multiple>), this returns
@@ -1152,7 +1004,7 @@
1152
1004
  return this.items.join(this.settings.delimiter);
1153
1005
  }
1154
1006
  },
1155
-
1007
+
1156
1008
  /**
1157
1009
  * Resets the selected items to the given value.
1158
1010
  *
@@ -1167,7 +1019,7 @@
1167
1019
  }
1168
1020
  });
1169
1021
  },
1170
-
1022
+
1171
1023
  /**
1172
1024
  * Sets the selected item.
1173
1025
  *
@@ -1179,9 +1031,9 @@
1179
1031
  var eventName;
1180
1032
  var i, idx, begin, end, item, swap;
1181
1033
  var $last;
1182
-
1034
+
1183
1035
  $item = $($item);
1184
-
1036
+
1185
1037
  // clear the active selection
1186
1038
  if (!$item.length) {
1187
1039
  $(self.$activeItems).removeClass('active');
@@ -1189,10 +1041,10 @@
1189
1041
  self.isFocused = self.isInputFocused;
1190
1042
  return;
1191
1043
  }
1192
-
1044
+
1193
1045
  // modify selection
1194
1046
  eventName = e && e.type.toLowerCase();
1195
-
1047
+
1196
1048
  if (eventName === 'mousedown' && self.isShiftDown && self.$activeItems.length) {
1197
1049
  $last = self.$control.children('.active:last');
1198
1050
  begin = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$last[0]]);
@@ -1222,10 +1074,10 @@
1222
1074
  $(self.$activeItems).removeClass('active');
1223
1075
  self.$activeItems = [$item.addClass('active')[0]];
1224
1076
  }
1225
-
1077
+
1226
1078
  self.isFocused = !!self.$activeItems.length || self.isInputFocused;
1227
1079
  },
1228
-
1080
+
1229
1081
  /**
1230
1082
  * Sets the selected item in the dropdown menu
1231
1083
  * of available options.
@@ -1238,33 +1090,33 @@
1238
1090
  var height_menu, height_item, y;
1239
1091
  var scroll_top, scroll_bottom;
1240
1092
  var self = this;
1241
-
1093
+
1242
1094
  if (self.$activeOption) self.$activeOption.removeClass('active');
1243
1095
  self.$activeOption = null;
1244
-
1096
+
1245
1097
  $option = $($option);
1246
1098
  if (!$option.length) return;
1247
-
1099
+
1248
1100
  self.$activeOption = $option.addClass('active');
1249
-
1101
+
1250
1102
  if (scroll || !isset(scroll)) {
1251
-
1103
+
1252
1104
  height_menu = self.$dropdown_content.height();
1253
1105
  height_item = self.$activeOption.outerHeight(true);
1254
1106
  scroll = self.$dropdown_content.scrollTop() || 0;
1255
1107
  y = self.$activeOption.offset().top - self.$dropdown_content.offset().top + scroll;
1256
1108
  scroll_top = y;
1257
1109
  scroll_bottom = y - height_menu + height_item;
1258
-
1110
+
1259
1111
  if (y + height_item > height_menu - scroll) {
1260
1112
  self.$dropdown_content.stop().animate({scrollTop: scroll_bottom}, animate ? self.settings.scrollDuration : 0);
1261
1113
  } else if (y < scroll) {
1262
1114
  self.$dropdown_content.stop().animate({scrollTop: scroll_top}, animate ? self.settings.scrollDuration : 0);
1263
1115
  }
1264
-
1116
+
1265
1117
  }
1266
1118
  },
1267
-
1119
+
1268
1120
  /**
1269
1121
  * Selects all items (CTRL + A).
1270
1122
  */
@@ -1273,20 +1125,20 @@
1273
1125
  this.isFocused = true;
1274
1126
  if (this.$activeItems.length) this.hideInput();
1275
1127
  },
1276
-
1128
+
1277
1129
  /**
1278
1130
  * Hides the input element out of view, while
1279
1131
  * retaining its focus.
1280
1132
  */
1281
1133
  hideInput: function() {
1282
1134
  var self = this;
1283
-
1135
+
1284
1136
  self.close();
1285
1137
  self.setTextboxValue('');
1286
1138
  self.$control_input.css({opacity: 0, position: 'absolute', left: -10000});
1287
1139
  self.isInputHidden = true;
1288
1140
  },
1289
-
1141
+
1290
1142
  /**
1291
1143
  * Restores input visibility.
1292
1144
  */
@@ -1294,7 +1146,7 @@
1294
1146
  this.$control_input.css({opacity: 1, position: 'relative', left: 0});
1295
1147
  this.isInputHidden = false;
1296
1148
  },
1297
-
1149
+
1298
1150
  /**
1299
1151
  * Gives the control focus. If "trigger" is falsy,
1300
1152
  * focus handlers won't be fired--causing the focus
@@ -1304,7 +1156,7 @@
1304
1156
  */
1305
1157
  focus: function(trigger) {
1306
1158
  var self = this;
1307
-
1159
+
1308
1160
  if (self.isDisabled) return;
1309
1161
  self.ignoreFocus = true;
1310
1162
  self.$control_input[0].focus();
@@ -1314,132 +1166,48 @@
1314
1166
  if (trigger) self.onFocus();
1315
1167
  }, 0);
1316
1168
  },
1317
-
1169
+
1318
1170
  /**
1319
1171
  * Forces the control out of focus.
1320
1172
  */
1321
1173
  blur: function() {
1322
1174
  this.$control_input.trigger('blur');
1323
1175
  },
1324
-
1176
+
1325
1177
  /**
1326
- * Splits a search string into an array of
1327
- * individual regexps to be used to match results.
1178
+ * Returns a function that scores an object
1179
+ * to show how good of a match it is to the
1180
+ * provided query.
1328
1181
  *
1329
1182
  * @param {string} query
1330
- * @returns {array}
1183
+ * @param {object} options
1184
+ * @return {function}
1331
1185
  */
1332
- parseSearchTokens: function(query) {
1333
- query = $.trim(String(query || '').toLowerCase());
1334
- if (!query || !query.length) return [];
1335
-
1336
- var i, n, regex, letter;
1337
- var tokens = [];
1338
- var words = query.split(/ +/);
1339
-
1340
- for (i = 0, n = words.length; i < n; i++) {
1341
- regex = escape_regex(words[i]);
1342
- if (this.settings.diacritics) {
1343
- for (letter in DIACRITICS) {
1344
- if (DIACRITICS.hasOwnProperty(letter)) {
1345
- regex = regex.replace(new RegExp(letter, 'g'), DIACRITICS[letter]);
1346
- }
1347
- }
1348
- }
1349
- tokens.push({
1350
- string : words[i],
1351
- regex : new RegExp(regex, 'i')
1352
- });
1353
- }
1354
-
1355
- return tokens;
1186
+ getScoreFunction: function(query) {
1187
+ return this.sifter.getScoreFunction(query, this.getSearchOptions());
1356
1188
  },
1357
-
1189
+
1358
1190
  /**
1359
- * Returns a function to be used to score individual results.
1360
- * Results will be sorted by the score (descending). Scores less
1361
- * than or equal to zero (no match) will not be included in the results.
1191
+ * Returns search options for sifter (the system
1192
+ * for scoring and sorting results).
1362
1193
  *
1363
- * @param {object} data
1364
- * @param {object} search
1365
- * @returns {function}
1194
+ * @see https://github.com/brianreavis/sifter.js
1195
+ * @return {object}
1366
1196
  */
1367
- getScoreFunction: function(search) {
1368
- var self = this;
1369
- var tokens = search.tokens;
1370
-
1371
- var calculateFieldScore = (function() {
1372
- if (!tokens.length) {
1373
- return function() { return 0; };
1374
- } else if (tokens.length === 1) {
1375
- return function(value) {
1376
- var score, pos;
1377
-
1378
- value = String(value || '').toLowerCase();
1379
- pos = value.search(tokens[0].regex);
1380
- if (pos === -1) return 0;
1381
- score = tokens[0].string.length / value.length;
1382
- if (pos === 0) score += 0.5;
1383
- return score;
1384
- };
1385
- } else {
1386
- return function(value) {
1387
- var score, pos, i, j;
1388
-
1389
- value = String(value || '').toLowerCase();
1390
- score = 0;
1391
- for (i = 0, j = tokens.length; i < j; i++) {
1392
- pos = value.search(tokens[i].regex);
1393
- if (pos === -1) return 0;
1394
- if (pos === 0) score += 0.5;
1395
- score += tokens[i].string.length / value.length;
1396
- }
1397
- return score / tokens.length;
1398
- };
1399
- }
1400
- })();
1401
-
1402
- var calculateScore = (function() {
1403
- var fields = self.settings.searchField;
1404
- if (typeof fields === 'string') {
1405
- fields = [fields];
1406
- }
1407
- if (!fields || !fields.length) {
1408
- return function() { return 0; };
1409
- } else if (fields.length === 1) {
1410
- var field = fields[0];
1411
- return function(data) {
1412
- if (!data.hasOwnProperty(field)) return 0;
1413
- return calculateFieldScore(data[field]);
1414
- };
1415
- } else {
1416
- return function(data) {
1417
- var n = 0;
1418
- var score = 0;
1419
- for (var i = 0, j = fields.length; i < j; i++) {
1420
- if (data.hasOwnProperty(fields[i])) {
1421
- score += calculateFieldScore(data[fields[i]]);
1422
- n++;
1423
- }
1424
- }
1425
- return score / n;
1426
- };
1427
- }
1428
- })();
1429
-
1430
- return calculateScore;
1197
+ getSearchOptions: function() {
1198
+ var settings = this.settings;
1199
+ var fields = settings.searchField;
1200
+
1201
+ return {
1202
+ fields : $.isArray(fields) ? fields : [fields],
1203
+ sort : settings.sortField,
1204
+ direction : settings.sortDirection,
1205
+ };
1431
1206
  },
1432
-
1207
+
1433
1208
  /**
1434
1209
  * Searches through available options and returns
1435
- * a sorted array of matches. Includes options that
1436
- * have already been selected.
1437
- *
1438
- * The `settings` parameter can contain:
1439
- *
1440
- * - searchField
1441
- * - sortField
1442
- * - sortDirection
1210
+ * a sorted array of matches.
1443
1211
  *
1444
1212
  * Returns an object containing:
1445
1213
  *
@@ -1449,109 +1217,43 @@
1449
1217
  * - items {array}
1450
1218
  *
1451
1219
  * @param {string} query
1452
- * @param {object} settings
1453
1220
  * @returns {object}
1454
1221
  */
1455
- search: function(query, settings) {
1456
- var self = this;
1457
- var value, score, search, calculateScore;
1458
-
1459
- settings = settings || {};
1460
- query = $.trim(String(query || '').toLowerCase());
1461
-
1222
+ search: function(query) {
1223
+ var i, value, score, result, calculateScore;
1224
+ var self = this;
1225
+ var settings = self.settings;
1226
+ var options = this.getSearchOptions();
1227
+
1228
+ // validate user-provided result scoring function
1229
+ if (settings.score) {
1230
+ calculateScore = self.settings.score.apply(this, [query]);
1231
+ if (typeof calculateScore !== 'function') {
1232
+ throw new Error('Selectize "score" setting must be a function that returns a function');
1233
+ }
1234
+ }
1235
+
1236
+ // perform search
1462
1237
  if (query !== self.lastQuery) {
1463
1238
  self.lastQuery = query;
1464
-
1465
- search = {
1466
- query : query,
1467
- tokens : self.parseSearchTokens(query),
1468
- total : 0,
1469
- items : []
1470
- };
1471
-
1472
- // generate result scoring function
1473
- if (self.settings.score) {
1474
- calculateScore = self.settings.score.apply(this, [search]);
1475
- if (typeof calculateScore !== 'function') {
1476
- throw new Error('Selectize "score" setting must be a function that returns a function');
1477
- }
1478
- } else {
1479
- calculateScore = self.getScoreFunction(search);
1480
- }
1481
-
1482
- // perform search and sort
1483
- if (query.length) {
1484
- for (value in self.options) {
1485
- if (self.options.hasOwnProperty(value)) {
1486
- score = calculateScore(self.options[value]);
1487
- if (score > 0) {
1488
- search.items.push({
1489
- score: score,
1490
- value: value
1491
- });
1492
- }
1493
- }
1494
- }
1495
- search.items.sort(function(a, b) {
1496
- return b.score - a.score;
1497
- });
1498
- } else {
1499
- for (value in self.options) {
1500
- if (self.options.hasOwnProperty(value)) {
1501
- search.items.push({
1502
- score: 1,
1503
- value: value
1504
- });
1505
- }
1506
- }
1507
- if (self.settings.sortField) {
1508
- search.items.sort((function() {
1509
- var field = self.settings.sortField;
1510
- var multiplier = self.settings.sortDirection === 'desc' ? -1 : 1;
1511
- return function(a, b) {
1512
- a = a && String(self.options[a.value][field] || '').toLowerCase();
1513
- b = b && String(self.options[b.value][field] || '').toLowerCase();
1514
- if (a > b) return 1 * multiplier;
1515
- if (b > a) return -1 * multiplier;
1516
- return 0;
1517
- };
1518
- })());
1519
- }
1520
- }
1521
- self.currentResults = search;
1239
+ result = self.sifter.search(query, $.extend(options, {score: calculateScore}));
1240
+ self.currentResults = result;
1522
1241
  } else {
1523
- search = $.extend(true, {}, self.currentResults);
1524
- }
1525
-
1526
- // apply limits and return
1527
- return self.prepareResults(search, settings);
1528
- },
1529
-
1530
- /**
1531
- * Filters out any items that have already been selected
1532
- * and applies search limits.
1533
- *
1534
- * @param {object} results
1535
- * @param {object} settings
1536
- * @returns {object}
1537
- */
1538
- prepareResults: function(search, settings) {
1539
- if (this.settings.hideSelected) {
1540
- for (var i = search.items.length - 1; i >= 0; i--) {
1541
- if (this.items.indexOf(String(search.items[i].value)) !== -1) {
1542
- search.items.splice(i, 1);
1242
+ result = $.extend(true, {}, self.currentResults);
1243
+ }
1244
+
1245
+ // filter out selected items
1246
+ if (settings.hideSelected) {
1247
+ for (i = result.items.length - 1; i >= 0; i--) {
1248
+ if (self.items.indexOf(hash_key(result.items[i].id)) !== -1) {
1249
+ result.items.splice(i, 1);
1543
1250
  }
1544
1251
  }
1545
1252
  }
1546
-
1547
- search.total = search.items.length;
1548
- if (typeof settings.limit === 'number') {
1549
- search.items = search.items.slice(0, settings.limit);
1550
- }
1551
-
1552
- return search;
1253
+
1254
+ return result;
1553
1255
  },
1554
-
1256
+
1555
1257
  /**
1556
1258
  * Refreshes the list of available options shown
1557
1259
  * in the autocomplete dropdown menu.
@@ -1562,24 +1264,24 @@
1562
1264
  if (typeof triggerDropdown === 'undefined') {
1563
1265
  triggerDropdown = true;
1564
1266
  }
1565
-
1267
+
1566
1268
  var self = this;
1567
1269
  var i, n, groups, groups_order, option, optgroup, html, html_children;
1568
1270
  var hasCreateOption;
1569
1271
  var query = self.$control_input.val();
1570
- var results = self.search(query, {});
1272
+ var results = self.search(query);
1571
1273
  var $active, $create;
1572
1274
  var $dropdown_content = self.$dropdown_content;
1573
-
1275
+
1574
1276
  // build markup
1575
1277
  n = results.items.length;
1576
1278
  if (typeof self.settings.maxOptions === 'number') {
1577
1279
  n = Math.min(n, self.settings.maxOptions);
1578
1280
  }
1579
-
1281
+
1580
1282
  // render and group available options individually
1581
1283
  groups = {};
1582
-
1284
+
1583
1285
  if (self.settings.optgroupOrder) {
1584
1286
  groups_order = self.settings.optgroupOrder;
1585
1287
  for (i = 0; i < groups_order.length; i++) {
@@ -1588,9 +1290,9 @@
1588
1290
  } else {
1589
1291
  groups_order = [];
1590
1292
  }
1591
-
1293
+
1592
1294
  for (i = 0; i < n; i++) {
1593
- option = self.options[results.items[i].value];
1295
+ option = self.options[results.items[i].id];
1594
1296
  optgroup = option[self.settings.optgroupField] || '';
1595
1297
  if (!self.optgroups.hasOwnProperty(optgroup)) {
1596
1298
  optgroup = '';
@@ -1601,7 +1303,7 @@
1601
1303
  }
1602
1304
  groups[optgroup].push(self.render('option', option));
1603
1305
  }
1604
-
1306
+
1605
1307
  // render optgroup headers & join groups
1606
1308
  html = [];
1607
1309
  for (i = 0, n = groups_order.length; i < n; i++) {
@@ -1618,30 +1320,30 @@
1618
1320
  html.push(groups[optgroup].join(''));
1619
1321
  }
1620
1322
  }
1621
-
1323
+
1622
1324
  $dropdown_content.html(html.join(''));
1623
-
1325
+
1624
1326
  // highlight matching terms inline
1625
1327
  if (self.settings.highlight && results.query.length && results.tokens.length) {
1626
1328
  for (i = 0, n = results.tokens.length; i < n; i++) {
1627
1329
  highlight($dropdown_content, results.tokens[i].regex);
1628
1330
  }
1629
1331
  }
1630
-
1332
+
1631
1333
  // add "selected" class to selected options
1632
1334
  if (!self.settings.hideSelected) {
1633
1335
  for (i = 0, n = self.items.length; i < n; i++) {
1634
1336
  self.getOption(self.items[i]).addClass('selected');
1635
1337
  }
1636
1338
  }
1637
-
1339
+
1638
1340
  // add create option
1639
1341
  hasCreateOption = self.settings.create && results.query.length;
1640
1342
  if (hasCreateOption) {
1641
1343
  $dropdown_content.prepend(self.render('option_create', {input: query}));
1642
1344
  $create = $($dropdown_content[0].childNodes[0]);
1643
1345
  }
1644
-
1346
+
1645
1347
  // activate
1646
1348
  self.hasOptions = results.items.length > 0 || hasCreateOption;
1647
1349
  if (self.hasOptions) {
@@ -1661,7 +1363,7 @@
1661
1363
  if (triggerDropdown && self.isOpen) { self.close(); }
1662
1364
  }
1663
1365
  },
1664
-
1366
+
1665
1367
  /**
1666
1368
  * Adds an available option. If it already exists,
1667
1369
  * nothing will happen. Note: this does not refresh
@@ -1670,31 +1372,29 @@
1670
1372
  *
1671
1373
  * Usage:
1672
1374
  *
1673
- * this.addOption(value, data)
1674
1375
  * this.addOption(data)
1675
1376
  *
1676
- * @param {string} value
1677
1377
  * @param {object} data
1678
1378
  */
1679
- addOption: function(value, data) {
1680
- var i, n, optgroup, self = this;
1681
-
1682
- if ($.isArray(value)) {
1683
- for (i = 0, n = value.length; i < n; i++) {
1684
- self.addOption(value[i][self.settings.valueField], value[i]);
1379
+ addOption: function(data) {
1380
+ var i, n, optgroup, value, self = this;
1381
+
1382
+ if ($.isArray(data)) {
1383
+ for (i = 0, n = data.length; i < n; i++) {
1384
+ self.addOption(data[i]);
1685
1385
  }
1686
1386
  return;
1687
1387
  }
1688
-
1689
- value = hash_key(value);
1690
- if (self.options.hasOwnProperty(value)) return;
1691
-
1388
+
1389
+ value = hash_key(data[self.settings.valueField]);
1390
+ if (!value || self.options.hasOwnProperty(value)) return;
1391
+
1692
1392
  self.userOptions[value] = true;
1693
1393
  self.options[value] = data;
1694
1394
  self.lastQuery = null;
1695
1395
  self.trigger('option_add', value, data);
1696
1396
  },
1697
-
1397
+
1698
1398
  /**
1699
1399
  * Registers a new optgroup for options
1700
1400
  * to be bucketed into.
@@ -1706,7 +1406,7 @@
1706
1406
  this.optgroups[id] = data;
1707
1407
  this.trigger('optgroup_add', value, data);
1708
1408
  },
1709
-
1409
+
1710
1410
  /**
1711
1411
  * Updates an option available for selection. If
1712
1412
  * it is visible in the selected items or options
@@ -1719,14 +1419,14 @@
1719
1419
  var self = this;
1720
1420
  var $item, $item_new;
1721
1421
  var value_new, index_item, cache_items, cache_options;
1722
-
1422
+
1723
1423
  value = hash_key(value);
1724
1424
  value_new = hash_key(data[self.settings.valueField]);
1725
-
1425
+
1726
1426
  // sanity checks
1727
1427
  if (!self.options.hasOwnProperty(value)) return;
1728
1428
  if (!value_new) throw new Error('Value must be set in option data');
1729
-
1429
+
1730
1430
  // update references
1731
1431
  if (value_new !== value) {
1732
1432
  delete self.options[value];
@@ -1736,11 +1436,11 @@
1736
1436
  }
1737
1437
  }
1738
1438
  self.options[value_new] = data;
1739
-
1439
+
1740
1440
  // invalidate render cache
1741
1441
  cache_items = self.renderCache['item'];
1742
1442
  cache_options = self.renderCache['option'];
1743
-
1443
+
1744
1444
  if (isset(cache_items)) {
1745
1445
  delete cache_items[value];
1746
1446
  delete cache_items[value_new];
@@ -1749,7 +1449,7 @@
1749
1449
  delete cache_options[value];
1750
1450
  delete cache_options[value_new];
1751
1451
  }
1752
-
1452
+
1753
1453
  // update the item if it's selected
1754
1454
  if (self.items.indexOf(value_new) !== -1) {
1755
1455
  $item = self.getItem(value);
@@ -1757,13 +1457,13 @@
1757
1457
  if ($item.hasClass('active')) $item_new.addClass('active');
1758
1458
  $item.replaceWith($item_new);
1759
1459
  }
1760
-
1460
+
1761
1461
  // update dropdown contents
1762
1462
  if (self.isOpen) {
1763
1463
  self.refreshOptions(false);
1764
1464
  }
1765
1465
  },
1766
-
1466
+
1767
1467
  /**
1768
1468
  * Removes a single option.
1769
1469
  *
@@ -1771,7 +1471,7 @@
1771
1471
  */
1772
1472
  removeOption: function(value) {
1773
1473
  var self = this;
1774
-
1474
+
1775
1475
  value = hash_key(value);
1776
1476
  delete self.userOptions[value];
1777
1477
  delete self.options[value];
@@ -1779,21 +1479,21 @@
1779
1479
  self.trigger('option_remove', value);
1780
1480
  self.removeItem(value);
1781
1481
  },
1782
-
1482
+
1783
1483
  /**
1784
1484
  * Clears all options.
1785
1485
  */
1786
1486
  clearOptions: function() {
1787
1487
  var self = this;
1788
-
1488
+
1789
1489
  self.loadedSearches = {};
1790
1490
  self.userOptions = {};
1791
- self.options = {};
1491
+ self.options = self.sifter.items = {};
1792
1492
  self.lastQuery = null;
1793
1493
  self.trigger('option_clear');
1794
1494
  self.clear();
1795
1495
  },
1796
-
1496
+
1797
1497
  /**
1798
1498
  * Returns the jQuery element of the option
1799
1499
  * matching the given value.
@@ -1805,7 +1505,7 @@
1805
1505
  value = hash_key(value);
1806
1506
  return value ? this.$dropdown_content.find('[data-selectable]').filter('[data-value="' + escape_quotes(value) + '"]:first') : $();
1807
1507
  },
1808
-
1508
+
1809
1509
  /**
1810
1510
  * Returns the jQuery element of the next or
1811
1511
  * previous selectable option.
@@ -1817,10 +1517,10 @@
1817
1517
  getAdjacentOption: function($option, direction) {
1818
1518
  var $options = this.$dropdown.find('[data-selectable]');
1819
1519
  var index = $options.index($option) + direction;
1820
-
1520
+
1821
1521
  return index >= 0 && index < $options.length ? $options.eq(index) : $();
1822
1522
  },
1823
-
1523
+
1824
1524
  /**
1825
1525
  * Returns the jQuery element of the item
1826
1526
  * matching the given value.
@@ -1831,7 +1531,7 @@
1831
1531
  getItem: function(value) {
1832
1532
  return this.$control.children('[data-value="' + escape_quotes(hash_key(value)) + '"]');
1833
1533
  },
1834
-
1534
+
1835
1535
  /**
1836
1536
  * "Selects" an item. Adds it to the list
1837
1537
  * at the current caret position.
@@ -1845,20 +1545,20 @@
1845
1545
  var inputMode = self.settings.mode;
1846
1546
  var i, active, options, value_next;
1847
1547
  value = hash_key(value);
1848
-
1548
+
1849
1549
  if (inputMode === 'single') self.clear();
1850
1550
  if (inputMode === 'multi' && self.isFull()) return;
1851
1551
  if (self.items.indexOf(value) !== -1) return;
1852
1552
  if (!self.options.hasOwnProperty(value)) return;
1853
-
1553
+
1854
1554
  $item = $(self.render('item', self.options[value]));
1855
1555
  self.items.splice(self.caretPos, 0, value);
1856
1556
  self.insertAtCaret($item);
1857
1557
  self.refreshClasses();
1858
-
1558
+
1859
1559
  if (self.isSetup) {
1860
1560
  options = self.$dropdown_content.find('[data-selectable]');
1861
-
1561
+
1862
1562
  // update menu / remove the option
1863
1563
  $option = self.getOption(value);
1864
1564
  value_next = self.getAdjacentOption($option, 1).attr('data-value');
@@ -1866,14 +1566,14 @@
1866
1566
  if (value_next) {
1867
1567
  self.setActiveOption(self.getOption(value_next));
1868
1568
  }
1869
-
1569
+
1870
1570
  // hide the menu if the maximum number of items have been selected or no options are left
1871
1571
  if (!options.length || (self.settings.maxItems !== null && self.items.length >= self.settings.maxItems)) {
1872
1572
  self.close();
1873
1573
  } else {
1874
1574
  self.positionDropdown();
1875
1575
  }
1876
-
1576
+
1877
1577
  // restore focus to input
1878
1578
  if (self.isFocused) {
1879
1579
  window.setTimeout(function() {
@@ -1886,14 +1586,14 @@
1886
1586
  }
1887
1587
  }, 0);
1888
1588
  }
1889
-
1589
+
1890
1590
  self.updatePlaceholder();
1891
1591
  self.trigger('item_add', value, $item);
1892
1592
  self.updateOriginalInput();
1893
1593
  }
1894
1594
  });
1895
1595
  },
1896
-
1596
+
1897
1597
  /**
1898
1598
  * Removes the selected item matching
1899
1599
  * the provided value.
@@ -1903,28 +1603,28 @@
1903
1603
  removeItem: function(value) {
1904
1604
  var self = this;
1905
1605
  var $item, i, idx;
1906
-
1606
+
1907
1607
  $item = (typeof value === 'object') ? value : self.getItem(value);
1908
1608
  value = hash_key($item.attr('data-value'));
1909
1609
  i = self.items.indexOf(value);
1910
-
1610
+
1911
1611
  if (i !== -1) {
1912
1612
  $item.remove();
1913
1613
  if ($item.hasClass('active')) {
1914
1614
  idx = self.$activeItems.indexOf($item[0]);
1915
1615
  self.$activeItems.splice(idx, 1);
1916
1616
  }
1917
-
1617
+
1918
1618
  self.items.splice(i, 1);
1919
1619
  self.lastQuery = null;
1920
1620
  if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
1921
1621
  self.removeOption(value);
1922
1622
  }
1923
-
1623
+
1924
1624
  if (i < self.caretPos) {
1925
1625
  self.setCaret(self.caretPos - 1);
1926
1626
  }
1927
-
1627
+
1928
1628
  self.refreshClasses();
1929
1629
  self.updatePlaceholder();
1930
1630
  self.updateOriginalInput();
@@ -1932,7 +1632,7 @@
1932
1632
  self.trigger('item_remove', value);
1933
1633
  }
1934
1634
  },
1935
-
1635
+
1936
1636
  /**
1937
1637
  * Invokes the `create` method provided in the
1938
1638
  * selectize options that should provide the data
@@ -1947,52 +1647,52 @@
1947
1647
  var caret = self.caretPos;
1948
1648
  if (!input.length) return;
1949
1649
  self.lock();
1950
-
1650
+
1951
1651
  var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) {
1952
1652
  var data = {};
1953
1653
  data[self.settings.labelField] = input;
1954
1654
  data[self.settings.valueField] = input;
1955
1655
  return data;
1956
1656
  };
1957
-
1657
+
1958
1658
  var create = once(function(data) {
1959
1659
  self.unlock();
1960
1660
  self.focus(false);
1961
-
1661
+
1962
1662
  if (!data || typeof data !== 'object') return;
1963
1663
  var value = hash_key(data[self.settings.valueField]);
1964
1664
  if (!value) return;
1965
-
1665
+
1966
1666
  self.setTextboxValue('');
1967
- self.addOption(value, data);
1667
+ self.addOption(data);
1968
1668
  self.setCaret(caret);
1969
1669
  self.addItem(value);
1970
1670
  self.refreshOptions(self.settings.mode !== 'single');
1971
1671
  self.focus(false);
1972
1672
  });
1973
-
1673
+
1974
1674
  var output = setup.apply(this, [input, create]);
1975
1675
  if (typeof output !== 'undefined') {
1976
1676
  create(output);
1977
1677
  }
1978
1678
  },
1979
-
1679
+
1980
1680
  /**
1981
1681
  * Re-renders the selected item lists.
1982
1682
  */
1983
1683
  refreshItems: function() {
1984
1684
  this.lastQuery = null;
1985
-
1685
+
1986
1686
  if (this.isSetup) {
1987
1687
  for (var i = 0; i < this.items.length; i++) {
1988
1688
  this.addItem(this.items);
1989
1689
  }
1990
1690
  }
1991
-
1691
+
1992
1692
  this.refreshClasses();
1993
1693
  this.updateOriginalInput();
1994
1694
  },
1995
-
1695
+
1996
1696
  /**
1997
1697
  * Updates all state-dependent CSS classes.
1998
1698
  */
@@ -2006,10 +1706,11 @@
2006
1706
  .toggleClass('locked', isLocked)
2007
1707
  .toggleClass('full', isFull).toggleClass('not-full', !isFull)
2008
1708
  .toggleClass('dropdown-active', self.isOpen)
1709
+ .toggleClass('has-options', !$.isEmptyObject(self.options))
2009
1710
  .toggleClass('has-items', self.items.length > 0);
2010
1711
  this.$control_input.data('grow', !isFull && !isLocked);
2011
1712
  },
2012
-
1713
+
2013
1714
  /**
2014
1715
  * Determines whether or not more items can be added
2015
1716
  * to the control without exceeding the user-defined maximum.
@@ -2019,14 +1720,14 @@
2019
1720
  isFull: function() {
2020
1721
  return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
2021
1722
  },
2022
-
1723
+
2023
1724
  /**
2024
1725
  * Refreshes the original <select> or <input>
2025
1726
  * element to reflect the current state.
2026
1727
  */
2027
1728
  updateOriginalInput: function() {
2028
1729
  var i, n, options, self = this;
2029
-
1730
+
2030
1731
  if (self.$input[0].tagName.toLowerCase() === 'select') {
2031
1732
  options = [];
2032
1733
  for (i = 0, n = self.items.length; i < n; i++) {
@@ -2039,13 +1740,13 @@
2039
1740
  } else {
2040
1741
  self.$input.val(self.getValue());
2041
1742
  }
2042
-
1743
+
2043
1744
  self.$input.trigger('change');
2044
1745
  if (self.isSetup) {
2045
1746
  self.trigger('change', self.$input.val());
2046
1747
  }
2047
1748
  },
2048
-
1749
+
2049
1750
  /**
2050
1751
  * Shows/hide the input placeholder depending
2051
1752
  * on if there items in the list already.
@@ -2053,7 +1754,7 @@
2053
1754
  updatePlaceholder: function() {
2054
1755
  if (!this.settings.placeholder) return;
2055
1756
  var $input = this.$control_input;
2056
-
1757
+
2057
1758
  if (this.items.length) {
2058
1759
  $input.removeAttr('placeholder');
2059
1760
  } else {
@@ -2061,14 +1762,14 @@
2061
1762
  }
2062
1763
  $input.triggerHandler('update');
2063
1764
  },
2064
-
1765
+
2065
1766
  /**
2066
1767
  * Shows the autocomplete dropdown containing
2067
1768
  * the available options.
2068
1769
  */
2069
1770
  open: function() {
2070
1771
  var self = this;
2071
-
1772
+
2072
1773
  if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return;
2073
1774
  self.focus(true);
2074
1775
  self.isOpen = true;
@@ -2078,13 +1779,13 @@
2078
1779
  self.$dropdown.css({visibility: 'visible'});
2079
1780
  self.trigger('dropdown_open', this.$dropdown);
2080
1781
  },
2081
-
1782
+
2082
1783
  /**
2083
1784
  * Closes the autocomplete dropdown menu.
2084
1785
  */
2085
1786
  close: function() {
2086
1787
  var self = this;
2087
-
1788
+
2088
1789
  if (!self.isOpen) return;
2089
1790
  self.$dropdown.hide();
2090
1791
  self.setActiveOption(null);
@@ -2092,7 +1793,7 @@
2092
1793
  self.refreshClasses();
2093
1794
  self.trigger('dropdown_close', self.$dropdown);
2094
1795
  },
2095
-
1796
+
2096
1797
  /**
2097
1798
  * Calculates and applies the appropriate
2098
1799
  * position of the dropdown.
@@ -2101,21 +1802,21 @@
2101
1802
  var $control = this.$control;
2102
1803
  var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position();
2103
1804
  offset.top += $control.outerHeight(true);
2104
-
1805
+
2105
1806
  this.$dropdown.css({
2106
1807
  width : $control.outerWidth(),
2107
1808
  top : offset.top,
2108
1809
  left : offset.left
2109
1810
  });
2110
1811
  },
2111
-
1812
+
2112
1813
  /**
2113
1814
  * Resets / clears all selected items
2114
1815
  * from the control.
2115
1816
  */
2116
1817
  clear: function() {
2117
1818
  var self = this;
2118
-
1819
+
2119
1820
  if (!self.items.length) return;
2120
1821
  self.$control.children(':not(input)').remove();
2121
1822
  self.items = [];
@@ -2126,7 +1827,7 @@
2126
1827
  self.showInput();
2127
1828
  self.trigger('clear');
2128
1829
  },
2129
-
1830
+
2130
1831
  /**
2131
1832
  * A helper method for inserting an element
2132
1833
  * at the current caret position.
@@ -2142,7 +1843,7 @@
2142
1843
  }
2143
1844
  this.setCaret(caret + 1);
2144
1845
  },
2145
-
1846
+
2146
1847
  /**
2147
1848
  * Removes the current selected item(s).
2148
1849
  *
@@ -2152,22 +1853,22 @@
2152
1853
  deleteSelection: function(e) {
2153
1854
  var i, n, direction, selection, values, caret, option_select, $option_select, $tail;
2154
1855
  var self = this;
2155
-
1856
+
2156
1857
  direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1;
2157
1858
  selection = getSelection(self.$control_input[0]);
2158
-
1859
+
2159
1860
  if (self.$activeOption && !self.settings.hideSelected) {
2160
1861
  option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value');
2161
1862
  }
2162
-
1863
+
2163
1864
  // determine items that will be removed
2164
1865
  values = [];
2165
-
1866
+
2166
1867
  if (self.$activeItems.length) {
2167
1868
  $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first'));
2168
1869
  caret = self.$control.children(':not(input)').index($tail);
2169
1870
  if (direction > 0) { caret++; }
2170
-
1871
+
2171
1872
  for (i = 0, n = self.$activeItems.length; i < n; i++) {
2172
1873
  values.push($(self.$activeItems[i]).attr('data-value'));
2173
1874
  }
@@ -2182,12 +1883,12 @@
2182
1883
  values.push(self.items[self.caretPos]);
2183
1884
  }
2184
1885
  }
2185
-
1886
+
2186
1887
  // allow the callback to abort
2187
1888
  if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete(values) === false)) {
2188
1889
  return false;
2189
1890
  }
2190
-
1891
+
2191
1892
  // perform removal
2192
1893
  if (typeof caret !== 'undefined') {
2193
1894
  self.setCaret(caret);
@@ -2195,10 +1896,10 @@
2195
1896
  while (values.length) {
2196
1897
  self.removeItem(values.pop());
2197
1898
  }
2198
-
1899
+
2199
1900
  self.showInput();
2200
1901
  self.refreshOptions(true);
2201
-
1902
+
2202
1903
  // select previous option
2203
1904
  if (option_select) {
2204
1905
  $option_select = self.getOption(option_select);
@@ -2206,10 +1907,10 @@
2206
1907
  self.setActiveOption($option_select);
2207
1908
  }
2208
1909
  }
2209
-
1910
+
2210
1911
  return true;
2211
1912
  },
2212
-
1913
+
2213
1914
  /**
2214
1915
  * Selects the previous / next item (depending
2215
1916
  * on the `direction` argument).
@@ -2223,18 +1924,18 @@
2223
1924
  advanceSelection: function(direction, e) {
2224
1925
  var tail, selection, idx, valueLength, cursorAtEdge, $tail;
2225
1926
  var self = this;
2226
-
1927
+
2227
1928
  if (direction === 0) return;
2228
-
1929
+
2229
1930
  tail = direction > 0 ? 'last' : 'first';
2230
1931
  selection = getSelection(self.$control_input[0]);
2231
-
1932
+
2232
1933
  if (self.isInputFocused && !self.isInputHidden) {
2233
1934
  valueLength = self.$control_input.val().length;
2234
1935
  cursorAtEdge = direction < 0
2235
1936
  ? selection.start === 0 && selection.length === 0
2236
1937
  : selection.start === valueLength;
2237
-
1938
+
2238
1939
  if (cursorAtEdge && !valueLength) {
2239
1940
  self.advanceCaret(direction, e);
2240
1941
  }
@@ -2248,7 +1949,7 @@
2248
1949
  }
2249
1950
  }
2250
1951
  },
2251
-
1952
+
2252
1953
  /**
2253
1954
  * Moves the caret left / right.
2254
1955
  *
@@ -2270,7 +1971,7 @@
2270
1971
  self.setCaret(self.caretPos + direction);
2271
1972
  }
2272
1973
  },
2273
-
1974
+
2274
1975
  /**
2275
1976
  * Moves the caret to the specified index.
2276
1977
  *
@@ -2278,13 +1979,13 @@
2278
1979
  */
2279
1980
  setCaret: function(i) {
2280
1981
  var self = this;
2281
-
1982
+
2282
1983
  if (self.settings.mode === 'single') {
2283
1984
  i = self.items.length;
2284
1985
  } else {
2285
1986
  i = Math.max(0, Math.min(self.items.length, i));
2286
1987
  }
2287
-
1988
+
2288
1989
  // the input must be moved by leaving it in place and moving the
2289
1990
  // siblings, due to the fact that focus cannot be restored once lost
2290
1991
  // on mobile webkit devices
@@ -2298,10 +1999,10 @@
2298
1999
  self.$control.append($child);
2299
2000
  }
2300
2001
  }
2301
-
2002
+
2302
2003
  self.caretPos = i;
2303
2004
  },
2304
-
2005
+
2305
2006
  /**
2306
2007
  * Disables user input on the control. Used while
2307
2008
  * items are being asynchronously created.
@@ -2311,7 +2012,7 @@
2311
2012
  this.isLocked = true;
2312
2013
  this.refreshClasses();
2313
2014
  },
2314
-
2015
+
2315
2016
  /**
2316
2017
  * Re-enables user input on the control.
2317
2018
  */
@@ -2319,25 +2020,51 @@
2319
2020
  this.isLocked = false;
2320
2021
  this.refreshClasses();
2321
2022
  },
2322
-
2023
+
2323
2024
  /**
2324
2025
  * Disables user input on the control completely.
2325
2026
  * While disabled, it cannot receive focus.
2326
2027
  */
2327
2028
  disable: function() {
2328
- this.isDisabled = true;
2329
- this.lock();
2029
+ var self = this;
2030
+ self.$input.prop('disabled', true);
2031
+ self.isDisabled = true;
2032
+ self.lock();
2330
2033
  },
2331
-
2034
+
2332
2035
  /**
2333
2036
  * Enables the control so that it can respond
2334
2037
  * to focus and user input.
2335
2038
  */
2336
2039
  enable: function() {
2337
- this.isDisabled = false;
2338
- this.unlock();
2040
+ var self = this;
2041
+ self.$input.prop('disabled', false);
2042
+ self.isDisabled = false;
2043
+ self.unlock();
2339
2044
  },
2340
-
2045
+
2046
+ /**
2047
+ * Completely destroys the control and
2048
+ * unbinds all event listeners so that it can
2049
+ * be garbage collected.
2050
+ */
2051
+ destroy: function() {
2052
+ var self = this;
2053
+ var eventNS = self.eventNS;
2054
+
2055
+ self.trigger('destroy');
2056
+ self.off();
2057
+ self.$wrapper.remove();
2058
+ self.$dropdown.remove();
2059
+ self.$input.show();
2060
+
2061
+ $(window).off(eventNS);
2062
+ $(document).off(eventNS);
2063
+ $(document.body).off(eventNS);
2064
+
2065
+ delete self.$input[0].selectize;
2066
+ },
2067
+
2341
2068
  /**
2342
2069
  * A helper method for rendering "item" and
2343
2070
  * "option" templates, given the data.
@@ -2351,13 +2078,13 @@
2351
2078
  var html = '';
2352
2079
  var cache = false;
2353
2080
  var self = this;
2354
- var regex_tag = /^[\ ]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
2355
-
2081
+ var regex_tag = /^[\t ]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
2082
+
2356
2083
  if (templateName === 'option' || templateName === 'item') {
2357
2084
  value = hash_key(data[self.settings.valueField]);
2358
2085
  cache = !!value;
2359
2086
  }
2360
-
2087
+
2361
2088
  // pull markup from cache if it exists
2362
2089
  if (cache) {
2363
2090
  if (!isset(self.renderCache[templateName])) {
@@ -2367,7 +2094,7 @@
2367
2094
  return self.renderCache[templateName][value];
2368
2095
  }
2369
2096
  }
2370
-
2097
+
2371
2098
  // render markup
2372
2099
  if (self.settings.render && typeof self.settings.render[templateName] === 'function') {
2373
2100
  html = self.settings.render[templateName].apply(this, [data, escape_html]);
@@ -2392,7 +2119,7 @@
2392
2119
  break;
2393
2120
  }
2394
2121
  }
2395
-
2122
+
2396
2123
  // add mandatory attributes
2397
2124
  if (templateName === 'option' || templateName === 'option_create') {
2398
2125
  html = html.replace(regex_tag, '<$1 data-selectable');
@@ -2404,17 +2131,18 @@
2404
2131
  if (templateName === 'option' || templateName === 'item') {
2405
2132
  html = html.replace(regex_tag, '<$1 data-value="' + escape_html(value || '') + '"');
2406
2133
  }
2407
-
2134
+
2408
2135
  // update cache
2409
2136
  if (cache) {
2410
2137
  self.renderCache[templateName][value] = html;
2411
2138
  }
2412
-
2139
+
2413
2140
  return html;
2414
2141
  }
2415
-
2142
+
2416
2143
  });
2417
-
2144
+
2145
+ Selectize.count = 0;
2418
2146
  Selectize.defaults = {
2419
2147
  plugins: [],
2420
2148
  delimiter: ',',
@@ -2427,10 +2155,10 @@
2427
2155
  maxItems: null,
2428
2156
  hideSelected: null,
2429
2157
  preload: false,
2430
-
2158
+
2431
2159
  scrollDuration: 60,
2432
2160
  loadThrottle: 300,
2433
-
2161
+
2434
2162
  dataAttr: 'data-data',
2435
2163
  optgroupField: 'optgroup',
2436
2164
  sortField: null,
@@ -2441,16 +2169,15 @@
2441
2169
  optgroupValueField: 'value',
2442
2170
  optgroupOrder: null,
2443
2171
  searchField: ['text'],
2444
-
2172
+
2445
2173
  mode: null,
2446
- theme: 'default',
2447
2174
  wrapperClass: 'selectize-control',
2448
2175
  inputClass: 'selectize-input',
2449
2176
  dropdownClass: 'selectize-dropdown',
2450
2177
  dropdownContentClass: 'selectize-dropdown-content',
2451
-
2178
+
2452
2179
  dropdownParent: null,
2453
-
2180
+
2454
2181
  /*
2455
2182
  load : null, // function(query, callback) { ... }
2456
2183
  score : null, // function(search) { ... }
@@ -2467,7 +2194,7 @@
2467
2194
  onType : null, // function(str) { ... }
2468
2195
  onDelete : null, // function(values) { ... }
2469
2196
  */
2470
-
2197
+
2471
2198
  render: {
2472
2199
  /*
2473
2200
  item: null,
@@ -2478,15 +2205,13 @@
2478
2205
  */
2479
2206
  }
2480
2207
  };
2481
-
2482
- /* --- file: "src/selectize.jquery.js" --- */
2483
-
2208
+
2484
2209
  $.fn.selectize = function(settings) {
2485
2210
  settings = settings || {};
2486
-
2211
+
2487
2212
  var defaults = $.fn.selectize.defaults;
2488
2213
  var dataAttr = settings.dataAttr || defaults.dataAttr;
2489
-
2214
+
2490
2215
  /**
2491
2216
  * Initializes selectize from a <input type="text"> element.
2492
2217
  *
@@ -2496,7 +2221,7 @@
2496
2221
  var init_textbox = function($input, settings_element) {
2497
2222
  var i, n, values, value = $.trim($input.val() || '');
2498
2223
  if (!value.length) return;
2499
-
2224
+
2500
2225
  values = value.split(settings.delimiter || defaults.delimiter);
2501
2226
  for (i = 0, n = values.length; i < n; i++) {
2502
2227
  settings_element.options[values[i]] = {
@@ -2504,10 +2229,10 @@
2504
2229
  'value' : values[i]
2505
2230
  };
2506
2231
  }
2507
-
2232
+
2508
2233
  settings_element.items = values;
2509
2234
  };
2510
-
2235
+
2511
2236
  /**
2512
2237
  * Initializes selectize from a <select> element.
2513
2238
  *
@@ -2518,7 +2243,7 @@
2518
2243
  var i, n, tagName;
2519
2244
  var $children;
2520
2245
  settings_element.maxItems = !!$input.attr('multiple') ? null : 1;
2521
-
2246
+
2522
2247
  var readData = function($el) {
2523
2248
  var data = dataAttr && $el.attr(dataAttr);
2524
2249
  if (typeof data === 'string' && data.length) {
@@ -2526,13 +2251,13 @@
2526
2251
  }
2527
2252
  return null;
2528
2253
  };
2529
-
2254
+
2530
2255
  var addOption = function($option, group) {
2531
2256
  $option = $($option);
2532
-
2257
+
2533
2258
  var value = $option.attr('value') || '';
2534
2259
  if (!value.length) return;
2535
-
2260
+
2536
2261
  settings_element.options[value] = readData($option) || {
2537
2262
  'text' : $option.text(),
2538
2263
  'value' : value,
@@ -2542,23 +2267,23 @@
2542
2267
  settings_element.items.push(value);
2543
2268
  }
2544
2269
  };
2545
-
2270
+
2546
2271
  var addGroup = function($optgroup) {
2547
2272
  var i, n, $options = $('option', $optgroup);
2548
2273
  $optgroup = $($optgroup);
2549
-
2274
+
2550
2275
  var id = $optgroup.attr('label');
2551
2276
  if (id && id.length) {
2552
2277
  settings_element.optgroups[id] = readData($optgroup) || {
2553
2278
  'label': id
2554
2279
  };
2555
2280
  }
2556
-
2281
+
2557
2282
  for (i = 0, n = $options.length; i < n; i++) {
2558
2283
  addOption($options[i], id);
2559
2284
  }
2560
2285
  };
2561
-
2286
+
2562
2287
  $children = $input.children();
2563
2288
  for (i = 0, n = $children.length; i < n; i++) {
2564
2289
  tagName = $children[i].tagName.toLowerCase();
@@ -2569,7 +2294,7 @@
2569
2294
  }
2570
2295
  }
2571
2296
  };
2572
-
2297
+
2573
2298
  return this.each(function() {
2574
2299
  var instance;
2575
2300
  var $input = $(this);
@@ -2580,49 +2305,31 @@
2580
2305
  'optgroups' : {},
2581
2306
  'items' : []
2582
2307
  };
2583
-
2308
+
2584
2309
  if (tag_name === 'select') {
2585
2310
  init_select($input, settings_element);
2586
2311
  } else {
2587
2312
  init_textbox($input, settings_element);
2588
2313
  }
2589
-
2314
+
2590
2315
  instance = new Selectize($input, $.extend(true, {}, defaults, settings_element, settings));
2591
2316
  $input.data('selectize', instance);
2592
2317
  $input.addClass('selectized');
2593
2318
  });
2594
2319
  };
2595
-
2320
+
2596
2321
  $.fn.selectize.defaults = Selectize.defaults;
2597
-
2598
- /* --- file: "src/plugins/drag_drop/plugin.js" --- */
2599
-
2600
- /**
2601
- * Plugin: "drag_drop" (selectize.js)
2602
- * Copyright (c) 2013 Brian Reavis & contributors
2603
- *
2604
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
2605
- * file except in compliance with the License. You may obtain a copy of the License at:
2606
- * http://www.apache.org/licenses/LICENSE-2.0
2607
- *
2608
- * Unless required by applicable law or agreed to in writing, software distributed under
2609
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
2610
- * ANY KIND, either express or implied. See the License for the specific language
2611
- * governing permissions and limitations under the License.
2612
- *
2613
- * @author Brian Reavis <brian@thirdroute.com>
2614
- */
2615
-
2616
- Selectize.registerPlugin('drag_drop', function(options) {
2617
- if (!$.fn.sortable) throw new Error('The "drag_drop" Selectize plugin requires jQuery UI "sortable".');
2322
+
2323
+ Selectize.define('drag_drop', function(options) {
2324
+ if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');
2618
2325
  if (this.settings.mode !== 'multi') return;
2619
2326
  var self = this;
2620
-
2327
+
2621
2328
  this.setup = (function() {
2622
2329
  var original = self.setup;
2623
2330
  return function() {
2624
2331
  original.apply(this, arguments);
2625
-
2332
+
2626
2333
  var $control = this.$control.sortable({
2627
2334
  items: '[data-value]',
2628
2335
  forcePlaceholderSize: true,
@@ -2643,37 +2350,19 @@
2643
2350
  });
2644
2351
  };
2645
2352
  })();
2646
-
2353
+
2647
2354
  });
2648
-
2649
- /* --- file: "src/plugins/dropdown_header/plugin.js" --- */
2650
-
2651
- /**
2652
- * Plugin: "dropdown_header" (selectize.js)
2653
- * Copyright (c) 2013 Brian Reavis & contributors
2654
- *
2655
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
2656
- * file except in compliance with the License. You may obtain a copy of the License at:
2657
- * http://www.apache.org/licenses/LICENSE-2.0
2658
- *
2659
- * Unless required by applicable law or agreed to in writing, software distributed under
2660
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
2661
- * ANY KIND, either express or implied. See the License for the specific language
2662
- * governing permissions and limitations under the License.
2663
- *
2664
- * @author Brian Reavis <brian@thirdroute.com>
2665
- */
2666
-
2667
- Selectize.registerPlugin('dropdown_header', function(options) {
2355
+
2356
+ Selectize.define('dropdown_header', function(options) {
2668
2357
  var self = this;
2669
-
2358
+
2670
2359
  options = $.extend({
2671
2360
  title : 'Untitled',
2672
2361
  headerClass : 'selectize-dropdown-header',
2673
2362
  titleRowClass : 'selectize-dropdown-header-title',
2674
2363
  labelClass : 'selectize-dropdown-header-label',
2675
2364
  closeClass : 'selectize-dropdown-header-close',
2676
-
2365
+
2677
2366
  html: function(data) {
2678
2367
  return (
2679
2368
  '<div class="' + data.headerClass + '">' +
@@ -2685,7 +2374,7 @@
2685
2374
  );
2686
2375
  }
2687
2376
  }, options);
2688
-
2377
+
2689
2378
  self.setup = (function() {
2690
2379
  var original = self.setup;
2691
2380
  return function() {
@@ -2694,58 +2383,40 @@
2694
2383
  self.$dropdown.prepend(self.$dropdown_header);
2695
2384
  };
2696
2385
  })();
2697
-
2386
+
2698
2387
  });
2699
-
2700
- /* --- file: "src/plugins/optgroup_columns/plugin.js" --- */
2701
-
2702
- /**
2703
- * Plugin: "optgroup_columns" (selectize.js)
2704
- * Copyright (c) 2013 Simon Hewitt & contributors
2705
- *
2706
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
2707
- * file except in compliance with the License. You may obtain a copy of the License at:
2708
- * http://www.apache.org/licenses/LICENSE-2.0
2709
- *
2710
- * Unless required by applicable law or agreed to in writing, software distributed under
2711
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
2712
- * ANY KIND, either express or implied. See the License for the specific language
2713
- * governing permissions and limitations under the License.
2714
- *
2715
- * @author Simon Hewitt <si@sjhewitt.co.uk>
2716
- */
2717
-
2718
- Selectize.registerPlugin('optgroup_columns', function(options) {
2388
+
2389
+ Selectize.define('optgroup_columns', function(options) {
2719
2390
  var self = this;
2720
-
2391
+
2721
2392
  options = $.extend({
2722
2393
  equalizeWidth : true,
2723
2394
  equalizeHeight : true
2724
2395
  }, options);
2725
-
2396
+
2726
2397
  this.getAdjacentOption = function($option, direction) {
2727
2398
  var $options = $option.closest('[data-group]').find('[data-selectable]');
2728
2399
  var index = $options.index($option) + direction;
2729
-
2400
+
2730
2401
  return index >= 0 && index < $options.length ? $options.eq(index) : $();
2731
2402
  };
2732
-
2403
+
2733
2404
  this.onKeyDown = (function() {
2734
2405
  var original = self.onKeyDown;
2735
2406
  return function(e) {
2736
2407
  var index, $option, $options, $optgroup;
2737
-
2408
+
2738
2409
  if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) {
2739
2410
  self.ignoreHover = true;
2740
2411
  $optgroup = this.$activeOption.closest('[data-group]');
2741
2412
  index = $optgroup.find('[data-selectable]').index(this.$activeOption);
2742
-
2413
+
2743
2414
  if(e.keyCode === KEY_LEFT) {
2744
2415
  $optgroup = $optgroup.prev('[data-group]');
2745
2416
  } else {
2746
2417
  $optgroup = $optgroup.next('[data-group]');
2747
2418
  }
2748
-
2419
+
2749
2420
  $options = $optgroup.find('[data-selectable]');
2750
2421
  $option = $options.eq(Math.min($options.length - 1, index));
2751
2422
  if ($option.length) {
@@ -2753,18 +2424,18 @@
2753
2424
  }
2754
2425
  return;
2755
2426
  }
2756
-
2427
+
2757
2428
  return original.apply(this, arguments);
2758
2429
  };
2759
2430
  })();
2760
-
2431
+
2761
2432
  var equalizeSizes = function() {
2762
2433
  var i, n, height_max, width, width_last, width_parent, $optgroups;
2763
-
2434
+
2764
2435
  $optgroups = $('[data-group]', self.$dropdown_content);
2765
2436
  n = $optgroups.length;
2766
2437
  if (!n || !self.$dropdown_content.width()) return;
2767
-
2438
+
2768
2439
  if (options.equalizeHeight) {
2769
2440
  height_max = 0;
2770
2441
  for (i = 0; i < n; i++) {
@@ -2772,7 +2443,7 @@
2772
2443
  }
2773
2444
  $optgroups.css({height: height_max});
2774
2445
  }
2775
-
2446
+
2776
2447
  if (options.equalizeWidth) {
2777
2448
  width_parent = self.$dropdown_content.innerWidth();
2778
2449
  width = Math.round(width_parent / n);
@@ -2783,42 +2454,24 @@
2783
2454
  }
2784
2455
  }
2785
2456
  };
2786
-
2457
+
2787
2458
  if (options.equalizeHeight || options.equalizeWidth) {
2788
2459
  hook.after(this, 'positionDropdown', equalizeSizes);
2789
2460
  hook.after(this, 'refreshOptions', equalizeSizes);
2790
2461
  }
2791
-
2792
-
2462
+
2463
+
2793
2464
  });
2794
-
2795
- /* --- file: "src/plugins/remove_button/plugin.js" --- */
2796
-
2797
- /**
2798
- * Plugin: "remove_button" (selectize.js)
2799
- * Copyright (c) 2013 Brian Reavis & contributors
2800
- *
2801
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
2802
- * file except in compliance with the License. You may obtain a copy of the License at:
2803
- * http://www.apache.org/licenses/LICENSE-2.0
2804
- *
2805
- * Unless required by applicable law or agreed to in writing, software distributed under
2806
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
2807
- * ANY KIND, either express or implied. See the License for the specific language
2808
- * governing permissions and limitations under the License.
2809
- *
2810
- * @author Brian Reavis <brian@thirdroute.com>
2811
- */
2812
-
2813
- Selectize.registerPlugin('remove_button', function(options) {
2465
+
2466
+ Selectize.define('remove_button', function(options) {
2814
2467
  var self = this;
2815
-
2468
+
2816
2469
  // override the item rendering method to add a "x" to each
2817
2470
  this.settings.render.item = function(data) {
2818
2471
  var label = data[self.settings.labelField];
2819
2472
  return '<div class="item">' + label + ' <a href="javascript:void(0)" class="remove" tabindex="-1" title="Remove">&times;</a></div>';
2820
2473
  };
2821
-
2474
+
2822
2475
  // override the setup method to add an extra "click" handler
2823
2476
  // that listens for mousedown events on the "x"
2824
2477
  this.setup = (function() {
@@ -2835,34 +2488,16 @@
2835
2488
  });
2836
2489
  };
2837
2490
  })();
2838
-
2491
+
2839
2492
  });
2840
-
2841
- /* --- file: "src/plugins/restore_on_backspace/plugin.js" --- */
2842
-
2843
- /**
2844
- * Plugin: "restore_on_backspace" (selectize.js)
2845
- * Copyright (c) 2013 Brian Reavis & contributors
2846
- *
2847
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
2848
- * file except in compliance with the License. You may obtain a copy of the License at:
2849
- * http://www.apache.org/licenses/LICENSE-2.0
2850
- *
2851
- * Unless required by applicable law or agreed to in writing, software distributed under
2852
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
2853
- * ANY KIND, either express or implied. See the License for the specific language
2854
- * governing permissions and limitations under the License.
2855
- *
2856
- * @author Brian Reavis <brian@thirdroute.com>
2857
- */
2858
-
2859
- Selectize.registerPlugin('restore_on_backspace', function(options) {
2493
+
2494
+ Selectize.define('restore_on_backspace', function(options) {
2860
2495
  var self = this;
2861
-
2496
+
2862
2497
  options.text = options.text || function(option) {
2863
2498
  return option[this.settings.labelField];
2864
2499
  };
2865
-
2500
+
2866
2501
  this.onKeyDown = (function(e) {
2867
2502
  var original = self.onKeyDown;
2868
2503
  return function(e) {
@@ -2885,5 +2520,4 @@
2885
2520
  });
2886
2521
 
2887
2522
  return Selectize;
2888
-
2889
- }));
2523
+ }));