tom-select-rails 2.2.1 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tom-select-rails/version.rb +1 -1
  3. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.complete.js +691 -859
  4. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.complete.js.map +1 -1
  5. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.js +487 -723
  6. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.js.map +1 -1
  7. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.popular.js +512 -743
  8. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.popular.js.map +1 -1
  9. data/vendor/assets/javascripts/tom-select-rails/cjs/utils.js +32 -28
  10. data/vendor/assets/javascripts/tom-select-rails/cjs/utils.js.map +1 -1
  11. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/caret_position/plugin.js +67 -23
  12. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/caret_position/plugin.js.map +1 -1
  13. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/change_listener/plugin.js +4 -3
  14. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/change_listener/plugin.js.map +1 -1
  15. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/checkbox_options/plugin.js +103 -41
  16. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/checkbox_options/plugin.js.map +1 -1
  17. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/clear_button/plugin.js +59 -20
  18. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/clear_button/plugin.js.map +1 -1
  19. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/drag_drop/plugin.js +249 -29
  20. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/drag_drop/plugin.js.map +1 -1
  21. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_header/plugin.js +59 -18
  22. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_header/plugin.js.map +1 -1
  23. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_input/plugin.js +72 -32
  24. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_input/plugin.js.map +1 -1
  25. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/input_autogrow/plugin.js +5 -7
  26. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/input_autogrow/plugin.js.map +1 -1
  27. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_active_items/plugin.js +2 -1
  28. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_active_items/plugin.js.map +1 -1
  29. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_backspace_delete/plugin.js +2 -2
  30. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_backspace_delete/plugin.js.map +1 -1
  31. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/optgroup_columns/plugin.js +59 -22
  32. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/optgroup_columns/plugin.js.map +1 -1
  33. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/remove_button/plugin.js +68 -23
  34. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/remove_button/plugin.js.map +1 -1
  35. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/restore_on_backspace/plugin.js +2 -3
  36. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/restore_on_backspace/plugin.js.map +1 -1
  37. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/virtual_scroll/plugin.js +97 -57
  38. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/virtual_scroll/plugin.js.map +1 -1
  39. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.complete.js +691 -859
  40. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.complete.js.map +1 -1
  41. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.js +487 -723
  42. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.js.map +1 -1
  43. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.popular.js +512 -743
  44. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.popular.js.map +1 -1
  45. data/vendor/assets/javascripts/tom-select-rails/esm/utils.js +32 -27
  46. data/vendor/assets/javascripts/tom-select-rails/esm/utils.js.map +1 -1
  47. data/vendor/assets/javascripts/tom-select-rails/js/plugins/caret_position.js +67 -23
  48. data/vendor/assets/javascripts/tom-select-rails/js/plugins/caret_position.js.map +1 -1
  49. data/vendor/assets/javascripts/tom-select-rails/js/plugins/change_listener.js +4 -3
  50. data/vendor/assets/javascripts/tom-select-rails/js/plugins/change_listener.js.map +1 -1
  51. data/vendor/assets/javascripts/tom-select-rails/js/plugins/checkbox_options.js +103 -41
  52. data/vendor/assets/javascripts/tom-select-rails/js/plugins/checkbox_options.js.map +1 -1
  53. data/vendor/assets/javascripts/tom-select-rails/js/plugins/clear_button.js +59 -20
  54. data/vendor/assets/javascripts/tom-select-rails/js/plugins/clear_button.js.map +1 -1
  55. data/vendor/assets/javascripts/tom-select-rails/js/plugins/drag_drop.js +249 -29
  56. data/vendor/assets/javascripts/tom-select-rails/js/plugins/drag_drop.js.map +1 -1
  57. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_header.js +59 -18
  58. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_header.js.map +1 -1
  59. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_input.js +72 -32
  60. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_input.js.map +1 -1
  61. data/vendor/assets/javascripts/tom-select-rails/js/plugins/input_autogrow.js +5 -7
  62. data/vendor/assets/javascripts/tom-select-rails/js/plugins/input_autogrow.js.map +1 -1
  63. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_active_items.js +2 -1
  64. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_active_items.js.map +1 -1
  65. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_backspace_delete.js +2 -2
  66. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_backspace_delete.js.map +1 -1
  67. data/vendor/assets/javascripts/tom-select-rails/js/plugins/optgroup_columns.js +59 -22
  68. data/vendor/assets/javascripts/tom-select-rails/js/plugins/optgroup_columns.js.map +1 -1
  69. data/vendor/assets/javascripts/tom-select-rails/js/plugins/remove_button.js +68 -23
  70. data/vendor/assets/javascripts/tom-select-rails/js/plugins/remove_button.js.map +1 -1
  71. data/vendor/assets/javascripts/tom-select-rails/js/plugins/restore_on_backspace.js +2 -3
  72. data/vendor/assets/javascripts/tom-select-rails/js/plugins/restore_on_backspace.js.map +1 -1
  73. data/vendor/assets/javascripts/tom-select-rails/js/plugins/virtual_scroll.js +97 -57
  74. data/vendor/assets/javascripts/tom-select-rails/js/plugins/virtual_scroll.js.map +1 -1
  75. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.js +487 -723
  76. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.js.map +1 -1
  77. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.min.js +168 -160
  78. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.min.js.map +1 -1
  79. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.js +691 -859
  80. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.js.map +1 -1
  81. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.min.js +208 -190
  82. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.min.js.map +1 -1
  83. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.js +512 -743
  84. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.js.map +1 -1
  85. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.min.js +180 -172
  86. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.min.js.map +1 -1
  87. data/vendor/assets/javascripts/tom-select-rails/types/contrib/microevent.d.ts +1 -1
  88. data/vendor/assets/javascripts/tom-select-rails/types/contrib/microplugin.d.ts +4 -4
  89. data/vendor/assets/javascripts/tom-select-rails/types/defaults.d.ts +1 -0
  90. data/vendor/assets/javascripts/tom-select-rails/types/getSettings.d.ts +2 -2
  91. data/vendor/assets/javascripts/tom-select-rails/types/plugins/checkbox_options/plugin.d.ts +3 -2
  92. data/vendor/assets/javascripts/tom-select-rails/types/plugins/checkbox_options/types.d.ts +14 -0
  93. data/vendor/assets/javascripts/tom-select-rails/types/plugins/clear_button/types.d.ts +1 -1
  94. data/vendor/assets/javascripts/tom-select-rails/types/plugins/dropdown_header/types.d.ts +1 -1
  95. data/vendor/assets/javascripts/tom-select-rails/types/plugins/remove_button/types.d.ts +1 -1
  96. data/vendor/assets/javascripts/tom-select-rails/types/plugins/restore_on_backspace/plugin.d.ts +1 -1
  97. data/vendor/assets/javascripts/tom-select-rails/types/tom-select.d.ts +12 -14
  98. data/vendor/assets/javascripts/tom-select-rails/types/types/core.d.ts +16 -12
  99. data/vendor/assets/javascripts/tom-select-rails/types/types/settings.d.ts +3 -2
  100. data/vendor/assets/javascripts/tom-select-rails/types/utils.d.ts +8 -1
  101. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.css +224 -216
  102. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.css.map +1 -1
  103. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.min.css +1 -1
  104. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.min.css.map +1 -1
  105. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.css +245 -252
  106. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.css.map +1 -1
  107. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.min.css +1 -1
  108. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.min.css.map +1 -1
  109. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.css +213 -210
  110. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.css.map +1 -1
  111. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.css +223 -215
  112. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.css.map +1 -1
  113. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.min.css +1 -1
  114. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.min.css.map +1 -1
  115. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.min.css +1 -1
  116. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.min.css.map +1 -1
  117. data/vendor/assets/stylesheets/tom-select-rails/scss/_dropdown.scss +7 -10
  118. data/vendor/assets/stylesheets/tom-select-rails/scss/_items.scss +2 -5
  119. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/checkbox_options.scss +8 -2
  120. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/clear_button.scss +16 -14
  121. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/drag_drop.scss +6 -12
  122. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/dropdown_header.scss +3 -2
  123. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/dropdown_input.scss +7 -11
  124. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/input_autogrow.scss +0 -3
  125. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/optgroup_columns.scss +7 -5
  126. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/remove_button.scss +20 -20
  127. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.bootstrap4.scss +33 -39
  128. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.bootstrap5.scss +54 -80
  129. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.default.scss +16 -14
  130. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.scss +52 -57
  131. metadata +8 -7
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Tom Select v2.2.1
2
+ * Tom Select v2.3.1
3
3
  * Licensed under the Apache License, Version 2.0 (the "License");
4
4
  */
5
5
 
@@ -28,13 +28,11 @@
28
28
  callback(event);
29
29
  });
30
30
  }
31
-
32
31
  class MicroEvent {
33
32
  constructor() {
34
33
  this._events = void 0;
35
34
  this._events = {};
36
35
  }
37
-
38
36
  on(events, fct) {
39
37
  forEvents(events, event => {
40
38
  const event_array = this._events[event] || [];
@@ -42,28 +40,23 @@
42
40
  this._events[event] = event_array;
43
41
  });
44
42
  }
45
-
46
43
  off(events, fct) {
47
44
  var n = arguments.length;
48
-
49
45
  if (n === 0) {
50
46
  this._events = {};
51
47
  return;
52
48
  }
53
-
54
49
  forEvents(events, event => {
55
50
  if (n === 1) {
56
51
  delete this._events[event];
57
52
  return;
58
53
  }
59
-
60
54
  const event_array = this._events[event];
61
55
  if (event_array === undefined) return;
62
56
  event_array.splice(event_array.indexOf(fct), 1);
63
57
  this._events[event] = event_array;
64
58
  });
65
59
  }
66
-
67
60
  trigger(events, ...args) {
68
61
  var self = this;
69
62
  forEvents(events, event => {
@@ -74,7 +67,6 @@
74
67
  });
75
68
  });
76
69
  }
77
-
78
70
  }
79
71
 
80
72
  /**
@@ -92,6 +84,7 @@
92
84
  *
93
85
  * @author Brian Reavis <brian@thirdroute.com>
94
86
  */
87
+
95
88
  function MicroPlugin(Interface) {
96
89
  Interface.plugins = {};
97
90
  return class extends Interface {
@@ -104,7 +97,6 @@
104
97
  loaded: {}
105
98
  };
106
99
  }
107
-
108
100
  /**
109
101
  * Registers a plugin.
110
102
  *
@@ -116,6 +108,7 @@
116
108
  'fn': fn
117
109
  };
118
110
  }
111
+
119
112
  /**
120
113
  * Initializes the listed plugins (with options).
121
114
  * Acceptable formats:
@@ -131,13 +124,10 @@
131
124
  *
132
125
  * @param {array|object} plugins
133
126
  */
134
-
135
-
136
127
  initializePlugins(plugins) {
137
128
  var key, name;
138
129
  const self = this;
139
130
  const queue = [];
140
-
141
131
  if (Array.isArray(plugins)) {
142
132
  plugins.forEach(plugin => {
143
133
  if (typeof plugin === 'string') {
@@ -155,46 +145,37 @@
155
145
  }
156
146
  }
157
147
  }
158
-
159
148
  while (name = queue.shift()) {
160
149
  self.require(name);
161
150
  }
162
151
  }
163
-
164
152
  loadPlugin(name) {
165
153
  var self = this;
166
154
  var plugins = self.plugins;
167
155
  var plugin = Interface.plugins[name];
168
-
169
156
  if (!Interface.plugins.hasOwnProperty(name)) {
170
157
  throw new Error('Unable to find "' + name + '" plugin');
171
158
  }
172
-
173
159
  plugins.requested[name] = true;
174
160
  plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
175
161
  plugins.names.push(name);
176
162
  }
163
+
177
164
  /**
178
165
  * Initializes a plugin.
179
166
  *
180
167
  */
181
-
182
-
183
168
  require(name) {
184
169
  var self = this;
185
170
  var plugins = self.plugins;
186
-
187
171
  if (!self.plugins.loaded.hasOwnProperty(name)) {
188
172
  if (plugins.requested[name]) {
189
173
  throw new Error('Plugin has circular dependency ("' + name + '")');
190
174
  }
191
-
192
175
  self.loadPlugin(name);
193
176
  }
194
-
195
177
  return plugins.loaded[name];
196
178
  }
197
-
198
179
  };
199
180
  }
200
181
 
@@ -337,8 +318,7 @@
337
318
  /** @type {TCodePoints} */
338
319
 
339
320
  const code_points = [[0, 65535]];
340
- const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}]'; // \u{2bc}
341
-
321
+ const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}\u{2bc}]';
342
322
  /** @type {TUnicodeMap} */
343
323
 
344
324
  let unicode_map;
@@ -348,13 +328,62 @@
348
328
  const max_char_length = 3;
349
329
  /** @type {TUnicodeMap} */
350
330
 
351
- const latin_convert = {
352
- 'æ': 'ae',
353
- 'ⱥ': 'a',
354
- 'ø': 'o',
355
- '': '/',
356
- '': '/'
331
+ const latin_convert = {};
332
+ /** @type {TUnicodeMap} */
333
+
334
+ const latin_condensed = {
335
+ '/': '⁄∕',
336
+ '0': '߀',
337
+ "a": "ⱥɐɑ",
338
+ "aa": "ꜳ",
339
+ "ae": "æǽǣ",
340
+ "ao": "ꜵ",
341
+ "au": "ꜷ",
342
+ "av": "ꜹꜻ",
343
+ "ay": "ꜽ",
344
+ "b": "ƀɓƃ",
345
+ "c": "ꜿƈȼↄ",
346
+ "d": "đɗɖᴅƌꮷԁɦ",
347
+ "e": "ɛǝᴇɇ",
348
+ "f": "ꝼƒ",
349
+ "g": "ǥɠꞡᵹꝿɢ",
350
+ "h": "ħⱨⱶɥ",
351
+ "i": "ɨı",
352
+ "j": "ɉȷ",
353
+ "k": "ƙⱪꝁꝃꝅꞣ",
354
+ "l": "łƚɫⱡꝉꝇꞁɭ",
355
+ "m": "ɱɯϻ",
356
+ "n": "ꞥƞɲꞑᴎлԉ",
357
+ "o": "øǿɔɵꝋꝍᴑ",
358
+ "oe": "œ",
359
+ "oi": "ƣ",
360
+ "oo": "ꝏ",
361
+ "ou": "ȣ",
362
+ "p": "ƥᵽꝑꝓꝕρ",
363
+ "q": "ꝗꝙɋ",
364
+ "r": "ɍɽꝛꞧꞃ",
365
+ "s": "ßȿꞩꞅʂ",
366
+ "t": "ŧƭʈⱦꞇ",
367
+ "th": "þ",
368
+ "tz": "ꜩ",
369
+ "u": "ʉ",
370
+ "v": "ʋꝟʌ",
371
+ "vy": "ꝡ",
372
+ "w": "ⱳ",
373
+ "y": "ƴɏỿ",
374
+ "z": "ƶȥɀⱬꝣ",
375
+ "hv": "ƕ"
357
376
  };
377
+
378
+ for (let latin in latin_condensed) {
379
+ let unicode = latin_condensed[latin] || '';
380
+
381
+ for (let i = 0; i < unicode.length; i++) {
382
+ let char = unicode.substring(i, i + 1);
383
+ latin_convert[char] = latin;
384
+ }
385
+ }
386
+
358
387
  const convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');
359
388
  /**
360
389
  * Initialize the unicode_map from the give code point ranges
@@ -375,38 +404,36 @@
375
404
 
376
405
  const normalize = (str, form = 'NFKD') => str.normalize(form);
377
406
  /**
378
- * Compatibility Decomposition without reordering string
407
+ * Remove accents without reordering string
379
408
  * calling str.normalize('NFKD') on \u{594}\u{595}\u{596} becomes \u{596}\u{594}\u{595}
409
+ * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
380
410
  * @param {string} str
411
+ * @return {string}
381
412
  */
382
413
 
383
- const decompose = str => {
384
- if (str.match(/[\u0f71-\u0f81]/)) {
385
- return toArray(str).reduce(
386
- /**
387
- * @param {string} result
388
- * @param {string} char
389
- */
390
- (result, char) => {
391
- return result + normalize(char);
392
- }, '');
393
- }
394
-
395
- return normalize(str);
414
+ const asciifold = str => {
415
+ return toArray(str).reduce(
416
+ /**
417
+ * @param {string} result
418
+ * @param {string} char
419
+ */
420
+ (result, char) => {
421
+ return result + _asciifold(char);
422
+ }, '');
396
423
  };
397
424
  /**
398
- * Remove accents
399
- * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
400
425
  * @param {string} str
401
426
  * @return {string}
402
427
  */
403
428
 
404
- const asciifold = str => {
405
- return decompose(str).toLowerCase().replace(convert_pat, (
429
+ const _asciifold = str => {
430
+ str = normalize(str).toLowerCase().replace(convert_pat, (
406
431
  /** @type {string} */
407
432
  char) => {
408
433
  return latin_convert[char] || '';
409
- });
434
+ }); //return str;
435
+
436
+ return normalize(str, 'NFC');
410
437
  };
411
438
  /**
412
439
  * Generate a list of unicode variants from the list of code points
@@ -437,13 +464,6 @@
437
464
  continue;
438
465
  }
439
466
 
440
- let decomposed = normalize(composed);
441
- let recomposed = normalize(decomposed, 'NFC');
442
-
443
- if (recomposed === composed && folded === decomposed) {
444
- continue;
445
- }
446
-
447
467
  yield {
448
468
  folded: folded,
449
469
  composed: composed,
@@ -1280,7 +1300,6 @@
1280
1300
  * ```
1281
1301
  *
1282
1302
  */
1283
-
1284
1303
  const iterate = (object, callback) => {
1285
1304
  if (Array.isArray(object)) {
1286
1305
  object.forEach(callback);
@@ -1299,58 +1318,52 @@
1299
1318
  *
1300
1319
  * param query should be {}
1301
1320
  */
1302
-
1303
1321
  const getDom = query => {
1304
1322
  if (query.jquery) {
1305
1323
  return query[0];
1306
1324
  }
1307
-
1308
1325
  if (query instanceof HTMLElement) {
1309
1326
  return query;
1310
1327
  }
1311
-
1312
1328
  if (isHtmlString(query)) {
1313
1329
  var tpl = document.createElement('template');
1314
1330
  tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
1315
-
1316
1331
  return tpl.content.firstChild;
1317
1332
  }
1318
-
1319
1333
  return document.querySelector(query);
1320
1334
  };
1321
1335
  const isHtmlString = arg => {
1322
1336
  if (typeof arg === 'string' && arg.indexOf('<') > -1) {
1323
1337
  return true;
1324
1338
  }
1325
-
1326
1339
  return false;
1327
1340
  };
1328
1341
  const escapeQuery = query => {
1329
1342
  return query.replace(/['"\\]/g, '\\$&');
1330
1343
  };
1344
+
1331
1345
  /**
1332
1346
  * Dispatch an event
1333
1347
  *
1334
1348
  */
1335
-
1336
1349
  const triggerEvent = (dom_el, event_name) => {
1337
1350
  var event = document.createEvent('HTMLEvents');
1338
1351
  event.initEvent(event_name, true, false);
1339
1352
  dom_el.dispatchEvent(event);
1340
1353
  };
1354
+
1341
1355
  /**
1342
1356
  * Apply CSS rules to a dom element
1343
1357
  *
1344
1358
  */
1345
-
1346
1359
  const applyCSS = (dom_el, css) => {
1347
1360
  Object.assign(dom_el.style, css);
1348
1361
  };
1362
+
1349
1363
  /**
1350
1364
  * Add css classes
1351
1365
  *
1352
1366
  */
1353
-
1354
1367
  const addClasses = (elmts, ...classes) => {
1355
1368
  var norm_classes = classesArray(classes);
1356
1369
  elmts = castAsArray(elmts);
@@ -1360,11 +1373,11 @@
1360
1373
  });
1361
1374
  });
1362
1375
  };
1376
+
1363
1377
  /**
1364
1378
  * Remove css classes
1365
1379
  *
1366
1380
  */
1367
-
1368
1381
  const removeClasses = (elmts, ...classes) => {
1369
1382
  var norm_classes = classesArray(classes);
1370
1383
  elmts = castAsArray(elmts);
@@ -1374,55 +1387,52 @@
1374
1387
  });
1375
1388
  });
