tom-select-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +26 -0
  4. data/Rakefile +3 -0
  5. data/lib/tom-select-rails/engine.rb +8 -0
  6. data/lib/tom-select-rails/version.rb +5 -0
  7. data/lib/tom-select-rails.rb +7 -0
  8. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.complete.js +4887 -0
  9. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.complete.js.map +1 -0
  10. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.js +4085 -0
  11. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.js.map +1 -0
  12. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.popular.js +4337 -0
  13. data/vendor/assets/javascripts/tom-select-rails/cjs/tom-select.popular.js.map +1 -0
  14. data/vendor/assets/javascripts/tom-select-rails/cjs/utils.js +196 -0
  15. data/vendor/assets/javascripts/tom-select-rails/cjs/utils.js.map +1 -0
  16. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/caret_position/plugin.js +162 -0
  17. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/caret_position/plugin.js.map +1 -0
  18. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/change_listener/plugin.js +50 -0
  19. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/change_listener/plugin.js.map +1 -0
  20. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/checkbox_options/plugin.js +172 -0
  21. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/checkbox_options/plugin.js.map +1 -0
  22. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/clear_button/plugin.js +91 -0
  23. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/clear_button/plugin.js.map +1 -0
  24. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/drag_drop/plugin.js +62 -0
  25. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/drag_drop/plugin.js.map +1 -0
  26. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_header/plugin.js +118 -0
  27. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_header/plugin.js.map +1 -0
  28. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_input/plugin.js +230 -0
  29. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/dropdown_input/plugin.js.map +1 -0
  30. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/input_autogrow/plugin.js +80 -0
  31. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/input_autogrow/plugin.js.map +1 -0
  32. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_active_items/plugin.js +25 -0
  33. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_active_items/plugin.js.map +1 -0
  34. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_backspace_delete/plugin.js +32 -0
  35. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/no_backspace_delete/plugin.js.map +1 -0
  36. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/optgroup_columns/plugin.js +108 -0
  37. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/optgroup_columns/plugin.js.map +1 -0
  38. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/remove_button/plugin.js +146 -0
  39. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/remove_button/plugin.js.map +1 -0
  40. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/restore_on_backspace/plugin.js +43 -0
  41. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/restore_on_backspace/plugin.js.map +1 -0
  42. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/virtual_scroll/plugin.js +261 -0
  43. data/vendor/assets/javascripts/tom-select-rails/esm/plugins/virtual_scroll/plugin.js.map +1 -0
  44. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.complete.js +4885 -0
  45. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.complete.js.map +1 -0
  46. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.js +4083 -0
  47. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.js.map +1 -0
  48. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.popular.js +4335 -0
  49. data/vendor/assets/javascripts/tom-select-rails/esm/tom-select.popular.js.map +1 -0
  50. data/vendor/assets/javascripts/tom-select-rails/esm/utils.js +181 -0
  51. data/vendor/assets/javascripts/tom-select-rails/esm/utils.js.map +1 -0
  52. data/vendor/assets/javascripts/tom-select-rails/js/plugins/caret_position.js +170 -0
  53. data/vendor/assets/javascripts/tom-select-rails/js/plugins/caret_position.js.map +1 -0
  54. data/vendor/assets/javascripts/tom-select-rails/js/plugins/change_listener.js +58 -0
  55. data/vendor/assets/javascripts/tom-select-rails/js/plugins/change_listener.js.map +1 -0
  56. data/vendor/assets/javascripts/tom-select-rails/js/plugins/checkbox_options.js +180 -0
  57. data/vendor/assets/javascripts/tom-select-rails/js/plugins/checkbox_options.js.map +1 -0
  58. data/vendor/assets/javascripts/tom-select-rails/js/plugins/clear_button.js +99 -0
  59. data/vendor/assets/javascripts/tom-select-rails/js/plugins/clear_button.js.map +1 -0
  60. data/vendor/assets/javascripts/tom-select-rails/js/plugins/drag_drop.js +70 -0
  61. data/vendor/assets/javascripts/tom-select-rails/js/plugins/drag_drop.js.map +1 -0
  62. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_header.js +126 -0
  63. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_header.js.map +1 -0
  64. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_input.js +238 -0
  65. data/vendor/assets/javascripts/tom-select-rails/js/plugins/dropdown_input.js.map +1 -0
  66. data/vendor/assets/javascripts/tom-select-rails/js/plugins/input_autogrow.js +88 -0
  67. data/vendor/assets/javascripts/tom-select-rails/js/plugins/input_autogrow.js.map +1 -0
  68. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_active_items.js +33 -0
  69. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_active_items.js.map +1 -0
  70. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_backspace_delete.js +40 -0
  71. data/vendor/assets/javascripts/tom-select-rails/js/plugins/no_backspace_delete.js.map +1 -0
  72. data/vendor/assets/javascripts/tom-select-rails/js/plugins/optgroup_columns.js +116 -0
  73. data/vendor/assets/javascripts/tom-select-rails/js/plugins/optgroup_columns.js.map +1 -0
  74. data/vendor/assets/javascripts/tom-select-rails/js/plugins/remove_button.js +154 -0
  75. data/vendor/assets/javascripts/tom-select-rails/js/plugins/remove_button.js.map +1 -0
  76. data/vendor/assets/javascripts/tom-select-rails/js/plugins/restore_on_backspace.js +51 -0
  77. data/vendor/assets/javascripts/tom-select-rails/js/plugins/restore_on_backspace.js.map +1 -0
  78. data/vendor/assets/javascripts/tom-select-rails/js/plugins/virtual_scroll.js +269 -0
  79. data/vendor/assets/javascripts/tom-select-rails/js/plugins/virtual_scroll.js.map +1 -0
  80. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.js +4092 -0
  81. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.js.map +1 -0
  82. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.min.js +303 -0
  83. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.base.min.js.map +1 -0
  84. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.js +4894 -0
  85. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.js.map +1 -0
  86. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.min.js +362 -0
  87. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.complete.min.js.map +1 -0
  88. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.js +4344 -0
  89. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.js.map +1 -0
  90. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.min.js +324 -0
  91. data/vendor/assets/javascripts/tom-select-rails/js/tom-select.popular.min.js.map +1 -0
  92. data/vendor/assets/javascripts/tom-select-rails/types/constants.d.ts +12 -0
  93. data/vendor/assets/javascripts/tom-select-rails/types/contrib/highlight.d.ts +13 -0
  94. data/vendor/assets/javascripts/tom-select-rails/types/contrib/microevent.d.ts +20 -0
  95. data/vendor/assets/javascripts/tom-select-rails/types/contrib/microplugin.d.ts +71 -0
  96. data/vendor/assets/javascripts/tom-select-rails/types/defaults.d.ts +51 -0
  97. data/vendor/assets/javascripts/tom-select-rails/types/getSettings.d.ts +3 -0
  98. data/vendor/assets/javascripts/tom-select-rails/types/plugins/caret_position/plugin.d.ts +16 -0
  99. data/vendor/assets/javascripts/tom-select-rails/types/plugins/change_listener/plugin.d.ts +16 -0
  100. data/vendor/assets/javascripts/tom-select-rails/types/plugins/checkbox_options/plugin.d.ts +16 -0
  101. data/vendor/assets/javascripts/tom-select-rails/types/plugins/clear_button/plugin.d.ts +17 -0
  102. data/vendor/assets/javascripts/tom-select-rails/types/plugins/clear_button/types.d.ts +5 -0
  103. data/vendor/assets/javascripts/tom-select-rails/types/plugins/drag_drop/plugin.d.ts +16 -0
  104. data/vendor/assets/javascripts/tom-select-rails/types/plugins/dropdown_header/plugin.d.ts +17 -0
  105. data/vendor/assets/javascripts/tom-select-rails/types/plugins/dropdown_header/types.d.ts +8 -0
  106. data/vendor/assets/javascripts/tom-select-rails/types/plugins/dropdown_input/plugin.d.ts +16 -0
  107. data/vendor/assets/javascripts/tom-select-rails/types/plugins/input_autogrow/plugin.d.ts +15 -0
  108. data/vendor/assets/javascripts/tom-select-rails/types/plugins/no_active_items/plugin.d.ts +15 -0
  109. data/vendor/assets/javascripts/tom-select-rails/types/plugins/no_backspace_delete/plugin.d.ts +15 -0
  110. data/vendor/assets/javascripts/tom-select-rails/types/plugins/optgroup_columns/plugin.d.ts +16 -0
  111. data/vendor/assets/javascripts/tom-select-rails/types/plugins/remove_button/plugin.d.ts +17 -0
  112. data/vendor/assets/javascripts/tom-select-rails/types/plugins/remove_button/types.d.ts +6 -0
  113. data/vendor/assets/javascripts/tom-select-rails/types/plugins/restore_on_backspace/plugin.d.ts +21 -0
  114. data/vendor/assets/javascripts/tom-select-rails/types/plugins/virtual_scroll/plugin.d.ts +16 -0
  115. data/vendor/assets/javascripts/tom-select-rails/types/tom-select.complete.d.ts +2 -0
  116. data/vendor/assets/javascripts/tom-select-rails/types/tom-select.d.ts +579 -0
  117. data/vendor/assets/javascripts/tom-select-rails/types/tom-select.popular.d.ts +2 -0
  118. data/vendor/assets/javascripts/tom-select-rails/types/types/core.d.ts +44 -0
  119. data/vendor/assets/javascripts/tom-select-rails/types/types/index.d.ts +2 -0
  120. data/vendor/assets/javascripts/tom-select-rails/types/types/settings.d.ts +81 -0
  121. data/vendor/assets/javascripts/tom-select-rails/types/utils.d.ts +76 -0
  122. data/vendor/assets/javascripts/tom-select-rails/types/vanilla.d.ts +76 -0
  123. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.css +548 -0
  124. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.css.map +1 -0
  125. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.min.css +2 -0
  126. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap4.min.css.map +1 -0
  127. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.css +592 -0
  128. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.css.map +1 -0
  129. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.min.css +2 -0
  130. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.bootstrap5.min.css.map +1 -0
  131. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.css +391 -0
  132. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.css.map +1 -0
  133. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.css +476 -0
  134. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.css.map +1 -0
  135. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.min.css +2 -0
  136. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.default.min.css.map +1 -0
  137. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.min.css +2 -0
  138. data/vendor/assets/stylesheets/tom-select-rails/css/tom-select.min.css.map +1 -0
  139. data/vendor/assets/stylesheets/tom-select-rails/scss/_dropdown.scss +104 -0
  140. data/vendor/assets/stylesheets/tom-select-rails/scss/_items.scss +115 -0
  141. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/checkbox_options.scss +5 -0
  142. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/clear_button.scss +30 -0
  143. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/drag_drop.scss +16 -0
  144. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/dropdown_header.scss +23 -0
  145. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/dropdown_input.scss +47 -0
  146. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/input_autogrow.scss +18 -0
  147. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/optgroup_columns.scss +23 -0
  148. data/vendor/assets/stylesheets/tom-select-rails/scss/plugins/remove_button.scss +44 -0
  149. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.bootstrap4.scss +219 -0
  150. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.bootstrap5.scss +274 -0
  151. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.default.scss +87 -0
  152. data/vendor/assets/stylesheets/tom-select-rails/scss/tom-select.scss +175 -0
  153. metadata +195 -0
