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