1376
1389
  };
1390
+
1377
1391
  /**
1378
1392
  * Return arguments
1379
1393
  *
1380
1394
  */
1381
-
1382
1395
  const classesArray = args => {
1383
1396
  var classes = [];
1384
1397
  iterate(args, _classes => {
1385
1398
  if (typeof _classes === 'string') {
1386
1399
  _classes = _classes.trim().split(/[\11\12\14\15\40]/);
1387
1400
  }
1388
-
1389
1401
  if (Array.isArray(_classes)) {
1390
1402
  classes = classes.concat(_classes);
1391
1403
  }
1392
1404
  });
1393
1405
  return classes.filter(Boolean);
1394
1406
  };
1407
+
1395
1408
  /**
1396
1409
  * Create an array from arg if it's not already an array
1397
1410
  *
1398
1411
  */
1399
-
1400
1412
  const castAsArray = arg => {
1401
1413
  if (!Array.isArray(arg)) {
1402
1414
  arg = [arg];
1403
1415
  }
1404
-
1405
1416
  return arg;
1406
1417
  };
1418
+
1407
1419
  /**
1408
1420
  * Get the closest node to the evt.target matching the selector
1409
1421
  * Stops at wrapper
1410
1422
  *
1411
1423
  */
1412
-
1413
1424
  const parentMatch = (target, selector, wrapper) => {
1414
1425
  if (wrapper && !wrapper.contains(target)) {
1415
1426
  return;
1416
1427
  }
1417
-
1418
1428
  while (target && target.matches) {
1419
1429
  if (target.matches(selector)) {
1420
1430
  return target;
1421
1431
  }
1422
-
1423
1432
  target = target.parentNode;
1424
1433
  }
1425
1434
  };
1435
+
1426
1436
  /**
1427
1437
  * Get the first or last item from an array
1428
1438
  *
@@ -1430,45 +1440,41 @@
1430
1440
  * <= 0 - left (first)
1431
1441
  *
1432
1442
  */
1433
-
1434
1443
  const getTail = (list, direction = 0) => {
1435
1444
  if (direction > 0) {
1436
1445
  return list[list.length - 1];
1437
1446
  }
1438
-
1439
1447
  return list[0];
1440
1448
  };
1449
+
1441
1450
  /**
1442
1451
  * Return true if an object is empty
1443
1452
  *
1444
1453
  */
1445
-
1446
1454
  const isEmptyObject = obj => {
1447
1455
  return Object.keys(obj).length === 0;
1448
1456
  };
1457
+
1449
1458
  /**
1450
1459
  * Get the index of an element amongst sibling nodes of the same type
1451
1460
  *
1452
1461
  */
1453
-
1454
1462
  const nodeIndex = (el, amongst) => {
1455
1463
  if (!el) return -1;
1456
1464
  amongst = amongst || el.nodeName;
1457
1465
  var i = 0;
1458
-
1459
1466
  while (el = el.previousElementSibling) {
1460
1467
  if (el.matches(amongst)) {
1461
1468
  i++;
1462
1469
  }
1463
1470
  }
1464
-
1465
1471
  return i;
1466
1472
  };
1473
+
1467
1474
  /**
1468
1475
  * Set attributes of an element
1469
1476
  *
1470
1477
  */
1471
-
1472
1478
  const setAttr = (el, attrs) => {
1473
1479
  iterate(attrs, (val, attr) => {
1474
1480
  if (val == null) {
@@ -1478,10 +1484,10 @@
1478
1484
  }
1479
1485
  });
1480
1486
  };
1487
+
1481
1488
  /**
1482
1489
  * Replace a node
1483
1490
  */
1484
-
1485
1491
  const replaceNode = (existing, replacement) => {
1486
1492
  if (existing.parentNode) existing.parentNode.replaceChild(replacement, existing);
1487
1493
  };
@@ -1493,19 +1499,20 @@
1493
1499
  * - Modified by Marshal <beatgates@gmail.com> 2011-6-24 (added regex)
1494
1500
  * - Modified by Brian Reavis <brian@thirdroute.com> 2012-8-27 (cleanup)
1495
1501
  */
1502
+
1496
1503
  const highlight = (element, regex) => {
1497
- if (regex === null) return; // convet string to regex
1504
+ if (regex === null) return;
1498
1505
 
1506
+ // convet string to regex
1499
1507
  if (typeof regex === 'string') {
1500
1508
  if (!regex.length) return;
1501
1509
  regex = new RegExp(regex, 'i');
1502
- } // Wrap matching part of text node with highlighting <span>, e.g.
1503
- // Soccer -> <span class="highlight">Soc</span>cer for regex = /soc/i
1504
-
1510
+ }
1505
1511
 
1512
+ // Wrap matching part of text node with highlighting <span>, e.g.
1513
+ // Soccer -> <span class="highlight">Soc</span>cer for regex = /soc/i
1506
1514
  const highlightText = node => {
1507
1515
  var match = node.data.match(regex);
1508
-
1509
1516
  if (match && node.data.length > 0) {
1510
1517
  var spannode = document.createElement('span');
1511
1518
  spannode.className = 'highlight';
@@ -1516,12 +1523,11 @@
1516
1523
  replaceNode(middlebit, spannode);
1517
1524
  return 1;
1518
1525
  }
1519
-
1520
1526
  return 0;
1521
- }; // Recurse element node, looking for child text nodes to highlight, unless element
1522
- // is childless, <script>, <style>, or already highlighted: <span class="hightlight">
1523
-
1527
+ };
1524
1528
 
1529
+ // Recurse element node, looking for child text nodes to highlight, unless element
1530
+ // is childless, <script>, <style>, or already highlighted: <span class="hightlight">
1525
1531
  const highlightChildren = node => {
1526
1532
  if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && (node.className !== 'highlight' || node.tagName !== 'SPAN')) {
1527
1533
  Array.from(node.childNodes).forEach(element => {
@@ -1529,23 +1535,20 @@
1529
1535
  });
1530
1536
  }
1531
1537
  };
1532
-
1533
1538
  const highlightRecursive = node => {
1534
1539
  if (node.nodeType === 3) {
1535
1540
  return highlightText(node);
1536
1541
  }
1537
-
1538
1542
  highlightChildren(node);
1539
1543
  return 0;
1540
1544
  };
1541
-
1542
1545
  highlightRecursive(element);
1543
1546
  };
1547
+
1544
1548
  /**
1545
1549
  * removeHighlight fn copied from highlight v5 and
1546
1550
  * edited to remove with(), pass js strict mode, and use without jquery
1547
1551
  */
