tom-select-rails 2.2.2 → 2.3.1

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