tom-select-rails 0.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) 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 +882 -310
  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 +838 -281
  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 +846 -287
  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 +1 -1
  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 +9 -5
  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 +1 -1
  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 +18 -11
  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 +13 -8
  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 +1 -1
  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 +12 -7
  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 +15 -9
  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 +3 -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 +1 -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 +1 -1
  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 +10 -4
  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 +17 -12
  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 +1 -1
  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 +35 -17
  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 +882 -310
  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 +838 -281
  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 +846 -287
  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 +1 -1
  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 +164 -160
  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 +1 -1
  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 +18 -11
  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 +94 -89
  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 +1 -1
  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 +121 -116
  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 +234 -228
  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 +3 -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 +1 -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 +1 -1
  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 +112 -106
  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 +149 -144
  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 +1 -1
  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 +277 -259
  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 +838 -281
  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 +331 -274
  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 +882 -310
  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 +281 -221
  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 +846 -287
  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 +350 -294
  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/tom-select.d.ts +26 -9
  88. data/vendor/assets/javascripts/tom-select-rails/types/types/core.d.ts +2 -1
  89. data/vendor/assets/javascripts/tom-select-rails/types/types/settings.d.ts +19 -22
  90. data/vendor/assets/javascripts/tom-select-rails/types/utils.d.ts +5 -5
  91. data/vendor/assets/javascripts/tom-select-rails/types/vanilla.d.ts +2 -2
  92. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.css +43 -25
  93. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.css.map +1 -1
  94. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.min.css +1 -1
  95. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.min.css.map +1 -1
  96. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.css +63 -40
  97. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.css.map +1 -1
  98. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.min.css +1 -1
  99. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.min.css.map +1 -1
  100. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.css +45 -27
  101. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.css.map +1 -1
  102. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.css +43 -25
  103. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.css.map +1 -1
  104. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.min.css +1 -1
  105. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.min.css.map +1 -1
  106. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.min.css +1 -1
  107. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.min.css.map +1 -1
  108. data/vendor/assets/stylesheets/tom-select-rails/scss/_dropdown.scss +0 -2
  109. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/clear_button.scss +12 -5
  110. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/remove_button.scss +34 -8
  111. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.bootstrap4.scss +1 -1
  112. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.bootstrap5.scss +20 -10
  113. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.default.scss +1 -1
  114. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.scss +14 -5
  115. metadata +3 -3
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Tom Select v2.0.3
2
+ * Tom Select v2.2.0
3
3
  * Licensed under the Apache License, Version 2.0 (the "License");
4
4
  */
5
5
 
@@ -33,9 +33,9 @@ class MicroEvent {
33
33
 
34
34
  on(events, fct) {
35
35
  forEvents(events, event => {
36
- this._events[event] = this._events[event] || [];
37
-
38
- this._events[event].push(fct);
36
+ const event_array = this._events[event] || [];
37
+ event_array.push(fct);
38
+ this._events[event] = event_array;
39
39
  });
40
40
  }
41
41
 
@@ -48,21 +48,26 @@ class MicroEvent {
48
48
  }
49
49
 
50
50
  forEvents(events, event => {
51
- if (n === 1) return delete this._events[event];
52
- if (event in this._events === false) return;
51
+ if (n === 1) {
52
+ delete this._events[event];
53
+ return;
54
+ }
53
55
 
54
- this._events[event].splice(this._events[event].indexOf(fct), 1);
56
+ const event_array = this._events[event];
57
+ if (event_array === undefined) return;
58
+ event_array.splice(event_array.indexOf(fct), 1);
59
+ this._events[event] = event_array;
55
60
  });
56
61
  }
57
62
 
58
63
  trigger(events, ...args) {
59
64
  var self = this;
60
65
  forEvents(events, event => {
61
- if (event in self._events === false) return;
62
-
63
- for (let fct of self._events[event]) {
66
+ const event_array = self._events[event];
67
+ if (event_array === undefined) return;
68
+ event_array.forEach(fct => {
64
69
  fct.apply(self, args);
65
- }
70
+ });
66
71
  });
67
72
  }
68
73
 
@@ -189,70 +194,124 @@ function MicroPlugin(Interface) {
189
194
  };
190
195
  }
191
196
 
192
- // https://github.com/andrewrk/node-diacritics/blob/master/index.js
193
- var latin_pat;
194
- const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}]'; // \u{2bc}
195
-
196
- const accent_reg = new RegExp(accent_pat, 'g');
197
- var diacritic_patterns;
198
- const latin_convert = {
199
- 'æ': 'ae',
200
- 'ⱥ': 'a',
201
- 'ø': 'o'
202
- };
203
- const convert_pat = new RegExp(Object.keys(latin_convert).join('|'), 'g');
197
+ /*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
204
198
  /**
205
- * code points generated from toCodePoints();
206
- * removed 65339 to 65345
199
+ * Convert array of strings to a regular expression
200
+ * ex ['ab','a'] => (?:ab|a)
201
+ * ex ['a','b'] => [ab]
202
+ * @param {string[]} chars
203
+ * @return {string}
207
204
  */
205
+ const arrayToPattern = chars => {
206
+ chars = chars.filter(Boolean);
208
207
 
209
- const code_points = [[67, 67], [160, 160], [192, 438], [452, 652], [961, 961], [1019, 1019], [1083, 1083], [1281, 1289], [1984, 1984], [5095, 5095], [7429, 7441], [7545, 7549], [7680, 7935], [8580, 8580], [9398, 9449], [11360, 11391], [42792, 42793], [42802, 42851], [42873, 42897], [42912, 42922], [64256, 64260], [65313, 65338], [65345, 65370]];
208
+ if (chars.length < 2) {
209
+ return chars[0] || '';
210
+ }
211
+
212
+ return maxValueLength(chars) == 1 ? '[' + chars.join('') + ']' : '(?:' + chars.join('|') + ')';
213
+ };
210
214
  /**
211
- * Remove accents
212
- * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
213
- *
215
+ * @param {string[]} array
216
+ * @return {string}
214
217
  */
215
218
 