1548
-
1549
1552
  const removeHighlight = el => {
1550
1553
  var elements = el.querySelectorAll("span.highlight");
1551
1554
  Array.prototype.forEach.call(elements, function (el) {
@@ -1592,6 +1595,7 @@
1592
1595
  preload: null,
1593
1596
  allowEmptyOption: false,
1594
1597
  //closeAfterSelect: false,
1598
+ refreshThrottle: 300,
1595
1599
  loadThrottle: 300,
1596
1600
  loadingClass: 'loading',
1597
1601
  dataAttr: null,
@@ -1621,7 +1625,6 @@
1621
1625
  shouldLoad: function (query) {
1622
1626
  return query.length > 0;
1623
1627
  },
1624
-
1625
1628
  /*
1626
1629
  load : null, // function(query, callback) { ... }
1627
1630
  score : null, // function(search) { ... }
@@ -1641,6 +1644,7 @@
1641
1644
  onType : null, // function(str) { ... }
1642
1645
  onDelete : null, // function(values) { ... }
1643
1646
  */
1647
+
1644
1648
  render: {
1645
1649
  /*
1646
1650
  item: null,
@@ -1674,29 +1678,38 @@
1674
1678
  if (typeof value === 'boolean') return value ? '1' : '0';
1675
1679
  return value + '';
1676
1680
  };
1681
+
1677
1682
  /**
1678
1683
  * Escapes a string for use within HTML.
1679
1684
  *
1680
1685
  */
1681
-
1682
1686
  const escape_html = str => {
1683
1687
  return (str + '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
1684
1688
  };
1689
+
1690
+ /**
1691
+ * use setTimeout if timeout > 0
1692
+ */
1693
+ const timeout = (fn, timeout) => {
1694
+ if (timeout > 0) {
1695
+ return setTimeout(fn, timeout);
1696
+ }
1697
+ fn.call(null);
1698
+ return null;
1699
+ };
1700
+
1685
1701
  /**
1686
1702
  * Debounce the user provided load function
1687
1703
  *
1688
1704
  */
1689
-
1690
1705
  const loadDebounce = (fn, delay) => {
1691
1706
  var timeout;
1692
1707
  return function (value, callback) {
1693
1708
  var self = this;
1694
-
1695
1709
  if (timeout) {
1696
1710
  self.loading = Math.max(self.loading - 1, 0);
1697
1711
  clearTimeout(timeout);
1698
1712
  }
1699
-
1700
1713
  timeout = setTimeout(function () {
1701
1714
  timeout = null;
1702
1715
  self.loadedSearches[value] = true;
@@ -1704,124 +1717,120 @@
1704
1717
  }, delay);
1705
1718
  };
1706
1719
  };
1720
+
1707
1721
  /**
1708
1722
  * Debounce all fired events types listed in `types`
1709
1723
  * while executing the provided `fn`.
1710
1724
  *
1711
1725
  */
1712
-
1713
1726
  const debounce_events = (self, types, fn) => {
1714
1727
  var type;
1715
1728
  var trigger = self.trigger;
1716
- var event_args = {}; // override trigger method
1729
+ var event_args = {};
1717
1730
 
1731
+ // override trigger method
1718
1732
  self.trigger = function () {
1719
1733
  var type = arguments[0];
1720
-
1721
1734
  if (types.indexOf(type) !== -1) {
1722
1735
  event_args[type] = arguments;
1723
1736
  } else {
1724
1737
  return trigger.apply(self, arguments);
1725
1738
  }
1726
- }; // invoke provided function
1727
-
1739
+ };
1728
1740
 
1741
+ // invoke provided function
1729
1742
  fn.apply(self, []);
1730
- self.trigger = trigger; // trigger queued events
1743
+ self.trigger = trigger;
1731
1744
 
1745
+ // trigger queued events
1732
1746
  for (type of types) {
1733
1747
  if (type in event_args) {
1734
1748
  trigger.apply(self, event_args[type]);
1735
1749
  }
1736
1750
  }
1737
1751
  };
1752
+
1738
1753
  /**
1739
1754
  * Determines the current selection within a text input control.
1740
1755
  * Returns an object containing:
1741
1756
  * - start
1742
1757
  * - length
1743
1758
  *
1759
+ * Note: "selectionStart, selectionEnd ... apply only to inputs of types text, search, URL, tel and password"
1760
+ * - https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
1744
1761
  */
1745
-
1746
1762
  const getSelection = input => {
1747
1763
  return {
1748
1764
  start: input.selectionStart || 0,
1749
1765
  length: (input.selectionEnd || 0) - (input.selectionStart || 0)
1750
1766
  };
1751
1767
  };
1768
+
1752
1769
  /**
1753
1770
  * Prevent default
1754
1771
  *
1755
1772
  */
1756
-
1757
1773
  const preventDefault = (evt, stop = false) => {
1758
1774
  if (evt) {
1759
1775
  evt.preventDefault();
1760
-
1761
1776
  if (stop) {
1762
1777
  evt.stopPropagation();
1763
1778
  }
1764
1779
  }
1765
1780
  };
1781
+
1766
1782
  /**
1767
- * Prevent default
1783
+ * Add event helper
1768
1784
  *
1769
1785
  */
1770
-
1771
1786
  const addEvent = (target, type, callback, options) => {
1772
1787
  target.addEventListener(type, callback, options);
1773
1788
  };
1789
+
1774
1790
  /**
1775
1791
  * Return true if the requested key is down
1776
1792
  * Will return false if more than one control character is pressed ( when [ctrl+shift+a] != [ctrl+a] )
1777
1793
  * The current evt may not always set ( eg calling advanceSelection() )
1778
1794
  *
1779
1795
  */
1780
-
1781
1796
  const isKeyDown = (key_name, evt) => {
1782
1797
  if (!evt) {
1783
1798
  return false;
1784
1799
  }
1785
-
1786
1800
  if (!evt[key_name]) {
1787
1801
  return false;
1788
1802
  }
1789
-
1790
1803
  var count = (evt.altKey ? 1 : 0) + (evt.ctrlKey ? 1 : 0) + (evt.shiftKey ? 1 : 0) + (evt.metaKey ? 1 : 0);
1791
-
1792
1804
  if (count === 1) {
1793
1805
  return true;
1794
1806
  }
1795
-
1796
1807
  return false;
1797
1808
  };
1809
+
1798
1810
  /**
1799
1811
  * Get the id of an element
1800
1812
  * If the id attribute is not set, set the attribute with the given id
1801
1813
  *
1802
1814
  */
1803
-
1804
1815
  const getId = (el, id) => {
1805
1816
  const existing_id = el.getAttribute('id');
1806
-
1807
1817
  if (existing_id) {
1808
1818
  return existing_id;
1809
1819
  }
1810
-
1811
1820
  el.setAttribute('id', id);
1812
1821
  return id;
1813
1822
  };
1823
+
1814
1824
  /**
1815
1825
  * Returns a string with backslashes added before characters that need to be escaped.
1816
1826
  */
1817
-
1818
1827
  const addSlashes = str => {
1819
1828
  return str.replace(/[\\"']/g, '\\$&');
1820
1829
  };
1830
+
1821
1831
  /**
1822
1832
  *
1823
1833
  */
1824
-
1825
1834
  const append = (parent, node) => {
1826
1835
  if (node) parent.append(node);
1827
1836
  };
@@ -1837,15 +1846,12 @@
1837
1846
  var field_optgroup_value = settings.optgroupValueField;
1838
1847
  var tag_name = input.tagName.toLowerCase();
1839
1848
  var placeholder = input.getAttribute('placeholder') || input.getAttribute('data-placeholder');
1840
-
1841
1849
  if (!placeholder && !settings.allowEmptyOption) {
1842
1850
  let option = input.querySelector('option[value=""]');
1843
-
1844
1851
  if (option) {
1845
1852
  placeholder = option.textContent;
1846
1853
  }
1847
1854
  }
1848
-
1849
1855
  var settings_element = {
1850
1856
  placeholder: placeholder,
1851
1857
  options: [],
@@ -1853,41 +1859,37 @@
1853
1859
  items: [],
1854
1860
  maxItems: null
1855
1861
  };
1862
+
1856
1863
  /**
1857
1864
  * Initialize from a <select> element.
1858
1865
  *
1859
1866
  */
1860
-
1861
1867
  var init_select = () => {
1862
1868
  var tagName;
1863
1869
  var options = settings_element.options;
1864
1870
  var optionsMap = {};
1865
1871
  var group_count = 1;
1866
-
1872
+ let $order = 0;
1867
1873
  var readData = el => {
1868
1874
  var data = Object.assign({}, el.dataset); // get plain object from DOMStringMap
1869
-
1870
1875
  var json = attr_data && data[attr_data];
1871
-
1872
1876
  if (typeof json === 'string' && json.length) {
1873
1877
  data = Object.assign(data, JSON.parse(json));
1874
1878
  }
1875
-
1876
1879
  return data;
1877
1880
  };
1878
-
1879
1881
  var addOption = (option, group) => {
1880
1882
  var value = hash_key(option.value);
1881
1883
  if (value == null) return;
1882
- if (!value && !settings.allowEmptyOption) return; // if the option already exists, it's probably been
1884
+ if (!value && !settings.allowEmptyOption) return;
1885
+
1886
+ // if the option already exists, it's probably been
1883
1887
  // duplicated in another optgroup. in this case, push
1884
1888
  // the current group to the "optgroup" property on the
1885
1889
  // existing option so that it's rendered in both places.
1886
-
1887
1890
  if (optionsMap.hasOwnProperty(value)) {
1888
1891
  if (group) {
1889
1892
  var arr = optionsMap[value][field_optgroup];
1890
-
1891
1893
  if (!arr) {
1892
1894
  optionsMap[value][field_optgroup] = group;
1893
1895
  } else if (!Array.isArray(arr)) {
@@ -1903,32 +1905,30 @@
1903
1905
  option_data[field_disabled] = option_data[field_disabled] || option.disabled;
1904
1906
  option_data[field_optgroup] = option_data[field_optgroup] || group;
1905
1907
  option_data.$option = option;
1908
+ option_data.$order = option_data.$order || ++$order;
1906
1909
  optionsMap[value] = option_data;
1907
1910
  options.push(option_data);
1908
1911
  }
1909
-
1910
1912
  if (option.selected) {
1911
1913
  settings_element.items.push(value);
1912
1914
  }
1913
1915
  };
1914
-
1915
1916
  var addGroup = optgroup => {
1916
1917
  var id, optgroup_data;
1917
1918
  optgroup_data = readData(optgroup);
1918
1919
  optgroup_data[field_optgroup_label] = optgroup_data[field_optgroup_label] || optgroup.getAttribute('label') || '';
1919
1920
  optgroup_data[field_optgroup_value] = optgroup_data[field_optgroup_value] || group_count++;
1920
1921
  optgroup_data[field_disabled] = optgroup_data[field_disabled] || optgroup.disabled;
1922
+ optgroup_data.$order = optgroup_data.$order || ++$order;
1921
1923
  settings_element.optgroups.push(optgroup_data);
1922
1924
  id = optgroup_data[field_optgroup_value];
1923
1925
  iterate(optgroup.children, option => {
1924
1926
  addOption(option, id);
1925
1927
  });
1926
1928
  };
1927
-
1928
1929
  settings_element.maxItems = input.hasAttribute('multiple') ? null : 1;
1929
1930
  iterate(input.children, child => {
1930
1931
  tagName = child.tagName.toLowerCase();
1931
-
1932
1932
  if (tagName === 'optgroup') {
1933
1933
  addGroup(child);
1934
1934
  } else if (tagName === 'option') {
@@ -1936,15 +1936,13 @@
1936
1936
  }
1937
1937
  });
1938
1938
  };
1939
+
1939
1940
  /**
1940
1941
  * Initialize from a <input type="text"> element.
1941
1942
  *
1942
1943
  */
1943
-
1944
-
1945
1944
  var init_textbox = () => {
1946
1945
  const data_raw = input.getAttribute(attr_data);
1947
-
1948
1946
  if (!data_raw) {
1949
1947
  var value = input.value.trim() || '';
1950
1948
  if (!settings.allowEmptyOption && !value.length) return;
@@ -1963,19 +1961,16 @@
1963
1961
  });
1964
1962
  }
1965
1963
  };
1966
-
1967
1964
  if (tag_name === 'select') {
1968
1965
  init_select();
1969
1966
  } else {
1970
1967
  init_textbox();
1971
1968
  }
1972
-
1973
1969
  return Object.assign({}, defaults, settings_element, settings_user);
1974
1970
  }
1975
1971
 
1976
1972
  var instance_i = 0;
1977
1973
  class TomSelect extends MicroPlugin(MicroEvent) {
1978
- // @deprecated 1.8
1979
1974
  constructor(input_arg, user_settings) {
1980
1975
  super();
1981
1976
  this.control_input = void 0;
@@ -1995,8 +1990,10 @@
1995
1990
  this.sifter = void 0;
1996
1991
  this.isOpen = false;
1997
1992
  this.isDisabled = false;
1993
+ this.isReadOnly = false;
1998
1994
  this.isRequired = void 0;
1999
1995
  this.isInvalid = false;
1996
+ // @deprecated 1.8
2000
1997
  this.isValid = true;
2001
1998
  this.isLocked = false;
2002
1999
  this.isFocused = false;
@@ -2016,19 +2013,20 @@
2016
2013
  this.options = {};
2017
2014
  this.userOptions = {};
2018
2015
  this.items = [];
2016
+ this.refreshTimeout = null;
2019
2017
  instance_i++;
2020
2018
  var dir;
2021
2019
  var input = getDom(input_arg);
2022
-
2023
2020
  if (input.tomselect) {
2024
2021
  throw new Error('Tom Select already initialized on this element');
2025
2022
  }
2023
+ input.tomselect = this;
2026
2024
 
2027
- input.tomselect = this; // detect rtl environment
2028
-
2025
+ // detect rtl environment
2029
2026
  var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null);
2030
- dir = computedStyle.getPropertyValue('direction'); // setup default state
2027
+ dir = computedStyle.getPropertyValue('direction');
2031
2028
 
2029
+ // setup default state
2032
2030
  const settings = getSettings(input, user_settings);
2033
2031
  this.settings = settings;
2034
2032
  this.input = input;
@@ -2036,30 +2034,28 @@
2036
2034
  this.is_select_tag = input.tagName.toLowerCase() === 'select';
2037
2035
  this.rtl = /rtl/i.test(dir);
2038
2036
  this.inputId = getId(input, 'tomselect-' + instance_i);
2039
- this.isRequired = input.required; // search system
2037
+ this.isRequired = input.required;
2040
2038
 
2039
+ // search system
2041
2040
  this.sifter = new Sifter(this.options, {
2042
2041
  diacritics: settings.diacritics
2043
- }); // option-dependent defaults
2042
+ });
2044
2043
 
2044
+ // option-dependent defaults
2045
2045
  settings.mode = settings.mode || (settings.maxItems === 1 ? 'single' : 'multi');
2046
-
2047
2046
  if (typeof settings.hideSelected !== 'boolean') {
2048
2047
  settings.hideSelected = settings.mode === 'multi';
2049
2048
  }
2050
-
2051
2049
  if (typeof settings.hidePlaceholder !== 'boolean') {
2052
2050
  settings.hidePlaceholder = settings.mode !== 'multi';
2053
- } // set up createFilter callback
2054
-
2051
+ }
2055
2052
 
2053
+ // set up createFilter callback
2056
2054
  var filter = settings.createFilter;
2057
-
2058
2055
  if (typeof filter !== 'function') {
2059
2056
  if (typeof filter === 'string') {
2060
2057
  filter = new RegExp(filter);
2061
2058
  }
2062
-
2063
2059
  if (filter instanceof RegExp) {
2064
2060
  settings.createFilter = input => filter.test(input);
2065
2061
  } else {
@@ -2068,16 +2064,14 @@
2068
2064
  };
2069
2065
  }
2070
2066
  }
2071
-
2072
2067
  this.initializePlugins(settings.plugins);
2073
2068
  this.setupCallbacks();
2074
- this.setupTemplates(); // Create all elements
2069
+ this.setupTemplates();
2075
2070
 
2071
+ // Create all elements
2076
2072
  const wrapper = getDom('<div>');
2077
2073
  const control = getDom('<div>');
2078
-
2079
2074
  const dropdown = this._render('dropdown');
2080
-
2081
2075
  const dropdown_content = getDom(`<div role="listbox" tabindex="-1">`);
2082
2076
  const classes = this.input.getAttribute('class') || '';
2083
2077
  const inputMode = settings.mode;
@@ -2086,19 +2080,19 @@
2086
2080
  addClasses(control, settings.controlClass);
2087
2081
  append(wrapper, control);
2088
2082
  addClasses(dropdown, settings.dropdownClass, inputMode);
2089
-
2090
2083
  if (settings.copyClassesToDropdown) {
2091
2084
  addClasses(dropdown, classes);
2092
2085
  }
2093
-
2094
2086
  addClasses(dropdown_content, settings.dropdownContentClass);
2095
2087
  append(dropdown, dropdown_content);
2096
- getDom(settings.dropdownParent || wrapper).appendChild(dropdown); // default controlInput
2088
+ getDom(settings.dropdownParent || wrapper).appendChild(dropdown);
2097
2089
 
2090
+ // default controlInput
2098
2091
  if (isHtmlString(settings.controlInput)) {
2099
- control_input = getDom(settings.controlInput); // set attributes
2092
+ control_input = getDom(settings.controlInput);
2100
2093
 
2101
- var attrs = ['autocorrect', 'autocapitalize', 'autocomplete'];
2094
+ // set attributes
2095
+ var attrs = ['autocorrect', 'autocapitalize', 'autocomplete', 'spellcheck'];
2102
2096
  iterate$1(attrs, attr => {
2103
2097
  if (input.getAttribute(attr)) {
2104
2098
  setAttr(control_input, {
@@ -2108,7 +2102,9 @@
2108
2102
  });
2109
2103
  control_input.tabIndex = -1;
2110
2104
  control.appendChild(control_input);
2111
- this.focus_node = control_input; // dom element
2105
+ this.focus_node = control_input;
2106
+
2107
+ // dom element
2112
2108
  } else if (settings.controlInput) {
2113
2109
  control_input = getDom(settings.controlInput);
2114
2110
  this.focus_node = control_input;
@@ -2116,7 +2112,6 @@
2116
2112
  control_input = getDom('<input/>');
2117
2113
  this.focus_node = control;
2118
2114
  }
2119
-
2120
2115
  this.wrapper = wrapper;
2121
2116
  this.dropdown = dropdown;
2122
2117
  this.dropdown_content = dropdown_content;
@@ -2124,12 +2119,11 @@
2124
2119
  this.control_input = control_input;
2125
2120
  this.setup();
2126
2121
  }
2122
+
2127
2123
  /**
2128
2124
  * set up event bindings.
2129
2125
  *
2130
2126
  */
2131
-
2132
-
2133
2127
  setup() {
2134
2128
  const self = this;
2135
2129
  const settings = self.settings;
@@ -2157,7 +2151,6 @@
2157
2151
  const query = "label[for='" + escapeQuery(self.inputId) + "']";
2158
2152
  const label = document.querySelector(query);
2159
2153
  const label_click = self.focus.bind(self);
2160
-
2161
2154
  if (label) {
2162
2155
  addEvent(label, 'click', label_click);
2163
2156
  setAttr(label, {
@@ -2171,38 +2164,32 @@
2171
2164
  'aria-labelledby': label_id
2172
2165
  });
2173
2166
  }
2174
-
2175
2167
  wrapper.style.width = input.style.width;
2176
-
2177
2168
  if (self.plugins.names.length) {
2178
2169
  const classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
2179
2170
  addClasses([wrapper, dropdown], classes_plugins);
2180
2171
  }
2181
-
2182
2172
  if ((settings.maxItems === null || settings.maxItems > 1) && self.is_select_tag) {
2183
2173
  setAttr(input, {
2184
2174
  multiple: 'multiple'
2185
2175
  });
2186
2176
  }
2187
-
2188
2177
  if (settings.placeholder) {
2189
2178
  setAttr(control_input, {
2190
2179
  placeholder: settings.placeholder
2191
2180
  });
2192
- } // if splitOn was not passed in, construct it from the delimiter to allow pasting universally
2193
-
2181
+ }
2194
2182
 
2183
+ // if splitOn was not passed in, construct it from the delimiter to allow pasting universally
2195
2184
  if (!settings.splitOn && settings.delimiter) {
2196
2185
  settings.splitOn = new RegExp('\\s*' + escape_regex(settings.delimiter) + '+\\s*');
2197
- } // debounce user defined load() if loadThrottle > 0
2198
- // after initializePlugins() so plugins can create/modify user defined loaders
2199
-
2186
+ }
2200
2187
 
2188
+ // debounce user defined load() if loadThrottle > 0
2189
+ // after initializePlugins() so plugins can create/modify user defined loaders
2201
2190
  if (settings.load && settings.loadThrottle) {
2202
2191
  settings.load = loadDebounce(settings.load, settings.loadThrottle);
2203
2192
  }
2204
-
2205
- self.control_input.type = input.type;
2206
2193
  addEvent(dropdown, 'mousemove', () => {
2207
2194
  self.ignoreHover = false;
2208
2195
  });
@@ -2211,11 +2198,11 @@
2211
2198
  if (target_match) self.onOptionHover(e, target_match);
2212
2199
  }, {
2213
2200
  capture: true
2214
- }); // clicking on an option should select it
2201
+ });
2215
2202
 
2203
+ // clicking on an option should select it
2216
2204
  addEvent(dropdown, 'click', evt => {
2217
2205
  const option = parentMatch(evt.target, '[data-selectable]');
2218
-
2219
2206
  if (option) {
2220
2207
  self.onOptionSelect(evt, option);
2221
2208
  preventDefault(evt, true);
@@ -2223,74 +2210,69 @@
2223
2210
  });
2224
2211
  addEvent(control, 'click', evt => {
2225
2212
  var target_match = parentMatch(evt.target, '[data-ts-item]', control);
2226
-
2227
2213
  if (target_match && self.onItemSelect(evt, target_match)) {
2228
2214
  preventDefault(evt, true);
2229
2215
  return;
2230
- } // retain focus (see control_input mousedown)
2231
-
2216
+ }
2232
2217
 
2218
+ // retain focus (see control_input mousedown)
2233
2219
  if (control_input.value != '') {
2234
2220
  return;
2235
2221
  }
2236
-
2237
2222
  self.onClick();
2238
2223
  preventDefault(evt, true);
2239
- }); // keydown on focus_node for arrow_down/arrow_up
2224
+ });
2240
2225
 
2241
- addEvent(focus_node, 'keydown', e => self.onKeyDown(e)); // keypress and input/keyup
2226
+ // keydown on focus_node for arrow_down/arrow_up
2227
+ addEvent(focus_node, 'keydown', e => self.onKeyDown(e));
2242
2228
 
2229
+ // keypress and input/keyup
2243
2230
  addEvent(control_input, 'keypress', e => self.onKeyPress(e));
2244
2231
  addEvent(control_input, 'input', e => self.onInput(e));
2245
- addEvent(focus_node, 'resize', () => self.positionDropdown(), passive_event);
2246
2232
  addEvent(focus_node, 'blur', e => self.onBlur(e));
2247
2233
  addEvent(focus_node, 'focus', e => self.onFocus(e));
2248
2234
  addEvent(control_input, 'paste', e => self.onPaste(e));
2249
-
2250
2235
  const doc_mousedown = evt => {
2251
2236
  // blur if target is outside of this instance
2252
2237
  // dropdown is not always inside wrapper
2253
2238
  const target = evt.composedPath()[0];
2254
-
2255
2239
  if (!wrapper.contains(target) && !dropdown.contains(target)) {
2256
2240
  if (self.isFocused) {
2257
2241
  self.blur();
2258
2242
  }
2259
-
2260
2243
  self.inputState();
2261
2244
  return;
2262
- } // retain focus by preventing native handling. if the
2245
+ }
2246
+
2247
+ // retain focus by preventing native handling. if the
2263
2248
  // event target is the input it should not be modified.
2264
2249
  // otherwise, text selection within the input won't work.
2265
2250
  // Fixes bug #212 which is no covered by tests
2266
-
2267
-
2268
2251
  if (target == control_input && self.isOpen) {
2269
- evt.stopPropagation(); // clicking anywhere in the control should not blur the control_input (which would close the dropdown)
2252
+ evt.stopPropagation();
2253
+
2254
+ // clicking anywhere in the control should not blur the control_input (which would close the dropdown)
2270
2255
  } else {
2271
2256
  preventDefault(evt, true);
2272
2257
  }
2273
2258
  };
2274
-
2275
2259
  const win_scroll = () => {
2276
2260
  if (self.isOpen) {
2277
2261
  self.positionDropdown();
2278
2262
  }
2279
2263
  };
2280
-
2281
2264
  addEvent(document, 'mousedown', doc_mousedown);
2282
2265
  addEvent(window, 'scroll', win_scroll, passive_event);
2283
2266
  addEvent(window, 'resize', win_scroll, passive_event);
2284
-
2285
2267
  this._destroy = () => {
2286
2268
  document.removeEventListener('mousedown', doc_mousedown);
2287
2269
  window.removeEventListener('scroll', win_scroll);
2288
2270
  window.removeEventListener('resize', win_scroll);
2289
2271
  if (label) label.removeEventListener('click', label_click);
2290
- }; // store original html and tab index so that they can be
2291
- // restored when the destroy() method is called.
2292
-
2272
+ };
2293
2273
 
2274
+ // store original html and tab index so that they can be
2275
+ // restored when the destroy() method is called.
2294
2276
  this.revertSettings = {
2295
2277
  innerHTML: input.innerHTML,
2296
2278
  tabIndex: input.tabIndex
@@ -2313,40 +2295,41 @@
2313
2295
  self.close(false);
2314
2296
  self.inputState();
2315
2297
  self.isSetup = true;
2316
-
2317
2298
  if (input.disabled) {
2318
2299
  self.disable();
2300
+ } else if (input.readOnly) {
2301
+ self.setReadOnly(true);
2319
2302
  } else {
2320
2303
  self.enable(); //sets tabIndex
2321
2304
  }
2322
2305
 
2323
2306
  self.on('change', this.onChange);
2324
2307
  addClasses(input, 'tomselected', 'ts-hidden-accessible');
2325
- self.trigger('initialize'); // preload options
2308
+ self.trigger('initialize');
2326
2309
 
2310
+ // preload options
2327
2311
  if (settings.preload === true) {
2328
2312
  self.preload();
2329
2313
  }
2330
2314
  }
2315
+
2331
2316
  /**
2332
2317
  * Register options and optgroups
2333
2318
  *
2334
2319
  */
2335
-
2336
-
2337
2320
  setupOptions(options = [], optgroups = []) {
2338
2321
  // build options table
2339
- this.addOptions(options); // build optgroup table
2322
+ this.addOptions(options);
2340
2323
 
2324
+ // build optgroup table
2341
2325
  iterate$1(optgroups, optgroup => {
2342
2326
  this.registerOptionGroup(optgroup);
2343
2327
  });
2344
2328
  }
2329
+
2345
2330
  /**
2346
2331
  * Sets up default rendering functions.
2347
2332
  */
2348
-
2349
-
2350
2333
  setupTemplates() {
2351
2334
  var self = this;
2352
2335
  var field_label = self.settings.labelField;
@@ -2383,12 +2366,11 @@
2383
2366
  };
2384
2367
  self.settings.render = Object.assign({}, templates, self.settings.render);
2385
2368
  }
2369
+
2386
2370
  /**
2387
2371
  * Maps fired events to callbacks provided
2388
2372
  * in the settings used when creating the control.
2389
2373
  */