@@ -0,0 +1,4885 @@
1
+ /**
2
+ * Tom Select v2.0.3
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ */
5
+
6
+ /**
7
+ * MicroEvent - to make any js object an event emitter
8
+ *
9
+ * - pure javascript - server compatible, browser compatible
10
+ * - dont rely on the browser doms
11
+ * - super simple - you get it immediatly, no mistery, no magic involved
12
+ *
13
+ * @author Jerome Etienne (https://github.com/jeromeetienne)
14
+ */
15
+
16
+ /**
17
+ * Execute callback for each event in space separated list of event names
18
+ *
19
+ */
20
+ function forEvents(events, callback) {
21
+ events.split(/\s+/).forEach(event => {
22
+ callback(event);
23
+ });
24
+ }
25
+
26
+ class MicroEvent {
27
+ constructor() {
28
+ this._events = void 0;
29
+ this._events = {};
30
+ }
31
+
32
+ on(events, fct) {
33
+ forEvents(events, event => {
34
+ this._events[event] = this._events[event] || [];
35
+
36
+ this._events[event].push(fct);
37
+ });
38
+ }
39
+
40
+ off(events, fct) {
41
+ var n = arguments.length;
42
+
43
+ if (n === 0) {
44
+ this._events = {};
45
+ return;
46
+ }
47
+
48
+ forEvents(events, event => {
49
+ if (n === 1) return delete this._events[event];
50
+ if (event in this._events === false) return;
51
+
52
+ this._events[event].splice(this._events[event].indexOf(fct), 1);
53
+ });
54
+ }
55
+
56
+ trigger(events, ...args) {
57
+ var self = this;
58
+ forEvents(events, event => {
59
+ if (event in self._events === false) return;
60
+
61
+ for (let fct of self._events[event]) {
62
+ fct.apply(self, args);
63
+ }
64
+ });
65
+ }
66
+
67
+ }
68
+
69
+ /**
70
+ * microplugin.js
71
+ * Copyright (c) 2013 Brian Reavis & contributors
72
+ *
73
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
74
+ * file except in compliance with the License. You may obtain a copy of the License at:
75
+ * http://www.apache.org/licenses/LICENSE-2.0
76
+ *
77
+ * Unless required by applicable law or agreed to in writing, software distributed under
78
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
79
+ * ANY KIND, either express or implied. See the License for the specific language
80
+ * governing permissions and limitations under the License.
81
+ *
82
+ * @author Brian Reavis <brian@thirdroute.com>
83
+ */
84
+ function MicroPlugin(Interface) {
85
+ Interface.plugins = {};
86
+ return class extends Interface {
87
+ constructor(...args) {
88
+ super(...args);
89
+ this.plugins = {
90
+ names: [],
91
+ settings: {},
92
+ requested: {},
93
+ loaded: {}
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Registers a plugin.
99
+ *
100
+ * @param {function} fn
101
+ */
102
+ static define(name, fn) {
103
+ Interface.plugins[name] = {
104
+ 'name': name,
105
+ 'fn': fn
106
+ };
107
+ }
108
+ /**
109
+ * Initializes the listed plugins (with options).
110
+ * Acceptable formats:
111
+ *
112
+ * List (without options):
113
+ * ['a', 'b', 'c']
114
+ *
115
+ * List (with options):
116
+ * [{'name': 'a', options: {}}, {'name': 'b', options: {}}]
117
+ *
118
+ * Hash (with options):
119
+ * {'a': { ... }, 'b': { ... }, 'c': { ... }}
120
+ *
121
+ * @param {array|object} plugins
122
+ */
123
+
124
+
125
+ initializePlugins(plugins) {
126
+ var key, name;
127
+ const self = this;
128
+ const queue = [];
129
+
130
+ if (Array.isArray(plugins)) {
131
+ plugins.forEach(plugin => {
132
+ if (typeof plugin === 'string') {
133
+ queue.push(plugin);
134
+ } else {
135
+ self.plugins.settings[plugin.name] = plugin.options;
136
+ queue.push(plugin.name);
137
+ }
138
+ });
139
+ } else if (plugins) {
140
+ for (key in plugins) {
141
+ if (plugins.hasOwnProperty(key)) {
142
+ self.plugins.settings[key] = plugins[key];
143
+ queue.push(key);
144
+ }
145
+ }
146
+ }
147
+
148
+ while (name = queue.shift()) {
149
+ self.require(name);
150
+ }
151
+ }
152
+
153
+ loadPlugin(name) {
154
+ var self = this;
155
+ var plugins = self.plugins;
156
+ var plugin = Interface.plugins[name];
157
+
158
+ if (!Interface.plugins.hasOwnProperty(name)) {
159
+ throw new Error('Unable to find "' + name + '" plugin');
160
+ }
161
+
162
+ plugins.requested[name] = true;
163
+ plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);
164
+ plugins.names.push(name);
165
+ }
166
+ /**
167
+ * Initializes a plugin.
168
+ *
169
+ */
170
+
171
+
172
+ require(name) {
173
+ var self = this;
174
+ var plugins = self.plugins;
175
+
176
+ if (!self.plugins.loaded.hasOwnProperty(name)) {
177
+ if (plugins.requested[name]) {
178
+ throw new Error('Plugin has circular dependency ("' + name + '")');
179
+ }
180
+
181
+ self.loadPlugin(name);
182
+ }
183
+
184
+ return plugins.loaded[name];
185
+ }
186
+
187
+ };
188
+ }
189
+
190
+ // https://github.com/andrewrk/node-diacritics/blob/master/index.js
191
+ var latin_pat;
192
+ const accent_pat = '[\u0300-\u036F\u{b7}\u{2be}]'; // \u{2bc}
193
+
194
+ const accent_reg = new RegExp(accent_pat, 'g');
195
+ var diacritic_patterns;
196
+ const latin_convert = {
197
+ 'æ': 'ae',
198
+ 'ⱥ': 'a',
199
+ 'ø': 'o'
200
+ };
201
+ const convert_pat = new RegExp(Object.keys(latin_convert).join('|'), 'g');
202
+ /**
203
+ * code points generated from toCodePoints();
204
+ * removed 65339 to 65345
205
+ */
206
+
207
+ const code_points = [[67, 67], [160, 160], [192, 438], [452, 652], [961, 961], [1019, 1019], [1083, 1083], [1281, 1289], [1984, 1984], [5095, 5095], [7429, 7441], [7545, 7549], [7680, 7935], [8580, 8580], [9398, 9449], [11360, 11391], [42792, 42793], [42802, 42851], [42873, 42897], [42912, 42922], [64256, 64260], [65313, 65338], [65345, 65370]];
208
+ /**
209
+ * Remove accents
210
+ * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703
211
+ *
212
+ */
213
+
214
+ const asciifold = str => {
215
+ return str.normalize('NFKD').replace(accent_reg, '').toLowerCase().replace(convert_pat, function (foreignletter) {
216
+ return latin_convert[foreignletter];
217
+ });
218
+ };
219
+ /**
220
+ * Convert array of strings to a regular expression
221
+ * ex ['ab','a'] => (?:ab|a)
222
+ * ex ['a','b'] => [ab]
223
+ *
224
+ */
225
+
226
+
227
+ const arrayToPattern = (chars, glue = '|') => {
228
+ if (chars.length == 1) {
229
+ return chars[0];
230
+ }
231
+
232
+ var longest = 1;
233
+ chars.forEach(a => {
234
+ longest = Math.max(longest, a.length);
235
+ });
236
+
237
+ if (longest == 1) {
238
+ return '[' + chars.join('') + ']';
239
+ }
240
+
241
+ return '(?:' + chars.join(glue) + ')';
242
+ };
243
+ /**
244
+ * Get all possible combinations of substrings that add up to the given string
245
+ * https://stackoverflow.com/questions/30169587/find-all-the-combination-of-substrings-that-add-up-to-the-given-string
246
+ *
247
+ */
248
+
249
+ const allSubstrings = input => {
250
+ if (input.length === 1) return [[input]];
251
+ var result = [];
252
+ allSubstrings(input.substring(1)).forEach(function (subresult) {
253
+ var tmp = subresult.slice(0);
254
+ tmp[0] = input.charAt(0) + tmp[0];
255
+ result.push(tmp);
256
+ tmp = subresult.slice(0);
257
+ tmp.unshift(input.charAt(0));
258
+ result.push(tmp);
259
+ });
260
+ return result;
261
+ };
262
+ /**
263
+ * Generate a list of diacritics from the list of code points
264
+ *
265
+ */
266
+
267
+ const generateDiacritics = () => {
268
+ var diacritics = {};
269
+ code_points.forEach(code_range => {
270
+ for (let i = code_range[0]; i <= code_range[1]; i++) {
271
+ let diacritic = String.fromCharCode(i);
272
+ let latin = asciifold(diacritic);
273
+
274
+ if (latin == diacritic.toLowerCase()) {
275
+ continue;
276
+ }
277
+
278
+ if (!(latin in diacritics)) {
279
+ diacritics[latin] = [latin];
280
+ }
281
+
282
+ var patt = new RegExp(arrayToPattern(diacritics[latin]), 'iu');
283
+
284
+ if (diacritic.match(patt)) {
285
+ continue;
286
+ }
287
+
288
+ diacritics[latin].push(diacritic);
289
+ }
290
+ });
291
+ var latin_chars = Object.keys(diacritics); // latin character pattern
292
+ // match longer substrings first
293
+
294
+ latin_chars = latin_chars.sort((a, b) => b.length - a.length);
295
+ latin_pat = new RegExp('(' + arrayToPattern(latin_chars) + accent_pat + '*)', 'g'); // build diacritic patterns
296
+ // ae needs:
297
+ // (?:(?:ae|Æ|Ǽ|Ǣ)|(?:A|Ⓐ|A...)(?:E|ɛ|Ⓔ...))
298
+
299
+ var diacritic_patterns = {};
300
+ latin_chars.sort((a, b) => a.length - b.length).forEach(latin => {
301
+ var substrings = allSubstrings(latin);
302
+ var pattern = substrings.map(sub_pat => {
303
+ sub_pat = sub_pat.map(l => {
304
+ if (diacritics.hasOwnProperty(l)) {
305
+ return arrayToPattern(diacritics[l]);
306
+ }
307
+
308
+ return l;
309
+ });
310
+ return arrayToPattern(sub_pat, '');
311
+ });
312
+ diacritic_patterns[latin] = arrayToPattern(pattern);
313
+ });
314
+ return diacritic_patterns;
315
+ };
316
+ /**
317
+ * Expand a regular expression pattern to include diacritics
318
+ * eg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/
319
+ *
320
+ */
321
+
322
+ const diacriticRegexPoints = regex => {
323
+ if (diacritic_patterns === undefined) {
324
+ diacritic_patterns = generateDiacritics();
325
+ }
326
+
327
+ const decomposed = regex.normalize('NFKD').toLowerCase();
328
+ return decomposed.split(latin_pat).map(part => {
329
+ if (part == '') {
330
+ return '';
331
+ } // "ffl" or "ffl"
332
+
333
+
334
+ const no_accent = asciifold(part);
335
+
336
+ if (diacritic_patterns.hasOwnProperty(no_accent)) {
337
+ return diacritic_patterns[no_accent];
338
+ } // 'أهلا' (\u{623}\u{647}\u{644}\u{627}) or 'أهلا' (\u{627}\u{654}\u{647}\u{644}\u{627})
339
+
340
+
341
+ const composed_part = part.normalize('NFC');
342
+
343
+ if (composed_part != part) {
344
+ return arrayToPattern([part, composed_part]);
345
+ }
346
+
347
+ return part;
348
+ }).join('');
349
+ };
350
+
351
+ // @ts-ignore TS2691 "An import path cannot end with a '.ts' extension"
352
+
353
+ /**
354
+ * A property getter resolving dot-notation
355
+ * @param {Object} obj The root object to fetch property on
356
+ * @param {String} name The optionally dotted property name to fetch
357
+ * @return {Object} The resolved property value
358
+ */
359
+ const getAttr = (obj, name) => {
360
+ if (!obj) return;
361
+ return obj[name];
362
+ };
363
+ /**
364
+ * A property getter resolving dot-notation
365
+ * @param {Object} obj The root object to fetch property on
366
+ * @param {String} name The optionally dotted property name to fetch
367
+ * @return {Object} The resolved property value
368
+ */
369
+
370
+ const getAttrNesting = (obj, name) => {
371
+ if (!obj) return;
372
+ var part,
373
+ names = name.split(".");
374
+
375
+ while ((part = names.shift()) && (obj = obj[part]));
376
+
377
+ return obj;
378
+ };
379
+ /**
380
+ * Calculates how close of a match the
381
+ * given value is against a search token.
382
+ *
383
+ */
384
+
385
+ const scoreValue = (value, token, weight) => {
386
+ var score, pos;
387
+ if (!value) return 0;
388
+ value = value + '';
389
+ pos = value.search(token.regex);
390
+ if (pos === -1) return 0;
391
+ score = token.string.length / value.length;
392
+ if (pos === 0) score += 0.5;
393
+ return score * weight;
394
+ };
395
+ /**
396
+ *
397
+ * https://stackoverflow.com/questions/63006601/why-does-u-throw-an-invalid-escape-error
398
+ */
399
+
400
+ const escape_regex = str => {
401
+ return (str + '').replace(/([\$\(-\+\.\?\[-\^\{-\}])/g, '\\$1');
402
+ };
403
+ /**
404
+ * Cast object property to an array if it exists and has a value
405
+ *
406
+ */
407
+
408
+ const propToArray = (obj, key) => {
409
+ var value = obj[key];
410
+ if (typeof value == 'function') return value;
411
+
412
+ if (value && !Array.isArray(value)) {
413
+ obj[key] = [value];
414
+ }
415
+ };
416
+ /**
417
+ * Iterates over arrays and hashes.
418
+ *
419
+ * ```
420
+ * iterate(this.items, function(item, id) {
421
+ * // invoked for each item
422
+ * });
423
+ * ```
424
+ *
425
+ */
426
+
427
+ const iterate = (object, callback) => {
428
+ if (Array.isArray(object)) {
429
+ object.forEach(callback);
430
+ } else {
431
+ for (var key in object) {
432
+ if (object.hasOwnProperty(key)) {
433
+ callback(object[key], key);
434
+ }
435
+ }
436
+ }
437
+ };
438
+ const cmp = (a, b) => {
439
+ if (typeof a === 'number' && typeof b === 'number') {
440
+ return a > b ? 1 : a < b ? -1 : 0;
441
+ }
442
+
443
+ a = asciifold(a + '').toLowerCase();
444
+ b = asciifold(b + '').toLowerCase();
445
+ if (a > b) return 1;
446
+ if (b > a) return -1;
447
+ return 0;
448
+ };
449
+
450
+ /**
451
+ * sifter.js
452
+ * Copyright (c) 2013–2020 Brian Reavis & contributors
453
+ *
454
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
455
+ * file except in compliance with the License. You may obtain a copy of the License at:
456
+ * http://www.apache.org/licenses/LICENSE-2.0
457
+ *
458
+ * Unless required by applicable law or agreed to in writing, software distributed under
459
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
460
+ * ANY KIND, either express or implied. See the License for the specific language
461
+ * governing permissions and limitations under the License.
462
+ *
463
+ * @author Brian Reavis <brian@thirdroute.com>
464
+ */
465
+
466
+ class Sifter {
467
+ // []|{};
468
+
469
+ /**
470
+ * Textually searches arrays and hashes of objects
471
+ * by property (or multiple properties). Designed
472
+ * specifically for autocomplete.
473
+ *
474
+ */
475
+ constructor(items, settings) {
476
+ this.items = void 0;
477
+ this.settings = void 0;
478
+ this.items = items;
479
+ this.settings = settings || {
480
+ diacritics: true
481
+ };
482
+ }
483
+
484
+ /**
485
+ * Splits a search string into an array of individual
486
+ * regexps to be used to match results.
487
+ *
488
+ */
489
+ tokenize(query, respect_word_boundaries, weights) {
490
+ if (!query || !query.length) return [];
491
+ const tokens = [];
492
+ const words = query.split(/\s+/);
493
+ var field_regex;
494
+
495
+ if (weights) {
496
+ field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\:(.*)$');
497
+ }
498
+
499
+ words.forEach(word => {
500
+ let field_match;
501
+ let field = null;
502
+ let regex = null; // look for "field:query" tokens
503
+
504
+ if (field_regex && (field_match = word.match(field_regex))) {
505
+ field = field_match[1];
506
+ word = field_match[2];
507
+ }
508
+
509
+ if (word.length > 0) {
510
+ regex = escape_regex(word);
511
+
512
+ if (this.settings.diacritics) {
513
+ regex = diacriticRegexPoints(regex);
514
+ }
515
+
516
+ if (respect_word_boundaries) regex = "\\b" + regex;
517
+ }
518
+
519
+ tokens.push({
520
+ string: word,
521
+ regex: regex ? new RegExp(regex, 'iu') : null,
522
+ field: field
523
+ });
524
+ });
525
+ return tokens;
526
+ }
527
+
528
+ /**
529
+ * Returns a function to be used to score individual results.
530
+ *
531
+ * Good matches will have a higher score than poor matches.
532
+ * If an item is not a match, 0 will be returned by the function.
533
+ *
534
+ * @returns {function}
535
+ */
536
+ getScoreFunction(query, options) {
537
+ var search = this.prepareSearch(query, options);
538
+ return this._getScoreFunction(search);
539
+ }
540
+
541
+ _getScoreFunction(search) {
542
+ const tokens = search.tokens,
543
+ token_count = tokens.length;
544
+
545
+ if (!token_count) {
546
+ return function () {
547
+ return 0;
548
+ };
549
+ }
550
+
551
+ const fields = search.options.fields,
552
+ weights = search.weights,
553
+ field_count = fields.length,
554
+ getAttrFn = search.getAttrFn;
555
+
556
+ if (!field_count) {
557
+ return function () {
558
+ return 1;
559
+ };
560
+ }
561
+ /**
562
+ * Calculates the score of an object
563
+ * against the search query.
564
+ *
565
+ */
566
+
567
+
568
+ const scoreObject = function () {
569
+ if (field_count === 1) {
570
+ return function (token, data) {
571
+ const field = fields[0].field;
572
+ return scoreValue(getAttrFn(data, field), token, weights[field]);
573
+ };
574
+ }
575
+
576
+ return function (token, data) {
577
+ var sum = 0; // is the token specific to a field?
578
+
579
+ if (token.field) {
580
+ const value = getAttrFn(data, token.field);
581
+
582
+ if (!token.regex && value) {
583
+ sum += 1 / field_count;
584
+ } else {
585
+ sum += scoreValue(value, token, 1);
586
+ }
587
+ } else {
588
+ iterate(weights, (weight, field) => {
589
+ sum += scoreValue(getAttrFn(data, field), token, weight);
590
+ });
591
+ }
592
+
593
+ return sum / field_count;
594
+ };
595
+ }();
596
+
597
+ if (token_count === 1) {
598
+ return function (data) {
599
+ return scoreObject(tokens[0], data);
600
+ };
601
+ }
602
+
603
+ if (search.options.conjunction === 'and') {
604
+ return function (data) {
605
+ var i = 0,
606
+ score,
607
+ sum = 0;
608
+
609
+ for (; i < token_count; i++) {
610
+ score = scoreObject(tokens[i], data);
611
+ if (score <= 0) return 0;
612
+ sum += score;
613
+ }
614
+
615
+ return sum / token_count;
616
+ };
617
+ } else {
618
+ return function (data) {
619
+ var sum = 0;
620
+ iterate(tokens, token => {
621
+ sum += scoreObject(token, data);
622
+ });
623
+ return sum / token_count;
624
+ };
625
+ }
626
+ }
627
+
628
+ /**
629
+ * Returns a function that can be used to compare two
630
+ * results, for sorting purposes. If no sorting should
631
+ * be performed, `null` will be returned.
632
+ *
633
+ * @return function(a,b)
634
+ */
635
+ getSortFunction(query, options) {
636
+ var search = this.prepareSearch(query, options);
637
+ return this._getSortFunction(search);
638
+ }
639
+
640
+ _getSortFunction(search) {
641
+ var i, n, implicit_score;
642
+ const self = this,
643
+ options = search.options,
644
+ sort = !search.query && options.sort_empty ? options.sort_empty : options.sort,
645
+ sort_flds = [],
646
+ multipliers = [];
647
+
648
+ if (typeof sort == 'function') {
649
+ return sort.bind(this);
650
+ }
651
+ /**
652
+ * Fetches the specified sort field value
653
+ * from a search result item.
654
+ *
655
+ */
656
+
657
+
658
+ const get_field = function get_field(name, result) {
659
+ if (name === '$score') return result.score;
660
+ return search.getAttrFn(self.items[result.id], name);
661
+ }; // parse options
662
+
663
+
664
+ if (sort) {
665
+ for (i = 0, n = sort.length; i < n; i++) {
666
+ if (search.query || sort[i].field !== '$score') {
667
+ sort_flds.push(sort[i]);
668
+ }
669
+ }
670
+ } // the "$score" field is implied to be the primary
671
+ // sort field, unless it's manually specified
672
+
673
+
674
+ if (search.query) {
675
+ implicit_score = true;
676
+
677
+ for (i = 0, n = sort_flds.length; i < n; i++) {
678
+ if (sort_flds[i].field === '$score') {
679
+ implicit_score = false;
680
+ break;
681
+ }
682
+ }
683
+
684
+ if (implicit_score) {
685
+ sort_flds.unshift({
686
+ field: '$score',
687
+ direction: 'desc'
688
+ });
689
+ }
690
+ } else {
691
+ for (i = 0, n = sort_flds.length; i < n; i++) {
692
+ if (sort_flds[i].field === '$score') {
693
+ sort_flds.splice(i, 1);
694
+ break;
695
+ }
696
+ }
697
+ }
698
+
699
+ for (i = 0, n = sort_flds.length; i < n; i++) {
700
+ multipliers.push(sort_flds[i].direction === 'desc' ? -1 : 1);
701
+ } // build function
702
+
703
+
704
+ const sort_flds_count = sort_flds.length;
705
+
706
+ if (!sort_flds_count) {
707
+ return null;
708
+ } else if (sort_flds_count === 1) {
709
+ const sort_fld = sort_flds[0].field;
710
+ const multiplier = multipliers[0];
711
+ return function (a, b) {
712
+ return multiplier * cmp(get_field(sort_fld, a), get_field(sort_fld, b));
713
+ };
714
+ } else {
715
+ return function (a, b) {
716
+ var i, result, field;
717
+
718
+ for (i = 0; i < sort_flds_count; i++) {
719
+ field = sort_flds[i].field;
720
+ result = multipliers[i] * cmp(get_field(field, a), get_field(field, b));
721
+ if (result) return result;
722
+ }
723
+
724
+ return 0;
725
+ };
726
+ }
727
+ }
728
+
729
+ /**
730
+ * Parses a search query and returns an object
731
+ * with tokens and fields ready to be populated
732
+ * with results.
733
+ *
734
+ */
735
+ prepareSearch(query, optsUser) {
736
+ const weights = {};
737
+ var options = Object.assign({}, optsUser);
738
+ propToArray(options, 'sort');
739
+ propToArray(options, 'sort_empty'); // convert fields to new format
740
+
741
+ if (options.fields) {
742
+ propToArray(options, 'fields');
743
+ const fields = [];
744
+ options.fields.forEach(field => {
745
+ if (typeof field == 'string') {
746
+ field = {
747
+ field: field,
748
+ weight: 1
749
+ };
750
+ }
751
+
752
+ fields.push(field);
753
+ weights[field.field] = 'weight' in field ? field.weight : 1;
754
+ });
755
+ options.fields = fields;
756
+ }
757
+
758
+ return {
759
+ options: options,
760
+ query: query.toLowerCase().trim(),
761
+ tokens: this.tokenize(query, options.respect_word_boundaries, weights),
762
+ total: 0,
763
+ items: [],
764
+ weights: weights,
765
+ getAttrFn: options.nesting ? getAttrNesting : getAttr
766
+ };
767
+ }
768
+
769
+ /**
770
+ * Searches through all items and returns a sorted array of matches.
771
+ *
772
+ */
773
+ search(query, options) {
774
+ var self = this,
775
+ score,
776
+ search;
777
+ search = this.prepareSearch(query, options);
778
+ options = search.options;
779
+ query = search.query; // generate result scoring function
780
+
781
+ const fn_score = options.score || self._getScoreFunction(search); // perform search and sort
782
+
783
+
784
+ if (query.length) {
785
+ iterate(self.items, (item, id) => {
786
+ score = fn_score(item);
787
+
788
+ if (options.filter === false || score > 0) {
789
+ search.items.push({
790
+ 'score': score,
791
+ 'id': id
792
+ });
793
+ }
794
+ });
795
+ } else {
796
+ iterate(self.items, (_, id) => {
797
+ search.items.push({
798
+ 'score': 1,
799
+ 'id': id
800
+ });
801
+ });
802
+ }
803
+
804
+ const fn_sort = self._getSortFunction(search);
805
+
806
+ if (fn_sort) search.items.sort(fn_sort); // apply limits
807
+
808
+ search.total = search.items.length;
809
+
810
+ if (typeof options.limit === 'number') {
811
+ search.items = search.items.slice(0, options.limit);
812
+ }
813
+
814
+ return search;
815
+ }
816
+
817
+ }
818
+
819
+ /**
820
+ * Return a dom element from either a dom query string, jQuery object, a dom element or html string
821
+ * https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
822
+ *
823
+ * param query should be {}
824
+ */
825
+
826
+ const getDom = query => {
827
+ if (query.jquery) {
828
+ return query[0];
829
+ }
830
+
831
+ if (query instanceof HTMLElement) {
832
+ return query;
833
+ }
834
+
835
+ if (isHtmlString(query)) {
836
+ let div = document.createElement('div');
837
+ div.innerHTML = query.trim(); // Never return a text node of whitespace as the result
838
+
839
+ return div.firstChild;
840
+ }
841
+
842
+ return document.querySelector(query);
843
+ };
844
+ const isHtmlString = arg => {
845
+ if (typeof arg === 'string' && arg.indexOf('<') > -1) {
846
+ return true;
847
+ }
848
+
849
+ return false;
850
+ };
851
+ const escapeQuery = query => {
852
+ return query.replace(/['"\\]/g, '\\$&');
853
+ };
854
+ /**
855
+ * Dispatch an event
856
+ *
857
+ */
858
+
859
+ const triggerEvent = (dom_el, event_name) => {
860
+ var event = document.createEvent('HTMLEvents');
861
+ event.initEvent(event_name, true, false);
862
+ dom_el.dispatchEvent(event);
863
+ };
864
+ /**
865
+ * Apply CSS rules to a dom element
866
+ *
867
+ */
868
+
869
+ const applyCSS = (dom_el, css) => {
870
+ Object.assign(dom_el.style, css);
871
+ };
872
+ /**
873
+ * Add css classes
874
+ *
875
+ */
876
+
877
+ const addClasses = (elmts, ...classes) => {
878
+ var norm_classes = classesArray(classes);
879
+ elmts = castAsArray(elmts);
880
+ elmts.map(el => {
881
+ norm_classes.map(cls => {
882
+ el.classList.add(cls);
883
+ });
884
+ });
885
+ };
886
+ /**
887
+ * Remove css classes
888
+ *
889
+ */
890
+
891
+ const removeClasses = (elmts, ...classes) => {
892
+ var norm_classes = classesArray(classes);
893
+ elmts = castAsArray(elmts);
894
+ elmts.map(el => {
895
+ norm_classes.map(cls => {
896
+ el.classList.remove(cls);
897
+ });
898
+ });
899
+ };
900
+ /**
901
+ * Return arguments
902
+ *
903
+ */
904
+
905
+ const classesArray = args => {
906
+ var classes = [];
907
+ iterate(args, _classes => {
908
+ if (typeof _classes === 'string') {
909
+ _classes = _classes.trim().split(/[\11\12\14\15\40]/);
910
+ }
911
+
912
+ if (Array.isArray(_classes)) {
913
+ classes = classes.concat(_classes);
914
+ }
915
+ });
916
+ return classes.filter(Boolean);
917
+ };
918
+ /**
919
+ * Create an array from arg if it's not already an array
920
+ *
921
+ */
922
+
923
+ const castAsArray = arg => {
924
+ if (!Array.isArray(arg)) {
925
+ arg = [arg];
926
+ }
927
+
928
+ return arg;
929
+ };
930
+ /**
931
+ * Get the closest node to the evt.target matching the selector
932
+ * Stops at wrapper
933
+ *
934
+ */
935
+
936
+ const parentMatch = (target, selector, wrapper) => {
937
+ if (wrapper && !wrapper.contains(target)) {
938
+ return;
939
+ }
940
+
941
+ while (target && target.matches) {
942
+ if (target.matches(selector)) {
943
+ return target;
944
+ }
945
+
946
+ target = target.parentNode;
947
+ }
948
+ };
949
+ /**
950
+ * Get the first or last item from an array
951
+ *
952
+ * > 0 - right (last)
953
+ * <= 0 - left (first)
954
+ *
955
+ */
956
+
957
+ const getTail = (list, direction = 0) => {
958
+ if (direction > 0) {
959
+ return list[list.length - 1];
960
+ }
961
+
962
+ return list[0];
963
+ };
964
+ /**
965
+ * Return true if an object is empty
966
+ *
967
+ */
968
+
969
+ const isEmptyObject = obj => {
970
+ return Object.keys(obj).length === 0;
971
+ };
972
+ /**
973
+ * Get the index of an element amongst sibling nodes of the same type
974
+ *
975
+ */
976
+
977
+ const nodeIndex = (el, amongst) => {
978
+ if (!el) return -1;
979
+ amongst = amongst || el.nodeName;
980
+ var i = 0;
981
+
982
+ while (el = el.previousElementSibling) {
983
+ if (el.matches(amongst)) {
984
+ i++;
985
+ }
986
+ }
987
+
988
+ return i;
989
+ };
990
+ /**
991
+ * Set attributes of an element
992
+ *
993
+ */
994
+
995
+ const setAttr = (el, attrs) => {
996
+ iterate(attrs, (val, attr) => {
997
+ if (val == null) {
998
+ el.removeAttribute(attr);
999
+ } else {
1000
+ el.setAttribute(attr, '' + val);
1001
+ }
1002
+ });
1003
+ };
1004
+ /**
1005
+ * Replace a node
1006
+ */
1007
+
1008
+ const replaceNode = (existing, replacement) => {
1009
+ if (existing.parentNode) existing.parentNode.replaceChild(replacement, existing);
1010
+ };
1011
+
1012
+ /**
1013
+ * highlight v3 | MIT license | Johann Burkard <jb@eaio.com>
1014
+ * Highlights arbitrary terms in a node.
1015
+ *
1016
+ * - Modified by Marshal <beatgates@gmail.com> 2011-6-24 (added regex)
1017
+ * - Modified by Brian Reavis <brian@thirdroute.com> 2012-8-27 (cleanup)
1018
+ */
1019
+ const highlight = (element, regex) => {
1020
+ if (regex === null) return; // convet string to regex
1021
+
1022
+ if (typeof regex === 'string') {
1023
+ if (!regex.length) return;
1024
+ regex = new RegExp(regex, 'i');
1025
+ } // Wrap matching part of text node with highlighting <span>, e.g.
1026
+ // Soccer -> <span class="highlight">Soc</span>cer for regex = /soc/i
1027
+
1028
+
1029
+ const highlightText = node => {
1030
+ var match = node.data.match(regex);
1031
+
1032
+ if (match && node.data.length > 0) {
1033
+ var spannode = document.createElement('span');
1034
+ spannode.className = 'highlight';
1035
+ var middlebit = node.splitText(match.index);
1036
+ middlebit.splitText(match[0].length);
1037
+ var middleclone = middlebit.cloneNode(true);
1038
+ spannode.appendChild(middleclone);
1039
+ replaceNode(middlebit, spannode);
1040
+ return 1;
1041
+ }
1042
+
1043
+ return 0;
1044
+ }; // Recurse element node, looking for child text nodes to highlight, unless element
1045
+ // is childless, <script>, <style>, or already highlighted: <span class="hightlight">
1046
+
1047
+
1048
+ const highlightChildren = node => {
1049
+ if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && (node.className !== 'highlight' || node.tagName !== 'SPAN')) {
1050
+ for (var i = 0; i < node.childNodes.length; ++i) {
1051
+ i += highlightRecursive(node.childNodes[i]);
1052
+ }
1053
+ }
1054
+ };
1055
+
1056
+ const highlightRecursive = node => {
1057
+ if (node.nodeType === 3) {
1058
+ return highlightText(node);
1059
+ }
1060
+
1061
+ highlightChildren(node);
1062
+ return 0;
1063
+ };
1064
+
1065
+ highlightRecursive(element);
1066
+ };
1067
+ /**
1068
+ * removeHighlight fn copied from highlight v5 and
1069
+ * edited to remove with(), pass js strict mode, and use without jquery
1070
+ */
1071
+
1072
+ const removeHighlight = el => {
1073
+ var elements = el.querySelectorAll("span.highlight");
1074
+ Array.prototype.forEach.call(elements, function (el) {
1075
+ var parent = el.parentNode;
1076
+ parent.replaceChild(el.firstChild, el);
1077
+ parent.normalize();
1078
+ });
1079
+ };
1080
+
1081
+ const KEY_A = 65;
1082
+ const KEY_RETURN = 13;
1083
+ const KEY_ESC = 27;
1084
+ const KEY_LEFT = 37;
1085
+ const KEY_UP = 38;
1086
+ const KEY_RIGHT = 39;
1087
+ const KEY_DOWN = 40;
1088
+ const KEY_BACKSPACE = 8;
1089
+ const KEY_DELETE = 46;
1090
+ const KEY_TAB = 9;
1091
+ const IS_MAC = typeof navigator === 'undefined' ? false : /Mac/.test(navigator.userAgent);
1092
+ const KEY_SHORTCUT = IS_MAC ? 'metaKey' : 'ctrlKey'; // ctrl key or apple key for ma
1093
+
1094
+ var defaults = {
1095
+ options: [],
1096
+ optgroups: [],
1097
+ plugins: [],
1098
+ delimiter: ',',
1099
+ splitOn: null,
1100
+ // regexp or string for splitting up values from a paste command
1101
+ persist: true,
1102
+ diacritics: true,
1103
+ create: null,
1104
+ createOnBlur: false,
1105
+ createFilter: null,
1106
+ highlight: true,
1107
+ openOnFocus: true,
1108
+ shouldOpen: null,
1109
+ maxOptions: 50,
1110
+ maxItems: null,
1111
+ hideSelected: null,
1112
+ duplicates: false,
1113
+ addPrecedence: false,
1114
+ selectOnTab: false,
1115
+ preload: null,
1116
+ allowEmptyOption: false,
1117
+ //closeAfterSelect: false,
1118
+ loadThrottle: 300,
1119
+ loadingClass: 'loading',
1120
+ dataAttr: null,
1121
+ //'data-data',
1122
+ optgroupField: 'optgroup',
1123
+ valueField: 'value',
1124
+ labelField: 'text',
1125
+ disabledField: 'disabled',
1126
+ optgroupLabelField: 'label',
1127
+ optgroupValueField: 'value',
1128
+ lockOptgroupOrder: false,
1129
+ sortField: '$order',
1130
+ searchField: ['text'],
1131
+ searchConjunction: 'and',
1132
+ mode: null,
1133
+ wrapperClass: 'ts-wrapper',
1134
+ controlClass: 'ts-control',
1135
+ dropdownClass: 'ts-dropdown',
1136
+ dropdownContentClass: 'ts-dropdown-content',
1137
+ itemClass: 'item',
1138
+ optionClass: 'option',
1139
+ dropdownParent: null,
1140
+ controlInput: '<input type="text" autocomplete="off" size="1" />',
1141
+ copyClassesToDropdown: false,
1142
+ placeholder: null,
1143
+ hidePlaceholder: null,
1144
+ shouldLoad: function (query) {
1145
+ return query.length > 0;
1146
+ },
1147
+
1148
+ /*
1149
+ load : null, // function(query, callback) { ... }
1150
+ score : null, // function(search) { ... }
1151
+ onInitialize : null, // function() { ... }
1152
+ onChange : null, // function(value) { ... }
1153
+ onItemAdd : null, // function(value, $item) { ... }
1154
+ onItemRemove : null, // function(value) { ... }
1155
+ onClear : null, // function() { ... }
1156
+ onOptionAdd : null, // function(value, data) { ... }
1157
+ onOptionRemove : null, // function(value) { ... }
1158
+ onOptionClear : null, // function() { ... }
1159
+ onOptionGroupAdd : null, // function(id, data) { ... }
1160
+ onOptionGroupRemove : null, // function(id) { ... }
1161
+ onOptionGroupClear : null, // function() { ... }
1162
+ onDropdownOpen : null, // function(dropdown) { ... }
1163
+ onDropdownClose : null, // function(dropdown) { ... }
1164
+ onType : null, // function(str) { ... }
1165
+ onDelete : null, // function(values) { ... }
1166
+ */
1167
+ render: {
1168
+ /*
1169
+ item: null,
1170
+ optgroup: null,
1171
+ optgroup_header: null,
1172
+ option: null,
1173
+ option_create: null
1174
+ */
1175
+ }
1176
+ };
1177
+
1178
+ /**
1179
+ * Converts a scalar to its best string representation
1180
+ * for hash keys and HTML attribute values.
1181
+ *
1182
+ * Transformations:
1183
+ * 'str' -> 'str'
1184
+ * null -> ''
1185
+ * undefined -> ''
1186
+ * true -> '1'
1187
+ * false -> '0'
1188
+ * 0 -> '0'
1189
+ * 1 -> '1'
1190
+ *
1191
+ */
1192
+ const hash_key = value => {
1193
+ if (typeof value === 'undefined' || value === null) return null;
1194
+ return get_hash(value);
1195
+ };
1196
+ const get_hash = value => {
1197
+ if (typeof value === 'boolean') return value ? '1' : '0';
1198
+ return value + '';
1199
+ };
1200
+ /**
1201
+ * Escapes a string for use within HTML.
1202
+ *
1203
+ */
1204
+
1205
+ const escape_html = str => {
1206
+ return (str + '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
1207
+ };
1208
+ /**
1209
+ * Debounce the user provided load function
1210
+ *
1211
+ */
1212
+
1213
+ const loadDebounce = (fn, delay) => {
1214
+ var timeout;
1215
+ return function (value, callback) {
1216
+ var self = this;
1217
+
1218
+ if (timeout) {
1219
+ self.loading = Math.max(self.loading - 1, 0);
1220
+ clearTimeout(timeout);
1221
+ }
1222
+
1223
+ timeout = setTimeout(function () {
1224
+ timeout = null;
1225
+ self.loadedSearches[value] = true;
1226
+ fn.call(self, value, callback);
1227
+ }, delay);
1228
+ };
1229
+ };
1230
+ /**
1231
+ * Debounce all fired events types listed in `types`
1232
+ * while executing the provided `fn`.
1233
+ *
1234
+ */
1235
+
1236
+ const debounce_events = (self, types, fn) => {
1237
+ var type;
1238
+ var trigger = self.trigger;
1239
+ var event_args = {}; // override trigger method
1240
+
1241
+ self.trigger = function () {
1242
+ var type = arguments[0];
1243
+
1244
+ if (types.indexOf(type) !== -1) {
1245
+ event_args[type] = arguments;
1246
+ } else {
1247
+ return trigger.apply(self, arguments);
1248
+ }
1249
+ }; // invoke provided function
1250
+
1251
+
1252
+ fn.apply(self, []);
1253
+ self.trigger = trigger; // trigger queued events
1254
+
1255
+ for (type of types) {
1256
+ if (type in event_args) {
1257
+ trigger.apply(self, event_args[type]);
1258
+ }
1259
+ }
1260
+ };
1261
+ /**
1262
+ * Determines the current selection within a text input control.
1263
+ * Returns an object containing:
1264
+ * - start
1265
+ * - length
1266
+ *
1267
+ */
1268
+
1269
+ const getSelection = input => {
1270
+ return {
1271
+ start: input.selectionStart || 0,
1272
+ length: (input.selectionEnd || 0) - (input.selectionStart || 0)
1273
+ };
1274
+ };
1275
+ /**
1276
+ * Prevent default
1277
+ *
1278
+ */
1279
+
1280
+ const preventDefault = (evt, stop = false) => {
1281
+ if (evt) {
1282
+ evt.preventDefault();
1283
+
1284
+ if (stop) {
1285
+ evt.stopPropagation();
1286
+ }
1287
+ }
1288
+ };
1289
+ /**
1290
+ * Prevent default
1291
+ *
1292
+ */
1293
+
1294
+ const addEvent = (target, type, callback, options) => {
1295
+ target.addEventListener(type, callback, options);
1296
+ };
1297
+ /**
1298
+ * Return true if the requested key is down
1299
+ * Will return false if more than one control character is pressed ( when [ctrl+shift+a] != [ctrl+a] )
1300
+ * The current evt may not always set ( eg calling advanceSelection() )
1301
+ *
1302
+ */
1303
+
1304
+ const isKeyDown = (key_name, evt) => {
1305
+ if (!evt) {
1306
+ return false;
1307
+ }
1308
+
1309
+ if (!evt[key_name]) {
1310
+ return false;
1311
+ }
1312
+
1313
+ var count = (evt.altKey ? 1 : 0) + (evt.ctrlKey ? 1 : 0) + (evt.shiftKey ? 1 : 0) + (evt.metaKey ? 1 : 0);
1314
+
1315
+ if (count === 1) {
1316
+ return true;
1317
+ }
1318
+
1319
+ return false;
1320
+ };
1321
+ /**
1322
+ * Get the id of an element
1323
+ * If the id attribute is not set, set the attribute with the given id
1324
+ *
1325
+ */
1326
+
1327
+ const getId = (el, id) => {
1328
+ const existing_id = el.getAttribute('id');
1329
+
1330
+ if (existing_id) {
1331
+ return existing_id;
1332
+ }
1333
+
1334
+ el.setAttribute('id', id);
1335
+ return id;
1336
+ };
1337
+ /**
1338
+ * Returns a string with backslashes added before characters that need to be escaped.
1339
+ */
1340
+
1341
+ const addSlashes = str => {
1342
+ return str.replace(/[\\"']/g, '\\$&');
1343
+ };
1344
+ /**
1345
+ *
1346
+ */
1347
+
1348
+ const append = (parent, node) => {
1349
+ if (node) parent.append(node);
1350
+ };
1351
+
1352
+ function getSettings(input, settings_user) {
1353
+ var settings = Object.assign({}, defaults, settings_user);
1354
+ var attr_data = settings.dataAttr;
1355
+ var field_label = settings.labelField;
1356
+ var field_value = settings.valueField;
1357
+ var field_disabled = settings.disabledField;
1358
+ var field_optgroup = settings.optgroupField;
1359
+ var field_optgroup_label = settings.optgroupLabelField;
1360
+ var field_optgroup_value = settings.optgroupValueField;
1361
+ var tag_name = input.tagName.toLowerCase();
1362
+ var placeholder = input.getAttribute('placeholder') || input.getAttribute('data-placeholder');
1363
+
1364
+ if (!placeholder && !settings.allowEmptyOption) {
1365
+ let option = input.querySelector('option[value=""]');
1366
+
1367
+ if (option) {
1368
+ placeholder = option.textContent;
1369
+ }
1370
+ }
1371
+
1372
+ var settings_element = {
1373
+ placeholder: placeholder,
1374
+ options: [],
1375
+ optgroups: [],
1376
+ items: [],
1377
+ maxItems: null
1378
+ };
1379
+ /**
1380
+ * Initialize from a <select> element.
1381
+ *
1382
+ */
1383
+
1384
+ var init_select = () => {
1385
+ var tagName;
1386
+ var options = settings_element.options;
1387
+ var optionsMap = {};
1388
+ var group_count = 1;
1389
+
1390
+ var readData = el => {
1391
+ var data = Object.assign({}, el.dataset); // get plain object from DOMStringMap
1392
+
1393
+ var json = attr_data && data[attr_data];
1394
+
1395
+ if (typeof json === 'string' && json.length) {
1396
+ data = Object.assign(data, JSON.parse(json));
1397
+ }
1398
+
1399
+ return data;
1400
+ };
1401
+
1402
+ var addOption = (option, group) => {
1403
+ var value = hash_key(option.value);
1404
+ if (value == null) return;
1405
+ if (!value && !settings.allowEmptyOption) return; // if the option already exists, it's probably been
1406
+ // duplicated in another optgroup. in this case, push
1407
+ // the current group to the "optgroup" property on the
1408
+ // existing option so that it's rendered in both places.
1409
+
1410
+ if (optionsMap.hasOwnProperty(value)) {
1411
+ if (group) {
1412
+ var arr = optionsMap[value][field_optgroup];
1413
+
1414
+ if (!arr) {
1415
+ optionsMap[value][field_optgroup] = group;
1416
+ } else if (!Array.isArray(arr)) {
1417
+ optionsMap[value][field_optgroup] = [arr, group];
1418
+ } else {
1419
+ arr.push(group);
1420
+ }
1421
+ }
1422
+ } else {
1423
+ var option_data = readData(option);
1424
+ option_data[field_label] = option_data[field_label] || option.textContent;
1425
+ option_data[field_value] = option_data[field_value] || value;
1426
+ option_data[field_disabled] = option_data[field_disabled] || option.disabled;
1427
+ option_data[field_optgroup] = option_data[field_optgroup] || group;
1428
+ option_data.$option = option;
1429
+ optionsMap[value] = option_data;
1430
+ options.push(option_data);
1431
+ }
1432
+
1433
+ if (option.selected) {
1434
+ settings_element.items.push(value);
1435
+ }
1436
+ };
1437
+
1438
+ var addGroup = optgroup => {
1439
+ var id, optgroup_data;
1440
+ optgroup_data = readData(optgroup);
1441
+ optgroup_data[field_optgroup_label] = optgroup_data[field_optgroup_label] || optgroup.getAttribute('label') || '';
1442
+ optgroup_data[field_optgroup_value] = optgroup_data[field_optgroup_value] || group_count++;
1443
+ optgroup_data[field_disabled] = optgroup_data[field_disabled] || optgroup.disabled;
1444
+ settings_element.optgroups.push(optgroup_data);
1445
+ id = optgroup_data[field_optgroup_value];
1446
+ iterate(optgroup.children, option => {
1447
+ addOption(option, id);
1448
+ });
1449
+ };
1450
+
1451
+ settings_element.maxItems = input.hasAttribute('multiple') ? null : 1;
1452
+ iterate(input.children, child => {
1453
+ tagName = child.tagName.toLowerCase();
1454
+
1455
+ if (tagName === 'optgroup') {
1456
+ addGroup(child);
1457
+ } else if (tagName === 'option') {
1458
+ addOption(child);
1459
+ }
1460
+ });
1461
+ };
1462
+ /**
1463
+ * Initialize from a <input type="text"> element.
1464
+ *
1465
+ */
1466
+
1467
+
1468
+ var init_textbox = () => {
1469
+ const data_raw = input.getAttribute(attr_data);
1470
+
1471
+ if (!data_raw) {
1472
+ var value = input.value.trim() || '';
1473
+ if (!settings.allowEmptyOption && !value.length) return;
1474
+ const values = value.split(settings.delimiter);
1475
+ iterate(values, value => {
1476
+ const option = {};
1477
+ option[field_label] = value;
1478
+ option[field_value] = value;
1479
+ settings_element.options.push(option);
1480
+ });
1481
+ settings_element.items = values;
1482
+ } else {
1483
+ settings_element.options = JSON.parse(data_raw);
1484
+ iterate(settings_element.options, opt => {
1485
+ settings_element.items.push(opt[field_value]);
1486
+ });
1487
+ }
1488
+ };
1489
+
1490
+ if (tag_name === 'select') {
1491
+ init_select();
1492
+ } else {
1493
+ init_textbox();
1494
+ }
1495
+
1496
+ return Object.assign({}, defaults, settings_element, settings_user);
1497
+ }
1498
+
1499
+ var instance_i = 0;
1500
+ class TomSelect extends MicroPlugin(MicroEvent) {
1501
+ // @deprecated 1.8
1502
+ constructor(input_arg, user_settings) {
1503
+ super();
1504
+ this.control_input = void 0;
1505
+ this.wrapper = void 0;
1506
+ this.dropdown = void 0;
1507
+ this.control = void 0;
1508
+ this.dropdown_content = void 0;
1509
+ this.focus_node = void 0;
1510
+ this.order = 0;
1511
+ this.settings = void 0;
1512
+ this.input = void 0;
1513
+ this.tabIndex = void 0;
1514
+ this.is_select_tag = void 0;
1515
+ this.rtl = void 0;
1516
+ this.inputId = void 0;
1517
+ this._destroy = void 0;
1518
+ this.sifter = void 0;
1519
+ this.isOpen = false;
1520
+ this.isDisabled = false;
1521
+ this.isRequired = void 0;
1522
+ this.isInvalid = false;
1523
+ this.isValid = true;
1524
+ this.isLocked = false;
1525
+ this.isFocused = false;
1526
+ this.isInputHidden = false;
1527
+ this.isSetup = false;
1528
+ this.ignoreFocus = false;
1529
+ this.hasOptions = false;
1530
+ this.currentResults = void 0;
1531
+ this.lastValue = '';
1532
+ this.caretPos = 0;
1533
+ this.loading = 0;
1534
+ this.loadedSearches = {};
1535
+ this.activeOption = null;
1536
+ this.activeItems = [];
1537
+ this.optgroups = {};
1538
+ this.options = {};
1539
+ this.userOptions = {};
1540
+ this.items = [];
1541
+ instance_i++;
1542
+ var dir;
1543
+ var input = getDom(input_arg);
1544
+
1545
+ if (input.tomselect) {
1546
+ throw new Error('Tom Select already initialized on this element');
1547
+ }
1548
+
1549
+ input.tomselect = this; // detect rtl environment
1550
+
1551
+ var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null);
1552
+ dir = computedStyle.getPropertyValue('direction'); // setup default state
1553
+
1554
+ const settings = getSettings(input, user_settings);
1555
+ this.settings = settings;
1556
+ this.input = input;
1557
+ this.tabIndex = input.tabIndex || 0;
1558
+ this.is_select_tag = input.tagName.toLowerCase() === 'select';
1559
+ this.rtl = /rtl/i.test(dir);
1560
+ this.inputId = getId(input, 'tomselect-' + instance_i);
1561
+ this.isRequired = input.required; // search system
1562
+
1563
+ this.sifter = new Sifter(this.options, {
1564
+ diacritics: settings.diacritics
1565
+ }); // option-dependent defaults
1566
+
1567
+ settings.mode = settings.mode || (settings.maxItems === 1 ? 'single' : 'multi');
1568
+
1569
+ if (typeof settings.hideSelected !== 'boolean') {
1570
+ settings.hideSelected = settings.mode === 'multi';
1571
+ }
1572
+
1573
+ if (typeof settings.hidePlaceholder !== 'boolean') {
1574
+ settings.hidePlaceholder = settings.mode !== 'multi';
1575
+ } // set up createFilter callback
1576
+
1577
+
1578
+ var filter = settings.createFilter;
1579
+
1580
+ if (typeof filter !== 'function') {
1581
+ if (typeof filter === 'string') {
1582
+ filter = new RegExp(filter);
1583
+ }
1584
+
1585
+ if (filter instanceof RegExp) {
1586
+ settings.createFilter = input => filter.test(input);
1587
+ } else {
1588
+ settings.createFilter = value => {
1589
+ return this.settings.duplicates || !this.options[value];
1590
+ };
1591
+ }
1592
+ }
1593
+
1594
+ this.initializePlugins(settings.plugins);
1595
+ this.setupCallbacks();
1596
+ this.setupTemplates(); // Create all elements
1597
+
1598
+ const wrapper = getDom('<div>');
1599
+ const control = getDom('<div>');
1600
+
1601
+ const dropdown = this._render('dropdown');
1602
+
1603
+ const dropdown_content = getDom(`<div role="listbox" tabindex="-1">`);
1604
+ const classes = this.input.getAttribute('class') || '';
1605
+ const inputMode = settings.mode;
1606
+ var control_input;
1607
+ addClasses(wrapper, settings.wrapperClass, classes, inputMode);
1608
+ addClasses(control, settings.controlClass);
1609
+ append(wrapper, control);
1610
+ addClasses(dropdown, settings.dropdownClass, inputMode);
1611
+
1612
+ if (settings.copyClassesToDropdown) {
1613
+ addClasses(dropdown, classes);
1614
+ }
1615
+
1616
+ addClasses(dropdown_content, settings.dropdownContentClass);
1617
+ append(dropdown, dropdown_content);
1618
+ getDom(settings.dropdownParent || wrapper).appendChild(dropdown); // default controlInput
1619
+
1620
+ if (isHtmlString(settings.controlInput)) {
1621
+ control_input = getDom(settings.controlInput); // set attributes
1622
+
1623
+ var attrs = ['autocorrect', 'autocapitalize', 'autocomplete'];
1624
+ iterate(attrs, attr => {
1625
+ if (input.getAttribute(attr)) {
1626
+ setAttr(control_input, {
1627
+ [attr]: input.getAttribute(attr)
1628
+ });
1629
+ }
1630
+ });
1631
+ control_input.tabIndex = -1;
1632
+ control.appendChild(control_input);
1633
+ this.focus_node = control_input; // dom element
1634
+ } else if (settings.controlInput) {
1635
+ control_input = getDom(settings.controlInput);
1636
+ this.focus_node = control_input;
1637
+ } else {
1638
+ control_input = getDom('<input/>');
1639
+ this.focus_node = control;
1640
+ }
1641
+
1642
+ this.wrapper = wrapper;
1643
+ this.dropdown = dropdown;
1644
+ this.dropdown_content = dropdown_content;
1645
+ this.control = control;
1646
+ this.control_input = control_input;
1647
+ this.setup();
1648
+ }
1649
+ /**
1650
+ * set up event bindings.
1651
+ *
1652
+ */
1653
+
1654
+
1655
+ setup() {
1656
+ const self = this;
1657
+ const settings = self.settings;
1658
+ const control_input = self.control_input;
1659
+ const dropdown = self.dropdown;
1660
+ const dropdown_content = self.dropdown_content;
1661
+ const wrapper = self.wrapper;
1662
+ const control = self.control;
1663
+ const input = self.input;
1664
+ const focus_node = self.focus_node;
1665
+ const passive_event = {
1666
+ passive: true
1667
+ };
1668
+ const listboxId = self.inputId + '-ts-dropdown';
1669
+ setAttr(dropdown_content, {
1670
+ id: listboxId
1671
+ });
1672
+ setAttr(focus_node, {
1673
+ role: 'combobox',
1674
+ 'aria-haspopup': 'listbox',
1675
+ 'aria-expanded': 'false',
1676
+ 'aria-controls': listboxId
1677
+ });
1678
+ const control_id = getId(focus_node, self.inputId + '-ts-control');
1679
+ const query = "label[for='" + escapeQuery(self.inputId) + "']";
1680
+ const label = document.querySelector(query);
1681
+ const label_click = self.focus.bind(self);
1682
+
1683
+ if (label) {
1684
+ addEvent(label, 'click', label_click);
1685
+ setAttr(label, {
1686
+ for: control_id
1687
+ });
1688
+ const label_id = getId(label, self.inputId + '-ts-label');
1689
+ setAttr(focus_node, {
1690
+ 'aria-labelledby': label_id
1691
+ });
1692
+ setAttr(dropdown_content, {
1693
+ 'aria-labelledby': label_id
1694
+ });
1695
+ }
1696
+
1697
+ wrapper.style.width = input.style.width;
1698
+
1699
+ if (self.plugins.names.length) {
1700
+ const classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-');
1701
+ addClasses([wrapper, dropdown], classes_plugins);
1702
+ }
1703
+
1704
+ if ((settings.maxItems === null || settings.maxItems > 1) && self.is_select_tag) {
1705
+ setAttr(input, {
1706
+ multiple: 'multiple'
1707
+ });
1708
+ }
1709
+
1710
+ if (settings.placeholder) {
1711
+ setAttr(control_input, {
1712
+ placeholder: settings.placeholder
1713
+ });
1714
+ } // if splitOn was not passed in, construct it from the delimiter to allow pasting universally
1715
+
1716
+
1717
+ if (!settings.splitOn && settings.delimiter) {
1718
+ settings.splitOn = new RegExp('\\s*' + escape_regex(settings.delimiter) + '+\\s*');
1719
+ } // debounce user defined load() if loadThrottle > 0
1720
+ // after initializePlugins() so plugins can create/modify user defined loaders
1721
+
1722
+
1723
+ if (settings.load && settings.loadThrottle) {
1724
+ settings.load = loadDebounce(settings.load, settings.loadThrottle);
1725
+ }
1726
+
1727
+ self.control_input.type = input.type; // clicking on an option should select it
1728
+
1729
+ addEvent(dropdown, 'click', evt => {
1730
+ const option = parentMatch(evt.target, '[data-selectable]');
1731
+
1732
+ if (option) {
1733
+ self.onOptionSelect(evt, option);
1734
+ preventDefault(evt, true);
1735
+ }
1736
+ });
1737
+ addEvent(control, 'click', evt => {
1738
+ var target_match = parentMatch(evt.target, '[data-ts-item]', control);
1739
+
1740
+ if (target_match && self.onItemSelect(evt, target_match)) {
1741
+ preventDefault(evt, true);
1742
+ return;
1743
+ } // retain focus (see control_input mousedown)
1744
+
1745
+
1746
+ if (control_input.value != '') {
1747
+ return;
1748
+ }
1749
+
1750
+ self.onClick();
1751
+ preventDefault(evt, true);
1752
+ }); // keydown on focus_node for arrow_down/arrow_up
1753
+
1754
+ addEvent(focus_node, 'keydown', e => self.onKeyDown(e)); // keypress and input/keyup
1755
+
1756
+ addEvent(control_input, 'keypress', e => self.onKeyPress(e));
1757
+ addEvent(control_input, 'input', e => self.onInput(e));
1758
+ addEvent(focus_node, 'resize', () => self.positionDropdown(), passive_event);
1759
+ addEvent(focus_node, 'blur', e => self.onBlur(e));
1760
+ addEvent(focus_node, 'focus', e => self.onFocus(e));
1761
+ addEvent(focus_node, 'paste', e => self.onPaste(e));
1762
+
1763
+ const doc_mousedown = evt => {
1764
+ // blur if target is outside of this instance
1765
+ // dropdown is not always inside wrapper
1766
+ const target = evt.composedPath()[0];
1767
+
1768
+ if (!wrapper.contains(target) && !dropdown.contains(target)) {
1769
+ if (self.isFocused) {
1770
+ self.blur();
1771
+ }
1772
+
1773
+ self.inputState();
1774
+ return;
1775
+ } // retain focus by preventing native handling. if the
1776
+ // event target is the input it should not be modified.
1777
+ // otherwise, text selection within the input won't work.
1778
+ // Fixes bug #212 which is no covered by tests
1779
+
1780
+
1781
+ if (target == control_input && self.isOpen) {
1782
+ evt.stopPropagation(); // clicking anywhere in the control should not blur the control_input (which would close the dropdown)
1783
+ } else {
1784
+ preventDefault(evt, true);
1785
+ }
1786
+ };
1787
+
1788
+ var win_scroll = () => {
1789
+ if (self.isOpen) {
1790
+ self.positionDropdown();
1791
+ }
1792
+ };
1793
+
1794
+ addEvent(document, 'mousedown', doc_mousedown);
1795
+ addEvent(window, 'scroll', win_scroll, passive_event);
1796
+ addEvent(window, 'resize', win_scroll, passive_event);
1797
+
1798
+ this._destroy = () => {
1799
+ document.removeEventListener('mousedown', doc_mousedown);
1800
+ window.removeEventListener('scroll', win_scroll);
1801
+ window.removeEventListener('resize', win_scroll);
1802
+ if (label) label.removeEventListener('click', label_click);
1803
+ }; // store original html and tab index so that they can be
1804
+ // restored when the destroy() method is called.
1805
+
1806
+
1807
+ this.revertSettings = {
1808
+ innerHTML: input.innerHTML,
1809
+ tabIndex: input.tabIndex
1810
+ };
1811
+ input.tabIndex = -1;
1812
+ input.insertAdjacentElement('afterend', self.wrapper);
1813
+ self.sync(false);
1814
+ settings.items = [];
1815
+ delete settings.optgroups;
1816
+ delete settings.options;
1817
+ addEvent(input, 'invalid', e => {
1818
+ if (self.isValid) {
1819
+ self.isValid = false;
1820
+ self.isInvalid = true;
1821
+ self.refreshState();
1822
+ }
1823
+ });
1824
+ self.updateOriginalInput();
1825
+ self.refreshItems();
1826
+ self.close(false);
1827
+ self.inputState();
1828
+ self.isSetup = true;
1829
+
1830
+ if (input.disabled) {
1831
+ self.disable();
1832
+ } else {
1833
+ self.enable(); //sets tabIndex
1834
+ }
1835
+
1836
+ self.on('change', this.onChange);
1837
+ addClasses(input, 'tomselected', 'ts-hidden-accessible');
1838
+ self.trigger('initialize'); // preload options
1839
+
1840
+ if (settings.preload === true) {
1841
+ self.preload();
1842
+ }
1843
+ }
1844
+ /**
1845
+ * Register options and optgroups
1846
+ *
1847
+ */
1848
+
1849
+
1850
+ setupOptions(options = [], optgroups = []) {
1851
+ // build options table
1852
+ this.addOptions(options); // build optgroup table
1853
+
1854
+ iterate(optgroups, optgroup => {
1855
+ this.registerOptionGroup(optgroup);
1856
+ });
1857
+ }
1858
+ /**
1859
+ * Sets up default rendering functions.
1860
+ */
1861
+
1862
+
1863
+ setupTemplates() {
1864
+ var self = this;
1865
+ var field_label = self.settings.labelField;
1866
+ var field_optgroup = self.settings.optgroupLabelField;
1867
+ var templates = {
1868
+ 'optgroup': data => {
1869
+ let optgroup = document.createElement('div');
1870
+ optgroup.className = 'optgroup';
1871
+ optgroup.appendChild(data.options);
1872
+ return optgroup;
1873
+ },
1874
+ 'optgroup_header': (data, escape) => {
1875
+ return '<div class="optgroup-header">' + escape(data[field_optgroup]) + '</div>';
1876
+ },
1877
+ 'option': (data, escape) => {
1878
+ return '<div>' + escape(data[field_label]) + '</div>';
1879
+ },
1880
+ 'item': (data, escape) => {
1881
+ return '<div>' + escape(data[field_label]) + '</div>';
1882
+ },
1883
+ 'option_create': (data, escape) => {
1884
+ return '<div class="create">Add <strong>' + escape(data.input) + '</strong>&hellip;</div>';
1885
+ },
1886
+ 'no_results': () => {
1887
+ return '<div class="no-results">No results found</div>';
1888
+ },
1889
+ 'loading': () => {
1890
+ return '<div class="spinner"></div>';
1891
+ },
1892
+ 'not_loading': () => {},
1893
+ 'dropdown': () => {
1894
+ return '<div></div>';
1895
+ }
1896
+ };
1897
+ self.settings.render = Object.assign({}, templates, self.settings.render);
1898
+ }
1899
+ /**
1900
+ * Maps fired events to callbacks provided
1901
+ * in the settings used when creating the control.
1902
+ */
1903
+
1904
+
1905
+ setupCallbacks() {
1906
+ var key, fn;
1907
+ var callbacks = {
1908
+ 'initialize': 'onInitialize',
1909
+ 'change': 'onChange',
1910
+ 'item_add': 'onItemAdd',
1911
+ 'item_remove': 'onItemRemove',
1912
+ 'item_select': 'onItemSelect',
1913
+ 'clear': 'onClear',
1914
+ 'option_add': 'onOptionAdd',
1915
+ 'option_remove': 'onOptionRemove',
1916
+ 'option_clear': 'onOptionClear',
1917
+ 'optgroup_add': 'onOptionGroupAdd',
1918
+ 'optgroup_remove': 'onOptionGroupRemove',
1919
+ 'optgroup_clear': 'onOptionGroupClear',
1920
+ 'dropdown_open': 'onDropdownOpen',
1921
+ 'dropdown_close': 'onDropdownClose',
1922
+ 'type': 'onType',
1923
+ 'load': 'onLoad',
1924
+ 'focus': 'onFocus',
1925
+ 'blur': 'onBlur'
1926
+ };
1927
+
1928
+ for (key in callbacks) {
1929
+ fn = this.settings[callbacks[key]];
1930
+ if (fn) this.on(key, fn);
1931
+ }
1932
+ }
1933
+ /**
1934
+ * Sync the Tom Select instance with the original input or select
1935
+ *
1936
+ */
1937
+
1938
+
1939
+ sync(get_settings = true) {
1940
+ const self = this;
1941
+ const settings = get_settings ? getSettings(self.input, {
1942
+ delimiter: self.settings.delimiter
1943
+ }) : self.settings;
1944
+ self.setupOptions(settings.options, settings.optgroups);
1945
+ self.setValue(settings.items || [], true); // silent prevents recursion
1946
+
1947
+ self.lastQuery = null; // so updated options will be displayed in dropdown
1948
+ }
1949
+ /**
1950
+ * Triggered when the main control element
1951
+ * has a click event.
1952
+ *
1953
+ */
1954
+
1955
+
1956
+ onClick() {
1957
+ var self = this;
1958
+
1959
+ if (self.activeItems.length > 0) {
1960
+ self.clearActiveItems();
1961
+ self.focus();
1962
+ return;
1963
+ }
1964
+
1965
+ if (self.isFocused && self.isOpen) {
1966
+ self.blur();
1967
+ } else {
1968
+ self.focus();
1969
+ }
1970
+ }
1971
+ /**
1972
+ * @deprecated v1.7
1973
+ *
1974
+ */
1975
+
1976
+
1977
+ onMouseDown() {}
1978
+ /**
1979
+ * Triggered when the value of the control has been changed.
1980
+ * This should propagate the event to the original DOM
1981
+ * input / select element.
1982
+ */
1983
+
1984
+
1985
+ onChange() {
1986
+ triggerEvent(this.input, 'input');
1987
+ triggerEvent(this.input, 'change');
1988
+ }
1989
+ /**
1990
+ * Triggered on <input> paste.
1991
+ *
1992
+ */
1993
+
1994
+
1995
+ onPaste(e) {
1996
+ var self = this;
1997
+
1998
+ if (self.isInputHidden || self.isLocked) {
1999
+ preventDefault(e);
2000
+ return;
2001
+ } // If a regex or string is included, this will split the pasted
2002
+ // input and create Items for each separate value
2003
+
2004
+
2005
+ if (self.settings.splitOn) {
2006
+ // Wait for pasted text to be recognized in value
2007
+ setTimeout(() => {
2008
+ var pastedText = self.inputValue();
2009
+
2010
+ if (!pastedText.match(self.settings.splitOn)) {
2011
+ return;
2012
+ }
2013
+
2014
+ var splitInput = pastedText.trim().split(self.settings.splitOn);
2015
+ iterate(splitInput, piece => {
2016
+ self.createItem(piece);
2017
+ });
2018
+ }, 0);
2019
+ }
2020
+ }
2021
+ /**
2022
+ * Triggered on <input> keypress.
2023
+ *
2024
+ */
2025
+
2026
+
2027
+ onKeyPress(e) {
2028
+ var self = this;
2029
+
2030
+ if (self.isLocked) {
2031
+ preventDefault(e);
2032
+ return;
2033
+ }
2034
+
2035
+ var character = String.fromCharCode(e.keyCode || e.which);
2036
+
2037
+ if (self.settings.create && self.settings.mode === 'multi' && character === self.settings.delimiter) {
2038
+ self.createItem();
2039
+ preventDefault(e);
2040
+ return;
2041
+ }
2042
+ }
2043
+ /**
2044
+ * Triggered on <input> keydown.
2045
+ *
2046
+ */
2047
+
2048
+
2049
+ onKeyDown(e) {
2050
+ var self = this;
2051
+
2052
+ if (self.isLocked) {
2053
+ if (e.keyCode !== KEY_TAB) {
2054
+ preventDefault(e);
2055
+ }
2056
+
2057
+ return;
2058
+ }
2059
+
2060
+ switch (e.keyCode) {
2061
+ // ctrl+A: select all
2062
+ case KEY_A:
2063
+ if (isKeyDown(KEY_SHORTCUT, e)) {
2064
+ if (self.control_input.value == '') {
2065
+ preventDefault(e);
2066
+ self.selectAll();
2067
+ return;
2068
+ }
2069
+ }
2070
+
2071
+ break;
2072
+ // esc: close dropdown
2073
+
2074
+ case KEY_ESC:
2075
+ if (self.isOpen) {
2076
+ preventDefault(e, true);
2077
+ self.close();
2078
+ }
2079
+
2080
+ self.clearActiveItems();
2081
+ return;
2082
+ // down: open dropdown or move selection down
2083
+
2084
+ case KEY_DOWN:
2085
+ if (!self.isOpen && self.hasOptions) {
2086
+ self.open();
2087
+ } else if (self.activeOption) {
2088
+ let next = self.getAdjacent(self.activeOption, 1);
2089
+ if (next) self.setActiveOption(next);
2090
+ }
2091
+
2092
+ preventDefault(e);
2093
+ return;
2094
+ // up: move selection up
2095
+
2096
+ case KEY_UP:
2097
+ if (self.activeOption) {
2098
+ let prev = self.getAdjacent(self.activeOption, -1);
2099
+ if (prev) self.setActiveOption(prev);
2100
+ }
2101
+
2102
+ preventDefault(e);
2103
+ return;
2104
+ // return: select active option
2105
+
2106
+ case KEY_RETURN:
2107
+ if (self.canSelect(self.activeOption)) {
2108
+ self.onOptionSelect(e, self.activeOption);
2109
+ preventDefault(e); // if the option_create=null, the dropdown might be closed
2110
+ } else if (self.settings.create && self.createItem()) {
2111
+ preventDefault(e);
2112
+ }
2113
+
2114
+ return;
2115
+ // left: modifiy item selection to the left
2116
+
2117
+ case KEY_LEFT:
2118
+ self.advanceSelection(-1, e);
2119
+ return;
2120
+ // right: modifiy item selection to the right
2121
+
2122
+ case KEY_RIGHT:
2123
+ self.advanceSelection(1, e);
2124
+ return;
2125
+ // tab: select active option and/or create item
2126
+
2127
+ case KEY_TAB:
2128
+ if (self.settings.selectOnTab) {
2129
+ if (self.canSelect(self.activeOption)) {
2130
+ self.onOptionSelect(e, self.activeOption); // prevent default [tab] behaviour of jump to the next field
2131
+ // if select isFull, then the dropdown won't be open and [tab] will work normally
2132
+
2133
+ preventDefault(e);
2134
+ }
2135
+
2136
+ if (self.settings.create && self.createItem()) {
2137
+ preventDefault(e);
2138
+ }
2139
+ }
2140
+
2141
+ return;
2142
+ // delete|backspace: delete items
2143
+
2144
+ case KEY_BACKSPACE:
2145
+ case KEY_DELETE:
2146
+ self.deleteSelection(e);
2147
+ return;
2148
+ } // don't enter text in the control_input when active items are selected
2149
+
2150
+
2151
+ if (self.isInputHidden && !isKeyDown(KEY_SHORTCUT, e)) {
2152
+ preventDefault(e);
2153
+ }
2154
+ }
2155
+ /**
2156
+ * Triggered on <input> keyup.
2157
+ *
2158
+ */
2159
+
2160
+
2161
+ onInput(e) {
2162
+ var self = this;
2163
+
2164
+ if (self.isLocked) {
2165
+ return;
2166
+ }
2167
+
2168
+ var value = self.inputValue();
2169
+
2170
+ if (self.lastValue !== value) {
2171
+ self.lastValue = value;
2172
+
2173
+ if (self.settings.shouldLoad.call(self, value)) {
2174
+ self.load(value);
2175
+ }
2176
+
2177
+ self.refreshOptions();
2178
+ self.trigger('type', value);
2179
+ }
2180
+ }
2181
+ /**
2182
+ * Triggered on <input> focus.
2183
+ *
2184
+ */
2185
+
2186
+
2187
+ onFocus(e) {
2188
+ var self = this;
2189
+ var wasFocused = self.isFocused;
2190
+
2191
+ if (self.isDisabled) {
2192
+ self.blur();
2193
+ preventDefault(e);
2194
+ return;
2195
+ }
2196
+
2197
+ if (self.ignoreFocus) return;
2198
+ self.isFocused = true;
2199
+ if (self.settings.preload === 'focus') self.preload();
2200
+ if (!wasFocused) self.trigger('focus');
2201
+
2202
+ if (!self.activeItems.length) {
2203
+ self.showInput();
2204
+ self.refreshOptions(!!self.settings.openOnFocus);
2205
+ }
2206
+
2207
+ self.refreshState();
2208
+ }
2209
+ /**
2210
+ * Triggered on <input> blur.
2211
+ *
2212
+ */
2213
+
2214
+
2215
+ onBlur(e) {
2216
+ if (document.hasFocus() === false) return;
2217
+ var self = this;
2218
+ if (!self.isFocused) return;
2219
+ self.isFocused = false;
2220
+ self.ignoreFocus = false;
2221
+
2222
+ var deactivate = () => {
2223
+ self.close();
2224
+ self.setActiveItem();
2225
+ self.setCaret(self.items.length);
2226
+ self.trigger('blur');
2227
+ };
2228
+
2229
+ if (self.settings.create && self.settings.createOnBlur) {
2230
+ self.createItem(null, false, deactivate);
2231
+ } else {
2232
+ deactivate();
2233
+ }
2234
+ }
2235
+ /**
2236
+ * Triggered when the user clicks on an option
2237
+ * in the autocomplete dropdown menu.
2238
+ *
2239
+ */
2240
+
2241
+
2242
+ onOptionSelect(evt, option) {
2243
+ var value,
2244
+ self = this; // should not be possible to trigger a option under a disabled optgroup
2245
+
2246
+ if (option.parentElement && option.parentElement.matches('[data-disabled]')) {
2247
+ return;
2248
+ }
2249
+
2250
+ if (option.classList.contains('create')) {
2251
+ self.createItem(null, true, () => {
2252
+ if (self.settings.closeAfterSelect) {
2253
+ self.close();
2254
+ }
2255
+ });
2256
+ } else {
2257
+ value = option.dataset.value;
2258
+
2259
+ if (typeof value !== 'undefined') {
2260
+ self.lastQuery = null;
2261
+ self.addItem(value);
2262
+
2263
+ if (self.settings.closeAfterSelect) {
2264
+ self.close();
2265
+ }
2266
+
2267
+ if (!self.settings.hideSelected && evt.type && /click/.test(evt.type)) {
2268
+ self.setActiveOption(option);
2269
+ }
2270
+ }
2271
+ }
2272
+ }
2273
+ /**
2274
+ * Return true if the given option can be selected
2275
+ *
2276
+ */
2277
+
2278
+
2279
+ canSelect(option) {
2280
+ if (this.isOpen && option && this.dropdown_content.contains(option)) {
2281
+ return true;
2282
+ }
2283
+
2284
+ return false;
2285
+ }
2286
+ /**
2287
+ * Triggered when the user clicks on an item
2288
+ * that has been selected.
2289
+ *
2290
+ */
2291
+
2292
+
2293
+ onItemSelect(evt, item) {
2294
+ var self = this;
2295
+
2296
+ if (!self.isLocked && self.settings.mode === 'multi') {
2297
+ preventDefault(evt);
2298
+ self.setActiveItem(item, evt);
2299
+ return true;
2300
+ }
2301
+
2302
+ return false;
2303
+ }
2304
+ /**
2305
+ * Determines whether or not to invoke
2306
+ * the user-provided option provider / loader
2307
+ *
2308
+ * Note, there is a subtle difference between
2309
+ * this.canLoad() and this.settings.shouldLoad();
2310
+ *
2311
+ * - settings.shouldLoad() is a user-input validator.
2312
+ * When false is returned, the not_loading template
2313
+ * will be added to the dropdown
2314
+ *
2315
+ * - canLoad() is lower level validator that checks
2316
+ * the Tom Select instance. There is no inherent user
2317
+ * feedback when canLoad returns false
2318
+ *
2319
+ */
2320
+
2321
+
2322
+ canLoad(value) {
2323
+ if (!this.settings.load) return false;
2324
+ if (this.loadedSearches.hasOwnProperty(value)) return false;
2325
+ return true;
2326
+ }
2327
+ /**
2328
+ * Invokes the user-provided option provider / loader.
2329
+ *
2330
+ */
2331
+
2332
+
2333
+ load(value) {
2334
+ const self = this;
2335
+ if (!self.canLoad(value)) return;
2336
+ addClasses(self.wrapper, self.settings.loadingClass);
2337
+ self.loading++;
2338
+ const callback = self.loadCallback.bind(self);
2339
+ self.settings.load.call(self, value, callback);
2340
+ }
2341
+ /**
2342
+ * Invoked by the user-provided option provider
2343
+ *
2344
+ */
2345
+
2346
+
2347
+ loadCallback(options, optgroups) {
2348
+ const self = this;
2349
+ self.loading = Math.max(self.loading - 1, 0);
2350
+ self.lastQuery = null;
2351
+ self.clearActiveOption(); // when new results load, focus should be on first option
2352
+
2353
+ self.setupOptions(options, optgroups);
2354
+ self.refreshOptions(self.isFocused && !self.isInputHidden);
2355
+
2356
+ if (!self.loading) {
2357
+ removeClasses(self.wrapper, self.settings.loadingClass);
2358
+ }
2359
+
2360
+ self.trigger('load', options, optgroups);
2361
+ }
2362
+
2363
+ preload() {
2364
+ var classList = this.wrapper.classList;
2365
+ if (classList.contains('preloaded')) return;
2366
+ classList.add('preloaded');
2367
+ this.load('');
2368
+ }
2369
+ /**
2370
+ * Sets the input field of the control to the specified value.
2371
+ *
2372
+ */
2373
+
2374
+
2375
+ setTextboxValue(value = '') {
2376
+ var input = this.control_input;
2377
+ var changed = input.value !== value;
2378
+
2379
+ if (changed) {
2380
+ input.value = value;
2381
+ triggerEvent(input, 'update');
2382
+ this.lastValue = value;
2383
+ }
2384
+ }
2385
+ /**
2386
+ * Returns the value of the control. If multiple items
2387
+ * can be selected (e.g. <select multiple>), this returns
2388
+ * an array. If only one item can be selected, this
2389
+ * returns a string.
2390
+ *
2391
+ */
2392
+
2393
+
2394
+ getValue() {
2395
+ if (this.is_select_tag && this.input.hasAttribute('multiple')) {
2396
+ return this.items;
2397
+ }
2398
+
2399
+ return this.items.join(this.settings.delimiter);
2400
+ }
2401
+ /**
2402
+ * Resets the selected items to the given value.
2403
+ *
2404
+ */
2405
+
2406
+
2407
+ setValue(value, silent) {
2408
+ var events = silent ? [] : ['change'];
2409
+ debounce_events(this, events, () => {
2410
+ this.clear(silent);
2411
+ this.addItems(value, silent);
2412
+ });
2413
+ }
2414
+ /**
2415
+ * Resets the number of max items to the given value
2416
+ *
2417
+ */
2418
+
2419
+
2420
+ setMaxItems(value) {
2421
+ if (value === 0) value = null; //reset to unlimited items.
2422
+
2423
+ this.settings.maxItems = value;
2424
+ this.refreshState();
2425
+ }
2426
+ /**
2427
+ * Sets the selected item.
2428
+ *
2429
+ */
2430
+
2431
+
2432
+ setActiveItem(item, e) {
2433
+ var self = this;
2434
+ var eventName;
2435
+ var i, begin, end, swap;
2436
+ var last;
2437
+ if (self.settings.mode === 'single') return; // clear the active selection
2438
+
2439
+ if (!item) {
2440
+ self.clearActiveItems();
2441
+
2442
+ if (self.isFocused) {
2443
+ self.showInput();
2444
+ }
2445
+
2446
+ return;
2447
+ } // modify selection
2448
+
2449
+
2450
+ eventName = e && e.type.toLowerCase();
2451
+
2452
+ if (eventName === 'click' && isKeyDown('shiftKey', e) && self.activeItems.length) {
2453
+ last = self.getLastActive();
2454
+ begin = Array.prototype.indexOf.call(self.control.children, last);
2455
+ end = Array.prototype.indexOf.call(self.control.children, item);
2456
+
2457
+ if (begin > end) {
2458
+ swap = begin;
2459
+ begin = end;
2460
+ end = swap;
2461
+ }
2462
+
2463
+ for (i = begin; i <= end; i++) {
2464
+ item = self.control.children[i];
2465
+
2466
+ if (self.activeItems.indexOf(item) === -1) {
2467
+ self.setActiveItemClass(item);
2468
+ }
2469
+ }
2470
+
2471
+ preventDefault(e);
2472
+ } else if (eventName === 'click' && isKeyDown(KEY_SHORTCUT, e) || eventName === 'keydown' && isKeyDown('shiftKey', e)) {
2473
+ if (item.classList.contains('active')) {
2474
+ self.removeActiveItem(item);
2475
+ } else {
2476
+ self.setActiveItemClass(item);
2477
+ }
2478
+ } else {
2479
+ self.clearActiveItems();
2480
+ self.setActiveItemClass(item);
2481
+ } // ensure control has focus
2482
+
2483
+
2484
+ self.hideInput();
2485
+
2486
+ if (!self.isFocused) {
2487
+ self.focus();
2488
+ }
2489
+ }
2490
+ /**
2491
+ * Set the active and last-active classes
2492
+ *
2493
+ */
2494
+
2495
+
2496
+ setActiveItemClass(item) {
2497
+ const self = this;
2498
+ const last_active = self.control.querySelector('.last-active');
2499
+ if (last_active) removeClasses(last_active, 'last-active');
2500
+ addClasses(item, 'active last-active');
2501
+ self.trigger('item_select', item);
2502
+
2503
+ if (self.activeItems.indexOf(item) == -1) {
2504
+ self.activeItems.push(item);
2505
+ }
2506
+ }
2507
+ /**
2508
+ * Remove active item
2509
+ *
2510
+ */
2511
+
2512
+
2513
+ removeActiveItem(item) {
2514
+ var idx = this.activeItems.indexOf(item);
2515
+ this.activeItems.splice(idx, 1);
2516
+ removeClasses(item, 'active');
2517
+ }
2518
+ /**
2519
+ * Clears all the active items
2520
+ *
2521
+ */
2522
+
2523
+
2524
+ clearActiveItems() {
2525
+ removeClasses(this.activeItems, 'active');
2526
+ this.activeItems = [];
2527
+ }
2528
+ /**
2529
+ * Sets the selected item in the dropdown menu
2530
+ * of available options.
2531
+ *
2532
+ */
2533
+
2534
+
2535
+ setActiveOption(option) {
2536
+ if (option === this.activeOption) {
2537
+ return;
2538
+ }
2539
+
2540
+ this.clearActiveOption();
2541
+ if (!option) return;
2542
+ this.activeOption = option;
2543
+ setAttr(this.focus_node, {
2544
+ 'aria-activedescendant': option.getAttribute('id')
2545
+ });
2546
+ setAttr(option, {
2547
+ 'aria-selected': 'true'
2548
+ });
2549
+ addClasses(option, 'active');
2550
+ this.scrollToOption(option);
2551
+ }
2552
+ /**
2553
+ * Sets the dropdown_content scrollTop to display the option
2554
+ *
2555
+ */
2556
+
2557
+
2558
+ scrollToOption(option, behavior) {
2559
+ if (!option) return;
2560
+ const content = this.dropdown_content;
2561
+ const height_menu = content.clientHeight;
2562
+ const scrollTop = content.scrollTop || 0;
2563
+ const height_item = option.offsetHeight;
2564
+ const y = option.getBoundingClientRect().top - content.getBoundingClientRect().top + scrollTop;
2565
+
2566
+ if (y + height_item > height_menu + scrollTop) {
2567
+ this.scroll(y - height_menu + height_item, behavior);
2568
+ } else if (y < scrollTop) {
2569
+ this.scroll(y, behavior);
2570
+ }
2571
+ }
2572
+ /**
2573
+ * Scroll the dropdown to the given position
2574
+ *
2575
+ */
2576
+
2577
+
2578
+ scroll(scrollTop, behavior) {
2579
+ const content = this.dropdown_content;
2580
+
2581
+ if (behavior) {
2582
+ content.style.scrollBehavior = behavior;
2583
+ }
2584
+
2585
+ content.scrollTop = scrollTop;
2586
+ content.style.scrollBehavior = '';
2587
+ }
2588
+ /**
2589
+ * Clears the active option
2590
+ *
2591
+ */
2592
+
2593
+
2594
+ clearActiveOption() {
2595
+ if (this.activeOption) {
2596
+ removeClasses(this.activeOption, 'active');
2597
+ setAttr(this.activeOption, {
2598
+ 'aria-selected': null
2599
+ });
2600
+ }
2601
+
2602
+ this.activeOption = null;
2603
+ setAttr(this.focus_node, {
2604
+ 'aria-activedescendant': null
2605
+ });
2606
+ }
2607
+ /**
2608
+ * Selects all items (CTRL + A).
2609
+ */
2610
+
2611
+
2612
+ selectAll() {
2613
+ const self = this;
2614
+ if (self.settings.mode === 'single') return;
2615
+ const activeItems = self.controlChildren();
2616
+ if (!activeItems.length) return;
2617
+ self.hideInput();
2618
+ self.close();
2619
+ self.activeItems = activeItems;
2620
+ iterate(activeItems, item => {
2621
+ self.setActiveItemClass(item);
2622
+ });
2623
+ }
2624
+ /**
2625
+ * Determines if the control_input should be in a hidden or visible state
2626
+ *
2627
+ */
2628
+
2629
+
2630
+ inputState() {
2631
+ var self = this;
2632
+ if (!self.control.contains(self.control_input)) return;
2633
+ setAttr(self.control_input, {
2634
+ placeholder: self.settings.placeholder
2635
+ });
2636
+
2637
+ if (self.activeItems.length > 0 || !self.isFocused && self.settings.hidePlaceholder && self.items.length > 0) {
2638
+ self.setTextboxValue();
2639
+ self.isInputHidden = true;
2640
+ } else {
2641
+ if (self.settings.hidePlaceholder && self.items.length > 0) {
2642
+ setAttr(self.control_input, {
2643
+ placeholder: ''
2644
+ });
2645
+ }
2646
+
2647
+ self.isInputHidden = false;
2648
+ }
2649
+
2650
+ self.wrapper.classList.toggle('input-hidden', self.isInputHidden);
2651
+ }
2652
+ /**
2653
+ * Hides the input element out of view, while
2654
+ * retaining its focus.
2655
+ * @deprecated 1.3
2656
+ */
2657
+
2658
+
2659
+ hideInput() {
2660
+ this.inputState();
2661
+ }
2662
+ /**
2663
+ * Restores input visibility.
2664
+ * @deprecated 1.3
2665
+ */
2666
+
2667
+
2668
+ showInput() {
2669
+ this.inputState();
2670
+ }
2671
+ /**
2672
+ * Get the input value
2673
+ */
2674
+
2675
+
2676
+ inputValue() {
2677
+ return this.control_input.value.trim();
2678
+ }
2679
+ /**
2680
+ * Gives the control focus.
2681
+ */
2682
+
2683
+
2684
+ focus() {
2685
+ var self = this;
2686
+ if (self.isDisabled) return;
2687
+ self.ignoreFocus = true;
2688
+
2689
+ if (self.control_input.offsetWidth) {
2690
+ self.control_input.focus();
2691
+ } else {
2692
+ self.focus_node.focus();
2693
+ }
2694
+
2695
+ setTimeout(() => {
2696
+ self.ignoreFocus = false;
2697
+ self.onFocus();
2698
+ }, 0);
2699
+ }
2700
+ /**
2701
+ * Forces the control out of focus.
2702
+ *
2703
+ */
2704
+
2705
+
2706
+ blur() {
2707
+ this.focus_node.blur();
2708
+ this.onBlur();
2709
+ }
2710
+ /**
2711
+ * Returns a function that scores an object
2712
+ * to show how good of a match it is to the
2713
+ * provided query.
2714
+ *
2715
+ * @return {function}
2716
+ */
2717
+
2718
+
2719
+ getScoreFunction(query) {
2720
+ return this.sifter.getScoreFunction(query, this.getSearchOptions());
2721
+ }
2722
+ /**
2723
+ * Returns search options for sifter (the system
2724
+ * for scoring and sorting results).
2725
+ *
2726
+ * @see https://github.com/orchidjs/sifter.js
2727
+ * @return {object}
2728
+ */
2729
+
2730
+
2731
+ getSearchOptions() {
2732
+ var settings = this.settings;
2733
+ var sort = settings.sortField;
2734
+
2735
+ if (typeof settings.sortField === 'string') {
2736
+ sort = [{
2737
+ field: settings.sortField
2738
+ }];
2739
+ }
2740
+
2741
+ return {
2742
+ fields: settings.searchField,
2743
+ conjunction: settings.searchConjunction,
2744
+ sort: sort,
2745
+ nesting: settings.nesting
2746
+ };
2747
+ }
2748
+ /**
2749
+ * Searches through available options and returns
2750
+ * a sorted array of matches.
2751
+ *
2752
+ */
2753
+
2754
+
2755
+ search(query) {
2756
+ var i, result, calculateScore;
2757
+ var self = this;
2758
+ var options = this.getSearchOptions(); // validate user-provided result scoring function
2759
+
2760
+ if (self.settings.score) {
2761
+ calculateScore = self.settings.score.call(self, query);
2762
+
2763
+ if (typeof calculateScore !== 'function') {
2764
+ throw new Error('Tom Select "score" setting must be a function that returns a function');
2765
+ }
2766
+ } // perform search
2767
+
2768
+
2769
+ if (query !== self.lastQuery) {
2770
+ self.lastQuery = query;
2771
+ result = self.sifter.search(query, Object.assign(options, {
2772
+ score: calculateScore
2773
+ }));
2774
+ self.currentResults = result;
2775
+ } else {
2776
+ result = Object.assign({}, self.currentResults);
2777
+ } // filter out selected items
2778
+
2779
+
2780
+ if (self.settings.hideSelected) {
2781
+ for (i = result.items.length - 1; i >= 0; i--) {
2782
+ let hashed = hash_key(result.items[i].id);
2783
+
2784
+ if (hashed && self.items.indexOf(hashed) !== -1) {
2785
+ result.items.splice(i, 1);
2786
+ }
2787
+ }
2788
+ }
2789
+
2790
+ return result;
2791
+ }
2792
+ /**
2793
+ * Refreshes the list of available options shown
2794
+ * in the autocomplete dropdown menu.
2795
+ *
2796
+ */
2797
+
2798
+
2799
+ refreshOptions(triggerDropdown = true) {
2800
+ var i, j, k, n, optgroup, optgroups, html, has_create_option, active_value, active_group;
2801
+ var create;
2802
+ const groups = {};
2803
+ const groups_order = [];
2804
+ var self = this;
2805
+ var query = self.inputValue();
2806
+ var results = self.search(query);
2807
+ var active_option = null; //self.activeOption;
2808
+
2809
+ var show_dropdown = self.settings.shouldOpen || false;
2810
+ var dropdown_content = self.dropdown_content;
2811
+
2812
+ if (self.activeOption) {
2813
+ active_value = self.activeOption.dataset.value;
2814
+ active_group = self.activeOption.closest('[data-group]');
2815
+ } // build markup
2816
+
2817
+
2818
+ n = results.items.length;
2819
+
2820
+ if (typeof self.settings.maxOptions === 'number') {
2821
+ n = Math.min(n, self.settings.maxOptions);
2822
+ }
2823
+
2824
+ if (n > 0) {
2825
+ show_dropdown = true;
2826
+ } // render and group available options individually
2827
+
2828
+
2829
+ for (i = 0; i < n; i++) {
2830
+ // get option dom element
2831
+ let opt_value = results.items[i].id;
2832
+ let option = self.options[opt_value];
2833
+ let option_el = self.getOption(opt_value, true); // toggle 'selected' class
2834
+
2835
+ if (!self.settings.hideSelected) {
2836
+ option_el.classList.toggle('selected', self.items.includes(opt_value));
2837
+ }
2838
+
2839
+ optgroup = option[self.settings.optgroupField] || '';
2840
+ optgroups = Array.isArray(optgroup) ? optgroup : [optgroup];
2841
+
2842
+ for (j = 0, k = optgroups && optgroups.length; j < k; j++) {
2843
+ optgroup = optgroups[j];
2844
+
2845
+ if (!self.optgroups.hasOwnProperty(optgroup)) {
2846
+ optgroup = '';
2847
+ }
2848
+
2849
+ if (!groups.hasOwnProperty(optgroup)) {
2850
+ groups[optgroup] = document.createDocumentFragment();
2851
+ groups_order.push(optgroup);
2852
+ } // nodes can only have one parent, so if the option is in mutple groups, we need a clone
2853
+
2854
+
2855
+ if (j > 0) {
2856
+ option_el = option_el.cloneNode(true);
2857
+ setAttr(option_el, {
2858
+ id: option.$id + '-clone-' + j,
2859
+ 'aria-selected': null
2860
+ });
2861
+ option_el.classList.add('ts-cloned');
2862
+ removeClasses(option_el, 'active');
2863
+ } // make sure we keep the activeOption in the same group
2864
+
2865
+
2866
+ if (!active_option && active_value == opt_value) {
2867
+ if (active_group) {
2868
+ if (active_group.dataset.group === optgroup) {
2869
+ active_option = option_el;
2870
+ }
2871
+ } else {
2872
+ active_option = option_el;
2873
+ }
2874
+ }
2875
+
2876
+ groups[optgroup].appendChild(option_el);
2877
+ }
2878
+ } // sort optgroups
2879
+
2880
+
2881
+ if (this.settings.lockOptgroupOrder) {
2882
+ groups_order.sort((a, b) => {
2883
+ var a_order = self.optgroups[a] && self.optgroups[a].$order || 0;
2884
+ var b_order = self.optgroups[b] && self.optgroups[b].$order || 0;
2885
+ return a_order - b_order;
2886
+ });
2887
+ } // render optgroup headers & join groups
2888
+
2889
+
2890
+ html = document.createDocumentFragment();
2891
+ iterate(groups_order, optgroup => {
2892
+ if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].children.length) {
2893
+ let group_options = document.createDocumentFragment();
2894
+ let header = self.render('optgroup_header', self.optgroups[optgroup]);
2895
+ append(group_options, header);
2896
+ append(group_options, groups[optgroup]);
2897
+ let group_html = self.render('optgroup', {
2898
+ group: self.optgroups[optgroup],
2899
+ options: group_options
2900
+ });
2901
+ append(html, group_html);
2902
+ } else {
2903
+ append(html, groups[optgroup]);
2904
+ }
2905
+ });
2906
+ dropdown_content.innerHTML = '';
2907
+ append(dropdown_content, html); // highlight matching terms inline
2908
+
2909
+ if (self.settings.highlight) {
2910
+ removeHighlight(dropdown_content);
2911
+
2912
+ if (results.query.length && results.tokens.length) {
2913
+ iterate(results.tokens, tok => {
2914
+ highlight(dropdown_content, tok.regex);
2915
+ });
2916
+ }
2917
+ } // helper method for adding templates to dropdown
2918
+
2919
+
2920
+ var add_template = template => {
2921
+ let content = self.render(template, {
2922
+ input: query
2923
+ });
2924
+
2925
+ if (content) {
2926
+ show_dropdown = true;
2927
+ dropdown_content.insertBefore(content, dropdown_content.firstChild);
2928
+ }
2929
+
2930
+ return content;
2931
+ }; // add loading message
2932
+
2933
+
2934
+ if (self.loading) {
2935
+ add_template('loading'); // invalid query
2936
+ } else if (!self.settings.shouldLoad.call(self, query)) {
2937
+ add_template('not_loading'); // add no_results message
2938
+ } else if (results.items.length === 0) {
2939
+ add_template('no_results');
2940
+ } // add create option
2941
+
2942
+
2943
+ has_create_option = self.canCreate(query);
2944
+
2945
+ if (has_create_option) {
2946
+ create = add_template('option_create');
2947
+ } // activate
2948
+
2949
+
2950
+ self.hasOptions = results.items.length > 0 || has_create_option;
2951
+
2952
+ if (show_dropdown) {
2953
+ if (results.items.length > 0) {
2954
+ if (!active_option && self.settings.mode === 'single' && self.items.length) {
2955
+ active_option = self.getOption(self.items[0]);
2956
+ }
2957
+
2958
+ if (!dropdown_content.contains(active_option)) {
2959
+ let active_index = 0;
2960
+
2961
+ if (create && !self.settings.addPrecedence) {
2962
+ active_index = 1;
2963
+ }
2964
+
2965
+ active_option = self.selectable()[active_index];
2966
+ }
2967
+ } else if (create) {
2968
+ active_option = create;
2969
+ }
2970
+
2971
+ if (triggerDropdown && !self.isOpen) {
2972
+ self.open();
2973
+ self.scrollToOption(active_option, 'auto');
2974
+ }
2975
+
2976
+ self.setActiveOption(active_option);
2977
+ } else {
2978
+ self.clearActiveOption();
2979
+
2980
+ if (triggerDropdown && self.isOpen) {
2981
+ self.close(false); // if create_option=null, we want the dropdown to close but not reset the textbox value
2982
+ }
2983
+ }
2984
+ }
2985
+ /**
2986
+ * Return list of selectable options
2987
+ *
2988
+ */
2989
+
2990
+
2991
+ selectable() {
2992
+ return this.dropdown_content.querySelectorAll('[data-selectable]');
2993
+ }
2994
+ /**
2995
+ * Adds an available option. If it already exists,
2996
+ * nothing will happen. Note: this does not refresh
2997
+ * the options list dropdown (use `refreshOptions`
2998
+ * for that).
2999
+ *
3000
+ * Usage:
3001
+ *
3002
+ * this.addOption(data)
3003
+ *
3004
+ */
3005
+
3006
+
3007
+ addOption(data, user_created = false) {
3008
+ const self = this; // @deprecated 1.7.7
3009
+ // use addOptions( array, user_created ) for adding multiple options
3010
+
3011
+ if (Array.isArray(data)) {
3012
+ self.addOptions(data, user_created);
3013
+ return false;
3014
+ }
3015
+
3016
+ const key = hash_key(data[self.settings.valueField]);
3017
+
3018
+ if (key === null || self.options.hasOwnProperty(key)) {
3019
+ return false;
3020
+ }
3021
+
3022
+ data.$order = data.$order || ++self.order;
3023
+ data.$id = self.inputId + '-opt-' + data.$order;
3024
+ self.options[key] = data;
3025
+ self.lastQuery = null;
3026
+
3027
+ if (user_created) {
3028
+ self.userOptions[key] = user_created;
3029
+ self.trigger('option_add', key, data);
3030
+ }
3031
+
3032
+ return key;
3033
+ }
3034
+ /**
3035
+ * Add multiple options
3036
+ *
3037
+ */
3038
+
3039
+
3040
+ addOptions(data, user_created = false) {
3041
+ iterate(data, dat => {
3042
+ this.addOption(dat, user_created);
3043
+ });
3044
+ }
3045
+ /**
3046
+ * @deprecated 1.7.7
3047
+ */
3048
+
3049
+
3050
+ registerOption(data) {
3051
+ return this.addOption(data);
3052
+ }
3053
+ /**
3054
+ * Registers an option group to the pool of option groups.
3055
+ *
3056
+ * @return {boolean|string}
3057
+ */
3058
+
3059
+
3060
+ registerOptionGroup(data) {
3061
+ var key = hash_key(data[this.settings.optgroupValueField]);
3062
+ if (key === null) return false;
3063
+ data.$order = data.$order || ++this.order;
3064
+ this.optgroups[key] = data;
3065
+ return key;
3066
+ }
3067
+ /**
3068
+ * Registers a new optgroup for options
3069
+ * to be bucketed into.
3070
+ *
3071
+ */
3072
+
3073
+
3074
+ addOptionGroup(id, data) {
3075
+ var hashed_id;
3076
+ data[this.settings.optgroupValueField] = id;
3077
+
3078
+ if (hashed_id = this.registerOptionGroup(data)) {
3079
+ this.trigger('optgroup_add', hashed_id, data);
3080
+ }
3081
+ }
3082
+ /**
3083
+ * Removes an existing option group.
3084
+ *
3085
+ */
3086
+
3087
+
3088
+ removeOptionGroup(id) {
3089
+ if (this.optgroups.hasOwnProperty(id)) {
3090
+ delete this.optgroups[id];
3091
+ this.clearCache();
3092
+ this.trigger('optgroup_remove', id);
3093
+ }
3094
+ }
3095
+ /**
3096
+ * Clears all existing option groups.
3097
+ */
3098
+
3099
+
3100
+ clearOptionGroups() {
3101
+ this.optgroups = {};
3102
+ this.clearCache();
3103
+ this.trigger('optgroup_clear');
3104
+ }
3105
+ /**
3106
+ * Updates an option available for selection. If
3107
+ * it is visible in the selected items or options
3108
+ * dropdown, it will be re-rendered automatically.
3109
+ *
3110
+ */
3111
+
3112
+
3113
+ updateOption(value, data) {
3114
+ const self = this;
3115
+ var item_new;
3116
+ var index_item;
3117
+ const value_old = hash_key(value);
3118
+ const value_new = hash_key(data[self.settings.valueField]); // sanity checks
3119
+
3120
+ if (value_old === null) return;
3121
+ if (!self.options.hasOwnProperty(value_old)) return;
3122
+ if (typeof value_new !== 'string') throw new Error('Value must be set in option data');
3123
+ const option = self.getOption(value_old);
3124
+ const item = self.getItem(value_old);
3125
+ data.$order = data.$order || self.options[value_old].$order;
3126
+ delete self.options[value_old]; // invalidate render cache
3127
+ // don't remove existing node yet, we'll remove it after replacing it
3128
+
3129
+ self.uncacheValue(value_new);
3130
+ self.options[value_new] = data; // update the option if it's in the dropdown
3131
+
3132
+ if (option) {
3133
+ if (self.dropdown_content.contains(option)) {
3134
+ const option_new = self._render('option', data);
3135
+
3136
+ replaceNode(option, option_new);
3137
+
3138
+ if (self.activeOption === option) {
3139
+ self.setActiveOption(option_new);
3140
+ }
3141
+ }
3142
+
3143
+ option.remove();
3144
+ } // update the item if we have one
3145
+
3146
+
3147
+ if (item) {
3148
+ index_item = self.items.indexOf(value_old);
3149
+
3150
+ if (index_item !== -1) {
3151
+ self.items.splice(index_item, 1, value_new);
3152
+ }
3153
+
3154
+ item_new = self._render('item', data);
3155
+ if (item.classList.contains('active')) addClasses(item_new, 'active');
3156
+ replaceNode(item, item_new);
3157
+ } // invalidate last query because we might have updated the sortField
3158
+
3159
+
3160
+ self.lastQuery = null;
3161
+ }
3162
+ /**
3163
+ * Removes a single option.
3164
+ *
3165
+ */
3166
+
3167
+
3168
+ removeOption(value, silent) {
3169
+ const self = this;
3170
+ value = get_hash(value);
3171
+ self.uncacheValue(value);
3172
+ delete self.userOptions[value];
3173
+ delete self.options[value];
3174
+ self.lastQuery = null;
3175
+ self.trigger('option_remove', value);
3176
+ self.removeItem(value, silent);
3177
+ }
3178
+ /**
3179
+ * Clears all options.
3180
+ */
3181
+
3182
+
3183
+ clearOptions() {
3184
+ this.loadedSearches = {};
3185
+ this.userOptions = {};
3186
+ this.clearCache();
3187
+ var selected = {};
3188
+ iterate(this.options, (option, key) => {
3189
+ if (this.items.indexOf(key) >= 0) {
3190
+ selected[key] = this.options[key];
3191
+ }
3192
+ });
3193
+ this.options = this.sifter.items = selected;
3194
+ this.lastQuery = null;
3195
+ this.trigger('option_clear');
3196
+ }
3197
+ /**
3198
+ * Returns the dom element of the option
3199
+ * matching the given value.
3200
+ *
3201
+ */
3202
+
3203
+
3204
+ getOption(value, create = false) {
3205
+ const hashed = hash_key(value);
3206
+
3207
+ if (hashed !== null && this.options.hasOwnProperty(hashed)) {
3208
+ const option = this.options[hashed];
3209
+
3210
+ if (option.$div) {
3211
+ return option.$div;
3212
+ }
3213
+
3214
+ if (create) {
3215
+ return this._render('option', option);
3216
+ }
3217
+ }
3218
+
3219
+ return null;
3220
+ }
3221
+ /**
3222
+ * Returns the dom element of the next or previous dom element of the same type
3223
+ * Note: adjacent options may not be adjacent DOM elements (optgroups)
3224
+ *
3225
+ */
3226
+
3227
+
3228
+ getAdjacent(option, direction, type = 'option') {
3229
+ var self = this,
3230
+ all;
3231
+
3232
+ if (!option) {
3233
+ return null;
3234
+ }
3235
+
3236
+ if (type == 'item') {
3237
+ all = self.controlChildren();
3238
+ } else {
3239
+ all = self.dropdown_content.querySelectorAll('[data-selectable]');
3240
+ }
3241
+
3242
+ for (let i = 0; i < all.length; i++) {
3243
+ if (all[i] != option) {
3244
+ continue;
3245
+ }
3246
+
3247
+ if (direction > 0) {
3248
+ return all[i + 1];
3249
+ }
3250
+
3251
+ return all[i - 1];
3252
+ }
3253
+
3254
+ return null;
3255
+ }
3256
+ /**
3257
+ * Returns the dom element of the item
3258
+ * matching the given value.
3259
+ *
3260
+ */
3261
+
3262
+
3263
+ getItem(item) {
3264
+ if (typeof item == 'object') {
3265
+ return item;
3266
+ }
3267
+
3268
+ var value = hash_key(item);
3269
+ return value !== null ? this.control.querySelector(`[data-value="${addSlashes(value)}"]`) : null;
3270
+ }
3271
+ /**
3272
+ * "Selects" multiple items at once. Adds them to the list
3273
+ * at the current caret position.
3274
+ *
3275
+ */
3276
+
3277
+
3278
+ addItems(values, silent) {
3279
+ var self = this;
3280
+ var items = Array.isArray(values) ? values : [values];
3281
+ items = items.filter(x => self.items.indexOf(x) === -1);
3282
+
3283
+ for (let i = 0, n = items.length; i < n; i++) {
3284
+ self.isPending = i < n - 1;
3285
+ self.addItem(items[i], silent);
3286
+ }
3287
+ }
3288
+ /**
3289
+ * "Selects" an item. Adds it to the list
3290
+ * at the current caret position.
3291
+ *
3292
+ */
3293
+
3294
+
3295
+ addItem(value, silent) {
3296
+ var events = silent ? [] : ['change', 'dropdown_close'];
3297
+ debounce_events(this, events, () => {
3298
+ var item, wasFull;
3299
+ const self = this;
3300
+ const inputMode = self.settings.mode;
3301
+ const hashed = hash_key(value);
3302
+
3303
+ if (hashed && self.items.indexOf(hashed) !== -1) {
3304
+ if (inputMode === 'single') {
3305
+ self.close();
3306
+ }
3307
+
3308
+ if (inputMode === 'single' || !self.settings.duplicates) {
3309
+ return;
3310
+ }
3311
+ }
3312
+
3313
+ if (hashed === null || !self.options.hasOwnProperty(hashed)) return;
3314
+ if (inputMode === 'single') self.clear(silent);
3315
+ if (inputMode === 'multi' && self.isFull()) return;
3316
+ item = self._render('item', self.options[hashed]);
3317
+
3318
+ if (self.control.contains(item)) {
3319
+ // duplicates
3320
+ item = item.cloneNode(true);
3321
+ }
3322
+
3323
+ wasFull = self.isFull();
3324
+ self.items.splice(self.caretPos, 0, hashed);
3325
+ self.insertAtCaret(item);
3326
+
3327
+ if (self.isSetup) {
3328
+ // update menu / remove the option (if this is not one item being added as part of series)
3329
+ if (!self.isPending && self.settings.hideSelected) {
3330
+ let option = self.getOption(hashed);
3331
+ let next = self.getAdjacent(option, 1);
3332
+
3333
+ if (next) {
3334
+ self.setActiveOption(next);
3335
+ }
3336
+ } // refreshOptions after setActiveOption(),
3337
+ // otherwise setActiveOption() will be called by refreshOptions() with the wrong value
3338
+
3339
+
3340
+ if (!self.isPending && !self.settings.closeAfterSelect) {
3341
+ self.refreshOptions(self.isFocused && inputMode !== 'single');
3342
+ } // hide the menu if the maximum number of items have been selected or no options are left
3343
+
3344
+
3345
+ if (self.settings.closeAfterSelect != false && self.isFull()) {
3346
+ self.close();
3347
+ } else if (!self.isPending) {
3348
+ self.positionDropdown();
3349
+ }
3350
+
3351
+ self.trigger('item_add', hashed, item);
3352
+
3353
+ if (!self.isPending) {
3354
+ self.updateOriginalInput({
3355
+ silent: silent
3356
+ });
3357
+ }
3358
+ }
3359
+
3360
+ if (!self.isPending || !wasFull && self.isFull()) {
3361
+ self.inputState();
3362
+ self.refreshState();
3363
+ }
3364
+ });
3365
+ }
3366
+ /**
3367
+ * Removes the selected item matching
3368
+ * the provided value.
3369
+ *
3370
+ */
3371
+
3372
+
3373
+ removeItem(item = null, silent) {
3374
+ const self = this;
3375
+ item = self.getItem(item);
3376
+ if (!item) return;
3377
+ var i, idx;
3378
+ const value = item.dataset.value;
3379
+ i = nodeIndex(item);
3380
+ item.remove();
3381
+
3382
+ if (item.classList.contains('active')) {
3383
+ idx = self.activeItems.indexOf(item);
3384
+ self.activeItems.splice(idx, 1);
3385
+ removeClasses(item, 'active');
3386
+ }
3387
+
3388
+ self.items.splice(i, 1);
3389
+ self.lastQuery = null;
3390
+
3391
+ if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
3392
+ self.removeOption(value, silent);
3393
+ }
3394
+
3395
+ if (i < self.caretPos) {
3396
+ self.setCaret(self.caretPos - 1);
3397
+ }
3398
+
3399
+ self.updateOriginalInput({
3400
+ silent: silent
3401
+ });
3402
+ self.refreshState();
3403
+ self.positionDropdown();
3404
+ self.trigger('item_remove', value, item);
3405
+ }
3406
+ /**
3407
+ * Invokes the `create` method provided in the
3408
+ * TomSelect options that should provide the data
3409
+ * for the new item, given the user input.
3410
+ *
3411
+ * Once this completes, it will be added
3412
+ * to the item list.
3413
+ *
3414
+ */
3415
+
3416
+
3417
+ createItem(input = null, triggerDropdown = true, callback = () => {}) {
3418
+ var self = this;
3419
+ var caret = self.caretPos;
3420
+ var output;
3421
+ input = input || self.inputValue();
3422
+
3423
+ if (!self.canCreate(input)) {
3424
+ callback();
3425
+ return false;
3426
+ }
3427
+
3428
+ self.lock();
3429
+ var created = false;
3430
+
3431
+ var create = data => {
3432
+ self.unlock();
3433
+ if (!data || typeof data !== 'object') return callback();
3434
+ var value = hash_key(data[self.settings.valueField]);
3435
+
3436
+ if (typeof value !== 'string') {
3437
+ return callback();
3438
+ }
3439
+
3440
+ self.setTextboxValue();
3441
+ self.addOption(data, true);
3442
+ self.setCaret(caret);
3443
+ self.addItem(value);
3444
+ callback(data);
3445
+ created = true;
3446
+ };
3447
+
3448
+ if (typeof self.settings.create === 'function') {
3449
+ output = self.settings.create.call(this, input, create);
3450
+ } else {
3451
+ output = {
3452
+ [self.settings.labelField]: input,
3453
+ [self.settings.valueField]: input
3454
+ };
3455
+ }
3456
+
3457
+ if (!created) {
3458
+ create(output);
3459
+ }
3460
+
3461
+ return true;
3462
+ }
3463
+ /**
3464
+ * Re-renders the selected item lists.
3465
+ */
3466
+
3467
+
3468
+ refreshItems() {
3469
+ var self = this;
3470
+ self.lastQuery = null;
3471
+
3472
+ if (self.isSetup) {
3473
+ self.addItems(self.items);
3474
+ }
3475
+
3476
+ self.updateOriginalInput();
3477
+ self.refreshState();
3478
+ }
3479
+ /**
3480
+ * Updates all state-dependent attributes
3481
+ * and CSS classes.
3482
+ */
3483
+
3484
+
3485
+ refreshState() {
3486
+ const self = this;
3487
+ self.refreshValidityState();
3488
+ const isFull = self.isFull();
3489
+ const isLocked = self.isLocked;
3490
+ self.wrapper.classList.toggle('rtl', self.rtl);
3491
+ const wrap_classList = self.wrapper.classList;
3492
+ wrap_classList.toggle('focus', self.isFocused);
3493
+ wrap_classList.toggle('disabled', self.isDisabled);
3494
+ wrap_classList.toggle('required', self.isRequired);
3495
+ wrap_classList.toggle('invalid', !self.isValid);
3496
+ wrap_classList.toggle('locked', isLocked);
3497
+ wrap_classList.toggle('full', isFull);
3498
+ wrap_classList.toggle('input-active', self.isFocused && !self.isInputHidden);
3499
+ wrap_classList.toggle('dropdown-active', self.isOpen);
3500
+ wrap_classList.toggle('has-options', isEmptyObject(self.options));
3501
+ wrap_classList.toggle('has-items', self.items.length > 0);
3502
+ }
3503
+ /**
3504
+ * Update the `required` attribute of both input and control input.
3505
+ *
3506
+ * The `required` property needs to be activated on the control input
3507
+ * for the error to be displayed at the right place. `required` also
3508
+ * needs to be temporarily deactivated on the input since the input is
3509
+ * hidden and can't show errors.
3510
+ */
3511
+
3512
+
3513
+ refreshValidityState() {
3514
+ var self = this;
3515
+
3516
+ if (!self.input.checkValidity) {
3517
+ return;
3518
+ }
3519
+
3520
+ self.isValid = self.input.checkValidity();
3521
+ self.isInvalid = !self.isValid;
3522
+ }
3523
+ /**
3524
+ * Determines whether or not more items can be added
3525
+ * to the control without exceeding the user-defined maximum.
3526
+ *
3527
+ * @returns {boolean}
3528
+ */
3529
+
3530
+
3531
+ isFull() {
3532
+ return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems;
3533
+ }
3534
+ /**
3535
+ * Refreshes the original <select> or <input>
3536
+ * element to reflect the current state.
3537
+ *
3538
+ */
3539
+
3540
+
3541
+ updateOriginalInput(opts = {}) {
3542
+ const self = this;
3543
+ var option, label;
3544
+ const empty_option = self.input.querySelector('option[value=""]');
3545
+
3546
+ if (self.is_select_tag) {
3547
+ const selected = [];
3548
+ const has_selected = self.input.querySelectorAll('option:checked').length;
3549
+
3550
+ function AddSelected(option_el, value, label) {
3551
+ if (!option_el) {
3552
+ option_el = getDom('<option value="' + escape_html(value) + '">' + escape_html(label) + '</option>');
3553
+ } // don't move empty option from top of list
3554
+ // fixes bug in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1725293
3555
+
3556
+
3557
+ if (option_el != empty_option) {
3558
+ self.input.append(option_el);
3559
+ }
3560
+
3561
+ selected.push(option_el); // marking empty option as selected can break validation
3562
+ // fixes https://github.com/orchidjs/tom-select/issues/303
3563
+
3564
+ if (option_el != empty_option || has_selected > 0) {
3565
+ option_el.selected = true;
3566
+ }
3567
+
3568
+ return option_el;
3569
+ } // unselect all selected options
3570
+
3571
+
3572
+ self.input.querySelectorAll('option:checked').forEach(option_el => {
3573
+ option_el.selected = false;
3574
+ }); // nothing selected?
3575
+
3576
+ if (self.items.length == 0 && self.settings.mode == 'single') {
3577
+ AddSelected(empty_option, "", ""); // order selected <option> tags for values in self.items
3578
+ } else {
3579
+ self.items.forEach(value => {
3580
+ option = self.options[value];
3581
+ label = option[self.settings.labelField] || '';
3582
+
3583
+ if (selected.includes(option.$option)) {
3584
+ const reuse_opt = self.input.querySelector(`option[value="${addSlashes(value)}"]:not(:checked)`);
3585
+ AddSelected(reuse_opt, value, label);
3586
+ } else {
3587
+ option.$option = AddSelected(option.$option, value, label);
3588
+ }
3589
+ });
3590
+ }
3591
+ } else {
3592
+ self.input.value = self.getValue();
3593
+ }
3594
+
3595
+ if (self.isSetup) {
3596
+ if (!opts.silent) {
3597
+ self.trigger('change', self.getValue());
3598
+ }
3599
+ }
3600
+ }
3601
+ /**
3602
+ * Shows the autocomplete dropdown containing
3603
+ * the available options.
3604
+ */
3605
+
3606
+
3607
+ open() {
3608
+ var self = this;
3609
+ if (self.isLocked || self.isOpen || self.settings.mode === 'multi' && self.isFull()) return;
3610
+ self.isOpen = true;
3611
+ setAttr(self.focus_node, {
3612
+ 'aria-expanded': 'true'
3613
+ });
3614
+ self.refreshState();
3615
+ applyCSS(self.dropdown, {
3616
+ visibility: 'hidden',
3617
+ display: 'block'
3618
+ });
3619
+ self.positionDropdown();
3620
+ applyCSS(self.dropdown, {
3621
+ visibility: 'visible',
3622
+ display: 'block'
3623
+ });
3624
+ self.focus();
3625
+ self.trigger('dropdown_open', self.dropdown);
3626
+ }
3627
+ /**
3628
+ * Closes the autocomplete dropdown menu.
3629
+ */
3630
+
3631
+
3632
+ close(setTextboxValue = true) {
3633
+ var self = this;
3634
+ var trigger = self.isOpen;
3635
+
3636
+ if (setTextboxValue) {
3637
+ // before blur() to prevent form onchange event
3638
+ self.setTextboxValue();
3639
+
3640
+ if (self.settings.mode === 'single' && self.items.length) {
3641
+ self.hideInput();
3642
+ }
3643
+ }
3644
+
3645
+ self.isOpen = false;
3646
+ setAttr(self.focus_node, {
3647
+ 'aria-expanded': 'false'
3648
+ });
3649
+ applyCSS(self.dropdown, {
3650
+ display: 'none'
3651
+ });
3652
+
3653
+ if (self.settings.hideSelected) {
3654
+ self.clearActiveOption();
3655
+ }
3656
+
3657
+ self.refreshState();
3658
+ if (trigger) self.trigger('dropdown_close', self.dropdown);
3659
+ }
3660
+ /**
3661
+ * Calculates and applies the appropriate
3662
+ * position of the dropdown if dropdownParent = 'body'.
3663
+ * Otherwise, position is determined by css
3664
+ */
3665
+
3666
+
3667
+ positionDropdown() {
3668
+ if (this.settings.dropdownParent !== 'body') {
3669
+ return;
3670
+ }
3671
+
3672
+ var context = this.control;
3673
+ var rect = context.getBoundingClientRect();
3674
+ var top = context.offsetHeight + rect.top + window.scrollY;
3675
+ var left = rect.left + window.scrollX;
3676
+ applyCSS(this.dropdown, {
3677
+ width: rect.width + 'px',
3678
+ top: top + 'px',
3679
+ left: left + 'px'
3680
+ });
3681
+ }
3682
+ /**
3683
+ * Resets / clears all selected items
3684
+ * from the control.
3685
+ *
3686
+ */
3687
+
3688
+
3689
+ clear(silent) {
3690
+ var self = this;
3691
+ if (!self.items.length) return;
3692
+ var items = self.controlChildren();
3693
+ iterate(items, item => {
3694
+ self.removeItem(item, true);
3695
+ });
3696
+ self.showInput();
3697
+ if (!silent) self.updateOriginalInput();
3698
+ self.trigger('clear');
3699
+ }
3700
+ /**
3701
+ * A helper method for inserting an element
3702
+ * at the current caret position.
3703
+ *
3704
+ */
3705
+
3706
+
3707
+ insertAtCaret(el) {
3708
+ const self = this;
3709
+ const caret = self.caretPos;
3710
+ const target = self.control;
3711
+ target.insertBefore(el, target.children[caret]);
3712
+ self.setCaret(caret + 1);
3713
+ }
3714
+ /**
3715
+ * Removes the current selected item(s).
3716
+ *
3717
+ */
3718
+
3719
+
3720
+ deleteSelection(e) {
3721
+ var direction, selection, caret, tail;
3722
+ var self = this;
3723
+ direction = e && e.keyCode === KEY_BACKSPACE ? -1 : 1;
3724
+ selection = getSelection(self.control_input); // determine items that will be removed
3725
+
3726
+ const rm_items = [];
3727
+
3728
+ if (self.activeItems.length) {
3729
+ tail = getTail(self.activeItems, direction);
3730
+ caret = nodeIndex(tail);
3731
+
3732
+ if (direction > 0) {
3733
+ caret++;
3734
+ }
3735
+
3736
+ iterate(self.activeItems, item => rm_items.push(item));
3737
+ } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) {
3738
+ const items = self.controlChildren();
3739
+
3740
+ if (direction < 0 && selection.start === 0 && selection.length === 0) {
3741
+ rm_items.push(items[self.caretPos - 1]);
3742
+ } else if (direction > 0 && selection.start === self.inputValue().length) {
3743
+ rm_items.push(items[self.caretPos]);
3744
+ }
3745
+ }
3746
+
3747
+ const values = rm_items.map(item => item.dataset.value); // allow the callback to abort
3748
+
3749
+ if (!values.length || typeof self.settings.onDelete === 'function' && self.settings.onDelete.call(self, values, e) === false) {
3750
+ return false;
3751
+ }
3752
+
3753
+ preventDefault(e, true); // perform removal
3754
+
3755
+ if (typeof caret !== 'undefined') {
3756
+ self.setCaret(caret);
3757
+ }
3758
+
3759
+ while (rm_items.length) {
3760
+ self.removeItem(rm_items.pop());
3761
+ }
3762
+
3763
+ self.showInput();
3764
+ self.positionDropdown();
3765
+ self.refreshOptions(false);
3766
+ return true;
3767
+ }
3768
+ /**
3769
+ * Selects the previous / next item (depending on the `direction` argument).
3770
+ *
3771
+ * > 0 - right
3772
+ * < 0 - left
3773
+ *
3774
+ */
3775
+
3776
+
3777
+ advanceSelection(direction, e) {
3778
+ var last_active,
3779
+ adjacent,
3780
+ self = this;
3781
+ if (self.rtl) direction *= -1;
3782
+ if (self.inputValue().length) return; // add or remove to active items
3783
+
3784
+ if (isKeyDown(KEY_SHORTCUT, e) || isKeyDown('shiftKey', e)) {
3785
+ last_active = self.getLastActive(direction);
3786
+
3787
+ if (last_active) {
3788
+ if (!last_active.classList.contains('active')) {
3789
+ adjacent = last_active;
3790
+ } else {
3791
+ adjacent = self.getAdjacent(last_active, direction, 'item');
3792
+ } // if no active item, get items adjacent to the control input
3793
+
3794
+ } else if (direction > 0) {
3795
+ adjacent = self.control_input.nextElementSibling;
3796
+ } else {
3797
+ adjacent = self.control_input.previousElementSibling;
3798
+ }
3799
+
3800
+ if (adjacent) {
3801
+ if (adjacent.classList.contains('active')) {
3802
+ self.removeActiveItem(last_active);
3803
+ }
3804
+
3805
+ self.setActiveItemClass(adjacent); // mark as last_active !! after removeActiveItem() on last_active
3806
+ } // move caret to the left or right
3807
+
3808
+ } else {
3809
+ self.moveCaret(direction);
3810
+ }
3811
+ }
3812
+
3813
+ moveCaret(direction) {}
3814
+ /**
3815
+ * Get the last active item
3816
+ *
3817
+ */
3818
+
3819
+
3820
+ getLastActive(direction) {
3821
+ let last_active = this.control.querySelector('.last-active');
3822
+
3823
+ if (last_active) {
3824
+ return last_active;
3825
+ }
3826
+
3827
+ var result = this.control.querySelectorAll('.active');
3828
+
3829
+ if (result) {
3830
+ return getTail(result, direction);
3831
+ }
3832
+ }
3833
+ /**
3834
+ * Moves the caret to the specified index.
3835
+ *
3836
+ * The input must be moved by leaving it in place and moving the
3837
+ * siblings, due to the fact that focus cannot be restored once lost
3838
+ * on mobile webkit devices
3839
+ *
3840
+ */
3841
+
3842
+
3843
+ setCaret(new_pos) {
3844
+ this.caretPos = this.items.length;
3845
+ }
3846
+ /**
3847
+ * Return list of item dom elements
3848
+ *
3849
+ */
3850
+
3851
+
3852
+ controlChildren() {
3853
+ return Array.from(this.control.querySelectorAll('[data-ts-item]'));
3854
+ }
3855
+ /**
3856
+ * Disables user input on the control. Used while
3857
+ * items are being asynchronously created.
3858
+ */
3859
+
3860
+
3861
+ lock() {
3862
+ this.isLocked = true;
3863
+ this.refreshState();
3864
+ }
3865
+ /**
3866
+ * Re-enables user input on the control.
3867
+ */
3868
+
3869
+
3870
+ unlock() {
3871
+ this.isLocked = false;
3872
+ this.refreshState();
3873
+ }
3874
+ /**
3875
+ * Disables user input on the control completely.
3876
+ * While disabled, it cannot receive focus.
3877
+ */
3878
+
3879
+
3880
+ disable() {
3881
+ var self = this;
3882
+ self.input.disabled = true;
3883
+ self.control_input.disabled = true;
3884
+ self.focus_node.tabIndex = -1;
3885
+ self.isDisabled = true;
3886
+ this.close();
3887
+ self.lock();
3888
+ }
3889
+ /**
3890
+ * Enables the control so that it can respond
3891
+ * to focus and user input.
3892
+ */
3893
+
3894
+
3895
+ enable() {
3896
+ var self = this;
3897
+ self.input.disabled = false;
3898
+ self.control_input.disabled = false;
3899
+ self.focus_node.tabIndex = self.tabIndex;
3900
+ self.isDisabled = false;
3901
+ self.unlock();
3902
+ }
3903
+ /**
3904
+ * Completely destroys the control and
3905
+ * unbinds all event listeners so that it can
3906
+ * be garbage collected.
3907
+ */
3908
+
3909
+
3910
+ destroy() {
3911
+ var self = this;
3912
+ var revertSettings = self.revertSettings;
3913
+ self.trigger('destroy');
3914
+ self.off();
3915
+ self.wrapper.remove();
3916
+ self.dropdown.remove();
3917
+ self.input.innerHTML = revertSettings.innerHTML;
3918
+ self.input.tabIndex = revertSettings.tabIndex;
3919
+ removeClasses(self.input, 'tomselected', 'ts-hidden-accessible');
3920
+
3921
+ self._destroy();
3922
+
3923
+ delete self.input.tomselect;
3924
+ }
3925
+ /**
3926
+ * A helper method for rendering "item" and
3927
+ * "option" templates, given the data.
3928
+ *
3929
+ */
3930
+
3931
+
3932
+ render(templateName, data) {
3933
+ if (typeof this.settings.render[templateName] !== 'function') {
3934
+ return null;
3935
+ }
3936
+
3937
+ return this._render(templateName, data);
3938
+ }
3939
+ /**
3940
+ * _render() can be called directly when we know we don't want to hit the cache
3941
+ * return type could be null for some templates, we need https://github.com/microsoft/TypeScript/issues/33014
3942
+ */
3943
+
3944
+
3945
+ _render(templateName, data) {
3946
+ var value = '',
3947
+ id,
3948
+ html;
3949
+ const self = this;
3950
+
3951
+ if (templateName === 'option' || templateName == 'item') {
3952
+ value = get_hash(data[self.settings.valueField]);
3953
+ } // render markup
3954
+
3955
+
3956
+ html = self.settings.render[templateName].call(this, data, escape_html);
3957
+
3958
+ if (html == null) {
3959
+ return html;
3960
+ }
3961
+
3962
+ html = getDom(html); // add mandatory attributes
3963
+
3964
+ if (templateName === 'option' || templateName === 'option_create') {
3965
+ if (data[self.settings.disabledField]) {
3966
+ setAttr(html, {
3967
+ 'aria-disabled': 'true'
3968
+ });
3969
+ } else {
3970
+ setAttr(html, {
3971
+ 'data-selectable': ''
3972
+ });
3973
+ }
3974
+ } else if (templateName === 'optgroup') {
3975
+ id = data.group[self.settings.optgroupValueField];
3976
+ setAttr(html, {
3977
+ 'data-group': id
3978
+ });
3979
+
3980
+ if (data.group[self.settings.disabledField]) {
3981
+ setAttr(html, {
3982
+ 'data-disabled': ''
3983
+ });
3984
+ }
3985
+ }
3986
+
3987
+ if (templateName === 'option' || templateName === 'item') {
3988
+ setAttr(html, {
3989
+ 'data-value': value
3990
+ }); // make sure we have some classes if a template is overwritten
3991
+
3992
+ if (templateName === 'item') {
3993
+ addClasses(html, self.settings.itemClass);
3994
+ setAttr(html, {
3995
+ 'data-ts-item': ''
3996
+ });
3997
+ } else {
3998
+ addClasses(html, self.settings.optionClass);
3999
+ setAttr(html, {
4000
+ role: 'option',
4001
+ id: data.$id
4002
+ }); // update cache
4003
+
4004
+ self.options[value].$div = html;
4005
+ }
4006
+ }
4007
+
4008
+ return html;
4009
+ }
4010
+ /**
4011
+ * Clears the render cache for a template. If
4012
+ * no template is given, clears all render
4013
+ * caches.
4014
+ *
4015
+ */
4016
+
4017
+
4018
+ clearCache() {
4019
+ iterate(this.options, (option, value) => {
4020
+ if (option.$div) {
4021
+ option.$div.remove();
4022
+ delete option.$div;
4023
+ }
4024
+ });
4025
+ }
4026
+ /**
4027
+ * Removes a value from item and option caches
4028
+ *
4029
+ */
4030
+
4031
+
4032
+ uncacheValue(value) {
4033
+ const option_el = this.getOption(value);
4034
+ if (option_el) option_el.remove();
4035
+ }
4036
+ /**
4037
+ * Determines whether or not to display the
4038
+ * create item prompt, given a user input.
4039
+ *
4040
+ */
4041
+
4042
+
4043
+ canCreate(input) {
4044
+ return this.settings.create && input.length > 0 && this.settings.createFilter.call(this, input);
4045
+ }
4046
+ /**
4047
+ * Wraps this.`method` so that `new_fn` can be invoked 'before', 'after', or 'instead' of the original method
4048
+ *
4049
+ * this.hook('instead','onKeyDown',function( arg1, arg2 ...){
4050
+ *
4051
+ * });
4052
+ */
4053
+
4054
+
4055
+ hook(when, method, new_fn) {
4056
+ var self = this;
4057
+ var orig_method = self[method];
4058
+
4059
+ self[method] = function () {
4060
+ var result, result_new;
4061
+
4062
+ if (when === 'after') {
4063
+ result = orig_method.apply(self, arguments);
4064
+ }
4065
+
4066
+ result_new = new_fn.apply(self, arguments);
4067
+
4068
+ if (when === 'instead') {
4069
+ return result_new;
4070
+ }
4071
+
4072
+ if (when === 'before') {
4073
+ result = orig_method.apply(self, arguments);
4074
+ }
4075
+
4076
+ return result;
4077
+ };
4078
+ }
4079
+
4080
+ }
4081
+
4082
+ /**
4083
+ * Plugin: "change_listener" (Tom Select)
4084
+ * Copyright (c) contributors
4085
+ *
4086
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4087
+ * file except in compliance with the License. You may obtain a copy of the License at:
4088
+ * http://www.apache.org/licenses/LICENSE-2.0
4089
+ *
4090
+ * Unless required by applicable law or agreed to in writing, software distributed under
4091
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4092
+ * ANY KIND, either express or implied. See the License for the specific language
4093
+ * governing permissions and limitations under the License.
4094
+ *
4095
+ */
4096
+ function change_listener () {
4097
+ addEvent(this.input, 'change', () => {
4098
+ this.sync();
4099
+ });
4100
+ }
4101
+
4102
+ /**
4103
+ * Plugin: "restore_on_backspace" (Tom Select)
4104
+ * Copyright (c) contributors
4105
+ *
4106
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4107
+ * file except in compliance with the License. You may obtain a copy of the License at:
4108
+ * http://www.apache.org/licenses/LICENSE-2.0
4109
+ *
4110
+ * Unless required by applicable law or agreed to in writing, software distributed under
4111
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4112
+ * ANY KIND, either express or implied. See the License for the specific language
4113
+ * governing permissions and limitations under the License.
4114
+ *
4115
+ */
4116
+ function checkbox_options () {
4117
+ var self = this;
4118
+ var orig_onOptionSelect = self.onOptionSelect;
4119
+ self.settings.hideSelected = false; // update the checkbox for an option
4120
+
4121
+ var UpdateCheckbox = function UpdateCheckbox(option) {
4122
+ setTimeout(() => {
4123
+ var checkbox = option.querySelector('input');
4124
+
4125
+ if (option.classList.contains('selected')) {
4126
+ checkbox.checked = true;
4127
+ } else {
4128
+ checkbox.checked = false;
4129
+ }
4130
+ }, 1);
4131
+ }; // add checkbox to option template
4132
+
4133
+
4134
+ self.hook('after', 'setupTemplates', () => {
4135
+ var orig_render_option = self.settings.render.option;
4136
+
4137
+ self.settings.render.option = (data, escape_html) => {
4138
+ var rendered = getDom(orig_render_option.call(self, data, escape_html));
4139
+ var checkbox = document.createElement('input');
4140
+ checkbox.addEventListener('click', function (evt) {
4141
+ preventDefault(evt);
4142
+ });
4143
+ checkbox.type = 'checkbox';
4144
+ const hashed = hash_key(data[self.settings.valueField]);
4145
+
4146
+ if (hashed && self.items.indexOf(hashed) > -1) {
4147
+ checkbox.checked = true;
4148
+ }
4149
+
4150
+ rendered.prepend(checkbox);
4151
+ return rendered;
4152
+ };
4153
+ }); // uncheck when item removed
4154
+
4155
+ self.on('item_remove', value => {
4156
+ var option = self.getOption(value);
4157
+
4158
+ if (option) {
4159
+ // if dropdown hasn't been opened yet, the option won't exist
4160
+ option.classList.remove('selected'); // selected class won't be removed yet
4161
+
4162
+ UpdateCheckbox(option);
4163
+ }
4164
+ }); // check when item added
4165
+
4166
+ self.on('item_add', value => {
4167
+ var option = self.getOption(value);
4168
+
4169
+ if (option) {
4170
+ // if dropdown hasn't been opened yet, the option won't exist
4171
+ UpdateCheckbox(option);
4172
+ }
4173
+ }); // remove items when selected option is clicked
4174
+
4175
+ self.hook('instead', 'onOptionSelect', (evt, option) => {
4176
+ if (option.classList.contains('selected')) {
4177
+ option.classList.remove('selected');
4178
+ self.removeItem(option.dataset.value);
4179
+ self.refreshOptions();
4180
+ preventDefault(evt, true);
4181
+ return;
4182
+ }
4183
+
4184
+ orig_onOptionSelect.call(self, evt, option);
4185
+ UpdateCheckbox(option);
4186
+ });
4187
+ }
4188
+
4189
+ /**
4190
+ * Plugin: "dropdown_header" (Tom Select)
4191
+ * Copyright (c) contributors
4192
+ *
4193
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4194
+ * file except in compliance with the License. You may obtain a copy of the License at:
4195
+ * http://www.apache.org/licenses/LICENSE-2.0
4196
+ *
4197
+ * Unless required by applicable law or agreed to in writing, software distributed under
4198
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4199
+ * ANY KIND, either express or implied. See the License for the specific language
4200
+ * governing permissions and limitations under the License.
4201
+ *
4202
+ */
4203
+ function clear_button (userOptions) {
4204
+ const self = this;
4205
+ const options = Object.assign({
4206
+ className: 'clear-button',
4207
+ title: 'Clear All',
4208
+ html: data => {
4209
+ return `<div class="${data.className}" title="${data.title}">&times;</div>`;
4210
+ }
4211
+ }, userOptions);
4212
+ self.on('initialize', () => {
4213
+ var button = getDom(options.html(options));
4214
+ button.addEventListener('click', evt => {
4215
+ if (self.isDisabled) {
4216
+ return;
4217
+ }
4218
+
4219
+ self.clear();
4220
+
4221
+ if (self.settings.mode === 'single' && self.settings.allowEmptyOption) {
4222
+ self.addItem('');
4223
+ }
4224
+
4225
+ evt.preventDefault();
4226
+ evt.stopPropagation();
4227
+ });
4228
+ self.control.appendChild(button);
4229
+ });
4230
+ }
4231
+
4232
+ /**
4233
+ * Plugin: "drag_drop" (Tom Select)
4234
+ * Copyright (c) contributors
4235
+ *
4236
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4237
+ * file except in compliance with the License. You may obtain a copy of the License at:
4238
+ * http://www.apache.org/licenses/LICENSE-2.0
4239
+ *
4240
+ * Unless required by applicable law or agreed to in writing, software distributed under
4241
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4242
+ * ANY KIND, either express or implied. See the License for the specific language
4243
+ * governing permissions and limitations under the License.
4244
+ *
4245
+ */
4246
+ function drag_drop () {
4247
+ var self = this;
4248
+ if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');
4249
+ if (self.settings.mode !== 'multi') return;
4250
+ var orig_lock = self.lock;
4251
+ var orig_unlock = self.unlock;
4252
+ self.hook('instead', 'lock', () => {
4253
+ var sortable = $(self.control).data('sortable');
4254
+ if (sortable) sortable.disable();
4255
+ return orig_lock.call(self);
4256
+ });
4257
+ self.hook('instead', 'unlock', () => {
4258
+ var sortable = $(self.control).data('sortable');
4259
+ if (sortable) sortable.enable();
4260
+ return orig_unlock.call(self);
4261
+ });
4262
+ self.on('initialize', () => {
4263
+ var $control = $(self.control).sortable({
4264
+ items: '[data-value]',
4265
+ forcePlaceholderSize: true,
4266
+ disabled: self.isLocked,
4267
+ start: (e, ui) => {
4268
+ ui.placeholder.css('width', ui.helper.css('width'));
4269
+ $control.css({
4270
+ overflow: 'visible'
4271
+ });
4272
+ },
4273
+ stop: () => {
4274
+ $control.css({
4275
+ overflow: 'hidden'
4276
+ });
4277
+ var values = [];
4278
+ $control.children('[data-value]').each(function () {
4279
+ if (this.dataset.value) values.push(this.dataset.value);
4280
+ });
4281
+ self.setValue(values);
4282
+ }
4283
+ });
4284
+ });
4285
+ }
4286
+
4287
+ /**
4288
+ * Plugin: "dropdown_header" (Tom Select)
4289
+ * Copyright (c) contributors
4290
+ *
4291
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4292
+ * file except in compliance with the License. You may obtain a copy of the License at:
4293
+ * http://www.apache.org/licenses/LICENSE-2.0
4294
+ *
4295
+ * Unless required by applicable law or agreed to in writing, software distributed under
4296
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4297
+ * ANY KIND, either express or implied. See the License for the specific language
4298
+ * governing permissions and limitations under the License.
4299
+ *
4300
+ */
4301
+ function dropdown_header (userOptions) {
4302
+ const self = this;
4303
+ const options = Object.assign({
4304
+ title: 'Untitled',
4305
+ headerClass: 'dropdown-header',
4306
+ titleRowClass: 'dropdown-header-title',
4307
+ labelClass: 'dropdown-header-label',
4308
+ closeClass: 'dropdown-header-close',
4309
+ html: data => {
4310
+ return '<div class="' + data.headerClass + '">' + '<div class="' + data.titleRowClass + '">' + '<span class="' + data.labelClass + '">' + data.title + '</span>' + '<a class="' + data.closeClass + '">&times;</a>' + '</div>' + '</div>';
4311
+ }
4312
+ }, userOptions);
4313
+ self.on('initialize', () => {
4314
+ var header = getDom(options.html(options));
4315
+ var close_link = header.querySelector('.' + options.closeClass);
4316
+
4317
+ if (close_link) {
4318
+ close_link.addEventListener('click', evt => {
4319
+ preventDefault(evt, true);
4320
+ self.close();
4321
+ });
4322
+ }
4323
+
4324
+ self.dropdown.insertBefore(header, self.dropdown.firstChild);
4325
+ });
4326
+ }
4327
+
4328
+ /**
4329
+ * Plugin: "dropdown_input" (Tom Select)
4330
+ * Copyright (c) contributors
4331
+ *
4332
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4333
+ * file except in compliance with the License. You may obtain a copy of the License at:
4334
+ * http://www.apache.org/licenses/LICENSE-2.0
4335
+ *
4336
+ * Unless required by applicable law or agreed to in writing, software distributed under
4337
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4338
+ * ANY KIND, either express or implied. See the License for the specific language
4339
+ * governing permissions and limitations under the License.
4340
+ *
4341
+ */
4342
+ function caret_position () {
4343
+ var self = this;
4344
+ /**
4345
+ * Moves the caret to the specified index.
4346
+ *
4347
+ * The input must be moved by leaving it in place and moving the
4348
+ * siblings, due to the fact that focus cannot be restored once lost
4349
+ * on mobile webkit devices
4350
+ *
4351
+ */
4352
+
4353
+ self.hook('instead', 'setCaret', new_pos => {
4354
+ if (self.settings.mode === 'single' || !self.control.contains(self.control_input)) {
4355
+ new_pos = self.items.length;
4356
+ } else {
4357
+ new_pos = Math.max(0, Math.min(self.items.length, new_pos));
4358
+
4359
+ if (new_pos != self.caretPos && !self.isPending) {
4360
+ self.controlChildren().forEach((child, j) => {
4361
+ if (j < new_pos) {
4362
+ self.control_input.insertAdjacentElement('beforebegin', child);
4363
+ } else {
4364
+ self.control.appendChild(child);
4365
+ }
4366
+ });
4367
+ }
4368
+ }
4369
+
4370
+ self.caretPos = new_pos;
4371
+ });
4372
+ self.hook('instead', 'moveCaret', direction => {
4373
+ if (!self.isFocused) return; // move caret before or after selected items
4374
+
4375
+ const last_active = self.getLastActive(direction);
4376
+
4377
+ if (last_active) {
4378
+ const idx = nodeIndex(last_active);
4379
+ self.setCaret(direction > 0 ? idx + 1 : idx);
4380
+ self.setActiveItem();
4381
+ removeClasses(last_active, 'last-active'); // move caret left or right of current position
4382
+ } else {
4383
+ self.setCaret(self.caretPos + direction);
4384
+ }
4385
+ });
4386
+ }
4387
+
4388
+ /**
4389
+ * Plugin: "dropdown_input" (Tom Select)
4390
+ * Copyright (c) contributors
4391
+ *
4392
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4393
+ * file except in compliance with the License. You may obtain a copy of the License at:
4394
+ * http://www.apache.org/licenses/LICENSE-2.0
4395
+ *
4396
+ * Unless required by applicable law or agreed to in writing, software distributed under
4397
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4398
+ * ANY KIND, either express or implied. See the License for the specific language
4399
+ * governing permissions and limitations under the License.
4400
+ *
4401
+ */
4402
+ function dropdown_input () {
4403
+ const self = this;
4404
+ self.settings.shouldOpen = true; // make sure the input is shown even if there are no options to display in the dropdown
4405
+
4406
+ self.hook('before', 'setup', () => {
4407
+ self.focus_node = self.control;
4408
+ addClasses(self.control_input, 'dropdown-input');
4409
+ const div = getDom('<div class="dropdown-input-wrap">');
4410
+ div.append(self.control_input);
4411
+ self.dropdown.insertBefore(div, self.dropdown.firstChild); // set a placeholder in the select control
4412
+
4413
+ const placeholder = getDom('<input class="items-placeholder" tabindex="-1" />');
4414
+ placeholder.placeholder = self.settings.placeholder || '';
4415
+ self.control.append(placeholder);
4416
+ });
4417
+ self.on('initialize', () => {
4418
+ // set tabIndex on control to -1, otherwise [shift+tab] will put focus right back on control_input
4419
+ self.control_input.addEventListener('keydown', evt => {
4420
+ //addEvent(self.control_input,'keydown' as const,(evt:KeyboardEvent) =>{
4421
+ switch (evt.keyCode) {
4422
+ case KEY_ESC:
4423
+ if (self.isOpen) {
4424
+ preventDefault(evt, true);
4425
+ self.close();
4426
+ }
4427
+
4428
+ self.clearActiveItems();
4429
+ return;
4430
+
4431
+ case KEY_TAB:
4432
+ self.focus_node.tabIndex = -1;
4433
+ break;
4434
+ }
4435
+
4436
+ return self.onKeyDown.call(self, evt);
4437
+ });
4438
+ self.on('blur', () => {
4439
+ self.focus_node.tabIndex = self.isDisabled ? -1 : self.tabIndex;
4440
+ }); // give the control_input focus when the dropdown is open
4441
+
4442
+ self.on('dropdown_open', () => {
4443
+ self.control_input.focus();
4444
+ }); // prevent onBlur from closing when focus is on the control_input
4445
+
4446
+ const orig_onBlur = self.onBlur;
4447
+ self.hook('instead', 'onBlur', evt => {
4448
+ if (evt && evt.relatedTarget == self.control_input) return;
4449
+ return orig_onBlur.call(self);
4450
+ });
4451
+ addEvent(self.control_input, 'blur', () => self.onBlur()); // return focus to control to allow further keyboard input
4452
+
4453
+ self.hook('before', 'close', () => {
4454
+ if (!self.isOpen) return;
4455
+ self.focus_node.focus();
4456
+ });
4457
+ });
4458
+ }
4459
+
4460
+ /**
4461
+ * Plugin: "input_autogrow" (Tom Select)
4462
+ *
4463
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4464
+ * file except in compliance with the License. You may obtain a copy of the License at:
4465
+ * http://www.apache.org/licenses/LICENSE-2.0
4466
+ *
4467
+ * Unless required by applicable law or agreed to in writing, software distributed under
4468
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4469
+ * ANY KIND, either express or implied. See the License for the specific language
4470
+ * governing permissions and limitations under the License.
4471
+ *
4472
+ */
4473
+ function input_autogrow () {
4474
+ var self = this;
4475
+ self.on('initialize', () => {
4476
+ var test_input = document.createElement('span');
4477
+ var control = self.control_input;
4478
+ test_input.style.cssText = 'position:absolute; top:-99999px; left:-99999px; width:auto; padding:0; white-space:pre; ';
4479
+ self.wrapper.appendChild(test_input);
4480
+ var transfer_styles = ['letterSpacing', 'fontSize', 'fontFamily', 'fontWeight', 'textTransform'];
4481
+
4482
+ for (const style_name of transfer_styles) {
4483
+ // @ts-ignore TS7015 https://stackoverflow.com/a/50506154/697576
4484
+ test_input.style[style_name] = control.style[style_name];
4485
+ }
4486
+ /**
4487
+ * Set the control width
4488
+ *
4489
+ */
4490
+
4491
+
4492
+ var resize = () => {
4493
+ if (self.items.length > 0) {
4494
+ test_input.textContent = control.value;
4495
+ control.style.width = test_input.clientWidth + 'px';
4496
+ } else {
4497
+ control.style.width = '';
4498
+ }
4499
+ };
4500
+
4501
+ resize();
4502
+ self.on('update item_add item_remove', resize);
4503
+ addEvent(control, 'input', resize);
4504
+ addEvent(control, 'keyup', resize);
4505
+ addEvent(control, 'blur', resize);
4506
+ addEvent(control, 'update', resize);
4507
+ });
4508
+ }
4509
+
4510
+ /**
4511
+ * Plugin: "input_autogrow" (Tom Select)
4512
+ *
4513
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4514
+ * file except in compliance with the License. You may obtain a copy of the License at:
4515
+ * http://www.apache.org/licenses/LICENSE-2.0
4516
+ *
4517
+ * Unless required by applicable law or agreed to in writing, software distributed under
4518
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4519
+ * ANY KIND, either express or implied. See the License for the specific language
4520
+ * governing permissions and limitations under the License.
4521
+ *
4522
+ */
4523
+ function no_backspace_delete () {
4524
+ var self = this;
4525
+ var orig_deleteSelection = self.deleteSelection;
4526
+ this.hook('instead', 'deleteSelection', evt => {
4527
+ if (self.activeItems.length) {
4528
+ return orig_deleteSelection.call(self, evt);
4529
+ }
4530
+
4531
+ return false;
4532
+ });
4533
+ }
4534
+
4535
+ /**
4536
+ * Plugin: "no_active_items" (Tom Select)
4537
+ *
4538
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4539
+ * file except in compliance with the License. You may obtain a copy of the License at:
4540
+ * http://www.apache.org/licenses/LICENSE-2.0
4541
+ *
4542
+ * Unless required by applicable law or agreed to in writing, software distributed under
4543
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4544
+ * ANY KIND, either express or implied. See the License for the specific language
4545
+ * governing permissions and limitations under the License.
4546
+ *
4547
+ */
4548
+ function no_active_items () {
4549
+ this.hook('instead', 'setActiveItem', () => {});
4550
+ this.hook('instead', 'selectAll', () => {});
4551
+ }
4552
+
4553
+ /**
4554
+ * Plugin: "optgroup_columns" (Tom Select.js)
4555
+ * Copyright (c) contributors
4556
+ *
4557
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4558
+ * file except in compliance with the License. You may obtain a copy of the License at:
4559
+ * http://www.apache.org/licenses/LICENSE-2.0
4560
+ *
4561
+ * Unless required by applicable law or agreed to in writing, software distributed under
4562
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4563
+ * ANY KIND, either express or implied. See the License for the specific language
4564
+ * governing permissions and limitations under the License.
4565
+ *
4566
+ */
4567
+ function optgroup_columns () {
4568
+ var self = this;
4569
+ var orig_keydown = self.onKeyDown;
4570
+ self.hook('instead', 'onKeyDown', evt => {
4571
+ var index, option, options, optgroup;
4572
+
4573
+ if (!self.isOpen || !(evt.keyCode === KEY_LEFT || evt.keyCode === KEY_RIGHT)) {
4574
+ return orig_keydown.call(self, evt);
4575
+ }
4576
+
4577
+ optgroup = parentMatch(self.activeOption, '[data-group]');
4578
+ index = nodeIndex(self.activeOption, '[data-selectable]');
4579
+
4580
+ if (!optgroup) {
4581
+ return;
4582
+ }
4583
+
4584
+ if (evt.keyCode === KEY_LEFT) {
4585
+ optgroup = optgroup.previousSibling;
4586
+ } else {
4587
+ optgroup = optgroup.nextSibling;
4588
+ }
4589
+
4590
+ if (!optgroup) {
4591
+ return;
4592
+ }
4593
+
4594
+ options = optgroup.querySelectorAll('[data-selectable]');
4595
+ option = options[Math.min(options.length - 1, index)];
4596
+
4597
+ if (option) {
4598
+ self.setActiveOption(option);
4599
+ }
4600
+ });
4601
+ }
4602
+
4603
+ /**
4604
+ * Plugin: "remove_button" (Tom Select)
4605
+ * Copyright (c) contributors
4606
+ *
4607
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4608
+ * file except in compliance with the License. You may obtain a copy of the License at:
4609
+ * http://www.apache.org/licenses/LICENSE-2.0
4610
+ *
4611
+ * Unless required by applicable law or agreed to in writing, software distributed under
4612
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4613
+ * ANY KIND, either express or implied. See the License for the specific language
4614
+ * governing permissions and limitations under the License.
4615
+ *
4616
+ */
4617
+ function remove_button (userOptions) {
4618
+ const options = Object.assign({
4619
+ label: '&times;',
4620
+ title: 'Remove',
4621
+ className: 'remove',
4622
+ append: true
4623
+ }, userOptions); //options.className = 'remove-single';
4624
+
4625
+ var self = this; // override the render method to add remove button to each item
4626
+
4627
+ if (!options.append) {
4628
+ return;
4629
+ }
4630
+
4631
+ var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>';
4632
+ self.hook('after', 'setupTemplates', () => {
4633
+ var orig_render_item = self.settings.render.item;
4634
+
4635
+ self.settings.render.item = (data, escape) => {
4636
+ var rendered = getDom(orig_render_item.call(self, data, escape));
4637
+ var close_button = getDom(html);
4638
+ rendered.appendChild(close_button);
4639
+ addEvent(close_button, 'mousedown', evt => {
4640
+ preventDefault(evt, true);
4641
+ });
4642
+ addEvent(close_button, 'click', evt => {
4643
+ // propagating will trigger the dropdown to show for single mode
4644
+ preventDefault(evt, true);
4645
+ if (self.isLocked) return;
4646
+ var value = rendered.dataset.value;
4647
+ self.removeItem(value);
4648
+ self.refreshOptions(false);
4649
+ self.inputState();
4650
+ });
4651
+ return rendered;
4652
+ };
4653
+ });
4654
+ }
4655
+
4656
+ /**
4657
+ * Plugin: "restore_on_backspace" (Tom Select)
4658
+ * Copyright (c) contributors
4659
+ *
4660
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4661
+ * file except in compliance with the License. You may obtain a copy of the License at:
4662
+ * http://www.apache.org/licenses/LICENSE-2.0
4663
+ *
4664
+ * Unless required by applicable law or agreed to in writing, software distributed under
4665
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4666
+ * ANY KIND, either express or implied. See the License for the specific language
4667
+ * governing permissions and limitations under the License.
4668
+ *
4669
+ */
4670
+ function restore_on_backspace (userOptions) {
4671
+ const self = this;
4672
+ const options = Object.assign({
4673
+ text: option => {
4674
+ return option[self.settings.labelField];
4675
+ }
4676
+ }, userOptions);
4677
+ self.on('item_remove', function (value) {
4678
+ if (!self.isFocused) {
4679
+ return;
4680
+ }
4681
+
4682
+ if (self.control_input.value.trim() === '') {
4683
+ var option = self.options[value];
4684
+
4685
+ if (option) {
4686
+ self.setTextboxValue(options.text.call(self, option));
4687
+ }
4688
+ }
4689
+ });
4690
+ }
4691
+
4692
+ /**
4693
+ * Plugin: "restore_on_backspace" (Tom Select)
4694
+ * Copyright (c) contributors
4695
+ *
4696
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
4697
+ * file except in compliance with the License. You may obtain a copy of the License at:
4698
+ * http://www.apache.org/licenses/LICENSE-2.0
4699
+ *
4700
+ * Unless required by applicable law or agreed to in writing, software distributed under
4701
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
4702
+ * ANY KIND, either express or implied. See the License for the specific language
4703
+ * governing permissions and limitations under the License.
4704
+ *
4705
+ */
4706
+ function virtual_scroll () {
4707
+ const self = this;
4708
+ const orig_canLoad = self.canLoad;
4709
+ const orig_clearActiveOption = self.clearActiveOption;
4710
+ const orig_loadCallback = self.loadCallback;
4711
+ var pagination = {};
4712
+ var dropdown_content;
4713
+ var loading_more = false;
4714
+ var load_more_opt;
4715
+
4716
+ if (!self.settings.shouldLoadMore) {
4717
+ // return true if additional results should be loaded
4718
+ self.settings.shouldLoadMore = function () {
4719
+ const scroll_percent = dropdown_content.clientHeight / (dropdown_content.scrollHeight - dropdown_content.scrollTop);
4720
+
4721
+ if (scroll_percent > 0.9) {
4722
+ return true;
4723
+ }
4724
+
4725
+ if (self.activeOption) {
4726
+ var selectable = self.selectable();
4727
+ var index = [...selectable].indexOf(self.activeOption);
4728
+
4729
+ if (index >= selectable.length - 2) {
4730
+ return true;
4731
+ }
4732
+ }
4733
+
4734
+ return false;
4735
+ };
4736
+ }
4737
+
4738
+ if (!self.settings.firstUrl) {
4739
+ throw 'virtual_scroll plugin requires a firstUrl() method';
4740
+ } // in order for virtual scrolling to work,
4741
+ // options need to be ordered the same way they're returned from the remote data source
4742
+
4743
+
4744
+ self.settings.sortField = [{
4745
+ field: '$order'
4746
+ }, {
4747
+ field: '$score'
4748
+ }]; // can we load more results for given query?
4749
+
4750
+ function canLoadMore(query) {
4751
+ if (typeof self.settings.maxOptions === 'number' && dropdown_content.children.length >= self.settings.maxOptions) {
4752
+ return false;
4753
+ }
4754
+
4755
+ if (query in pagination && pagination[query]) {
4756
+ return true;
4757
+ }
4758
+
4759
+ return false;
4760
+ } // set the next url that will be
4761
+
4762
+
4763
+ self.setNextUrl = function (value, next_url) {
4764
+ pagination[value] = next_url;
4765
+ }; // getUrl() to be used in settings.load()
4766
+
4767
+
4768
+ self.getUrl = function (query) {
4769
+ if (query in pagination) {
4770
+ const next_url = pagination[query];
4771
+ pagination[query] = false;
4772
+ return next_url;
4773
+ } // if the user goes back to a previous query
4774
+ // we need to load the first page again
4775
+
4776
+
4777
+ pagination = {};
4778
+ return self.settings.firstUrl.call(self, query);
4779
+ }; // don't clear the active option (and cause unwanted dropdown scroll)
4780
+ // while loading more results
4781
+
4782
+
4783
+ self.hook('instead', 'clearActiveOption', () => {
4784
+ if (loading_more) {
4785
+ return;
4786
+ }
4787
+
4788
+ return orig_clearActiveOption.call(self);
4789
+ }); // override the canLoad method
4790
+
4791
+ self.hook('instead', 'canLoad', query => {
4792
+ // first time the query has been seen
4793
+ if (!(query in pagination)) {
4794
+ return orig_canLoad.call(self, query);
4795
+ }
4796
+
4797
+ return canLoadMore(query);
4798
+ }); // wrap the load
4799
+
4800
+ self.hook('instead', 'loadCallback', (options, optgroups) => {
4801
+ if (!loading_more) {
4802
+ self.clearOptions();
4803
+ } else if (load_more_opt && options.length > 0) {
4804
+ load_more_opt.dataset.value = options[0][self.settings.valueField];
4805
+ }
4806
+
4807
+ orig_loadCallback.call(self, options, optgroups);
4808
+ loading_more = false;
4809
+ }); // add templates to dropdown
4810
+ // loading_more if we have another url in the queue
4811
+ // no_more_results if we don't have another url in the queue
4812
+
4813
+ self.hook('after', 'refreshOptions', () => {
4814
+ const query = self.lastValue;
4815
+ var option;
4816
+
4817
+ if (canLoadMore(query)) {
4818
+ option = self.render('loading_more', {
4819
+ query: query
4820
+ });
4821
+
4822
+ if (option) {
4823
+ option.setAttribute('data-selectable', ''); // so that navigating dropdown with [down] keypresses can navigate to this node
4824
+
4825
+ load_more_opt = option;
4826
+ }
4827
+ } else if (query in pagination && !dropdown_content.querySelector('.no-results')) {
4828
+ option = self.render('no_more_results', {
4829
+ query: query
4830
+ });
4831
+ }
4832
+
4833
+ if (option) {
4834
+ addClasses(option, self.settings.optionClass);
4835
+ dropdown_content.append(option);
4836
+ }
4837
+ }); // add scroll listener and default templates
4838
+
4839
+ self.on('initialize', () => {
4840
+ dropdown_content = self.dropdown_content; // default templates
4841
+
4842
+ self.settings.render = Object.assign({}, {
4843
+ loading_more: function () {
4844
+ return `<div class="loading-more-results">Loading more results ... </div>`;
4845
+ },
4846
+ no_more_results: function () {
4847
+ return `<div class="no-more-results">No more results</div>`;
4848
+ }
4849
+ }, self.settings.render); // watch dropdown content scroll position
4850
+
4851
+ dropdown_content.addEventListener('scroll', function () {
4852
+ if (!self.settings.shouldLoadMore.call(self)) {
4853
+ return;
4854
+ } // !important: this will get checked again in load() but we still need to check here otherwise loading_more will be set to true
4855
+
4856
+
4857
+ if (!canLoadMore(self.lastValue)) {
4858
+ return;
4859
+ } // don't call load() too much
4860
+
4861
+
4862
+ if (loading_more) return;
4863
+ loading_more = true;
4864
+ self.load.call(self, self.lastValue);
4865
+ });
4866
+ });
4867
+ }
4868
+
4869
+ TomSelect.define('change_listener', change_listener);
4870
+ TomSelect.define('checkbox_options', checkbox_options);
4871
+ TomSelect.define('clear_button', clear_button);
4872
+ TomSelect.define('drag_drop', drag_drop);
4873
+ TomSelect.define('dropdown_header', dropdown_header);
4874
+ TomSelect.define('caret_position', caret_position);
4875
+ TomSelect.define('dropdown_input', dropdown_input);
4876
+ TomSelect.define('input_autogrow', input_autogrow);
4877
+ TomSelect.define('no_backspace_delete', no_backspace_delete);
4878
+ TomSelect.define('no_active_items', no_active_items);
4879
+ TomSelect.define('optgroup_columns', optgroup_columns);
4880
+ TomSelect.define('remove_button', remove_button);
4881
+ TomSelect.define('restore_on_backspace', restore_on_backspace);
4882
+ TomSelect.define('virtual_scroll', virtual_scroll);
4883
+
4884
+ export { TomSelect as default };
4885
+ //# sourceMappingURL=tom-select.complete.js.map