216
- const asciifold = str => {
217
- return str.normalize('NFKD').replace(accent_reg, '').toLowerCase().replace(convert_pat, function (foreignletter) {
218
- return latin_convert[foreignletter];
219
+ const sequencePattern = array => {
220
+ if (!hasDuplicates(array)) {
221
+ return array.join('');
222
+ }
223
+
224
+ let pattern = '';
225
+ let prev_char_count = 0;
226
+
227
+ const prev_pattern = () => {
228
+ if (prev_char_count > 1) {
229
+ pattern += '{' + prev_char_count + '}';
230
+ }
231
+ };
232
+
233
+ array.forEach((char, i) => {
234
+ if (char === array[i - 1]) {
235
+ prev_char_count++;
236
+ return;
237
+ }
238
+
239
+ prev_pattern();
240
+ pattern += char;
241
+ prev_char_count = 1;
219
242
  });
243
+ prev_pattern();
244
+ return pattern;
220
245
  };
221
246
  /**
222
247
  * Convert array of strings to a regular expression
223
248
  * ex ['ab','a'] => (?:ab|a)
224
249
  * ex ['a','b'] => [ab]
225
- *
250
+ * @param {Set<string>} chars
251
+ * @return {string}
226
252
  */
227
253
 
254
+ const setToPattern = chars => {
255
+ let array = toArray(chars);
256
+ return arrayToPattern(array);
257
+ };
258
+ /**
259
+ *
260
+ * https://stackoverflow.com/questions/7376598/in-javascript-how-do-i-check-if-an-array-has-duplicate-values
261
+ * @param {any[]} array
262
+ */
228
263
 
229
- const arrayToPattern = (chars, glue = '|') => {
230
- if (chars.length == 1) {
231
- return chars[0];
232
- }
264
+ const hasDuplicates = array => {
265
+ return new Set(array).size !== array.length;
266
+ };
267
+ /**
268
+ * https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
269
+ * @param {string} str
270
+ * @return {string}
271
+ */
233
272
 
234
- var longest = 1;
235
- chars.forEach(a => {
236
- longest = Math.max(longest, a.length);
237
- });
273
+ const escape_regex = str => {
274
+ return (str + '').replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu, '\\$1');
275
+ };
276
+ /**
277
+ * Return the max length of array values
278
+ * @param {string[]} array
279
+ *
280
+ */
238
281
 
239
- if (longest == 1) {
240
- return '[' + chars.join('') + ']';
241
- }
282
+ const maxValueLength = array => {
283
+ return array.reduce((longest, value) => Math.max(longest, unicodeLength(value)), 0);
284
+ };
285
+ /**
286
+ * @param {string} str
287
+ */
242
288
 
243
- return '(?:' + chars.join(glue) + ')';
289
+ const unicodeLength = str => {
290
+ return toArray(str).length;
244
291
  };
292
+ /**
293
+ * @param {any} p
294
+ * @return {any[]}
295
+ */
296
+
297
+ const toArray = p => Array.from(p);
298
+
299
+ /*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
245
300
  /**
246
301
  * Get all possible combinations of substrings that add up to the given string
247
302
  * https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string
248
- *
303
+ * @param {string} input
304
+ * @return {string[][]}
249
305
  */
250
-
251
306
  const allSubstrings = input => {
252
307
  if (input.length === 1) return [[input]];
253
- var result = [];
254
- allSubstrings(input.substring(1)).forEach(function (subresult) {
255
- var tmp = subresult.slice(0);
308
+ /** @type {string[][]} */
309
+
310
+ let result = [];
311
+ const start = input.substring(1);
312
+ const suba = allSubstrings(start);
313
+ suba.forEach(function (subresult) {
314
+ let tmp = subresult.slice(0);
256
315
  tmp[0] = input.charAt(0) + tmp[0];
257
316
  result.push(tmp);
258
317
  tmp = subresult.slice(0);
@@ -261,96 +320,500 @@ const allSubstrings = input => {
261
320
  });
262
321
  return result;
263
322
  };
323
+
324
+ /*! @orchidjs/unicode-variants | https://github.com/orchidjs/unicode-variants | Apache License (v2) */
325
+
264
326
  /**
265
- * Generate a list of diacritics from the list of code points
327
+ * @typedef {{[key:string]:string}} TUnicodeMap
328
+ * @typedef {{[key:string]:Set<string>}} TUnicodeSets
329
+ * @typedef {[[number,number]]} TCodePoints
330
+ * @typedef {{folded:string,composed:string,code_point:number}} TCodePointObj
331
+ * @typedef {{start:number,end:number,length:number,substr:string}} TSequencePart
332
+ */
333
+ /** @type {TCodePoints} */
334
+
335
+ const code_points = [[0, 65535]];
336
+ const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}]'; // \u{2bc}
337
+
338
+ /** @type {TUnicodeMap} */
339
+
340
+ let unicode_map;
341
+ /** @type {RegExp} */
342
+
343
+ let multi_char_reg;
344
+ const max_char_length = 3;
345
+ /** @type {TUnicodeMap} */
346
+
347
+ const latin_convert = {
348
+ 'æ': 'ae',
349
+ 'ⱥ': 'a',
350
+ 'ø': 'o',
351
+ '⁄': '/',
352
+ '∕': '/'
353
+ };
354
+ const convert_pat = new RegExp(Object.keys(latin_convert).join('|') + '|' + accent_pat, 'gu');
355
+ /**
356
+ * Initialize the unicode_map from the give code point ranges
266
357
  *
358
+ * @param {TCodePoints=} _code_points
359
+ */
360
+
361
+ const initialize = _code_points => {
362
+ if (unicode_map !== undefined) return;
363
+ unicode_map = generateMap(_code_points || code_points);
364
+ };
365
+ /**
366
+ * Helper method for normalize a string
367
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
368
+ * @param {string} str
369
+ * @param {string} form
370
+ */
371
+
372
+ const normalize = (str, form = 'NFKD') => str.normalize(form);
373
+ /**
374
+ * Compatibility Decomposition without reordering string
375
+ * calling str.normalize('NFKD') on \u{594}\u{595}\u{596} becomes \u{596}\u{594}\u{595}
376
+ * @param {string} str
377
+ */
378
+
379
+ const decompose = str => {
380
+ if (str.match(/[\u0f71-\u0f81]/)) {
381
+ return toArray(str).reduce(
382
+ /**
383
+ * @param {string} result
384
+ * @param {string} char
385
+ */
386
+ (result, char) => {
387
+ return result + normalize(char);
388
+ }, '');
389
+ }
390
+
391
+ return normalize(str);
392
+ };
393
+ /**
394
+ * Remove accents
395
+ * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
396
+ * @param {string} str
397
+ * @return {string}
398
+ */
399
+
400
+ const asciifold = str => {
401
+ return decompose(str).toLowerCase().replace(convert_pat, (
402
+ /** @type {string} */
403
+ char) => {
404
+ return latin_convert[char] || '';
405
+ });
406
+ };
407
+ /**
408
+ * Generate a list of unicode variants from the list of code points
409
+ * @param {TCodePoints} code_points
410
+ * @yield {TCodePointObj}
267
411
  */
268
412
 
269
- const generateDiacritics = () => {
270
- var diacritics = {};
271
- code_points.forEach(code_range => {
272
- for (let i = code_range[0]; i <= code_range[1]; i++) {
273
- let diacritic = String.fromCharCode(i);
274
- let latin = asciifold(diacritic);
413
+ function* generator(code_points) {
414
+ for (const [code_point_min, code_point_max] of code_points) {
415
+ for (let i = code_point_min; i <= code_point_max; i++) {
416
+ let composed = String.fromCharCode(i);
417
+ let folded = asciifold(composed);
418
+
419
+ if (folded == composed.toLowerCase()) {
420
+ continue;
421
+ } // skip when folded is a string longer than 3 characters long
422
+ // bc the resulting regex patterns will be long
423
+ // eg:
424
+ // folded صلى الله عليه وسلم length 18 code point 65018
425
+ // folded جل جلاله length 8 code point 65019
426
+
275
427
 
276
- if (latin == diacritic.toLowerCase()) {
428
+ if (folded.length > max_char_length) {
277
429
  continue;
278
430
  }
279
431
 
280
- if (!(latin in diacritics)) {
281
- diacritics[latin] = [latin];
432
+ if (folded.length == 0) {
433
+ continue;
282
434
  }
283
435
 
284
- var patt = new RegExp(arrayToPattern(diacritics[latin]), 'iu');
436
+ let decomposed = normalize(composed);
437
+ let recomposed = normalize(decomposed, 'NFC');
285
438
 
286
- if (diacritic.match(patt)) {
439
+ if (recomposed === composed && folded === decomposed) {
287
440
  continue;
288
441
  }
289
442
 
290
- diacritics[latin].push(diacritic);
443
+ yield {
444
+ folded: folded,
445
+ composed: composed,
446
+ code_point: i
447
+ };
448
+ }
449
+ }
450
+ }
451
+ /**
452
+ * Generate a unicode map from the list of code points
453
+ * @param {TCodePoints} code_points
454
+ * @return {TUnicodeSets}
455
+ */
456
+
457
+ const generateSets = code_points => {
458
+ /** @type {{[key:string]:Set<string>}} */
459
+ const unicode_sets = {};
460
+ /**
461
+ * @param {string} folded
462
+ * @param {string} to_add
463
+ */
464
+
465
+ const addMatching = (folded, to_add) => {
466
+ /** @type {Set<string>} */
467
+ const folded_set = unicode_sets[folded] || new Set();
468
+ const patt = new RegExp('^' + setToPattern(folded_set) + '$', 'iu');
469
+
470
+ if (to_add.match(patt)) {
471
+ return;
291
472
  }
473
+
474
+ folded_set.add(escape_regex(to_add));
475
+ unicode_sets[folded] = folded_set;
476
+ };
477
+
478
+ for (let value of generator(code_points)) {
479
+ addMatching(value.folded, value.folded);
480
+ addMatching(value.folded, value.composed);
481
+ }
482
+
483
+ return unicode_sets;
484
+ };
485
+ /**
486
+ * Generate a unicode map from the list of code points
487
+ * ae => (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
488
+ *
489
+ * @param {TCodePoints} code_points
490
+ * @return {TUnicodeMap}
491
+ */
492
+
493
+ const generateMap = code_points => {
494
+ /** @type {TUnicodeSets} */
495
+ const unicode_sets = generateSets(code_points);
496
+ /** @type {TUnicodeMap} */
497
+
498
+ const unicode_map = {};
499
+ /** @type {string[]} */
500
+
501
+ let multi_char = [];
502
+
503
+ for (let folded in unicode_sets) {
504
+ let set = unicode_sets[folded];
505
+
506
+ if (set) {
507
+ unicode_map[folded] = setToPattern(set);
508
+ }
509
+
510
+ if (folded.length > 1) {
511
+ multi_char.push(escape_regex(folded));
512
+ }
513
+ }
514
+
515
+ multi_char.sort((a, b) => b.length - a.length);
516
+ const multi_char_patt = arrayToPattern(multi_char);
517
+ multi_char_reg = new RegExp('^' + multi_char_patt, 'u');
518
+ return unicode_map;
519
+ };
520
+ /**
521
+ * Map each element of an array from it's folded value to all possible unicode matches
522
+ * @param {string[]} strings
523
+ * @param {number} min_replacement
524
+ * @return {string}
525
+ */
526
+
527
+ const mapSequence = (strings, min_replacement = 1) => {
528
+ let chars_replaced = 0;
529
+ strings = strings.map(str => {
530
+ if (unicode_map[str]) {
531
+ chars_replaced += str.length;
532
+ }
533
+
534
+ return unicode_map[str] || str;
292
535
  });
293
- var latin_chars = Object.keys(diacritics); // latin character pattern
294
- // match longer substrings first
295
-
296
- latin_chars = latin_chars.sort((a, b) => b.length - a.length);
297
- latin_pat = new RegExp('(' + arrayToPattern(latin_chars) + accent_pat + '*)', 'g'); // build diacritic patterns
298
- // ae needs:
299
- // (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
300
-
301
- var diacritic_patterns = {};
302
- latin_chars.sort((a, b) => a.length - b.length).forEach(latin => {
303
- var substrings = allSubstrings(latin);
304
- var pattern = substrings.map(sub_pat => {
305
- sub_pat = sub_pat.map(l => {
306
- if (diacritics.hasOwnProperty(l)) {
307
- return arrayToPattern(diacritics[l]);
536
+
537
+ if (chars_replaced >= min_replacement) {
538
+ return sequencePattern(strings);
539
+ }
540
+
541
+ return '';
542
+ };
543
+ /**
544
+ * Convert a short string and split it into all possible patterns
545
+ * Keep a pattern only if min_replacement is met
546
+ *
547
+ * 'abc'
548
+ * => [['abc'],['ab','c'],['a','bc'],['a','b','c']]
549
+ * => ['abc-pattern','ab-c-pattern'...]
550
+ *
551
+ *
552
+ * @param {string} str
553
+ * @param {number} min_replacement
554
+ * @return {string}
555
+ */
556
+
557
+ const substringsToPattern = (str, min_replacement = 1) => {
558
+ min_replacement = Math.max(min_replacement, str.length - 1);
559
+ return arrayToPattern(allSubstrings(str).map(sub_pat => {
560
+ return mapSequence(sub_pat, min_replacement);
561
+ }));
562
+ };
563
+ /**
564
+ * Convert an array of sequences into a pattern
565
+ * [{start:0,end:3,length:3,substr:'iii'}...] => (?:iii...)
566
+ *
567
+ * @param {Sequence[]} sequences
568
+ * @param {boolean} all
569
+ */
570
+
571
+ const sequencesToPattern = (sequences, all = true) => {
572
+ let min_replacement = sequences.length > 1 ? 1 : 0;
573
+ return arrayToPattern(sequences.map(sequence => {
574
+ let seq = [];
575
+ const len = all ? sequence.length() : sequence.length() - 1;
576
+
577
+ for (let j = 0; j < len; j++) {
578
+ seq.push(substringsToPattern(sequence.substrs[j] || '', min_replacement));
579
+ }
580
+
581
+ return sequencePattern(seq);
582
+ }));
583
+ };
584
+ /**
585
+ * Return true if the sequence is already in the sequences
586
+ * @param {Sequence} needle_seq
587
+ * @param {Sequence[]} sequences
588
+ */
589
+
590
+
591
+ const inSequences = (needle_seq, sequences) => {
592
+ for (const seq of sequences) {
593
+ if (seq.start != needle_seq.start || seq.end != needle_seq.end) {
594
+ continue;
595
+ }
596
+
597
+ if (seq.substrs.join('') !== needle_seq.substrs.join('')) {
598
+ continue;
599
+ }
600
+
601
+ let needle_parts = needle_seq.parts;
602
+ /**
603
+ * @param {TSequencePart} part
604
+ */
605
+
606
+ const filter = part => {
607
+ for (const needle_part of needle_parts) {
608
+ if (needle_part.start === part.start && needle_part.substr === part.substr) {
609
+ return false;
308
610
  }
309
611
 
310
- return l;
311
- });
312
- return arrayToPattern(sub_pat, '');
313
- });
314
- diacritic_patterns[latin] = arrayToPattern(pattern);
315
- });
316
- return diacritic_patterns;
612
+ if (part.length == 1 || needle_part.length == 1) {
613
+ continue;
614
+ } // check for overlapping parts
615
+ // a = ['::=','==']
616
+ // b = ['::','===']
617
+ // a = ['r','sm']
618
+ // b = ['rs','m']
619
+
620
+
621
+ if (part.start < needle_part.start && part.end > needle_part.start) {
622
+ return true;
623
+ }
624
+
625
+ if (needle_part.start < part.start && needle_part.end > part.start) {
626
+ return true;
627
+ }
628
+ }
629
+
630
+ return false;
631
+ };
632
+
633
+ let filtered = seq.parts.filter(filter);
634
+
635
+ if (filtered.length > 0) {
636
+ continue;
637
+ }
638
+
639
+ return true;
640
+ }
641
+
642
+ return false;
317
643
  };
644
+
645
+ class Sequence {
646
+ constructor() {
647
+ /** @type {TSequencePart[]} */
648
+ this.parts = [];
649
+ /** @type {string[]} */
650
+
651
+ this.substrs = [];
652
+ this.start = 0;
653
+ this.end = 0;
654
+ }
655
+ /**
656
+ * @param {TSequencePart|undefined} part
657
+ */
658
+
659
+
660
+ add(part) {
661
+ if (part) {
662
+ this.parts.push(part);
663
+ this.substrs.push(part.substr);
664
+ this.start = Math.min(part.start, this.start);
665
+ this.end = Math.max(part.end, this.end);
666
+ }
667
+ }
668
+
669
+ last() {
670
+ return this.parts[this.parts.length - 1];
671
+ }
672
+
673
+ length() {
674
+ return this.parts.length;
675
+ }
676
+ /**
677
+ * @param {number} position
678
+ * @param {TSequencePart} last_piece
679
+ */
680
+
681
+
682
+ clone(position, last_piece) {
683
+ let clone = new Sequence();
684
+ let parts = JSON.parse(JSON.stringify(this.parts));
685
+ let last_part = parts.pop();
686
+
687
+ for (const part of parts) {
688
+ clone.add(part);
689
+ }
690
+
691
+ let last_substr = last_piece.substr.substring(0, position - last_part.start);
692
+ let clone_last_len = last_substr.length;
693
+ clone.add({
694
+ start: last_part.start,
695
+ end: last_part.start + clone_last_len,
696
+ length: clone_last_len,
697
+ substr: last_substr
698
+ });
699
+ return clone;
700
+ }
701
+
702
+ }
318
703
  /**
319
- * Expand a regular expression pattern to include diacritics
704
+ * Expand a regular expression pattern to include unicode variants
320
705
  * eg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/
321
706
  *
707
+ * Issue:
708
+ * ﺊﺋ [ 'ﺊ = \\u{fe8a}', 'ﺋ = \\u{fe8b}' ]
709
+ * becomes: ئئ [ 'ي = \\u{64a}', 'ٔ = \\u{654}', 'ي = \\u{64a}', 'ٔ = \\u{654}' ]
710
+ *
711
+ * İIJ = IIJ = ⅡJ
712
+ *
713
+ * 1/2/4
714
+ *
715
+ * @param {string} str
716
+ * @return {string|undefined}
322
717
  */
323
718
 
324
- const diacriticRegexPoints = regex => {
325
- if (diacritic_patterns === undefined) {
326
- diacritic_patterns = generateDiacritics();
327
- }
328
719
 
329
- const decomposed = regex.normalize('NFKD').toLowerCase();
330
- return decomposed.split(latin_pat).map(part => {
331
- if (part == '') {
332
- return '';
333
- } // "ffl" or "ffl"
720
+ const getPattern = str => {
721
+ initialize();
722
+ str = asciifold(str);
723
+ let pattern = '';
724
+ let sequences = [new Sequence()];
725
+
726
+ for (let i = 0; i < str.length; i++) {
727
+ let substr = str.substring(i);
728
+ let match = substr.match(multi_char_reg);
729
+ const char = str.substring(i, i + 1);
730
+ const match_str = match ? match[0] : null; // loop through sequences
731
+ // add either the char or multi_match
732
+
733
+ let overlapping = [];
734
+ let added_types = new Set();
735
+
736
+ for (const sequence of sequences) {
737
+ const last_piece = sequence.last();
738
+
739
+ if (!last_piece || last_piece.length == 1 || last_piece.end <= i) {
740
+ // if we have a multi match
741
+ if (match_str) {
742
+ const len = match_str.length;
743
+ sequence.add({
744
+ start: i,
745
+ end: i + len,
746
+ length: len,
747
+ substr: match_str
748
+ });
749
+ added_types.add('1');
750
+ } else {
751
+ sequence.add({
752
+ start: i,
753
+ end: i + 1,
754
+ length: 1,
755
+ substr: char
756
+ });
757
+ added_types.add('2');
758
+ }
759
+ } else if (match_str) {
760
+ let clone = sequence.clone(i, last_piece);
761
+ const len = match_str.length;
762
+ clone.add({
763
+ start: i,
764
+ end: i + len,
765
+ length: len,
766
+ substr: match_str
767
+ });
768
+ overlapping.push(clone);
769
+ } else {
770
+ // don't add char
771
+ // adding would create invalid patterns: 234 => [2,34,4]
772
+ added_types.add('3');
773
+ }
774
+ } // if we have overlapping
334
775
 
335
776
 
336
- const no_accent = asciifold(part);
777
+ if (overlapping.length > 0) {
778
+ // ['ii','iii'] before ['i','i','iii']
779
+ overlapping = overlapping.sort((a, b) => {
780
+ return a.length() - b.length();
781
+ });
337
782
 
338
- if (diacritic_patterns.hasOwnProperty(no_accent)) {
339
- return diacritic_patterns[no_accent];
340
- } // 'أهلا' (\u{623}\u{647}\u{644}\u{627}) or 'أهلا' (\u{627}\u{654}\u{647}\u{644}\u{627})
783
+ for (let clone of overlapping) {
784
+ // don't add if we already have an equivalent sequence
785
+ if (inSequences(clone, sequences)) {
786
+ continue;
787
+ }
788
+
789
+ sequences.push(clone);
790
+ }
791
+
792
+ continue;
793
+ } // if we haven't done anything unique
794
+ // clean up the patterns
795
+ // helps keep patterns smaller
796
+ // if str = 'r₨㎧aarss', pattern will be 446 instead of 655
341
797
 
342
798
 
343
- const composed_part = part.normalize('NFC');
799
+ if (i > 0 && added_types.size == 1 && !added_types.has('3')) {
800
+ pattern += sequencesToPattern(sequences, false);
801
+ let new_seq = new Sequence();
802
+ const old_seq = sequences[0];
344
803
 
345
- if (composed_part != part) {
346
- return arrayToPattern([part, composed_part]);
804
+ if (old_seq) {
805
+ new_seq.add(old_seq.last());
806
+ }
807
+
808
+ sequences = [new_seq];
347
809
  }
810
+ }
348
811
 
349
- return part;
350
- }).join('');
812
+ pattern += sequencesToPattern(sequences, true);
813
+ return pattern;
351
814
  };
352
815
 
353
- // @ts-ignore TS2691 "An import path cannot end with a '.ts' extension"
816
+ /*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */
354
817
 
355
818
  /**
356
819
  * A property getter resolving dot-notation
@@ -388,20 +851,13 @@ const scoreValue = (value, token, weight) => {
388
851
  var score, pos;
389
852
  if (!value) return 0;
390
853
  value = value + '';
854
+ if (token.regex == null) return 0;
391
855
  pos = value.search(token.regex);
392
856
  if (pos === -1) return 0;
393
857
  score = token.string.length / value.length;
394
858
  if (pos === 0) score += 0.5;
395
859
  return score * weight;
396
860
  };
397
- /**
398
- *
399
- * https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
400
- */
401
-
402
- const escape_regex = str => {
403
- return (str + '').replace(/([\$\(-\+\.\?\[-\^\{-\}])/g, '\\$1');
404
- };
405
861
  /**
406
862
  * Cast object property to an array if it exists and has a value
407
863
  *
@@ -426,7 +882,7 @@ const propToArray = (obj, key) => {
426
882
  *
427
883
  */
428
884
 
429
- const iterate = (object, callback) => {
885
+ const iterate$1 = (object, callback) => {
430
886
  if (Array.isArray(object)) {
431
887
  object.forEach(callback);
432
888
  } else {
@@ -449,6 +905,8 @@ const cmp = (a, b) => {
449
905
  return 0;
450
906
  };
451
907
 
908
+ /*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */
909
+
452
910
  /**
453
911
  * sifter.js
454
912
  * Copyright (c) 2013–2020 Brian Reavis & contributors
@@ -509,13 +967,13 @@ class Sifter {
509
967
  }
510
968
 
511
969
  if (word.length > 0) {
512
- regex = escape_regex(word);
513
-
514
970
  if (this.settings.diacritics) {
515
- regex = diacriticRegexPoints(regex);
971
+ regex = getPattern(word) || null;
972
+ } else {
973
+ regex = escape_regex(word);
516
974
  }
517
975
 
518
- if (respect_word_boundaries) regex = "\\b" + regex;
976
+ if (regex && respect_word_boundaries) regex = "\\b" + regex;
519
977
  }
520
978
 
521
979
  tokens.push({
@@ -533,12 +991,17 @@ class Sifter {
533
991
  * Good matches will have a higher score than poor matches.
534
992
  * If an item is not a match, 0 will be returned by the function.
535
993
  *
536
- * @returns {function}
994
+ * @returns {T.ScoreFn}
537
995
  */
538
996
  getScoreFunction(query, options) {
539
997
  var search = this.prepareSearch(query, options);
540
998
  return this._getScoreFunction(search);
541
999
  }
1000
+ /**
1001
+ * @returns {T.ScoreFn}
1002
+ *
1003
+ */
1004
+
542
1005
 
543
1006
  _getScoreFunction(search) {
544
1007
  const tokens = search.tokens,
@@ -571,7 +1034,7 @@ class Sifter {
571
1034
  if (field_count === 1) {
572
1035
  return function (token, data) {
573
1036
  const field = fields[0].field;
574
- return scoreValue(getAttrFn(data, field), token, weights[field]);
1037
+ return scoreValue(getAttrFn(data, field), token, weights[field] || 1);
575
1038
  };
576
1039
  }
577
1040
 
@@ -587,7 +1050,7 @@ class Sifter {
587
1050
  sum += scoreValue(value, token, 1);
588
1051
  }
589
1052
  } else {
590
- iterate(weights, (weight, field) => {
1053
+ iterate$1(weights, (weight, field) => {
591
1054
  sum += scoreValue(getAttrFn(data, field), token, weight);
592
1055
  });
593
1056
  }
@@ -604,12 +1067,11 @@ class Sifter {
604
1067
 
605
1068
  if (search.options.conjunction === 'and') {
606
1069
  return function (data) {
607
- var i = 0,
608
- score,
1070
+ var score,
609
1071
  sum = 0;
610
1072
 
611
- for (; i < token_count; i++) {
612
- score = scoreObject(tokens[i], data);
1073
+ for (let token of tokens) {
1074
+ score = scoreObject(token, data);
613
1075
  if (score <= 0) return 0;
614
1076
  sum += score;
615
1077
  }
@@ -619,7 +1081,7 @@ class Sifter {
619
1081
  } else {
620
1082
  return function (data) {
621
1083
  var sum = 0;
622
- iterate(tokens, token => {
1084
+ iterate$1(tokens, token => {
623
1085
  sum += scoreObject(token, data);
624
1086
  });
625
1087
  return sum / token_count;
@@ -640,12 +1102,11 @@ class Sifter {
640
1102
  }
641
1103
 
642
1104
  _getSortFunction(search) {
643
- var i, n, implicit_score;
1105
+ var implicit_score,
1106
+ sort_flds = [];
644
1107
  const self = this,
645
1108
  options = search.options,
646
- sort = !search.query && options.sort_empty ? options.sort_empty : options.sort,
647
- sort_flds = [],
648
- multipliers = [];
1109
+ sort = !search.query && options.sort_empty ? options.sort_empty : options.sort;
649
1110
 
650
1111
  if (typeof sort == 'function') {
651
1112
  return sort.bind(this);
@@ -664,9 +1125,9 @@ class Sifter {
664
1125
 
665
1126
 
666
1127
  if (sort) {
667
- for (i = 0, n = sort.length; i < n; i++) {
668
- if (search.query || sort[i].field !== '$score') {
669
- sort_flds.push(sort[i]);
1128
+ for (let s of sort) {
1129
+ if (search.query || s.field !== '$score') {
1130
+ sort_flds.push(s);
670
1131
  }
671
1132
  }
672
1133
  } // the "$score" field is implied to be the primary
@@ -676,8 +1137,8 @@ class Sifter {
676
1137
  if (search.query) {
677
1138
  implicit_score = true;
678
1139
 
679
- for (i = 0, n = sort_flds.length; i < n; i++) {
680
- if (sort_flds[i].field === '$score') {
1140
+ for (let fld of sort_flds) {
1141
+ if (fld.field === '$score') {
681
1142
  implicit_score = false;
682
1143
  break;
683
1144
  }
@@ -688,18 +1149,10 @@ class Sifter {
688
1149
  field: '$score',
689
1150
  direction: 'desc'
690
1151
  });
691
- }
692
- } else {
693
- for (i = 0, n = sort_flds.length; i < n; i++) {
694
- if (sort_flds[i].field === '$score') {
695
- sort_flds.splice(i, 1);
696
- break;
697
- }
698
- }
699
- }
1152
+ } // without a search.query, all items will have the same score
700
1153
 
701
- for (i = 0, n = sort_flds.length; i < n; i++) {
702
- multipliers.push(sort_flds[i].direction === 'desc' ? -1 : 1);
1154
+ } else {
1155
+ sort_flds = sort_flds.filter(fld => fld.field !== '$score');
703
1156
  } // build function
704
1157
 
705
1158
 
@@ -707,25 +1160,20 @@ class Sifter {
707
1160
 
708
1161
  if (!sort_flds_count) {
709
1162
  return null;
710
- } else if (sort_flds_count === 1) {
711
- const sort_fld = sort_flds[0].field;
712
- const multiplier = multipliers[0];
713
- return function (a, b) {
714
- return multiplier * cmp(get_field(sort_fld, a), get_field(sort_fld, b));
715
- };
716
- } else {
717
- return function (a, b) {
718
- var i, result, field;
1163
+ }
719
1164
 
720
- for (i = 0; i < sort_flds_count; i++) {
721
- field = sort_flds[i].field;
722
- result = multipliers[i] * cmp(get_field(field, a), get_field(field, b));
723
- if (result) return result;
724
- }
1165
+ return function (a, b) {
1166
+ var result, field;
725
1167
 
726
- return 0;
727
- };
728
- }
1168
+ for (let sort_fld of sort_flds) {
1169
+ field = sort_fld.field;
1170
+ let multiplier = sort_fld.direction === 'desc' ? -1 : 1;
1171
+ result = multiplier * cmp(get_field(field, a), get_field(field, b));
1172
+ if (result) return result;
1173
+ }
1174
+
1175
+ return 0;
1176
+ };
729
1177
  }
730
1178
 
731
1179
  /**
@@ -784,7 +1232,7 @@ class Sifter {
784
1232
 
785
1233
 
786
1234
  if (query.length) {
787
- iterate(self.items, (item, id) => {
1235
+ iterate$1(self.items, (item, id) => {
788
1236
  score = fn_score(item);
789
1237
 
790
1238
  if (options.filter === false || score > 0) {
@@ -795,7 +1243,7 @@ class Sifter {
795
1243
  }
796
1244
  });
797
1245
  } else {
798
- iterate(self.items, (_, id) => {
1246
+ iterate$1(self.items, (_, id) => {
799
1247
  search.items.push({
800
1248
  'score': 1,
801
1249
  'id': id
@@ -818,6 +1266,29 @@ class Sifter {
818
1266
 
819
1267
  }
820
1268
 
1269
+ /**
1270
+ * Iterates over arrays and hashes.
1271
+ *
1272
+ * ```
1273
+ * iterate(this.items, function(item, id) {
1274
+ * // invoked for each item
1275
+ * });
1276
+ * ```
1277
+ *
1278
+ */
1279
+
1280
+ const iterate = (object, callback) => {
1281
+ if (Array.isArray(object)) {
1282
+ object.forEach(callback);
1283
+ } else {
1284
+ for (var key in object) {
1285
+ if (object.hasOwnProperty(key)) {
1286
+ callback(object[key], key);
1287
+ }
1288
+ }
1289
+ }
1290
+ };
1291
+
821
1292
  /**
822
1293
  * Return a dom element from either a dom query string, jQuery object, a dom element or html string
823
1294
  * https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
@@ -835,10 +1306,10 @@ const getDom = query => {
835
1306
  }
836
1307
 
837
1308
  if (isHtmlString(query)) {
838
- let div = document.createElement('div');
839
- div.innerHTML = query.trim(); // Never return a text node of whitespace as the result
1309
+ var tpl = document.createElement('template');
1310
+ tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
840
1311
 
841
- return div.firstChild;
1312
+ return tpl.content.firstChild;
842
1313
  }
843
1314
 
844
1315
  return document.querySelector(query);
@@ -1049,9 +1520,9 @@ const highlight = (element, regex) => {
1049
1520
 
1050
1521
  const highlightChildren = node => {
1051
1522
  if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && (node.className !== 'highlight' || node.tagName !== 'SPAN')) {
1052
- for (var i = 0; i < node.childNodes.length; ++i) {
1053
- i += highlightRecursive(node.childNodes[i]);
1054
- }
1523
+ Array.from(node.childNodes).forEach(element => {
1524
+ highlightRecursive(element);
1525
+ });
1055
1526
  }
1056
1527
  };
1057
1528
 
@@ -1528,6 +1999,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1528
1999
  this.isInputHidden = false;
1529
2000
  this.isSetup = false;
1530
2001
  this.ignoreFocus = false;
2002
+ this.ignoreHover = false;
1531
2003
  this.hasOptions = false;
1532
2004
  this.currentResults = void 0;
1533
2005
  this.lastValue = '';
@@ -1623,7 +2095,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1623
2095
  control_input = getDom(settings.controlInput); // set attributes
1624
2096
 
1625
2097
  var attrs = ['autocorrect', 'autocapitalize', 'autocomplete'];
1626
- iterate(attrs, attr => {
2098
+ iterate$1(attrs, attr => {
1627
2099
  if (input.getAttribute(attr)) {
1628
2100
  setAttr(control_input, {
1629
2101
  [attr]: input.getAttribute(attr)
@@ -1726,7 +2198,16 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1726
2198
  settings.load = loadDebounce(settings.load, settings.loadThrottle);
1727
2199
  }
1728
2200
 
1729
- self.control_input.type = input.type; // clicking on an option should select it
2201
+ self.control_input.type = input.type;
2202
+ addEvent(dropdown, 'mousemove', () => {
2203
+ self.ignoreHover = false;
2204
+ });
2205
+ addEvent(dropdown, 'mouseenter', e => {
2206
+ var target_match = parentMatch(e.target, '[data-selectable]', dropdown);
2207
+ if (target_match) self.onOptionHover(e, target_match);
2208
+ }, {
2209
+ capture: true
2210
+ }); // clicking on an option should select it
1730
2211
 
1731
2212
  addEvent(dropdown, 'click', evt => {
1732
2213
  const option = parentMatch(evt.target, '[data-selectable]');
@@ -1760,7 +2241,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1760
2241
  addEvent(focus_node, 'resize', () => self.positionDropdown(), passive_event);
1761
2242
  addEvent(focus_node, 'blur', e => self.onBlur(e));
1762
2243
  addEvent(focus_node, 'focus', e => self.onFocus(e));
1763
- addEvent(focus_node, 'paste', e => self.onPaste(e));
2244
+ addEvent(control_input, 'paste', e => self.onPaste(e));
1764
2245
 
1765
2246
  const doc_mousedown = evt => {
1766
2247
  // blur if target is outside of this instance
@@ -1787,7 +2268,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1787
2268
  }
1788
2269
  };
1789
2270
 
1790
- var win_scroll = () => {
2271
+ const win_scroll = () => {
1791
2272
  if (self.isOpen) {
1792
2273
  self.positionDropdown();
1793
2274
  }
@@ -1816,7 +2297,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1816
2297
  settings.items = [];
1817
2298
  delete settings.optgroups;
1818
2299
  delete settings.options;
1819
- addEvent(input, 'invalid', e => {
2300
+ addEvent(input, 'invalid', () => {
1820
2301
  if (self.isValid) {
1821
2302
  self.isValid = false;
1822
2303
  self.isInvalid = true;
@@ -1853,7 +2334,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
1853
2334
  // build options table
1854
2335
  this.addOptions(options); // build optgroup table
1855
2336
 
1856
- iterate(optgroups, optgroup => {
2337
+ iterate$1(optgroups, optgroup => {
1857
2338
  this.registerOptionGroup(optgroup);
1858
2339
  });
1859
2340
  }
@@ -2004,21 +2485,31 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2004
2485
  // input and create Items for each separate value
2005
2486
 
2006
2487
 
2007
- if (self.settings.splitOn) {
2008
- // Wait for pasted text to be recognized in value
2009
- setTimeout(() => {
2010
- var pastedText = self.inputValue();
2488
+ if (!self.settings.splitOn) {
2489
+ return;
2490
+ } // Wait for pasted text to be recognized in value
2011
2491
 
2012
- if (!pastedText.match(self.settings.splitOn)) {
2013
- return;
2014
- }
2015
2492
 
2016
- var splitInput = pastedText.trim().split(self.settings.splitOn);
2017
- iterate(splitInput, piece => {
2018
- self.createItem(piece);
2019
- });
2020
- }, 0);
2021
- }
2493
+ setTimeout(() => {
2494
+ var pastedText = self.inputValue();
2495
+
2496
+ if (!pastedText.match(self.settings.splitOn)) {
2497
+ return;
2498
+ }
2499
+
2500
+ var splitInput = pastedText.trim().split(self.settings.splitOn);
2501
+ iterate$1(splitInput, piece => {
2502
+ const hash = hash_key(piece);
2503
+
2504
+ if (hash) {
2505
+ if (this.options[piece]) {
2506
+ self.addItem(piece);
2507
+ } else {
2508
+ self.createItem(piece);
2509
+ }
2510
+ }
2511
+ });
2512
+ }, 0);
2022
2513
  }
2023
2514
  /**
2024
2515
  * Triggered on <input> keypress.
@@ -2050,6 +2541,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2050
2541
 
2051
2542
  onKeyDown(e) {
2052
2543
  var self = this;
2544
+ self.ignoreHover = true;
2053
2545
 
2054
2546
  if (self.isLocked) {
2055
2547
  if (e.keyCode !== KEY_TAB) {
@@ -2110,6 +2602,8 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2110
2602
  self.onOptionSelect(e, self.activeOption);
2111
2603
  preventDefault(e); // if the option_create=null, the dropdown might be closed
2112
2604
  } else if (self.settings.create && self.createItem()) {
2605
+ preventDefault(e); // don't submit form when searching for a value
2606
+ } else if (document.activeElement == self.control_input && self.isOpen) {
2113
2607
  preventDefault(e);
2114
2608
  }
2115
2609
 
@@ -2180,6 +2674,17 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2180
2674
  self.trigger('type', value);
2181
2675
  }
2182
2676
  }
2677
+ /**
2678
+ * Triggered when the user rolls over
2679
+ * an option in the autocomplete dropdown menu.
2680
+ *
2681
+ */
2682
+
2683
+
2684
+ onOptionHover(evt, option) {
2685
+ if (this.ignoreHover) return;
2686
+ this.setActiveOption(option, false);
2687
+ }
2183
2688
  /**
2184
2689
  * Triggered on <input> focus.
2185
2690
  *
@@ -2229,7 +2734,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2229
2734
  };
2230
2735
 
2231
2736
  if (self.settings.create && self.settings.createOnBlur) {
2232
- self.createItem(null, false, deactivate);
2737
+ self.createItem(null, deactivate);
2233
2738
  } else {
2234
2739
  deactivate();
2235
2740
  }
@@ -2250,7 +2755,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2250
2755
  }
2251
2756
 
2252
2757
  if (option.classList.contains('create')) {
2253
- self.createItem(null, true, () => {
2758
+ self.createItem(null, () => {
2254
2759
  if (self.settings.closeAfterSelect) {
2255
2760
  self.close();
2256
2761
  }
@@ -2534,7 +3039,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2534
3039
  */
2535
3040
 
2536
3041
 
2537
- setActiveOption(option) {
3042
+ setActiveOption(option, scroll = true) {
2538
3043
  if (option === this.activeOption) {
2539
3044
  return;
2540
3045
  }
@@ -2549,7 +3054,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2549
3054
  'aria-selected': 'true'
2550
3055
  });
2551
3056
  addClasses(option, 'active');
2552
- this.scrollToOption(option);
3057
+ if (scroll) this.scrollToOption(option);
2553
3058
  }
2554
3059
  /**
2555
3060
  * Sets the dropdown_content scrollTop to display the option
@@ -2619,7 +3124,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2619
3124
  self.hideInput();
2620
3125
  self.close();
2621
3126
  self.activeItems = activeItems;
2622
- iterate(activeItems, item => {
3127
+ iterate$1(activeItems, item => {
2623
3128
  self.setActiveItemClass(item);
2624
3129
  });
2625
3130
  }
@@ -2755,7 +3260,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2755
3260
 
2756
3261
 
2757
3262
  search(query) {
2758
- var i, result, calculateScore;
3263
+ var result, calculateScore;
2759
3264
  var self = this;
2760
3265
  var options = this.getSearchOptions(); // validate user-provided result scoring function
2761
3266
 
@@ -2780,13 +3285,10 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2780
3285
 
2781
3286
 
2782
3287
  if (self.settings.hideSelected) {
2783
- for (i = result.items.length - 1; i >= 0; i--) {
2784
- let hashed = hash_key(result.items[i].id);
2785
-
2786
- if (hashed && self.items.indexOf(hashed) !== -1) {
2787
- result.items.splice(i, 1);
2788
- }
2789
- }
3288
+ result.items = result.items.filter(item => {
3289
+ let hashed = hash_key(item.id);
3290
+ return !(hashed && self.items.indexOf(hashed) !== -1);
3291
+ });
2790
3292
  }
2791
3293
 
2792
3294
  return result;
@@ -2799,21 +3301,24 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2799
3301
 
2800
3302
 
2801
3303
  refreshOptions(triggerDropdown = true) {
2802
- var i, j, k, n, optgroup, optgroups, html, has_create_option, active_value, active_group;
3304
+ var i, j, k, n, optgroup, optgroups, html, has_create_option, active_group;
2803
3305
  var create;
2804
3306
  const groups = {};
2805
3307
  const groups_order = [];
2806
3308
  var self = this;
2807
3309
  var query = self.inputValue();
3310
+ const same_query = query === self.lastQuery || query == '' && self.lastQuery == null;
2808
3311
  var results = self.search(query);
2809
- var active_option = null; //self.activeOption;
2810
-
3312
+ var active_option = null;
2811
3313
  var show_dropdown = self.settings.shouldOpen || false;
2812
3314
  var dropdown_content = self.dropdown_content;
2813
3315
 
2814
- if (self.activeOption) {
2815
- active_value = self.activeOption.dataset.value;
2816
- active_group = self.activeOption.closest('[data-group]');
3316
+ if (same_query) {
3317
+ active_option = self.activeOption;
3318
+
3319
+ if (active_option) {
3320
+ active_group = active_option.closest('[data-group]');
3321
+ }
2817
3322
  } // build markup
2818
3323
 
2819
3324
 
@@ -2830,12 +3335,16 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2830
3335
 
2831
3336
  for (i = 0; i < n; i++) {
2832
3337
  // get option dom element
2833
- let opt_value = results.items[i].id;
3338
+ let item = results.items[i];
3339
+ if (!item) continue;
3340
+ let opt_value = item.id;
2834
3341
  let option = self.options[opt_value];
2835
- let option_el = self.getOption(opt_value, true); // toggle 'selected' class
3342
+ if (option === undefined) continue;
3343
+ let opt_hash = get_hash(opt_value);
3344
+ let option_el = self.getOption(opt_hash, true); // toggle 'selected' class
2836
3345
 
2837
3346
  if (!self.settings.hideSelected) {
2838
- option_el.classList.toggle('selected', self.items.includes(opt_value));
3347
+ option_el.classList.toggle('selected', self.items.includes(opt_hash));
2839
3348
  }
2840
3349
 
2841
3350
  optgroup = option[self.settings.optgroupField] || '';
@@ -2848,8 +3357,10 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2848
3357
  optgroup = '';
2849
3358
  }
2850
3359
 
2851
- if (!groups.hasOwnProperty(optgroup)) {
2852
- groups[optgroup] = document.createDocumentFragment();
3360
+ let group_fragment = groups[optgroup];
3361
+
3362
+ if (group_fragment === undefined) {
3363
+ group_fragment = document.createDocumentFragment();
2853
3364
  groups_order.push(optgroup);
2854
3365
  } // nodes can only have one parent, so if the option is in mutple groups, we need a clone
2855
3366
 
@@ -2861,48 +3372,50 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2861
3372
  'aria-selected': null
2862
3373
  });
2863
3374
  option_el.classList.add('ts-cloned');
2864
- removeClasses(option_el, 'active');
2865
- } // make sure we keep the activeOption in the same group
3375
+ removeClasses(option_el, 'active'); // make sure we keep the activeOption in the same group
2866
3376
 
2867
-
2868
- if (!active_option && active_value == opt_value) {
2869
- if (active_group) {
2870
- if (active_group.dataset.group === optgroup) {
3377
+ if (self.activeOption && self.activeOption.dataset.value == opt_value) {
3378
+ if (active_group && active_group.dataset.group === optgroup.toString()) {
2871
3379
  active_option = option_el;
2872
3380
  }
2873
- } else {
2874
- active_option = option_el;
2875
3381
  }
2876
3382
  }
2877
3383
 
2878
- groups[optgroup].appendChild(option_el);
3384
+ group_fragment.appendChild(option_el);
3385
+ groups[optgroup] = group_fragment;
2879
3386
  }
2880
3387
  } // sort optgroups
2881
3388
 
2882
3389
 
2883
- if (this.settings.lockOptgroupOrder) {
3390
+ if (self.settings.lockOptgroupOrder) {
2884
3391
  groups_order.sort((a, b) => {
2885
- var a_order = self.optgroups[a] && self.optgroups[a].$order || 0;
2886
- var b_order = self.optgroups[b] && self.optgroups[b].$order || 0;
3392
+ const grp_a = self.optgroups[a];
3393
+ const grp_b = self.optgroups[b];
3394
+ const a_order = grp_a && grp_a.$order || 0;
3395
+ const b_order = grp_b && grp_b.$order || 0;
2887
3396
  return a_order - b_order;
2888
3397
  });
2889
3398
  } // render optgroup headers & join groups
2890
3399
 
2891
3400
 
2892
3401
  html = document.createDocumentFragment();
2893
- iterate(groups_order, optgroup => {
2894
- if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].children.length) {
3402
+ iterate$1(groups_order, optgroup => {
3403
+ let group_fragment = groups[optgroup];
3404
+ if (!group_fragment || !group_fragment.children.length) return;
3405
+ let group_heading = self.optgroups[optgroup];
3406
+
3407
+ if (group_heading !== undefined) {
2895
3408
  let group_options = document.createDocumentFragment();
2896
- let header = self.render('optgroup_header', self.optgroups[optgroup]);
3409
+ let header = self.render('optgroup_header', group_heading);
2897
3410
  append(group_options, header);
2898
- append(group_options, groups[optgroup]);
3411
+ append(group_options, group_fragment);
2899
3412
  let group_html = self.render('optgroup', {
2900
- group: self.optgroups[optgroup],
3413
+ group: group_heading,
2901
3414
  options: group_options
2902
3415
  });
2903
3416
  append(html, group_html);
2904
3417
  } else {
2905
- append(html, groups[optgroup]);
3418
+ append(html, group_fragment);
2906
3419
  }
2907
3420
  });
2908
3421
  dropdown_content.innerHTML = '';
@@ -2912,7 +3425,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2912
3425
  removeHighlight(dropdown_content);
2913
3426
 
2914
3427
  if (results.query.length && results.tokens.length) {
2915
- iterate(results.tokens, tok => {
3428
+ iterate$1(results.tokens, tok => {
2916
3429
  highlight(dropdown_content, tok.regex);
2917
3430
  });
2918
3431
  }
@@ -2953,7 +3466,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
2953
3466
 
2954
3467
  if (show_dropdown) {
2955
3468
  if (results.items.length > 0) {
2956
- if (!active_option && self.settings.mode === 'single' && self.items.length) {
3469
+ if (!active_option && self.settings.mode === 'single' && self.items[0] != undefined) {
2957
3470
  active_option = self.getOption(self.items[0]);
2958
3471
  }
2959
3472
 
@@ -3040,7 +3553,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3040
3553
 
3041
3554
 
3042
3555
  addOptions(data, user_created = false) {
3043
- iterate(data, dat => {
3556
+ iterate$1(data, dat => {
3044
3557
  this.addOption(dat, user_created);
3045
3558
  });
3046
3559
  }
@@ -3120,11 +3633,12 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3120
3633
  const value_new = hash_key(data[self.settings.valueField]); // sanity checks
3121
3634
 
3122
3635
  if (value_old === null) return;
3123
- if (!self.options.hasOwnProperty(value_old)) return;
3636
+ const data_old = self.options[value_old];
3637
+ if (data_old == undefined) return;
3124
3638
  if (typeof value_new !== 'string') throw new Error('Value must be set in option data');
3125
3639
  const option = self.getOption(value_old);
3126
3640
  const item = self.getItem(value_old);
3127
- data.$order = data.$order || self.options[value_old].$order;
3641
+ data.$order = data.$order || data_old.$order;
3128
3642
  delete self.options[value_old]; // invalidate render cache
3129
3643
  // don't remove existing node yet, we'll remove it after replacing it
3130
3644
 
@@ -3182,20 +3696,35 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3182
3696
  */
3183
3697
 
3184
3698
 
3185
- clearOptions() {
3699
+ clearOptions(filter) {
3700
+ const boundFilter = (filter || this.clearFilter).bind(this);
3186
3701
  this.loadedSearches = {};
3187
3702
  this.userOptions = {};
3188
3703
  this.clearCache();
3189
- var selected = {};
3190
- iterate(this.options, (option, key) => {
3191
- if (this.items.indexOf(key) >= 0) {
3192
- selected[key] = this.options[key];
3704
+ const selected = {};
3705
+ iterate$1(this.options, (option, key) => {
3706
+ if (boundFilter(option, key)) {
3707
+ selected[key] = option;
3193
3708
  }
3194
3709
  });
3195
3710
  this.options = this.sifter.items = selected;
3196
3711
  this.lastQuery = null;
3197
3712
  this.trigger('option_clear');
3198
3713
  }
3714
+ /**
3715
+ * Used by clearOptions() to decide whether or not an option should be removed
3716
+ * Return true to keep an option, false to remove
3717
+ *
3718
+ */
3719
+
3720
+
3721
+ clearFilter(option, value) {
3722
+ if (this.items.indexOf(value) >= 0) {
3723
+ return true;
3724
+ }
3725
+
3726
+ return false;
3727
+ }
3199
3728
  /**
3200
3729
  * Returns the dom element of the option
3201
3730
  * matching the given value.
@@ -3205,10 +3734,10 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3205
3734
 
3206
3735
  getOption(value, create = false) {
3207
3736
  const hashed = hash_key(value);
3737
+ if (hashed === null) return null;
3738
+ const option = this.options[hashed];
3208
3739
 
3209
- if (hashed !== null && this.options.hasOwnProperty(hashed)) {
3210
- const option = this.options[hashed];
3211
-
3740
+ if (option != undefined) {
3212
3741
  if (option.$div) {
3213
3742
  return option.$div;
3214
3743
  }
@@ -3281,11 +3810,11 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3281
3810
  var self = this;
3282
3811
  var items = Array.isArray(values) ? values : [values];
3283
3812
  items = items.filter(x => self.items.indexOf(x) === -1);
3284
-
3285
- for (let i = 0, n = items.length; i < n; i++) {
3286
- self.isPending = i < n - 1;
3287
- self.addItem(items[i], silent);
3288
- }
3813
+ const last_item = items[items.length - 1];
3814
+ items.forEach(item => {
3815
+ self.isPending = item !== last_item;
3816
+ self.addItem(item, silent);
3817
+ });
3289
3818
  }
3290
3819
  /**
3291
3820
  * "Selects" an item. Adds it to the list
@@ -3416,7 +3945,16 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3416
3945
  */
3417
3946
 
3418
3947
 
3419
- createItem(input = null, triggerDropdown = true, callback = () => {}) {
3948
+ createItem(input = null, callback = () => {}) {
3949
+ // triggerDropdown parameter @deprecated 2.1.1
3950
+ if (arguments.length === 3) {
3951
+ callback = arguments[2];
3952
+ }
3953
+
3954
+ if (typeof callback != 'function') {
3955
+ callback = () => {};
3956
+ }
3957
+
3420
3958
  var self = this;
3421
3959
  var caret = self.caretPos;
3422
3960
  var output;
@@ -3515,11 +4053,11 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3515
4053
  refreshValidityState() {
3516
4054
  var self = this;
3517
4055
 
3518
- if (!self.input.checkValidity) {
4056
+ if (!self.input.validity) {
3519
4057
  return;
3520
4058
  }
3521
4059
 
3522
- self.isValid = self.input.checkValidity();
4060
+ self.isValid = self.input.validity.valid;
3523
4061
  self.isInvalid = !self.isValid;
3524
4062
  }
3525
4063
  /**
@@ -3692,7 +4230,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3692
4230
  var self = this;
3693
4231
  if (!self.items.length) return;
3694
4232
  var items = self.controlChildren();
3695
- iterate(items, item => {
4233
+ iterate$1(items, item => {
3696
4234
  self.removeItem(item, true);
3697
4235
  });
3698
4236
  self.showInput();
@@ -3710,7 +4248,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3710
4248
  const self = this;
3711
4249
  const caret = self.caretPos;
3712
4250
  const target = self.control;
3713
- target.insertBefore(el, target.children[caret]);
4251
+ target.insertBefore(el, target.children[caret] || null);
3714
4252
  self.setCaret(caret + 1);
3715
4253
  }
3716
4254
  /**
@@ -3735,20 +4273,23 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3735
4273
  caret++;
3736
4274
  }
3737
4275
 
3738
- iterate(self.activeItems, item => rm_items.push(item));
4276
+ iterate$1(self.activeItems, item => rm_items.push(item));
3739
4277
  } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
3740
4278
  const items = self.controlChildren();
4279
+ let rm_item;
3741
4280
 
3742
4281
  if (direction < 0 && selection.start === 0 && selection.length === 0) {
3743
- rm_items.push(items[self.caretPos - 1]);
4282
+ rm_item = items[self.caretPos - 1];
3744
4283
  } else if (direction > 0 && selection.start === self.inputValue().length) {
3745
- rm_items.push(items[self.caretPos]);
4284
+ rm_item = items[self.caretPos];
3746
4285
  }
3747
- }
3748
4286
 
3749
- const values = rm_items.map(item => item.dataset.value); // allow the callback to abort
4287
+ if (rm_item !== undefined) {
4288
+ rm_items.push(rm_item);
4289
+ }
4290
+ }
3750
4291
 
3751
- if (!values.length || typeof self.settings.onDelete === 'function' && self.settings.onDelete.call(self, values, e) === false) {
4292
+ if (!self.shouldDelete(rm_items, e)) {
3752
4293
  return false;
3753
4294
  }
3754
4295
 
@@ -3767,6 +4308,20 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3767
4308
  self.refreshOptions(false);
3768
4309
  return true;
3769
4310
  }
4311
+ /**
4312
+ * Return true if the items should be deleted
4313
+ */
4314
+
4315
+
4316
+ shouldDelete(items, evt) {
4317
+ const values = items.map(item => item.dataset.value); // allow the callback to abort
4318
+
4319
+ if (!values.length || typeof this.settings.onDelete === 'function' && this.settings.onDelete(values, evt) === false) {
4320
+ return false;
4321
+ }
4322
+
4323
+ return true;
4324
+ }
3770
4325
  /**
3771
4326
  * Selects the previous / next item (depending on the `direction` argument).
3772
4327
  *
@@ -3932,26 +4487,11 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3932
4487
 
3933
4488
 
3934
4489
  render(templateName, data) {
3935
- if (typeof this.settings.render[templateName] !== 'function') {
3936
- return null;
3937
- }
3938
-
3939
- return this._render(templateName, data);
3940
- }
3941
- /**
3942
- * _render() can be called directly when we know we don't want to hit the cache
3943
- * return type could be null for some templates, we need https://github.com/microsoft/TypeScript/issues/33014
3944
- */
3945
-
3946
-
3947
- _render(templateName, data) {
3948
- var value = '',
3949
- id,
3950
- html;
4490
+ var id, html;
3951
4491
  const self = this;
3952
4492
 
3953
- if (templateName === 'option' || templateName == 'item') {
3954
- value = get_hash(data[self.settings.valueField]);
4493
+ if (typeof this.settings.render[templateName] !== 'function') {
4494
+ return null;
3955
4495
  } // render markup
3956
4496
 
3957
4497
 
@@ -3987,6 +4527,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
3987
4527
  }
3988
4528
 
3989
4529
  if (templateName === 'option' || templateName === 'item') {
4530
+ const value = get_hash(data[self.settings.valueField]);
3990
4531
  setAttr(html, {
3991
4532
  'data-value': value
3992
4533
  }); // make sure we have some classes if a template is overwritten
@@ -4003,12 +4544,28 @@ class TomSelect extends MicroPlugin(MicroEvent) {
4003
4544
  id: data.$id
4004
4545
  }); // update cache
4005
4546
 
4006
- self.options[value].$div = html;
4547
+ data.$div = html;
4548
+ self.options[value] = data;
4007
4549
  }
4008
4550
  }
4009
4551
 
4010
4552
  return html;
4011
4553
  }
4554
+ /**
4555
+ * Type guarded rendering
4556
+ *
4557
+ */
4558
+
4559
+
4560
+ _render(templateName, data) {
4561
+ const html = this.render(templateName, data);
4562
+
4563
+ if (html == null) {
4564
+ throw 'HTMLElement expected';
4565
+ }
4566
+
4567
+ return html;
4568
+ }
4012
4569
  /**
4013
4570
  * Clears the render cache for a template. If
4014
4571
  * no template is given, clears all render
@@ -4018,7 +4575,7 @@ class TomSelect extends MicroPlugin(MicroEvent) {
4018
4575
 
4019
4576
 
4020
4577
  clearCache() {
4021
- iterate(this.options, (option, value) => {
4578
+ iterate$1(this.options, option => {
4022
4579
  if (option.$div) {
4023
4580
  option.$div.remove();
4024
4581
  delete option.$div;