2390
-
2391
-
2392
2374
  setupCallbacks() {
2393
2375
  var key, fn;
2394
2376
  var callbacks = {
@@ -2411,18 +2393,16 @@
2411
2393
  'focus': 'onFocus',
2412
2394
  'blur': 'onBlur'
2413
2395
  };
2414
-
2415
2396
  for (key in callbacks) {
2416
2397
  fn = this.settings[callbacks[key]];
2417
2398
  if (fn) this.on(key, fn);
2418
2399
  }
2419
2400
  }
2401
+
2420
2402
  /**
2421
2403
  * Sync the Tom Select instance with the original input or select
2422
2404
  *
2423
2405
  */
2424
-
2425
-
2426
2406
  sync(get_settings = true) {
2427
2407
  const self = this;
2428
2408
  const settings = get_settings ? getSettings(self.input, {
@@ -2433,78 +2413,68 @@
2433
2413
 
2434
2414
  self.lastQuery = null; // so updated options will be displayed in dropdown
2435
2415
  }
2416
+
2436
2417
  /**
2437
2418
  * Triggered when the main control element
2438
2419
  * has a click event.
2439
2420
  *
2440
2421
  */
2441
-
2442
-
2443
2422
  onClick() {
2444
2423
  var self = this;
2445
-
2446
2424
  if (self.activeItems.length > 0) {
2447
2425
  self.clearActiveItems();
2448
2426
  self.focus();
2449
2427
  return;
2450
2428
  }
2451
-
2452
2429
  if (self.isFocused && self.isOpen) {
2453
2430
  self.blur();
2454
2431
  } else {
2455
2432
  self.focus();
2456
2433
  }
2457
2434
  }
2435
+
2458
2436
  /**
2459
2437
  * @deprecated v1.7
2460
2438
  *
2461
2439
  */
2462
-
2463
-
2464
2440
  onMouseDown() {}
2441
+
2465
2442
  /**
2466
2443
  * Triggered when the value of the control has been changed.
2467
2444
  * This should propagate the event to the original DOM
2468
2445
  * input / select element.
2469
2446
  */
2470
-
2471
-
2472
2447
  onChange() {
2473
2448
  triggerEvent(this.input, 'input');
2474
2449
  triggerEvent(this.input, 'change');
2475
2450
  }
2451
+
2476
2452
  /**
2477
2453
  * Triggered on <input> paste.
2478
2454
  *
2479
2455
  */
2480
-
2481
-
2482
2456
  onPaste(e) {
2483
2457
  var self = this;
2484
-
2485
2458
  if (self.isInputHidden || self.isLocked) {
2486
2459
  preventDefault(e);
2487
2460
  return;
2488
- } // If a regex or string is included, this will split the pasted
2489
- // input and create Items for each separate value
2490
-
2461
+ }
2491
2462
 
2463
+ // If a regex or string is included, this will split the pasted
2464
+ // input and create Items for each separate value
2492
2465
  if (!self.settings.splitOn) {
2493
2466
  return;
2494
- } // Wait for pasted text to be recognized in value
2495
-
2467
+ }
2496
2468
 
2469
+ // Wait for pasted text to be recognized in value
2497
2470
  setTimeout(() => {
2498
2471
  var pastedText = self.inputValue();
2499
-
2500
2472
  if (!pastedText.match(self.settings.splitOn)) {
2501
2473
  return;
2502
2474
  }
2503
-
2504
2475
  var splitInput = pastedText.trim().split(self.settings.splitOn);
2505
2476
  iterate$1(splitInput, piece => {
2506
2477
  const hash = hash_key(piece);
2507
-
2508
2478
  if (hash) {
2509
2479
  if (this.options[piece]) {
2510
2480
  self.addItem(piece);
@@ -2515,46 +2485,38 @@
2515
2485
  });
2516
2486
  }, 0);
2517
2487
  }
2488
+
2518
2489
  /**
2519
2490
  * Triggered on <input> keypress.
2520
2491
  *
2521
2492
  */
2522
-
2523
-
2524
2493
  onKeyPress(e) {
2525
2494
  var self = this;
2526
-
2527
2495
  if (self.isLocked) {
2528
2496
  preventDefault(e);
2529
2497
  return;
2530
2498
  }
2531
-
2532
2499
  var character = String.fromCharCode(e.keyCode || e.which);
2533
-
2534
2500
  if (self.settings.create && self.settings.mode === 'multi' && character === self.settings.delimiter) {
2535
2501
  self.createItem();
2536
2502
  preventDefault(e);
2537
2503
  return;
2538
2504
  }
2539
2505
  }
2506
+
2540
2507
  /**
2541
2508
  * Triggered on <input> keydown.
2542
2509
  *
2543
2510
  */
2544
-
2545
-
2546
2511
  onKeyDown(e) {
2547
2512
  var self = this;
2548
2513
  self.ignoreHover = true;
2549
-
2550
2514
  if (self.isLocked) {
2551
2515
  if (e.keyCode !== KEY_TAB) {
2552
2516
  preventDefault(e);
2553
2517
  }
2554
-
2555
2518
  return;
2556
2519
  }
2557
-
2558
2520
  switch (e.keyCode) {
2559
2521
  // ctrl+A: select all
2560
2522
  case KEY_A:
@@ -2565,20 +2527,18 @@
2565
2527
  return;
2566
2528
  }
2567
2529
  }
2568
-
2569
2530
  break;
2570
- // esc: close dropdown
2571
2531
 
2532
+ // esc: close dropdown
2572
2533
  case KEY_ESC:
2573
2534
  if (self.isOpen) {
2574
2535
  preventDefault(e, true);
2575
2536
  self.close();
2576
2537
  }
2577
-
2578
2538
  self.clearActiveItems();
2579
2539
  return;
2580
- // down: open dropdown or move selection down
2581
2540
 
2541
+ // down: open dropdown or move selection down
2582
2542
  case KEY_DOWN:
2583
2543
  if (!self.isOpen && self.hasOptions) {
2584
2544
  self.open();
@@ -2586,178 +2546,174 @@
2586
2546
  let next = self.getAdjacent(self.activeOption, 1);
2587
2547
  if (next) self.setActiveOption(next);
2588
2548
  }
2589
-
2590
2549
  preventDefault(e);
2591
2550
  return;
2592
- // up: move selection up
2593
2551
 
2552
+ // up: move selection up
2594
2553
  case KEY_UP:
2595
2554
  if (self.activeOption) {
2596
2555
  let prev = self.getAdjacent(self.activeOption, -1);
2597
2556
  if (prev) self.setActiveOption(prev);
2598
2557
  }
2599
-
2600
2558
  preventDefault(e);
2601
2559
  return;
2602
- // return: select active option
2603
2560
 
2561
+ // return: select active option
2604
2562
  case KEY_RETURN:
2605
2563
  if (self.canSelect(self.activeOption)) {
2606
2564
  self.onOptionSelect(e, self.activeOption);
2607
- preventDefault(e); // if the option_create=null, the dropdown might be closed
2565
+ preventDefault(e);
2566
+
2567
+ // if the option_create=null, the dropdown might be closed
2608
2568
  } else if (self.settings.create && self.createItem()) {
2609
- preventDefault(e); // don't submit form when searching for a value
2569
+ preventDefault(e);
2570
+
2571
+ // don't submit form when searching for a value
2610
2572
  } else if (document.activeElement == self.control_input && self.isOpen) {
2611
2573
  preventDefault(e);
2612
2574
  }
2613
-
2614
2575
  return;
2615
- // left: modifiy item selection to the left
2616
2576
 
2577
+ // left: modifiy item selection to the left
2617
2578
  case KEY_LEFT:
2618
2579
  self.advanceSelection(-1, e);
2619
2580
  return;
2620
- // right: modifiy item selection to the right
2621
2581
 
2582
+ // right: modifiy item selection to the right
2622
2583
  case KEY_RIGHT:
2623
2584
  self.advanceSelection(1, e);
2624
2585
  return;
2625
- // tab: select active option and/or create item
2626
2586
 
2587
+ // tab: select active option and/or create item
2627
2588
  case KEY_TAB:
2628
2589
  if (self.settings.selectOnTab) {
2629
2590
  if (self.canSelect(self.activeOption)) {
2630
- self.onOptionSelect(e, self.activeOption); // prevent default [tab] behaviour of jump to the next field
2631
- // if select isFull, then the dropdown won't be open and [tab] will work normally
2591
+ self.onOptionSelect(e, self.activeOption);
2632
2592
 
2593
+ // prevent default [tab] behaviour of jump to the next field
2594
+ // if select isFull, then the dropdown won't be open and [tab] will work normally
2633
2595
  preventDefault(e);
2634
2596
  }
2635
-
2636
2597
  if (self.settings.create && self.createItem()) {
2637
2598
  preventDefault(e);
2638
2599
  }
2639
2600
  }
2640
-
2641
2601
  return;
2642
- // delete|backspace: delete items
2643
2602
 
2603
+ // delete|backspace: delete items
2644
2604
  case KEY_BACKSPACE:
2645
2605
  case KEY_DELETE:
2646
2606
  self.deleteSelection(e);
2647
2607
  return;
2648
- } // don't enter text in the control_input when active items are selected
2649
-
2608
+ }
2650
2609
 
2610
+ // don't enter text in the control_input when active items are selected
2651
2611
  if (self.isInputHidden && !isKeyDown(KEY_SHORTCUT, e)) {
2652
2612
  preventDefault(e);
2653
2613
  }
2654
2614
  }
2615
+
2655
2616
  /**
2656
2617
  * Triggered on <input> keyup.
2657
2618
  *
2658
2619
  */
