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