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