2659
-
2660
-
2661
2620
  onInput(e) {
2662
- var self = this;
2663
-
2664
- if (self.isLocked) {
2621
+ if (this.isLocked) {
2665
2622
  return;
2666
2623
  }
2667
-
2668
- var value = self.inputValue();
2669
-
2670
- if (self.lastValue !== value) {
2671
- self.lastValue = value;
2672
-
2673
- if (self.settings.shouldLoad.call(self, value)) {
2674
- self.load(value);
2675
- }
2676
-
2677
- self.refreshOptions();
2678
- self.trigger('type', value);
2624
+ const value = this.inputValue();
2625
+ if (this.lastValue === value) return;
2626
+ this.lastValue = value;
2627
+ if (value == '') {
2628
+ this._onInput();
2629
+ return;
2679
2630
  }
2631
+ if (this.refreshTimeout) {
2632
+ clearTimeout(this.refreshTimeout);
2633
+ }
2634
+ this.refreshTimeout = timeout(() => {
2635
+ this.refreshTimeout = null;
2636
+ this._onInput();
2637
+ }, this.settings.refreshThrottle);
2638
+ }
2639
+ _onInput() {
2640
+ const value = this.lastValue;
2641
+ if (this.settings.shouldLoad.call(this, value)) {
2642
+ this.load(value);
2643
+ }
2644
+ this.refreshOptions();
2645
+ this.trigger('type', value);
2680
2646
  }
2647
+
2681
2648
  /**
2682
2649
  * Triggered when the user rolls over
2683
2650
  * an option in the autocomplete dropdown menu.
2684
2651
  *
2685
2652
  */
2686
-
2687
-
2688
2653
  onOptionHover(evt, option) {
2689
2654
  if (this.ignoreHover) return;
2690
2655
  this.setActiveOption(option, false);
2691
2656
  }
2657
+
2692
2658
  /**
2693
2659
  * Triggered on <input> focus.
2694
2660
  *
2695
2661
  */
2696
-
2697
-
2698
2662
  onFocus(e) {
2699
2663
  var self = this;
2700
2664
  var wasFocused = self.isFocused;
2701
-
2702
- if (self.isDisabled) {
2665
+ if (self.isDisabled || self.isReadOnly) {
2703
2666
  self.blur();
2704
2667
  preventDefault(e);
2705
2668
  return;
2706
2669
  }
2707
-
2708
2670
  if (self.ignoreFocus) return;
2709
2671
  self.isFocused = true;
2710
2672
  if (self.settings.preload === 'focus') self.preload();
2711
2673
  if (!wasFocused) self.trigger('focus');
2712
-
2713
2674
  if (!self.activeItems.length) {
2714
- self.showInput();
2675
+ self.inputState();
2715
2676
  self.refreshOptions(!!self.settings.openOnFocus);
2716
2677
  }
2717
-
2718
2678
  self.refreshState();
2719
2679
  }
2680
+
2720
2681
  /**
2721
2682
  * Triggered on <input> blur.
2722
2683
  *
2723
2684
  */
2724
-
2725
-
2726
2685
  onBlur(e) {
2727
2686
  if (document.hasFocus() === false) return;
2728
2687
  var self = this;
2729
2688
  if (!self.isFocused) return;
2730
2689
  self.isFocused = false;
2731
2690
  self.ignoreFocus = false;
2732
-
2733
2691
  var deactivate = () => {
2734
2692
  self.close();
2735
2693
  self.setActiveItem();
2736
2694
  self.setCaret(self.items.length);
2737
2695
  self.trigger('blur');
2738
2696
  };
2739
-
2740
2697
  if (self.settings.create && self.settings.createOnBlur) {
2741
2698
  self.createItem(null, deactivate);
2742
2699
  } else {
2743
2700
  deactivate();
2744
2701
  }
2745
2702
  }
2703
+
2746
2704
  /**
2747
2705
  * Triggered when the user clicks on an option
2748
2706
  * in the autocomplete dropdown menu.
2749
2707
  *
2750
2708
  */
2751
-
2752
-
2753
2709
  onOptionSelect(evt, option) {
2754
2710
  var value,
2755
- self = this; // should not be possible to trigger a option under a disabled optgroup
2711
+ self = this;
2756
2712
 
2713
+ // should not be possible to trigger a option under a disabled optgroup
2757
2714
  if (option.parentElement && option.parentElement.matches('[data-disabled]')) {
2758
2715
  return;
2759
2716
  }
2760
-
2761
2717
  if (option.classList.contains('create')) {
2762
2718
  self.createItem(null, () => {
2763
2719
  if (self.settings.closeAfterSelect) {
@@ -2766,52 +2722,45 @@
2766
2722
  });
2767
2723
  } else {
2768
2724
  value = option.dataset.value;
2769
-
2770
2725
  if (typeof value !== 'undefined') {
2771
2726
  self.lastQuery = null;
2772
2727
  self.addItem(value);
2773
-
2774
2728
  if (self.settings.closeAfterSelect) {
2775
2729
  self.close();
2776
2730
  }
2777
-
2778
2731
  if (!self.settings.hideSelected && evt.type && /click/.test(evt.type)) {
2779
2732
  self.setActiveOption(option);
2780
2733
  }
2781
2734
  }
2782
2735
  }
2783
2736
  }
2737
+
2784
2738
  /**
2785
2739
  * Return true if the given option can be selected
2786
2740
  *
2787
2741
  */
2788
-
2789
-
2790
2742
  canSelect(option) {
2791
2743
  if (this.isOpen && option && this.dropdown_content.contains(option)) {
2792
2744
  return true;
2793
2745
  }
2794
-
2795
2746
  return false;
2796
2747
  }
2748
+
2797
2749
  /**
2798
2750
  * Triggered when the user clicks on an item
2799
2751
  * that has been selected.
2800
2752
  *
2801
2753
  */
2802
-
2803
-
2804
2754
  onItemSelect(evt, item) {
2805
2755
  var self = this;
2806
-
2807
2756
  if (!self.isLocked && self.settings.mode === 'multi') {
2808
2757
  preventDefault(evt);
2809
2758
  self.setActiveItem(item, evt);
2810
2759
  return true;
2811
2760
  }
2812
-
2813
2761
  return false;
2814
2762
  }
2763
+
2815
2764
  /**
2816
2765
  * Determines whether or not to invoke
2817
2766
  * the user-provided option provider / loader
@@ -2828,19 +2777,16 @@
2828
2777
  * feedback when canLoad returns false
2829
2778
  *
2830
2779
  */
2831
-
2832
-
2833
2780
  canLoad(value) {
2834
2781
  if (!this.settings.load) return false;
2835
2782
  if (this.loadedSearches.hasOwnProperty(value)) return false;
2836
2783
  return true;
2837
2784
  }
2785
+
2838
2786
  /**
2839
2787
  * Invokes the user-provided option provider / loader.
2840
2788
  *
2841
2789
  */
2842
-
2843
-
2844
2790
  load(value) {
2845
2791
  const self = this;
2846
2792
  if (!self.canLoad(value)) return;
@@ -2849,50 +2795,44 @@
2849
2795
  const callback = self.loadCallback.bind(self);
2850
2796
  self.settings.load.call(self, value, callback);
2851
2797
  }
2798
+
2852
2799
  /**
2853
2800
  * Invoked by the user-provided option provider
2854
2801
  *
2855
2802
  */
2856
-
2857
-
2858
2803
  loadCallback(options, optgroups) {
2859
2804
  const self = this;
2860
2805
  self.loading = Math.max(self.loading - 1, 0);
2861
2806
  self.lastQuery = null;
2862
2807
  self.clearActiveOption(); // when new results load, focus should be on first option
2863
-
2864
2808
  self.setupOptions(options, optgroups);
2865
2809
  self.refreshOptions(self.isFocused && !self.isInputHidden);
2866
-
2867
2810
  if (!self.loading) {
2868
2811
  removeClasses(self.wrapper, self.settings.loadingClass);
2869
2812
  }
2870
-
2871
2813
  self.trigger('load', options, optgroups);
2872
2814
  }
2873
-
2874
2815
  preload() {
2875
2816
  var classList = this.wrapper.classList;
2876
2817
  if (classList.contains('preloaded')) return;
2877
2818
  classList.add('preloaded');
2878
2819
  this.load('');
2879
2820
  }
2821
+
2880
2822
  /**
2881
2823
  * Sets the input field of the control to the specified value.
2882
2824
  *
2883
2825
  */
2884
-
2885
-
2886
2826
  setTextboxValue(value = '') {
2887
2827
  var input = this.control_input;
2888
2828
  var changed = input.value !== value;
2889
-
2890
2829
  if (changed) {
2891
2830
  input.value = value;
2892
2831
  triggerEvent(input, 'update');
2893
2832
  this.lastValue = value;
2894
2833
  }
2895
2834
  }
2835
+
2896
2836
  /**
2897
2837
  * Returns the value of the control. If multiple items
2898
2838
  * can be selected (e.g. <select multiple>), this returns
@@ -2900,21 +2840,17 @@
2900
2840
  * returns a string.
2901
2841
  *
2902
2842
  */
2903
-
2904
-
2905
2843
  getValue() {
2906
2844
  if (this.is_select_tag && this.input.hasAttribute('multiple')) {
2907
2845
  return this.items;
2908
2846
  }
2909
-
2910
2847
  return this.items.join(this.settings.delimiter);
2911
2848
  }
2849
+
2912
2850
  /**
2913
2851
  * Resets the selected items to the given value.
2914
2852
  *
2915
2853
  */
2916
-
2917
-
2918
2854
  setValue(value, silent) {
2919
2855
  var events = silent ? [] : ['change'];
2920
2856
  debounce_events(this, events, () => {
@@ -2922,63 +2858,54 @@
2922
2858
  this.addItems(value, silent);
2923
2859
  });
2924
2860
  }
2861
+
2925
2862
  /**
2926
2863
  * Resets the number of max items to the given value
2927
2864
  *
2928
2865
  */
2929
-
2930
-
2931
2866
  setMaxItems(value) {
2932
2867
  if (value === 0) value = null; //reset to unlimited items.
2933
-
2934
2868
  this.settings.maxItems = value;
2935
2869
  this.refreshState();
2936
2870
  }
2871
+
2937
2872
  /**
2938
2873
  * Sets the selected item.
2939
2874
  *
2940
2875
  */
2941
-
2942
-
2943
2876
  setActiveItem(item, e) {
2944
2877
  var self = this;
2945
2878
  var eventName;
2946
2879
  var i, begin, end, swap;
2947
2880
  var last;
2948
- if (self.settings.mode === 'single') return; // clear the active selection
2881
+ if (self.settings.mode === 'single') return;
2949
2882
 
2883
+ // clear the active selection
2950
2884
  if (!item) {
2951
2885
  self.clearActiveItems();
2952
-
2953
2886
  if (self.isFocused) {
2954
- self.showInput();
2887
+ self.inputState();
2955
2888
  }
2956
-
2957
2889
  return;
2958
- } // modify selection
2959
-
2890
+ }
2960
2891
 
2892
+ // modify selection
2961
2893
  eventName = e && e.type.toLowerCase();
2962
-
2963
2894
  if (eventName === 'click' && isKeyDown('shiftKey', e) && self.activeItems.length) {
2964
2895
  last = self.getLastActive();
2965
2896
  begin = Array.prototype.indexOf.call(self.control.children, last);
2966
2897
  end = Array.prototype.indexOf.call(self.control.children, item);
2967
-
2968
2898
  if (begin > end) {
2969
2899
  swap = begin;
2970
2900
  begin = end;
2971
2901
  end = swap;
2972
2902
  }
2973
-
2974
2903
  for (i = begin; i <= end; i++) {
2975
2904
  item = self.control.children[i];
2976
-
2977
2905
  if (self.activeItems.indexOf(item) === -1) {
2978
2906
  self.setActiveItemClass(item);
2979
2907
  }
2980
2908
  }
2981
-
2982
2909
  preventDefault(e);
2983
2910
  } else if (eventName === 'click' && isKeyDown(KEY_SHORTCUT, e) || eventName === 'keydown' && isKeyDown('shiftKey', e)) {
2984
2911
  if (item.classList.contains('active')) {
@@ -2989,65 +2916,58 @@
2989
2916
  } else {
2990
2917
  self.clearActiveItems();
2991
2918
  self.setActiveItemClass(item);
2992
- } // ensure control has focus
2993
-
2994
-
2995
- self.hideInput();
2919
+ }
2996
2920
 
2921
+ // ensure control has focus
2922
+ self.inputState();
2997
2923
  if (!self.isFocused) {
2998
2924
  self.focus();
2999
2925
  }
3000
2926
  }
2927
+
3001
2928
  /**
3002
2929
  * Set the active and last-active classes
3003
2930
  *
3004
2931
  */
3005
-
3006
-
3007
2932
  setActiveItemClass(item) {
3008
2933
  const self = this;
3009
2934
  const last_active = self.control.querySelector('.last-active');
3010
2935
  if (last_active) removeClasses(last_active, 'last-active');
3011
2936
  addClasses(item, 'active last-active');
3012
2937
  self.trigger('item_select', item);
3013
-
3014
2938
  if (self.activeItems.indexOf(item) == -1) {
3015
2939
  self.activeItems.push(item);
3016
2940
  }
3017
2941
  }
2942
+
3018
2943
  /**
3019
2944
  * Remove active item
3020
2945
  *
3021
2946
  */
3022
-
3023
-
3024
2947
  removeActiveItem(item) {
3025
2948
  var idx = this.activeItems.indexOf(item);
3026
2949
  this.activeItems.splice(idx, 1);
3027
2950
  removeClasses(item, 'active');
3028
2951
  }
2952
+
3029
2953
  /**
3030
2954
  * Clears all the active items
3031
2955
  *
3032
2956
  */
3033
-
3034
-
3035
2957
  clearActiveItems() {
3036
2958
  removeClasses(this.activeItems, 'active');
3037
2959
  this.activeItems = [];
3038
2960
  }
2961
+
3039
2962
  /**
3040
2963
  * Sets the selected item in the dropdown menu
3041
2964
  * of available options.
3042
2965
  *
3043
2966
  */
3044
-
3045
-
3046
2967
  setActiveOption(option, scroll = true) {
3047
2968
  if (option === this.activeOption) {
3048
2969
  return;
3049
2970
  }
3050
-
3051
2971
  this.clearActiveOption();
3052
2972
  if (!option) return;
3053
2973
  this.activeOption = option;
@@ -3060,12 +2980,11 @@
3060
2980
  addClasses(option, 'active');
3061
2981
  if (scroll) this.scrollToOption(option);
3062
2982
  }
2983
+
3063
2984
  /**
3064
2985
  * Sets the dropdown_content scrollTop to display the option
3065
2986
  *
3066
2987
  */
3067
-
3068
-
3069
2988
  scrollToOption(option, behavior) {
3070
2989
  if (!option) return;
3071
2990
  const content = this.dropdown_content;
@@ -3073,35 +2992,30 @@
3073
2992
  const scrollTop = content.scrollTop || 0;
3074
2993
  const height_item = option.offsetHeight;
3075
2994
  const y = option.getBoundingClientRect().top - content.getBoundingClientRect().top + scrollTop;
3076
-
3077
2995
  if (y + height_item > height_menu + scrollTop) {
3078
2996
  this.scroll(y - height_menu + height_item, behavior);
3079
2997
  } else if (y < scrollTop) {
3080
2998
  this.scroll(y, behavior);
3081
2999
  }
3082
3000
  }
3001
+
3083
3002
  /**
3084
3003
  * Scroll the dropdown to the given position
3085
3004
  *
3086
3005
  */
3087
-
3088
-
3089
3006
  scroll(scrollTop, behavior) {
3090
3007
  const content = this.dropdown_content;
3091
-
3092
3008
  if (behavior) {
3093
3009
  content.style.scrollBehavior = behavior;
3094
3010
  }
3095
-
3096
3011
  content.scrollTop = scrollTop;
3097
3012
  content.style.scrollBehavior = '';
3098
3013
  }
3014
+
3099
3015
  /**
3100
3016
  * Clears the active option
3101
3017
  *
3102
3018
  */
3103
-
3104
-
3105
3019
  clearActiveOption() {
3106
3020
  if (this.activeOption) {
3107
3021
  removeClasses(this.activeOption, 'active');
@@ -3109,42 +3023,38 @@
3109
3023
  'aria-selected': null
3110
3024
  });
3111
3025
  }
3112
-
3113
3026
  this.activeOption = null;
3114
3027
  setAttr(this.focus_node, {
3115
3028
  'aria-activedescendant': null
3116
3029
  });
3117
3030
  }
3031
+
3118
3032
  /**
3119
3033
  * Selects all items (CTRL + A).
3120
3034
  */
3121
-
3122
-
3123
3035
  selectAll() {
3124
3036
  const self = this;
3125
3037
  if (self.settings.mode === 'single') return;
3126
3038
  const activeItems = self.controlChildren();
3127
3039
  if (!activeItems.length) return;
3128
- self.hideInput();
3040
+ self.inputState();
3129
3041
  self.close();
3130
3042
  self.activeItems = activeItems;
3131
3043
  iterate$1(activeItems, item => {
3132
3044
  self.setActiveItemClass(item);
3133
3045
  });
3134
3046
  }
3047
+
3135
3048
  /**
3136
3049
  * Determines if the control_input should be in a hidden or visible state
3137
3050
  *
3138
3051
  */
3139
-
3140
-
3141
3052
  inputState() {
3142
3053
  var self = this;
3143
3054
  if (!self.control.contains(self.control_input)) return;
3144
3055
  setAttr(self.control_input, {
3145
3056
  placeholder: self.settings.placeholder
3146
3057
  });
3147
-
3148
3058
  if (self.activeItems.length > 0 || !self.isFocused && self.settings.hidePlaceholder && self.items.length > 0) {
3149
3059
  self.setTextboxValue();
3150
3060
  self.isInputHidden = true;
@@ -3154,70 +3064,45 @@
3154
3064
  placeholder: ''
3155
3065
  });
3156
3066
  }
3157
-
3158
3067
  self.isInputHidden = false;
3159
3068
  }
3160
-
3161
3069
  self.wrapper.classList.toggle('input-hidden', self.isInputHidden);
3162
3070
  }
3163
- /**
3164
- * Hides the input element out of view, while
3165
- * retaining its focus.
3166
- * @deprecated 1.3
3167
- */
3168
-
3169
-
3170
- hideInput() {
3171
- this.inputState();
3172
- }
3173
- /**
3174
- * Restores input visibility.
3175
- * @deprecated 1.3
3176
- */
3177
-
3178
3071
 
3179
- showInput() {
3180
- this.inputState();
3181
- }
3182
3072
  /**
3183
3073
  * Get the input value
3184
3074
  */
3185
-
3186
-
3187
3075
  inputValue() {
3188
3076
  return this.control_input.value.trim();
3189
3077
  }
3078
+
3190
3079
  /**
3191
3080
  * Gives the control focus.
3192
3081
  */
3193
-
3194
-
3195
3082
  focus() {
3196
3083
  var self = this;
3197
- if (self.isDisabled) return;
3084
+ if (self.isDisabled || self.isReadOnly) return;
3198
3085
  self.ignoreFocus = true;
3199
-
3200
3086
  if (self.control_input.offsetWidth) {
3201
3087
  self.control_input.focus();
3202
3088
  } else {
3203
3089
  self.focus_node.focus();
3204
3090
  }
3205
-
3206
3091
  setTimeout(() => {
3207
3092
  self.ignoreFocus = false;
3208
3093
  self.onFocus();
3209
3094
  }, 0);
3210
3095
  }
3096
+
3211
3097
  /**
3212
3098
  * Forces the control out of focus.
3213
3099
  *
3214
3100
  */
3215
-
3216
-
3217
3101
  blur() {
3218
3102
  this.focus_node.blur();
3219
3103
  this.onBlur();
3220
3104
  }
3105
+
3221
3106
  /**
3222
3107
  * Returns a function that scores an object
3223
3108
  * to show how good of a match it is to the
@@ -3225,11 +3110,10 @@
3225
3110
  *
3226
3111
  * @return {function}
3227
3112
  */
3228
-
3229
-
3230
3113
  getScoreFunction(query) {
3231
3114
  return this.sifter.getScoreFunction(query, this.getSearchOptions());
3232
3115
  }
3116
+
3233
3117
  /**
3234
3118
  * Returns search options for sifter (the system
3235
3119
  * for scoring and sorting results).
@@ -3237,18 +3121,14 @@
3237
3121
  * @see https://github.com/orchidjs/sifter.js
3238
3122
  * @return {object}
3239
3123
  */
3240
-
3241
-
3242
3124
  getSearchOptions() {
3243
3125
  var settings = this.settings;
3244
3126
  var sort = settings.sortField;
3245
-
3246
3127
  if (typeof settings.sortField === 'string') {
3247
3128
  sort = [{
3248
3129
  field: settings.sortField
3249
3130
  }];
3250
3131
  }
3251
-
3252
3132
  return {
3253
3133
  fields: settings.searchField,
3254
3134
  conjunction: settings.searchConjunction,
@@ -3256,27 +3136,26 @@
3256
3136
  nesting: settings.nesting
3257
3137
  };
3258
3138
  }
3139
+
3259
3140
  /**
3260
3141
  * Searches through available options and returns
3261
3142
  * a sorted array of matches.
3262
3143
  *
3263
3144
  */
3264
-
3265
-
3266
3145
  search(query) {
3267
3146
  var result, calculateScore;
3268
3147
  var self = this;
3269
- var options = this.getSearchOptions(); // validate user-provided result scoring function
3148
+ var options = this.getSearchOptions();
3270
3149
 
3150
+ // validate user-provided result scoring function
3271
3151
  if (self.settings.score) {
3272
3152
  calculateScore = self.settings.score.call(self, query);
3273
-
3274
3153
  if (typeof calculateScore !== 'function') {
3275
3154
  throw new Error('Tom Select "score" setting must be a function that returns a function');
3276
3155
  }
3277
- } // perform search
3278
-
3156
+ }
3279
3157
 
3158
+ // perform search
3280
3159
  if (query !== self.lastQuery) {
3281
3160
  self.lastQuery = query;
3282
3161
  result = self.sifter.search(query, Object.assign(options, {
@@ -3285,25 +3164,23 @@
3285
3164
  self.currentResults = result;
3286
3165
  } else {
3287
3166
  result = Object.assign({}, self.currentResults);
3288
- } // filter out selected items
3289
-
3167
+ }
3290
3168
 
3169
+ // filter out selected items
3291
3170
  if (self.settings.hideSelected) {
3292
3171
  result.items = result.items.filter(item => {
3293
3172
  let hashed = hash_key(item.id);
3294
3173
  return !(hashed && self.items.indexOf(hashed) !== -1);
3295
3174
  });
3296
3175
  }
3297
-
3298
3176
  return result;
3299
3177
  }
3178
+
3300
3179
  /**
3301
3180
  * Refreshes the list of available options shown
3302
3181
  * in the autocomplete dropdown menu.
3303
3182
  *
3304
3183
  */
3305
-
3306
-
3307
3184
  refreshOptions(triggerDropdown = true) {
3308
3185
  var i, j, k, n, optgroup, optgroups, html, has_create_option, active_group;
3309
3186
  var create;
@@ -3316,27 +3193,42 @@
3316
3193
  var active_option = null;
3317
3194
  var show_dropdown = self.settings.shouldOpen || false;
3318
3195
  var dropdown_content = self.dropdown_content;
3319
-
3320
3196
  if (same_query) {
3321
3197
  active_option = self.activeOption;
3322
-
3323
3198
  if (active_option) {
3324
3199
  active_group = active_option.closest('[data-group]');
3325
3200
  }
3326
- } // build markup
3327
-
3201
+ }
3328
3202
 
3203
+ // build markup
3329
3204
  n = results.items.length;
3330
-
3331
3205
  if (typeof self.settings.maxOptions === 'number') {
3332
3206
  n = Math.min(n, self.settings.maxOptions);
3333
3207
  }
3334
-
3335
3208
  if (n > 0) {
3336
3209
  show_dropdown = true;
3337
- } // render and group available options individually
3210
+ }
3338
3211
 
3212
+ // get fragment for group and the position of the group in group_order
3213
+ const getGroupFragment = (optgroup, order) => {
3214
+ let group_order_i = groups[optgroup];
3215
+ if (group_order_i !== undefined) {
3216
+ let order_group = groups_order[group_order_i];
3217
+ if (order_group !== undefined) {
3218
+ return [group_order_i, order_group.fragment];
3219
+ }
3220
+ }
3221
+ let group_fragment = document.createDocumentFragment();
3222
+ group_order_i = groups_order.length;
3223
+ groups_order.push({
3224
+ fragment: group_fragment,
3225
+ order,
3226
+ optgroup
3227
+ });
3228
+ return [group_order_i, group_fragment];
3229
+ };
3339
3230
 
3231
+ // render and group available options individually
3340
3232
  for (i = 0; i < n; i++) {
3341
3233
  // get option dom element
3342
3234
  let item = results.items[i];
@@ -3345,30 +3237,26 @@
3345
3237
  let option = self.options[opt_value];
3346
3238
  if (option === undefined) continue;
3347
3239
  let opt_hash = get_hash(opt_value);
3348
- let option_el = self.getOption(opt_hash, true); // toggle 'selected' class
3240
+ let option_el = self.getOption(opt_hash, true);
3349
3241
 
3242
+ // toggle 'selected' class
3350
3243
  if (!self.settings.hideSelected) {
3351
3244
  option_el.classList.toggle('selected', self.items.includes(opt_hash));
3352
3245
  }
3353
-
3354
3246
  optgroup = option[self.settings.optgroupField] || '';
3355
3247
  optgroups = Array.isArray(optgroup) ? optgroup : [optgroup];
3356
-
3357
3248
  for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
3358
3249
  optgroup = optgroups[j];
3359
-
3360
- if (!self.optgroups.hasOwnProperty(optgroup)) {
3250
+ let order = option.$order;
3251
+ let self_optgroup = self.optgroups[optgroup];
3252
+ if (self_optgroup === undefined) {
3361
3253
  optgroup = '';
3254
+ } else {
3255
+ order = self_optgroup.$order;
3362
3256
  }
3257
+ const [group_order_i, group_fragment] = getGroupFragment(optgroup, order);
3363
3258
 
3364
- let group_fragment = groups[optgroup];
3365
-
3366
- if (group_fragment === undefined) {
3367
- group_fragment = document.createDocumentFragment();
3368
- groups_order.push(optgroup);
3369
- } // nodes can only have one parent, so if the option is in mutple groups, we need a clone
3370
-
3371
-
3259
+ // nodes can only have one parent, so if the option is in mutple groups, we need a clone
3372
3260
  if (j > 0) {
3373
3261
  option_el = option_el.cloneNode(true);
3374
3262
  setAttr(option_el, {
@@ -3376,38 +3264,36 @@
3376
3264
  'aria-selected': null
3377
3265
  });
3378
3266
  option_el.classList.add('ts-cloned');
3379
- removeClasses(option_el, 'active'); // make sure we keep the activeOption in the same group
3267
+ removeClasses(option_el, 'active');
3380
3268
 
3269
+ // make sure we keep the activeOption in the same group
3381
3270
  if (self.activeOption && self.activeOption.dataset.value == opt_value) {
3382
3271
  if (active_group && active_group.dataset.group === optgroup.toString()) {
3383
3272
  active_option = option_el;
3384
3273
  }
3385
3274
  }
3386
3275
  }
3387
-
3388
3276
  group_fragment.appendChild(option_el);
3389
- groups[optgroup] = group_fragment;
3277
+ if (optgroup != '') {
3278
+ groups[optgroup] = group_order_i;
3279
+ }
3390
3280
  }
3391
- } // sort optgroups
3392
-
3281
+ }
3393
3282
 
