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