3283
+ // sort optgroups
3394
3284
  if (self.settings.lockOptgroupOrder) {
3395
3285
  groups_order.sort((a, b) => {
3396
- const grp_a = self.optgroups[a];
3397
- const grp_b = self.optgroups[b];
3398
- const a_order = grp_a && grp_a.$order || 0;
3399
- const b_order = grp_b && grp_b.$order || 0;
3400
- return a_order - b_order;
3286
+ return a.order - b.order;
3401
3287
  });
3402
- } // render optgroup headers & join groups
3403
-
3288
+ }
3404
3289
 
3290
+ // render optgroup headers & join groups
3405
3291
  html = document.createDocumentFragment();
3406
- iterate$1(groups_order, optgroup => {
3407
- let group_fragment = groups[optgroup];
3292
+ iterate$1(groups_order, group_order => {
3293
+ let group_fragment = group_order.fragment;
3294
+ let optgroup = group_order.optgroup;
3408
3295
  if (!group_fragment || !group_fragment.children.length) return;
3409
3296
  let group_heading = self.optgroups[optgroup];
3410
-
3411
3297
  if (group_heading !== undefined) {
3412
3298
  let group_options = document.createDocumentFragment();
3413
3299
  let header = self.render('optgroup_header', group_heading);
@@ -3423,93 +3309,87 @@
3423
3309
  }
3424
3310
  });
3425
3311
  dropdown_content.innerHTML = '';
3426
- append(dropdown_content, html); // highlight matching terms inline
3312
+ append(dropdown_content, html);
3427
3313
 
3314
+ // highlight matching terms inline
3428
3315
  if (self.settings.highlight) {
3429
3316
  removeHighlight(dropdown_content);
3430
-
3431
3317
  if (results.query.length && results.tokens.length) {
3432
3318
  iterate$1(results.tokens, tok => {
3433
3319
  highlight(dropdown_content, tok.regex);
3434
3320
  });
3435
3321
  }
3436
- } // helper method for adding templates to dropdown
3437
-
3322
+ }
3438
3323
 
3324
+ // helper method for adding templates to dropdown
3439
3325
  var add_template = template => {
3440
3326
  let content = self.render(template, {
3441
3327
  input: query
3442
3328
  });
3443
-
3444
3329
  if (content) {
3445
3330
  show_dropdown = true;
3446
3331
  dropdown_content.insertBefore(content, dropdown_content.firstChild);
3447
3332
  }
3448
-
3449
3333
  return content;
3450
- }; // add loading message
3451
-
3334
+ };
3452
3335
 
3336
+ // add loading message
3453
3337
  if (self.loading) {
3454
- add_template('loading'); // invalid query
3338
+ add_template('loading');
3339
+
3340
+ // invalid query
3455
3341
  } else if (!self.settings.shouldLoad.call(self, query)) {
3456
- add_template('not_loading'); // add no_results message
3342
+ add_template('not_loading');
3343
+
3344
+ // add no_results message
3457
3345
  } else if (results.items.length === 0) {
3458
3346
  add_template('no_results');
3459
- } // add create option
3460
-
3347
+ }
3461
3348
 
3349
+ // add create option
3462
3350
  has_create_option = self.canCreate(query);
3463
-
3464
3351
  if (has_create_option) {
3465
3352
  create = add_template('option_create');
3466
- } // activate
3467
-
3353
+ }
3468
3354
 
3355
+ // activate
3469
3356
  self.hasOptions = results.items.length > 0 || has_create_option;
3470
-
3471
3357
  if (show_dropdown) {
3472
3358
  if (results.items.length > 0) {
3473
3359
  if (!active_option && self.settings.mode === 'single' && self.items[0] != undefined) {
3474
3360
  active_option = self.getOption(self.items[0]);
3475
3361
  }
3476
-
3477
3362
  if (!dropdown_content.contains(active_option)) {
3478
3363
  let active_index = 0;
3479
-
3480
3364
  if (create && !self.settings.addPrecedence) {
3481
3365
  active_index = 1;
3482
3366
  }
3483
-
3484
3367
  active_option = self.selectable()[active_index];
3485
3368
  }
3486
3369
  } else if (create) {
3487
3370
  active_option = create;
3488
3371
  }
3489
-
3490
3372
  if (triggerDropdown && !self.isOpen) {
3491
3373
  self.open();
3492
3374
  self.scrollToOption(active_option, 'auto');
3493
3375
  }
3494
-
3495
3376
  self.setActiveOption(active_option);
3496
3377
  } else {
3497
3378
  self.clearActiveOption();
3498
-
3499
3379
  if (triggerDropdown && self.isOpen) {
3500
3380
  self.close(false); // if create_option=null, we want the dropdown to close but not reset the textbox value
3501
3381
  }
3502
3382
  }
3503
3383
  }
3384
+
3504
3385
  /**
3505
3386
  * Return list of selectable options
3506
3387
  *
3507
3388
  */
3508
-
3509
-
3510
3389
  selectable() {
3511
3390
  return this.dropdown_content.querySelectorAll('[data-selectable]');
3512
3391
  }
3392
+
3513
3393
  /**
3514
3394
  * Adds an available option. If it already exists,
3515
3395
  * nothing will happen. Note: this does not refresh
@@ -3521,61 +3401,52 @@
3521
3401
  * this.addOption(data)
3522
3402
  *
3523
3403
  */
3524
-
3525
-
3526
3404
  addOption(data, user_created = false) {
3527
- const self = this; // @deprecated 1.7.7
3528
- // use addOptions( array, user_created ) for adding multiple options
3405
+ const self = this;
3529
3406
 
3407
+ // @deprecated 1.7.7
3408
+ // use addOptions( array, user_created ) for adding multiple options
3530
3409
  if (Array.isArray(data)) {
3531
3410
  self.addOptions(data, user_created);
3532
3411
  return false;
3533
3412
  }
3534
-
3535
3413
  const key = hash_key(data[self.settings.valueField]);
3536
-
3537
3414
  if (key === null || self.options.hasOwnProperty(key)) {
3538
3415
  return false;
3539
3416
  }
3540
-
3541
3417
  data.$order = data.$order || ++self.order;
3542
3418
  data.$id = self.inputId + '-opt-' + data.$order;
3543
3419
  self.options[key] = data;
3544
3420
  self.lastQuery = null;
3545
-
3546
3421
  if (user_created) {
3547
3422
  self.userOptions[key] = user_created;
3548
3423
  self.trigger('option_add', key, data);
3549
3424
  }
3550
-
3551
3425
  return key;
3552
3426
  }
3427
+
3553
3428
  /**
3554
3429
  * Add multiple options
3555
3430
  *
3556
3431
  */
3557
-
3558
-
3559
3432
  addOptions(data, user_created = false) {
3560
3433
  iterate$1(data, dat => {
3561
3434
  this.addOption(dat, user_created);
3562
3435
  });
3563
3436
  }
3437
+
3564
3438
  /**
3565
3439
  * @deprecated 1.7.7
3566
3440
  */
3567
-
3568
-
3569
3441
  registerOption(data) {
3570
3442
  return this.addOption(data);
3571
3443
  }
3444
+
3572
3445
  /**
3573
3446
  * Registers an option group to the pool of option groups.
3574
3447
  *
3575
3448
  * @return {boolean|string}
3576
3449
  */
3577
-
3578
-
3579
3450
  registerOptionGroup(data) {
3580
3451
  var key = hash_key(data[this.settings.optgroupValueField]);
3581
3452
  if (key === null) return false;
@@ -3583,27 +3454,24 @@
3583
3454
  this.optgroups[key] = data;
3584
3455
  return key;
3585
3456
  }
3457
+
3586
3458
  /**
3587
3459
  * Registers a new optgroup for options
3588
3460
  * to be bucketed into.
3589
3461
  *
3590
3462
  */
3591
-
3592
-
3593
3463
  addOptionGroup(id, data) {
3594
3464
  var hashed_id;
3595
3465
  data[this.settings.optgroupValueField] = id;
3596
-
3597
3466
  if (hashed_id = this.registerOptionGroup(data)) {
3598
3467
  this.trigger('optgroup_add', hashed_id, data);
3599
3468
  }
3600
3469
  }
3470
+
3601
3471
  /**
3602
3472
  * Removes an existing option group.
3603
3473
  *
3604
3474
  */
3605
-
3606
-
3607
3475
  removeOptionGroup(id) {
3608
3476
  if (this.optgroups.hasOwnProperty(id)) {
3609
3477
  delete this.optgroups[id];
@@ -3611,31 +3479,30 @@
3611
3479
  this.trigger('optgroup_remove', id);
3612
3480
  }
3613
3481
  }
3482
+
3614
3483
  /**
3615
3484
  * Clears all existing option groups.
3616
3485
  */
3617
-
3618
-
3619
3486
  clearOptionGroups() {
3620
3487
  this.optgroups = {};
3621
3488
  this.clearCache();
3622
3489
  this.trigger('optgroup_clear');
3623
3490
  }
3491
+
3624
3492
  /**
3625
3493
  * Updates an option available for selection. If
3626
3494
  * it is visible in the selected items or options
3627
3495
  * dropdown, it will be re-rendered automatically.
3628
3496
  *
3629
3497
  */
3630
-
3631
-
3632
3498
  updateOption(value, data) {
3633
3499
  const self = this;
3634
3500
  var item_new;
3635
3501
  var index_item;
3636
3502
  const value_old = hash_key(value);
3637
- const value_new = hash_key(data[self.settings.valueField]); // sanity checks
3503
+ const value_new = hash_key(data[self.settings.valueField]);
3638
3504
 
3505
+ // sanity checks
3639
3506
  if (value_old === null) return;
3640
3507
  const data_old = self.options[value_old];
3641
3508
  if (data_old == undefined) return;
@@ -3643,48 +3510,44 @@
3643
3510
  const option = self.getOption(value_old);
3644
3511
  const item = self.getItem(value_old);
3645
3512
  data.$order = data.$order || data_old.$order;
3646
- delete self.options[value_old]; // invalidate render cache
3647
- // don't remove existing node yet, we'll remove it after replacing it
3513
+ delete self.options[value_old];
3648
3514
 
3515
+ // invalidate render cache
3516
+ // don't remove existing node yet, we'll remove it after replacing it
3649
3517
  self.uncacheValue(value_new);
3650
- self.options[value_new] = data; // update the option if it's in the dropdown
3518
+ self.options[value_new] = data;
3651
3519
 
3520
+ // update the option if it's in the dropdown
3652
3521
  if (option) {
3653
3522
  if (self.dropdown_content.contains(option)) {
3654
3523
  const option_new = self._render('option', data);
3655
-
3656
3524
  replaceNode(option, option_new);
3657
-
3658
3525
  if (self.activeOption === option) {
3659
3526
  self.setActiveOption(option_new);
3660
3527
  }
3661
3528
  }
3662
-
3663
3529
  option.remove();
3664
- } // update the item if we have one
3665
-
3530
+ }
3666
3531
 
3532
+ // update the item if we have one
3667
3533
  if (item) {
3668
3534
  index_item = self.items.indexOf(value_old);
3669
-
3670
3535
  if (index_item !== -1) {
3671
3536
  self.items.splice(index_item, 1, value_new);
3672
3537
  }
3673
-
3674
3538
  item_new = self._render('item', data);
3675
3539
  if (item.classList.contains('active')) addClasses(item_new, 'active');
3676
3540
  replaceNode(item, item_new);
3677
- } // invalidate last query because we might have updated the sortField
3678
-
3541
+ }
3679
3542
 
3543
+ // invalidate last query because we might have updated the sortField
3680
3544
  self.lastQuery = null;
3681
3545
  }
3546
+
3682
3547
  /**
3683
3548
  * Removes a single option.
3684
3549
  *
3685
3550
  */
3686
-
3687
-
3688
3551
  removeOption(value, silent) {
3689
3552
  const self = this;
3690
3553
  value = get_hash(value);
@@ -3695,11 +3558,10 @@
3695
3558
  self.trigger('option_remove', value);
3696
3559
  self.removeItem(value, silent);
3697
3560
  }
3561
+
3698
3562
  /**
3699
3563
  * Clears all options.
3700
3564
  */
3701
-
3702
-
3703
3565
  clearOptions(filter) {
3704
3566
  const boundFilter = (filter || this.clearFilter).bind(this);
3705
3567
  this.loadedSearches = {};
@@ -3715,101 +3577,85 @@
3715
3577
  this.lastQuery = null;
3716
3578
  this.trigger('option_clear');
3717
3579
  }
3580
+
3718
3581
  /**
3719
3582
  * Used by clearOptions() to decide whether or not an option should be removed
3720
3583
  * Return true to keep an option, false to remove
3721
3584
  *
3722
3585
  */
3723
-
3724
-
3725
3586
  clearFilter(option, value) {
3726
3587
  if (this.items.indexOf(value) >= 0) {
3727
3588
  return true;
3728
3589
  }
3729
-
3730
3590
  return false;
3731
3591
  }
3592
+
3732
3593
  /**
3733
3594
  * Returns the dom element of the option
3734
3595
  * matching the given value.
3735
3596
  *
3736
3597
  */
3737
-
3738
-
3739
3598
  getOption(value, create = false) {
3740
3599
  const hashed = hash_key(value);
3741
3600
  if (hashed === null) return null;
3742
3601
  const option = this.options[hashed];
3743
-
3744
3602
  if (option != undefined) {
3745
3603
  if (option.$div) {
3746
3604
  return option.$div;
3747
3605
  }
3748
-
3749
3606
  if (create) {
3750
3607
  return this._render('option', option);
3751
3608
  }
3752
3609
  }
3753
-
3754
3610
  return null;
3755
3611
  }
3612
+
3756
3613
  /**
3757
3614
  * Returns the dom element of the next or previous dom element of the same type
3758
3615
  * Note: adjacent options may not be adjacent DOM elements (optgroups)
3759
3616
  *
3760
3617
  */
3761
-
3762
-
3763
3618
  getAdjacent(option, direction, type = 'option') {
3764
3619
  var self = this,
3765
- all;
3766
-
3620
+ all;
3767
3621
  if (!option) {
3768
3622
  return null;
3769
3623
  }
3770
-
3771
3624
  if (type == 'item') {
3772
3625
  all = self.controlChildren();
3773
3626
  } else {
3774
3627
  all = self.dropdown_content.querySelectorAll('[data-selectable]');
3775
3628
  }
3776
-
3777
3629
  for (let i = 0; i < all.length; i++) {
3778
3630
  if (all[i] != option) {
3779
3631
  continue;
3780
3632
  }
3781
-
3782
3633
  if (direction > 0) {
3783
3634
  return all[i + 1];
3784
3635
  }
3785
-
3786
3636
  return all[i - 1];
3787
3637
  }
3788
-
3789
3638
  return null;
3790
3639
  }
3640
+
3791
3641
  /**
3792
3642
  * Returns the dom element of the item
3793
3643
  * matching the given value.
3794
3644
  *
3795
3645
  */
3796
-
3797
-
3798
3646
  getItem(item) {
3799
3647
  if (typeof item == 'object') {
3800
3648
  return item;
3801
3649
  }
3802
-
3803
3650
  var value = hash_key(item);
3804
3651
  return value !== null ? this.control.querySelector(`[data-value="${addSlashes(value)}"]`) : null;
3805
3652
  }
3653
+
3806
3654
  /**
3807
3655
  * "Selects" multiple items at once. Adds them to the list
3808
3656
  * at the current caret position.
3809
3657
  *
3810
3658
  */
3811
-
3812
-
3813
3659
  addItems(values, silent) {
3814
3660
  var self = this;
3815
3661
  var items = Array.isArray(values) ? values : [values];
@@ -3820,13 +3666,12 @@
3820
3666
  self.addItem(item, silent);
3821
3667
  });
3822
3668
  }
3669
+
3823
3670
  /**
3824
3671
  * "Selects" an item. Adds it to the list
3825
3672
  * at the current caret position.
3826
3673
  *
3827
3674
  */
3828
-
3829
-
3830
3675
  addItem(value, silent) {
3831
3676
  var events = silent ? [] : ['change', 'dropdown_close'];
3832
3677
  debounce_events(this, events, () => {
@@ -3834,77 +3679,66 @@
3834
3679
  const self = this;
3835
3680
  const inputMode = self.settings.mode;
3836
3681
  const hashed = hash_key(value);
3837
-
3838
3682
  if (hashed && self.items.indexOf(hashed) !== -1) {
3839
3683
  if (inputMode === 'single') {
3840
3684
  self.close();
3841
3685
  }
3842
-
3843
3686
  if (inputMode === 'single' || !self.settings.duplicates) {
3844
3687
  return;
3845
3688
  }
3846
3689
  }
3847
-
3848
3690
  if (hashed === null || !self.options.hasOwnProperty(hashed)) return;
3849
3691
  if (inputMode === 'single') self.clear(silent);
3850
3692
  if (inputMode === 'multi' && self.isFull()) return;
3851
3693
  item = self._render('item', self.options[hashed]);
3852
-
3853
3694
  if (self.control.contains(item)) {
3854
3695
  // duplicates
3855
3696
  item = item.cloneNode(true);
3856
3697
  }
3857
-
3858
3698
  wasFull = self.isFull();
3859
3699
  self.items.splice(self.caretPos, 0, hashed);
3860
3700
  self.insertAtCaret(item);
3861
-
3862
3701
  if (self.isSetup) {
3863
3702
  // update menu / remove the option (if this is not one item being added as part of series)
3864
3703
  if (!self.isPending && self.settings.hideSelected) {
3865
3704
  let option = self.getOption(hashed);
3866
3705
  let next = self.getAdjacent(option, 1);
3867
-
3868
3706
  if (next) {
3869
3707
  self.setActiveOption(next);
3870
3708
  }
3871
- } // refreshOptions after setActiveOption(),
3872
- // otherwise setActiveOption() will be called by refreshOptions() with the wrong value
3873
-
3709
+ }
3874
3710
 
3711
+ // refreshOptions after setActiveOption(),
3712
+ // otherwise setActiveOption() will be called by refreshOptions() with the wrong value
3875
3713
  if (!self.isPending && !self.settings.closeAfterSelect) {
3876
3714
  self.refreshOptions(self.isFocused && inputMode !== 'single');
3877
- } // hide the menu if the maximum number of items have been selected or no options are left
3878
-
3715
+ }
3879
3716
 
3717
+ // hide the menu if the maximum number of items have been selected or no options are left
3880
3718
  if (self.settings.closeAfterSelect != false && self.isFull()) {
3881
3719
  self.close();
3882
3720
  } else if (!self.isPending) {
3883
3721
  self.positionDropdown();
3884
3722
  }
3885
-
3886
3723
  self.trigger('item_add', hashed, item);
3887
-
3888
3724
  if (!self.isPending) {
3889
3725
  self.updateOriginalInput({
3890
3726
  silent: silent
3891
3727
  });
3892
3728
  }
3893
3729
  }
3894
-
3895
3730
  if (!self.isPending || !wasFull && self.isFull()) {
3896
3731
  self.inputState();
3897
3732
  self.refreshState();
3898
3733
  }
3899
3734
  });
3900
3735
  }
3736
+
3901
3737
  /**
3902
3738
  * Removes the selected item matching
3903
3739
  * the provided value.
3904
3740
  *
3905
3741
  */
3906
-
3907
-
3908
3742
  removeItem(item = null, silent) {
3909
3743
  const self = this;
3910
3744
  item = self.getItem(item);
@@ -3913,24 +3747,19 @@
3913
3747
  const value = item.dataset.value;
3914
3748
  i = nodeIndex(item);
3915
3749
  item.remove();
3916
-
3917
3750
  if (item.classList.contains('active')) {
3918
3751
  idx = self.activeItems.indexOf(item);
3919
3752
  self.activeItems.splice(idx, 1);
3920
3753
  removeClasses(item, 'active');
3921
3754
  }
3922
-
3923
3755
  self.items.splice(i, 1);
3924
3756
  self.lastQuery = null;
3925
-
3926
3757
  if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
3927
3758
  self.removeOption(value, silent);
3928
3759
  }
3929
-
3930
3760
  if (i < self.caretPos) {
3931
3761
  self.setCaret(self.caretPos - 1);
3932
3762
  }
3933
-
3934
3763
  self.updateOriginalInput({
3935
3764
  silent: silent
3936
3765
  });
@@ -3938,6 +3767,7 @@
3938
3767
  self.positionDropdown();
3939
3768
  self.trigger('item_remove', value, item);
3940
3769
  }
3770
+
3941
3771
  /**
3942
3772
  * Invokes the `create` method provided in the
3943
3773
  * TomSelect options that should provide the data
@@ -3947,40 +3777,31 @@
3947
3777
  * to the item list.
3948
3778
  *
3949
3779
  */
3950
-
3951
-
3952
3780
  createItem(input = null, callback = () => {}) {
3953
3781
  // triggerDropdown parameter @deprecated 2.1.1
3954
3782
  if (arguments.length === 3) {
3955
3783
  callback = arguments[2];
3956
3784
  }
3957
-
3958
3785
  if (typeof callback != 'function') {
3959
3786
  callback = () => {};
3960
3787
  }
3961
-
3962
3788
  var self = this;
3963
3789
  var caret = self.caretPos;
3964
3790
  var output;
3965
3791
  input = input || self.inputValue();
3966
-
3967
3792
  if (!self.canCreate(input)) {
3968
3793
  callback();
3969
3794
  return false;
3970
3795
  }
3971
-
3972
3796
  self.lock();
3973
3797
  var created = false;
3974
-
3975
3798
  var create = data => {
3976
3799
  self.unlock();
3977
3800
  if (!data || typeof data !== 'object') return callback();
3978
3801
  var value = hash_key(data[self.settings.valueField]);
3979
-
3980
3802
  if (typeof value !== 'string') {
3981
3803
  return callback();
3982
3804
  }
3983
-
3984
3805
  self.setTextboxValue();
3985
3806
  self.addOption(data, true);
3986
3807
  self.setCaret(caret);
@@ -3988,7 +3809,6 @@
3988
3809
  callback(data);
3989
3810
  created = true;
3990
3811
  };
3991
-
3992
3812
  if (typeof self.settings.create === 'function') {
3993
3813
  output = self.settings.create.call(this, input, create);
3994
3814
  } else {
@@ -3997,35 +3817,29 @@
3997
3817
  [self.settings.valueField]: input
3998
3818
  };
3999
3819
  }
4000
-
4001
3820
  if (!created) {
4002
3821
  create(output);
4003
3822
  }
4004
-
4005
3823
  return true;
4006
3824
  }
3825
+
4007
3826
  /**
4008
3827
  * Re-renders the selected item lists.
4009
3828
  */
4010
-
4011
-
4012
3829
  refreshItems() {
4013
3830
  var self = this;
4014
3831
  self.lastQuery = null;
4015
-
4016
3832
  if (self.isSetup) {
4017
3833
  self.addItems(self.items);
4018
3834
  }
4019
-
4020
3835
  self.updateOriginalInput();
4021
3836
  self.refreshState();
4022
3837
  }
3838
+
4023
3839
  /**
4024
3840
  * Updates all state-dependent attributes
4025
3841
  * and CSS classes.
4026
3842
  */
4027
-
4028
-
4029
3843
  refreshState() {
4030
3844
  const self = this;
4031
3845
  self.refreshValidityState();
@@ -4035,6 +3849,7 @@
4035
3849
  const wrap_classList = self.wrapper.classList;
4036
3850
  wrap_classList.toggle('focus', self.isFocused);
4037
3851
  wrap_classList.toggle('disabled', self.isDisabled);
3852
+ wrap_classList.toggle('readonly', self.isReadOnly);
4038
3853
  wrap_classList.toggle('required', self.isRequired);
4039
3854
  wrap_classList.toggle('invalid', !self.isValid);
4040
3855
  wrap_classList.toggle('locked', isLocked);
@@ -4044,6 +3859,7 @@
4044
3859
  wrap_classList.toggle('has-options', isEmptyObject(self.options));
4045
3860
  wrap_classList.toggle('has-items', self.items.length > 0);
4046
3861
  }
3862
+
4047
3863
  /**
4048
3864
  * Update the `required` attribute of both input and control input.
4049
3865
  *
@@ -4052,78 +3868,71 @@
4052
3868
  * needs to be temporarily deactivated on the input since the input is
4053
3869
  * hidden and can't show errors.
4054
3870
  */
4055
-
4056
-
4057
3871
  refreshValidityState() {
4058
3872
  var self = this;
4059
-
4060
3873
  if (!self.input.validity) {
4061
3874
  return;
4062
3875
  }
4063
-
4064
3876
  self.isValid = self.input.validity.valid;
4065
3877
  self.isInvalid = !self.isValid;
4066
3878
  }
3879
+
4067
3880
  /**
4068
3881
  * Determines whether or not more items can be added
4069
3882
  * to the control without exceeding the user-defined maximum.
4070
3883
  *
4071
3884
  * @returns {boolean}
4072
3885
  */
4073
-
4074
-
4075
3886
  isFull() {
4076
3887
  return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
4077
3888
  }
3889
+
4078
3890
  /**
4079
3891
  * Refreshes the original <select> or <input>
4080
3892
  * element to reflect the current state.
4081
3893
  *
4082
3894
  */
4083
-
4084
-
4085
3895
  updateOriginalInput(opts = {}) {
4086
3896
  const self = this;
4087
3897
  var option, label;
4088
3898
  const empty_option = self.input.querySelector('option[value=""]');
4089
-
4090
3899
  if (self.is_select_tag) {
4091
3900
  const selected = [];
4092
3901
  const has_selected = self.input.querySelectorAll('option:checked').length;
4093
-
4094
3902
  function AddSelected(option_el, value, label) {
4095
3903
  if (!option_el) {
4096
3904
  option_el = getDom('<option value="' + escape_html(value) + '">' + escape_html(label) + '</option>');
4097
- } // don't move empty option from top of list
4098
- // fixes bug in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1725293
4099
-
3905
+ }
4100
3906
 
3907
+ // don't move empty option from top of list
3908
+ // fixes bug in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1725293
4101
3909
  if (option_el != empty_option) {
4102
3910
  self.input.append(option_el);
4103
3911
  }
3912
+ selected.push(option_el);
4104
3913
 
4105
- selected.push(option_el); // marking empty option as selected can break validation
3914
+ // marking empty option as selected can break validation
4106
3915
  // fixes https://github.com/orchidjs/tom-select/issues/303
4107
-
4108
3916
  if (option_el != empty_option || has_selected > 0) {
4109
3917
  option_el.selected = true;
4110
3918
  }
4111
-
4112
3919
  return option_el;
4113
- } // unselect all selected options
4114
-
3920
+ }
4115
3921
 
3922
+ // unselect all selected options
4116
3923
  self.input.querySelectorAll('option:checked').forEach(option_el => {
4117
3924
  option_el.selected = false;
4118
- }); // nothing selected?
3925
+ });
4119
3926
 
3927
+ // nothing selected?
4120
3928
  if (self.items.length == 0 && self.settings.mode == 'single') {
4121
- AddSelected(empty_option, "", ""); // order selected <option> tags for values in self.items
3929
+ AddSelected(empty_option, "", "");
3930
+
3931
+ // order selected <option> tags for values in self.items
4122
3932
  } else {
4123
3933
  self.items.forEach(value => {
4124
3934
  option = self.options[value];
4125
3935
  label = option[self.settings.labelField] || '';
4126
-
4127
3936
  if (selected.includes(option.$option)) {
4128
3937
  const reuse_opt = self.input.querySelector(`option[value="${addSlashes(value)}"]:not(:checked)`);
4129
3938
  AddSelected(reuse_opt, value, label);
@@ -4135,19 +3944,17 @@
4135
3944
  } else {
4136
3945
  self.input.value = self.getValue();
4137
3946
  }
4138
-
4139
3947
  if (self.isSetup) {
4140
3948
  if (!opts.silent) {
4141
3949
  self.trigger('change', self.getValue());
4142
3950
  }
4143
3951
  }
4144
3952
  }
3953
+
4145
3954
  /**
4146
3955
  * Shows the autocomplete dropdown containing
4147
3956
  * the available options.
4148
3957
  */
4149
-
4150
-
4151
3958
  open() {
4152
3959
  var self = this;
4153
3960
  if (self.isLocked || self.isOpen || self.settings.mode === 'multi' && self.isFull()) return;
@@ -4168,24 +3975,20 @@
4168
3975
  self.focus();
4169
3976
  self.trigger('dropdown_open', self.dropdown);
4170
3977
  }
3978
+
4171
3979
  /**
4172
3980
  * Closes the autocomplete dropdown menu.
4173
3981
  */
4174
-
4175
-
4176
3982
  close(setTextboxValue = true) {
4177
3983
  var self = this;
4178
3984
  var trigger = self.isOpen;
4179
-
4180
3985
  if (setTextboxValue) {
4181
3986
  // before blur() to prevent form onchange event
4182
3987
  self.setTextboxValue();
4183
-
4184
3988
  if (self.settings.mode === 'single' && self.items.length) {
4185
- self.hideInput();
3989
+ self.inputState();
4186
3990
  }
4187
3991
  }
4188
-
4189
3992
  self.isOpen = false;
4190
3993
  setAttr(self.focus_node, {
4191
3994
  'aria-expanded': 'false'
@@ -4193,26 +3996,22 @@
4193
3996
  applyCSS(self.dropdown, {
4194
3997
  display: 'none'
4195
3998
  });
4196
-
4197
3999
  if (self.settings.hideSelected) {
4198
4000
  self.clearActiveOption();
4199
4001
  }
4200
-
4201
4002
  self.refreshState();
4202
4003
  if (trigger) self.trigger('dropdown_close', self.dropdown);
4203
4004
  }
4005
+
4204
4006
  /**
4205
4007
  * Calculates and applies the appropriate
4206
4008
  * position of the dropdown if dropdownParent = 'body'.
4207
4009
  * Otherwise, position is determined by css
4208
4010
  */
4209
-
4210
-
4211
4011
  positionDropdown() {
4212
4012
  if (this.settings.dropdownParent !== 'body') {
4213
4013
  return;
4214
4014
  }
4215
-
4216
4015
  var context = this.control;
4217
4016
  var rect = context.getBoundingClientRect();
4218
4017
  var top = context.offsetHeight + rect.top + window.scrollY;
@@ -4223,13 +4022,12 @@
4223
4022
  left: left + 'px'
4224
4023
  });
4225
4024
  }
4025
+
4226
4026
  /**
4227
4027
  * Resets / clears all selected items
4228
4028
  * from the control.
4229
4029
  *
4230
4030
  */
4231
-
4232
-
4233
4031
  clear(silent) {
4234
4032
  var self = this;
4235
4033
  if (!self.items.length) return;
@@ -4237,17 +4035,16 @@
4237
4035
  iterate$1(items, item => {
4238
4036
  self.removeItem(item, true);
4239
4037
  });
4240
- self.showInput();
4038
+ self.inputState();
4241
4039
  if (!silent) self.updateOriginalInput();
4242
4040
  self.trigger('clear');
4243
4041
  }
4042
+
4244
4043
  /**
4245
4044
  * A helper method for inserting an element
4246
4045
  * at the current caret position.
4247
4046
  *
4248
4047
  */
4249
-
4250
-
4251
4048
  insertAtCaret(el) {
4252
4049
  const self = this;
4253
4050
  const caret = self.caretPos;
@@ -4255,77 +4052,69 @@
4255
4052
  target.insertBefore(el, target.children[caret] || null);
4256
4053
  self.setCaret(caret + 1);
4257
4054
  }
4055
+
4258
4056
  /**
4259
4057
  * Removes the current selected item(s).
4260
4058
  *
4261
4059
  */
4262
-
4263
-
4264
4060
  deleteSelection(e) {
4265
4061
  var direction, selection, caret, tail;
4266
4062
  var self = this;
4267
4063
  direction = e && e.keyCode === KEY_BACKSPACE ? -1 : 1;
4268
- selection = getSelection(self.control_input); // determine items that will be removed
4064
+ selection = getSelection(self.control_input);
4269
4065
 
4066
+ // determine items that will be removed
4270
4067
  const rm_items = [];
4271
-
4272
4068
  if (self.activeItems.length) {
4273
4069
  tail = getTail(self.activeItems, direction);
4274
4070
  caret = nodeIndex(tail);
4275
-
4276
4071
  if (direction > 0) {
4277
4072
  caret++;
4278
4073
  }
4279
-
4280
4074
  iterate$1(self.activeItems, item => rm_items.push(item));
4281
4075
  } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
4282
4076
  const items = self.controlChildren();
4283
4077
  let rm_item;
4284
-
4285
4078
  if (direction < 0 && selection.start === 0 && selection.length === 0) {
4286
4079
  rm_item = items[self.caretPos - 1];
4287
4080
  } else if (direction > 0 && selection.start === self.inputValue().length) {
4288
4081
  rm_item = items[self.caretPos];
4289
4082
  }
4290
-
4291
4083
  if (rm_item !== undefined) {
4292
4084
  rm_items.push(rm_item);
4293
4085
  }
4294
4086
  }
4295
-
4296
4087
  if (!self.shouldDelete(rm_items, e)) {
4297
4088
  return false;
4298
4089
  }
4090
+ preventDefault(e, true);
4299
4091
 
4300
- preventDefault(e, true); // perform removal
4301
-
4092
+ // perform removal
4302
4093
  if (typeof caret !== 'undefined') {
4303
4094
  self.setCaret(caret);
4304
4095
  }
4305
-
4306
4096
  while (rm_items.length) {
4307
4097
  self.removeItem(rm_items.pop());
4308
4098
  }
4309
-
4310
- self.showInput();
4099
+ self.inputState();
4311
4100
  self.positionDropdown();
4312
4101
  self.refreshOptions(false);
4313
4102
  return true;
4314
4103
  }
4104
+
4315
4105
  /**
4316
4106
  * Return true if the items should be deleted
4317
4107
  */
4318
-
4319
-
4320
4108
  shouldDelete(items, evt) {
4321
- const values = items.map(item => item.dataset.value); // allow the callback to abort
4109
+ const values = items.map(item => item.dataset.value);
4322
4110
 
4111
+ // allow the callback to abort
4323
4112
  if (!values.length || typeof this.settings.onDelete === 'function' && this.settings.onDelete(values, evt) === false) {
4324
4113
  return false;
4325
4114
  }
4326
-
4327
4115
  return true;
4328
4116
  }
4117
+
4329
4118
  /**
4330
4119
  * Selects the previous / next item (depending on the `direction` argument).
4331
4120
  *
@@ -4333,64 +4122,58 @@
4333
4122
  * < 0 - left
4334
4123
  *
4335
4124
  */
4336
-
4337
-
4338
4125
  advanceSelection(direction, e) {
4339
4126
  var last_active,
4340
- adjacent,
4341
- self = this;
4127
+ adjacent,
4128
+ self = this;
4342
4129
  if (self.rtl) direction *= -1;
4343
- if (self.inputValue().length) return; // add or remove to active items
4130
+ if (self.inputValue().length) return;
4344
4131
 
4132
+ // add or remove to active items
4345
4133
  if (isKeyDown(KEY_SHORTCUT, e) || isKeyDown('shiftKey', e)) {
4346
4134
  last_active = self.getLastActive(direction);
4347
-
4348
4135
  if (last_active) {
4349
4136
  if (!last_active.classList.contains('active')) {
4350
4137
  adjacent = last_active;
4351
4138
  } else {
4352
4139
  adjacent = self.getAdjacent(last_active, direction, 'item');
4353
- } // if no active item, get items adjacent to the control input
4140
+ }
4354
4141
 
4142
+ // if no active item, get items adjacent to the control input
4355
4143
  } else if (direction > 0) {
4356
4144
  adjacent = self.control_input.nextElementSibling;
4357
4145
  } else {
4358
4146
  adjacent = self.control_input.previousElementSibling;
4359
4147
  }
4360
-
4361
4148
  if (adjacent) {
4362
4149
  if (adjacent.classList.contains('active')) {
4363
4150
  self.removeActiveItem(last_active);
4364
4151
  }
4365
-
4366
4152
  self.setActiveItemClass(adjacent); // mark as last_active !! after removeActiveItem() on last_active
4367
- } // move caret to the left or right
4153
+ }
4368
4154
 
4155
+ // move caret to the left or right
4369
4156
  } else {
4370
4157
  self.moveCaret(direction);
4371
4158
  }
4372
4159
  }
4373
-
4374
4160
  moveCaret(direction) {}
4161
+
4375
4162
  /**
4376
4163
  * Get the last active item
4377
4164
  *
4378
4165
  */
4379
-
4380
-
4381
4166
  getLastActive(direction) {
4382
4167
  let last_active = this.control.querySelector('.last-active');
4383
-
4384
4168
  if (last_active) {
4385
4169
  return last_active;
4386
4170
  }
4387
-
4388
4171
  var result = this.control.querySelectorAll('.active');
4389
-
4390
4172
  if (result) {
4391
4173
  return getTail(result, direction);
4392
4174
  }
4393
4175
  }
4176
+
4394
4177
  /**
4395
4178
  * Moves the caret to the specified index.
4396
4179
  *
@@ -4399,75 +4182,76 @@
4399
4182
  * on mobile webkit devices
4400
4183
  *
4401
4184
  */
4402
-
4403
-
4404
4185
  setCaret(new_pos) {
4405
4186
  this.caretPos = this.items.length;
4406
4187
  }
4188
+
4407
4189
  /**
4408
4190
  * Return list of item dom elements
4409
4191
  *
4410
4192
  */
4411
-
4412
-
4413
4193
  controlChildren() {
4414
4194
  return Array.from(this.control.querySelectorAll('[data-ts-item]'));
4415
4195
  }
4196
+
4416
4197
  /**
4417
4198
  * Disables user input on the control. Used while
4418
4199
  * items are being asynchronously created.
4419
4200
  */
4420
-
4421
-
4422
4201
  lock() {
4423
- this.isLocked = true;
4424
- this.refreshState();
4202
+ this.setLocked(true);
4425
4203
  }
4204
+
4426
4205
  /**
4427
4206
  * Re-enables user input on the control.
4428
4207
  */
4429
-
4430
-
4431
4208
  unlock() {
4432
- this.isLocked = false;
4209
+ this.setLocked(false);
4210
+ }
4211
+
4212
+ /**
4213
+ * Disable or enable user input on the control
4214
+ */
4215
+ setLocked(lock = this.isReadOnly || this.isDisabled) {
4216
+ this.isLocked = lock;
4433
4217
  this.refreshState();
4434
4218
  }
4219
+
4435
4220
  /**
4436
4221
  * Disables user input on the control completely.
4437
4222
  * While disabled, it cannot receive focus.
4438
4223
  */
4439
-
4440
-
4441
4224
  disable() {
4442
- var self = this;
4443
- self.input.disabled = true;
4444
- self.control_input.disabled = true;
4445
- self.focus_node.tabIndex = -1;
4446
- self.isDisabled = true;
4225
+ this.setDisabled(true);
4447
4226
  this.close();
4448
- self.lock();
4449
4227
  }
4228
+
4450
4229
  /**
4451
4230
  * Enables the control so that it can respond
4452
4231
  * to focus and user input.
4453
4232
  */
4454
-
4455
-
4456
4233
  enable() {
4457
- var self = this;
4458
- self.input.disabled = false;
4459
- self.control_input.disabled = false;
4460
- self.focus_node.tabIndex = self.tabIndex;
4461
- self.isDisabled = false;
4462
- self.unlock();
4234
+ this.setDisabled(false);
4463
4235
  }
4236
+ setDisabled(disabled) {
4237
+ this.focus_node.tabIndex = disabled ? -1 : this.tabIndex;
4238
+ this.isDisabled = disabled;
4239
+ this.input.disabled = disabled;
4240
+ this.control_input.disabled = disabled;
4241
+ this.setLocked();
4242
+ }
4243
+ setReadOnly(isReadOnly) {
4244
+ this.isReadOnly = isReadOnly;
4245
+ this.input.readOnly = isReadOnly;
4246
+ this.control_input.readOnly = isReadOnly;
4247
+ this.setLocked();
4248
+ }
4249
+
4464
4250
  /**
4465
4251
  * Completely destroys the control and
4466
4252
  * unbinds all event listeners so that it can
4467
4253
  * be garbage collected.
4468
4254
  */
4469
-
4470
-
4471
4255
  destroy() {
4472
4256
  var self = this;
4473
4257
  var revertSettings = self.revertSettings;
@@ -4478,35 +4262,30 @@
4478
4262
  self.input.innerHTML = revertSettings.innerHTML;
4479
4263
  self.input.tabIndex = revertSettings.tabIndex;
4480
4264
  removeClasses(self.input, 'tomselected', 'ts-hidden-accessible');
4481
-
4482
4265
  self._destroy();
4483
-
4484
4266
  delete self.input.tomselect;
4485
4267
  }
4268
+
4486
4269
  /**
4487
4270
  * A helper method for rendering "item" and
4488
4271
  * "option" templates, given the data.
4489
4272
  *
4490
4273
  */
4491
-
4492
-
4493
4274
  render(templateName, data) {
4494
4275
  var id, html;
4495
4276
  const self = this;
4496
-
4497
4277
  if (typeof this.settings.render[templateName] !== 'function') {
4498
4278
  return null;
4499
- } // render markup
4500
-
4279
+ }
4501
4280
 
4281
+ // render markup
4502
4282
  html = self.settings.render[templateName].call(this, data, escape_html);
4503
-
4504
- if (html == null) {
4505
- return html;
4283
+ if (!html) {
4284
+ return null;
4506
4285
  }
4286
+ html = getDom(html);
4507
4287
 
4508
- html = getDom(html); // add mandatory attributes
4509
-
4288
+ // add mandatory attributes
4510
4289
  if (templateName === 'option' || templateName === 'option_create') {
4511
4290
  if (data[self.settings.disabledField]) {
4512
4291
  setAttr(html, {
@@ -4522,20 +4301,19 @@
4522
4301
  setAttr(html, {
4523
4302
  'data-group': id
4524
4303
  });
4525
-
4526
4304
  if (data.group[self.settings.disabledField]) {
4527
4305
  setAttr(html, {
4528
4306
  'data-disabled': ''
4529
4307
  });
4530
4308
  }
4531
4309
  }
4532
-
4533
4310
  if (templateName === 'option' || templateName === 'item') {
4534
4311
  const value = get_hash(data[self.settings.valueField]);
4535
4312
  setAttr(html, {
4536
4313
  'data-value': value
4537
- }); // make sure we have some classes if a template is overwritten
4314
+ });
4538
4315
 
4316
+ // make sure we have some classes if a template is overwritten
4539
4317
  if (templateName === 'item') {
4540
4318
  addClasses(html, self.settings.itemClass);
4541
4319
  setAttr(html, {
@@ -4546,38 +4324,34 @@
4546
4324
  setAttr(html, {
4547
4325
  role: 'option',
4548
4326
  id: data.$id
4549
- }); // update cache
4327
+ });
4550
4328
 
4329
+ // update cache
4551
4330
  data.$div = html;
4552
4331
  self.options[value] = data;
4553
4332
  }
4554
4333
  }
4555
-
4556
4334
  return html;
4557
4335
  }
4336
+
4558
4337
  /**
4559
4338
  * Type guarded rendering
4560
4339
  *
4561
4340
  */
4562
-
4563
-
4564
4341
  _render(templateName, data) {
4565
4342
  const html = this.render(templateName, data);
4566
-
4567
4343
  if (html == null) {
4568
4344
  throw 'HTMLElement expected';
4569
4345
  }
4570
-
4571
4346
  return html;
4572
4347
  }
4348
+
4573
4349
  /**
4574
4350
  * Clears the render cache for a template. If
4575
4351
  * no template is given, clears all render
4576
4352
  * caches.
4577
4353
  *
4578
4354
  */
4579
-
4580
-
4581
4355
  clearCache() {
4582
4356
  iterate$1(this.options, option => {
4583
4357
  if (option.$div) {
@@ -4586,26 +4360,25 @@
4586
4360
  }
4587
4361
  });
4588
4362
  }
4363
+
4589
4364
  /**
4590
4365
  * Removes a value from item and option caches
4591
4366
  *
4592
4367
  */
4593
-
4594
-
4595
4368
  uncacheValue(value) {
4596
4369
  const option_el = this.getOption(value);
4597
4370
  if (option_el) option_el.remove();
4598
4371
  }
4372
+
4599
4373
  /**
4600
4374
  * Determines whether or not to display the
4601
4375
  * create item prompt, given a user input.
4602
4376
  *
4603
4377
  */
4604
-
4605
-
4606
4378
  canCreate(input) {
4607
4379
  return this.settings.create && input.length > 0 && this.settings.createFilter.call(this, input);
4608
4380
  }
4381
+
4609
4382
  /**
4610
4383
  * Wraps this.`method` so that `new_fn` can be invoked 'before', 'after', or 'instead' of the original method
4611
4384
  *
@@ -4613,33 +4386,24 @@
4613
4386
  *
4614
4387
  * });
4615
4388
  */
4616
-
4617
-
4618
4389
  hook(when, method, new_fn) {
4619
4390
  var self = this;
4620
4391
  var orig_method = self[method];
4621
-
4622
4392
  self[method] = function () {
4623
4393
  var result, result_new;
4624
-
4625
4394
  if (when === 'after') {
4626
4395
  result = orig_method.apply(self, arguments);
4627
4396
  }
4628
-
4629
4397
  result_new = new_fn.apply(self, arguments);
4630
-
4631
4398
  if (when === 'instead') {
4632
4399
  return result_new;
4633
4400
  }
4634
-
4635
4401
  if (when === 'before') {
4636
4402
  result = orig_method.apply(self, arguments);
4637
4403
  }
4638
-
4639
4404
  return result;
4640
4405
  };
4641
4406
  }
4642
-
4643
4407
  }
4644
4408
 
4645
4409
  /**
@@ -4656,8 +4420,10 @@
4656
4420
  * governing permissions and limitations under the License.
4657
4421
  *
4658
4422
  */
4423
+
4659
4424
  function caret_position () {
4660
4425
  var self = this;
4426
+
4661
4427
  /**
4662
4428
  * Moves the caret to the specified index.
4663
4429
  *
@@ -4666,13 +4432,11 @@
4666
4432
  * on mobile webkit devices
4667
4433
  *
4668
4434
  */
4669
-
4670
4435
  self.hook('instead', 'setCaret', new_pos => {
4671
4436
  if (self.settings.mode === 'single' || !self.control.contains(self.control_input)) {
4672
4437
  new_pos = self.items.length;
4673
4438
  } else {
4674
4439
  new_pos = Math.max(0, Math.min(self.items.length, new_pos));
4675
-
4676
4440
  if (new_pos != self.caretPos && !self.isPending) {
4677
4441
  self.controlChildren().forEach((child, j) => {
4678
4442
  if (j < new_pos) {
@@ -4683,19 +4447,20 @@
4683
4447
  });
4684
4448
  }
4685
4449
  }
4686
-
4687
4450
  self.caretPos = new_pos;
4688
4451
  });
4689
4452
  self.hook('instead', 'moveCaret', direction => {
4690
- if (!self.isFocused) return; // move caret before or after selected items
4453
+ if (!self.isFocused) return;
4691
4454
 
4455
+ // move caret before or after selected items
4692
4456
  const last_active = self.getLastActive(direction);
4693
-
4694
4457
  if (last_active) {
4695
4458
  const idx = nodeIndex(last_active);
4696
4459
  self.setCaret(direction > 0 ? idx + 1 : idx);
4697
4460
  self.setActiveItem();
4698
- removeClasses(last_active, 'last-active'); // move caret left or right of current position
4461
+ removeClasses(last_active, 'last-active');
4462
+
4463
+ // move caret left or right of current position
4699
4464
  } else {
4700
4465
  self.setCaret(self.caretPos + direction);
4701
4466
  }
@@ -4716,6 +4481,7 @@
4716
4481
  * governing permissions and limitations under the License.
4717
4482
  *
4718
4483
  */
4484
+
4719
4485
  function dropdown_input () {
4720
4486
  const self = this;
4721
4487
  self.settings.shouldOpen = true; // make sure the input is shown even if there are no options to display in the dropdown
@@ -4725,8 +4491,9 @@
4725
4491
  addClasses(self.control_input, 'dropdown-input');
4726
4492
  const div = getDom('<div class="dropdown-input-wrap">');
4727
4493
  div.append(self.control_input);
4728
- self.dropdown.insertBefore(div, self.dropdown.firstChild); // set a placeholder in the select control
4494
+ self.dropdown.insertBefore(div, self.dropdown.firstChild);
4729
4495
 
4496
+ // set a placeholder in the select control
4730
4497
  const placeholder = getDom('<input class="items-placeholder" tabindex="-1" />');
4731
4498
  placeholder.placeholder = self.settings.placeholder || '';
4732
4499
  self.control.append(placeholder);
@@ -4741,32 +4508,32 @@
4741
4508
  preventDefault(evt, true);
4742
4509
  self.close();
4743
4510
  }
4744
-
4745
4511
  self.clearActiveItems();
4746
4512
  return;
4747
-
4748
4513
  case KEY_TAB:
4749
4514
  self.focus_node.tabIndex = -1;
4750
4515
  break;
4751
4516
  }
4752
-
4753
4517
  return self.onKeyDown.call(self, evt);
4754
4518
  });
4755
4519
  self.on('blur', () => {
4756
4520
  self.focus_node.tabIndex = self.isDisabled ? -1 : self.tabIndex;
4757
- }); // give the control_input focus when the dropdown is open
4521
+ });
4758
4522
 
4523
+ // give the control_input focus when the dropdown is open
4759
4524
  self.on('dropdown_open', () => {
4760
4525
  self.control_input.focus();
4761
- }); // prevent onBlur from closing when focus is on the control_input
4526
+ });
4762
4527
 
4528
+ // prevent onBlur from closing when focus is on the control_input
4763
4529
  const orig_onBlur = self.onBlur;
4764
4530
  self.hook('instead', 'onBlur', evt => {
4765
4531
  if (evt && evt.relatedTarget == self.control_input) return;
4766
4532
  return orig_onBlur.call(self);
4767
4533
  });
4768
- addEvent(self.control_input, 'blur', () => self.onBlur()); // return focus to control to allow further keyboard input
4534
+ addEvent(self.control_input, 'blur', () => self.onBlur());
4769
4535
 
4536
+ // return focus to control to allow further keyboard input
4770
4537
  self.hook('before', 'close', () => {
4771
4538
  if (!self.isOpen) return;
4772
4539
  self.focus_node.focus({
@@ -4789,6 +4556,7 @@
4789
4556
  * governing permissions and limitations under the License.
4790
4557
  *
4791
4558
  */
4559
+
4792
4560
  function no_backspace_delete () {
4793
4561
  var self = this;
4794
4562
  var orig_deleteSelection = self.deleteSelection;
@@ -4796,7 +4564,6 @@
4796
4564
  if (self.activeItems.length) {
4797
4565
  return orig_deleteSelection.call(self, evt);
4798
4566
  }
4799
-
4800
4567
  return false;
4801
4568
  });
4802
4569
  }
@@ -4815,24 +4582,25 @@
4815
4582
  * governing permissions and limitations under the License.
4816
4583
  *
4817
4584
  */
4585
+
4818
4586
  function remove_button (userOptions) {
4819
4587
  const options = Object.assign({
4820
4588
  label: '&times;',
4821
4589
  title: 'Remove',
4822
4590
  className: 'remove',
4823
4591
  append: true
4824
- }, userOptions); //options.className = 'remove-single';
4592
+ }, userOptions);
4825
4593
 
4826
- var self = this; // override the render method to add remove button to each item
4594
+ //options.className = 'remove-single';
4595
+ var self = this;
4827
4596
 
4597
+ // override the render method to add remove button to each item
4828
4598
  if (!options.append) {
4829
4599
  return;
4830
4600
  }
4831
-
4832
4601
  var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
4833
4602
  self.hook('after', 'setupTemplates', () => {
4834
4603
  var orig_render_item = self.settings.render.item;
4835
-
4836
4604
  self.settings.render.item = (data, escape) => {
4837
4605
  var item = getDom(orig_render_item.call(self, data, escape));
4838
4606
  var close_button = getDom(html);
@@ -4841,6 +4609,8 @@
4841
4609
  preventDefault(evt, true);
4842
4610
  });
4843
4611
  addEvent(close_button, 'click', evt => {
4612
+ if (self.isLocked) return;
4613
+
4844
4614
  // propagating will trigger the dropdown to show for single mode
4845
4615
  preventDefault(evt, true);
4846
4616
  if (self.isLocked) return;
@@ -4868,6 +4638,7 @@
4868
4638
  * governing permissions and limitations under the License.
4869
4639
  *
4870
4640
  */
4641
+
4871
4642
  function restore_on_backspace (userOptions) {
4872
4643
  const self = this;
4873
4644
  const options = Object.assign({
@@ -4879,10 +4650,8 @@
4879
4650
  if (!self.isFocused) {
4880
4651
  return;
4881
4652
  }
4882
-
4883
4653
  if (self.control_input.value.trim() === '') {
4884
4654
  var option = self.options[value];
4885
-
4886
4655
  if (option) {
4887
4656
  self.setTextboxValue(options.text.call(self, option));
4888
4657
  }