rails-active-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +6 -0
  3. data/app/assets/stylesheets.css +73555 -0
  4. data/app/components/accordion_component.rb +34 -0
  5. data/app/components/ad_component.rb +28 -0
  6. data/app/components/api_component.rb +24 -0
  7. data/app/components/breadcrumb_component.rb +26 -0
  8. data/app/components/button_component.rb +49 -0
  9. data/app/components/calendar_component.rb +34 -0
  10. data/app/components/card_component.rb +56 -0
  11. data/app/components/checkbox_component.rb +41 -0
  12. data/app/components/column_component.rb +62 -0
  13. data/app/components/comment_component.rb +45 -0
  14. data/app/components/concerns/alignable.rb +21 -0
  15. data/app/components/concerns/attachable.rb +16 -0
  16. data/app/components/concerns/orientable.rb +21 -0
  17. data/app/components/concerns/positionable.rb +21 -0
  18. data/app/components/concerns/sizeable.rb +18 -0
  19. data/app/components/container_component.rb +23 -0
  20. data/app/components/dimmer_component.rb +30 -0
  21. data/app/components/divider_component.rb +30 -0
  22. data/app/components/dropdown_component.rb +63 -0
  23. data/app/components/embed_component.rb +32 -0
  24. data/app/components/emoji_component.rb +15 -0
  25. data/app/components/feed_component.rb +22 -0
  26. data/app/components/flag_component.rb +15 -0
  27. data/app/components/flyout_component.rb +41 -0
  28. data/app/components/form_component.rb +39 -0
  29. data/app/components/grid_component.rb +85 -0
  30. data/app/components/h_stack_component.rb +67 -0
  31. data/app/components/header_component.rb +60 -0
  32. data/app/components/icon_component.rb +41 -0
  33. data/app/components/image_component.rb +46 -0
  34. data/app/components/input_component.rb +52 -0
  35. data/app/components/item_component.rb +39 -0
  36. data/app/components/item_group_component.rb +30 -0
  37. data/app/components/label_component.rb +49 -0
  38. data/app/components/link_component.rb +23 -0
  39. data/app/components/list_component.rb +39 -0
  40. data/app/components/loader_component.rb +33 -0
  41. data/app/components/menu_component.rb +64 -0
  42. data/app/components/menu_item_component.rb +52 -0
  43. data/app/components/message_component.rb +54 -0
  44. data/app/components/modal_component.rb +50 -0
  45. data/app/components/nag_component.rb +25 -0
  46. data/app/components/overlay_component.rb +16 -0
  47. data/app/components/placeholder_component.rb +39 -0
  48. data/app/components/popup_component.rb +31 -0
  49. data/app/components/progress_component.rb +48 -0
  50. data/app/components/pusher_component.rb +18 -0
  51. data/app/components/rail_component.rb +31 -0
  52. data/app/components/rating_component.rb +41 -0
  53. data/app/components/reset_component.rb +12 -0
  54. data/app/components/reveal_component.rb +39 -0
  55. data/app/components/row_component.rb +39 -0
  56. data/app/components/search_component.rb +44 -0
  57. data/app/components/segment_component.rb +57 -0
  58. data/app/components/segment_group_component.rb +36 -0
  59. data/app/components/shape_component.rb +25 -0
  60. data/app/components/sidebar_component.rb +33 -0
  61. data/app/components/site_component.rb +12 -0
  62. data/app/components/slider_component.rb +46 -0
  63. data/app/components/state_component.rb +25 -0
  64. data/app/components/statistic_component.rb +43 -0
  65. data/app/components/step_component.rb +56 -0
  66. data/app/components/step_group_component.rb +38 -0
  67. data/app/components/sticky_component.rb +22 -0
  68. data/app/components/sub_header_component.rb +15 -0
  69. data/app/components/sub_menu_component.rb +24 -0
  70. data/app/components/tab_component.rb +24 -0
  71. data/app/components/table_cell_component.rb +60 -0
  72. data/app/components/table_component.rb +160 -0
  73. data/app/components/table_row_component.rb +43 -0
  74. data/app/components/text_component.rb +73 -0
  75. data/app/components/toast_component.rb +36 -0
  76. data/app/components/transition_component.rb +32 -0
  77. data/app/components/v_stack_component.rb +31 -0
  78. data/app/components/visibility_component.rb +22 -0
  79. data/app/helpers/component_helper.rb +109 -0
  80. data/app/helpers/fui_helper.rb +53 -0
  81. data/app/javascript/accordion.js +547 -0
  82. data/app/javascript/accordion.min.js +11 -0
  83. data/app/javascript/api.js +1112 -0
  84. data/app/javascript/api.min.js +11 -0
  85. data/app/javascript/calendar.js +1960 -0
  86. data/app/javascript/calendar.min.js +11 -0
  87. data/app/javascript/checkbox.js +819 -0
  88. data/app/javascript/checkbox.min.js +11 -0
  89. data/app/javascript/dimmer.js +686 -0
  90. data/app/javascript/dimmer.min.js +11 -0
  91. data/app/javascript/dropdown.js +4019 -0
  92. data/app/javascript/dropdown.min.js +11 -0
  93. data/app/javascript/embed.js +646 -0
  94. data/app/javascript/embed.min.js +11 -0
  95. data/app/javascript/flyout.js +1405 -0
  96. data/app/javascript/flyout.min.js +11 -0
  97. data/app/javascript/form.js +2070 -0
  98. data/app/javascript/form.min.js +11 -0
  99. data/app/javascript/jquery.js +10716 -0
  100. data/app/javascript/jquery.min.js +2 -0
  101. data/app/javascript/modal.js +1507 -0
  102. data/app/javascript/modal.min.js +11 -0
  103. data/app/javascript/nag.js +522 -0
  104. data/app/javascript/nag.min.js +11 -0
  105. data/app/javascript/popup.js +1457 -0
  106. data/app/javascript/popup.min.js +11 -0
  107. data/app/javascript/progress.js +922 -0
  108. data/app/javascript/progress.min.js +11 -0
  109. data/app/javascript/rating.js +496 -0
  110. data/app/javascript/rating.min.js +11 -0
  111. data/app/javascript/search.js +1519 -0
  112. data/app/javascript/search.min.js +11 -0
  113. data/app/javascript/shape.js +721 -0
  114. data/app/javascript/shape.min.js +11 -0
  115. data/app/javascript/sidebar.js +952 -0
  116. data/app/javascript/sidebar.min.js +11 -0
  117. data/app/javascript/site.js +415 -0
  118. data/app/javascript/site.min.js +11 -0
  119. data/app/javascript/slider.js +1449 -0
  120. data/app/javascript/slider.min.js +11 -0
  121. data/app/javascript/state.js +653 -0
  122. data/app/javascript/state.min.js +11 -0
  123. data/app/javascript/sticky.js +852 -0
  124. data/app/javascript/sticky.min.js +11 -0
  125. data/app/javascript/tab.js +867 -0
  126. data/app/javascript/tab.min.js +11 -0
  127. data/app/javascript/toast.js +916 -0
  128. data/app/javascript/toast.min.js +11 -0
  129. data/app/javascript/transition.js +955 -0
  130. data/app/javascript/transition.min.js +11 -0
  131. data/app/javascript/ui/controllers/fui_accordion_controller.js +45 -0
  132. data/app/javascript/ui/controllers/fui_api_controller.js +80 -0
  133. data/app/javascript/ui/controllers/fui_calendar_controller.js +66 -0
  134. data/app/javascript/ui/controllers/fui_checkbox_controller.js +48 -0
  135. data/app/javascript/ui/controllers/fui_dimmer_controller.js +45 -0
  136. data/app/javascript/ui/controllers/fui_dropdown_controller.js +68 -0
  137. data/app/javascript/ui/controllers/fui_embed_controller.js +49 -0
  138. data/app/javascript/ui/controllers/fui_flyout_controller.js +49 -0
  139. data/app/javascript/ui/controllers/fui_form_controller.js +62 -0
  140. data/app/javascript/ui/controllers/fui_modal_controller.js +61 -0
  141. data/app/javascript/ui/controllers/fui_nag_controller.js +52 -0
  142. data/app/javascript/ui/controllers/fui_popup_controller.js +58 -0
  143. data/app/javascript/ui/controllers/fui_progress_controller.js +60 -0
  144. data/app/javascript/ui/controllers/fui_rating_controller.js +49 -0
  145. data/app/javascript/ui/controllers/fui_search_controller.js +76 -0
  146. data/app/javascript/ui/controllers/fui_shape_controller.js +45 -0
  147. data/app/javascript/ui/controllers/fui_sidebar_controller.js +48 -0
  148. data/app/javascript/ui/controllers/fui_site_controller.js +29 -0
  149. data/app/javascript/ui/controllers/fui_slider_controller.js +53 -0
  150. data/app/javascript/ui/controllers/fui_state_controller.js +63 -0
  151. data/app/javascript/ui/controllers/fui_sticky_controller.js +50 -0
  152. data/app/javascript/ui/controllers/fui_tab_controller.js +57 -0
  153. data/app/javascript/ui/controllers/fui_toast_controller.js +60 -0
  154. data/app/javascript/ui/controllers/fui_transition_controller.js +60 -0
  155. data/app/javascript/ui/controllers/fui_visibility_controller.js +55 -0
  156. data/app/javascript/ui/index.js +114 -0
  157. data/app/javascript/visibility.js +1196 -0
  158. data/app/javascript/visibility.min.js +11 -0
  159. data/app/lib/component.rb +63 -0
  160. data/config/importmap.rb +27 -0
  161. data/config/initializers/ruby_template_handler.rb +31 -0
  162. data/config/routes.rb +2 -0
  163. data/lib/tasks/ui_tasks.rake +4 -0
  164. data/lib/ui/engine.rb +27 -0
  165. data/lib/ui/version.rb +3 -0
  166. data/lib/ui.rb +6 -0
  167. metadata +220 -0
@@ -0,0 +1,1519 @@
1
+ /*!
2
+ * # Fomantic-UI 2.9.4 - Search
3
+ * https://github.com/fomantic/Fomantic-UI/
4
+ *
5
+ *
6
+ * Released under the MIT license
7
+ * https://opensource.org/licenses/MIT
8
+ *
9
+ */
10
+
11
+ (function ($, window, document) {
12
+ 'use strict';
13
+
14
+ function isFunction(obj) {
15
+ return typeof obj === 'function' && typeof obj.nodeType !== 'number';
16
+ }
17
+
18
+ window = window !== undefined && window.Math === Math
19
+ ? window
20
+ : globalThis;
21
+
22
+ $.fn.search = function (...args) {
23
+ const $allModules = $(this);
24
+
25
+ let time = Date.now();
26
+ let performance = [];
27
+
28
+ const parameters = args[0];
29
+ const methodInvoked = typeof parameters === 'string';
30
+ const queryArguments = args.slice(1);
31
+ let returnedValue;
32
+ $allModules.each(function () {
33
+ const settings = $.isPlainObject(parameters)
34
+ ? $.extend(true, {}, $.fn.search.settings, parameters)
35
+ : $.extend({}, $.fn.search.settings);
36
+
37
+ const className = settings.className;
38
+ const metadata = settings.metadata;
39
+ const regExp = settings.regExp;
40
+ const fields = settings.fields;
41
+ const selector = settings.selector;
42
+ const error = settings.error;
43
+ const namespace = settings.namespace;
44
+
45
+ const eventNamespace = '.' + namespace;
46
+ const moduleNamespace = namespace + '-module';
47
+
48
+ const $module = $(this);
49
+ let $prompt = $module.find(selector.prompt);
50
+ let $searchButton = $module.find(selector.searchButton);
51
+ let $results = $module.find(selector.results);
52
+ let $result = $module.find(selector.result);
53
+ let $category = $module.find(selector.category);
54
+
55
+ const element = this;
56
+ let instance = $module.data(moduleNamespace);
57
+
58
+ let disabledBubbled = false;
59
+ let resultsDismissed = false;
60
+
61
+ const module = {
62
+
63
+ initialize: function () {
64
+ module.verbose('Initializing module');
65
+ module.get.settings();
66
+ module.determine.searchFields();
67
+ module.bind.events();
68
+ module.set.type();
69
+ module.create.results();
70
+ module.instantiate();
71
+ },
72
+ instantiate: function () {
73
+ module.verbose('Storing instance of module', module);
74
+ instance = module;
75
+ $module
76
+ .data(moduleNamespace, module);
77
+ },
78
+ destroy: function () {
79
+ module.verbose('Destroying instance');
80
+ $module
81
+ .off(eventNamespace)
82
+ .removeData(moduleNamespace);
83
+ },
84
+
85
+ refresh: function () {
86
+ module.debug('Refreshing selector cache');
87
+ $prompt = $module.find(selector.prompt);
88
+ $searchButton = $module.find(selector.searchButton);
89
+ $category = $module.find(selector.category);
90
+ $results = $module.find(selector.results);
91
+ $result = $module.find(selector.result);
92
+ },
93
+
94
+ refreshResults: function () {
95
+ $results = $module.find(selector.results);
96
+ $result = $module.find(selector.result);
97
+ },
98
+
99
+ bind: {
100
+ events: function () {
101
+ module.verbose('Binding events to search');
102
+ if (settings.automatic) {
103
+ $module
104
+ .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input);
105
+ $prompt
106
+ .attr('autocomplete', module.is.chrome() ? 'fomantic-search' : 'off');
107
+ }
108
+ $module
109
+ // prompt
110
+ .on('focus' + eventNamespace, selector.prompt, module.event.focus)
111
+ .on('blur' + eventNamespace, selector.prompt, module.event.blur)
112
+ .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard)
113
+ // search button
114
+ .on('click' + eventNamespace, selector.searchButton, module.query)
115
+ // results
116
+ .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
117
+ .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup)
118
+ .on('click' + eventNamespace, selector.result, module.event.result.click)
119
+ .on('click' + eventNamespace, selector.remove, module.event.remove.click);
120
+ },
121
+ },
122
+
123
+ determine: {
124
+ searchFields: function () {
125
+ // this makes sure $.extend does not add specified search fields to default fields
126
+ // this is the only setting that should not extend defaults
127
+ if (parameters && parameters.searchFields !== undefined) {
128
+ settings.searchFields = Array.isArray(parameters.searchFields)
129
+ ? parameters.searchFields
130
+ : [parameters.searchFields];
131
+ }
132
+ },
133
+ },
134
+
135
+ event: {
136
+ input: function () {
137
+ if (settings.searchDelay) {
138
+ clearTimeout(module.timer);
139
+ module.timer = setTimeout(function () {
140
+ if (module.is.focused()) {
141
+ module.query();
142
+ }
143
+ }, settings.searchDelay);
144
+ } else {
145
+ module.query();
146
+ }
147
+ },
148
+ focus: function () {
149
+ module.set.focus();
150
+ if (settings.searchOnFocus && module.has.minimumCharacters()) {
151
+ module.query(function () {
152
+ if (module.can.show()) {
153
+ module.showResults();
154
+ }
155
+ });
156
+ }
157
+ },
158
+ blur: function (event) {
159
+ const pageLostFocus = document.activeElement === this;
160
+ const callback = function () {
161
+ module.cancel.query();
162
+ module.remove.focus();
163
+ module.timer = setTimeout(function () {
164
+ module.hideResults();
165
+ }, settings.hideDelay);
166
+ };
167
+ if (pageLostFocus) {
168
+ return;
169
+ }
170
+ resultsDismissed = false;
171
+ if (module.resultsClicked) {
172
+ module.debug('Determining if user action caused search to close');
173
+ $module
174
+ .one('click.close' + eventNamespace, selector.results, function (event) {
175
+ if (module.is.inMessage(event) || disabledBubbled) {
176
+ $prompt.trigger('focus');
177
+
178
+ return;
179
+ }
180
+ disabledBubbled = false;
181
+ if (!module.is.animating() && !module.is.hidden()) {
182
+ callback();
183
+ }
184
+ });
185
+ } else {
186
+ module.debug('Input blurred without user action, closing results');
187
+ callback();
188
+ }
189
+ },
190
+ remove: {
191
+ click: function () {
192
+ module.clear.value();
193
+ $prompt.trigger('focus');
194
+ },
195
+ },
196
+ result: {
197
+ mousedown: function () {
198
+ module.resultsClicked = true;
199
+ },
200
+ mouseup: function () {
201
+ module.resultsClicked = false;
202
+ },
203
+ click: function (event) {
204
+ module.debug('Search result selected');
205
+ const $result = $(this);
206
+ const $title = $result.find(selector.title).eq(0);
207
+ const $link = $result.is('a[href]') ? $result : $();
208
+ const href = $link.attr('href') || false;
209
+ const target = $link.attr('target') || false;
210
+ // title is used for result lookup
211
+ const value = $title.length > 0
212
+ ? $title.text()
213
+ : false;
214
+ const results = module.get.results();
215
+ const result = $result.data(metadata.result) || module.get.result(value, results);
216
+ const oldValue = module.get.value();
217
+ if (isFunction(settings.onSelect) && settings.onSelect.call(element, result, results) === false) {
218
+ module.debug('Custom onSelect callback cancelled default select action');
219
+ disabledBubbled = true;
220
+
221
+ return;
222
+ }
223
+ module.hideResults();
224
+ if (value && module.get.value() === oldValue) {
225
+ module.set.value(value);
226
+ }
227
+ if (href) {
228
+ event.preventDefault();
229
+ module.verbose('Opening search link found in result', $link);
230
+ if (target === '_blank' || event.ctrlKey) {
231
+ window.open(href);
232
+ } else {
233
+ window.location.href = href;
234
+ }
235
+ }
236
+ },
237
+ },
238
+ },
239
+ ensureVisible: function ($el) {
240
+ if ($el.length === 0) {
241
+ return;
242
+ }
243
+ const elTop = $el.position().top;
244
+ const elBottom = elTop + $el.outerHeight(true);
245
+
246
+ const resultsScrollTop = $results.scrollTop();
247
+ const resultsHeight = $results.height();
248
+
249
+ if (elTop < 0) {
250
+ $results.scrollTop(resultsScrollTop + elTop);
251
+ } else if (resultsHeight < elBottom) {
252
+ $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
253
+ }
254
+ },
255
+ handleKeyboard: function (event) {
256
+ // force selector refresh
257
+ const $result = $module.find(selector.result);
258
+ const $category = $module.find(selector.category);
259
+ const $activeResult = $result.filter('.' + className.active);
260
+ const currentIndex = $result.index($activeResult);
261
+ const resultSize = $result.length;
262
+ const hasActiveResult = $activeResult.length > 0;
263
+
264
+ const keyCode = event.which;
265
+ const keys = {
266
+ backspace: 8,
267
+ enter: 13,
268
+ escape: 27,
269
+ upArrow: 38,
270
+ downArrow: 40,
271
+ };
272
+ let newIndex;
273
+ // search shortcuts
274
+ if (keyCode === keys.escape) {
275
+ if (!module.is.visible()) {
276
+ module.verbose('Escape key pressed, blurring search field');
277
+ $prompt.trigger('blur');
278
+ } else {
279
+ module.hideResults();
280
+ }
281
+ event.stopPropagation();
282
+ resultsDismissed = true;
283
+ }
284
+ if (module.is.visible()) {
285
+ if (keyCode === keys.enter) {
286
+ module.verbose('Enter key pressed, selecting active result');
287
+ if ($result.filter('.' + className.active).length > 0) {
288
+ module.event.result.click.call($result.filter('.' + className.active), event);
289
+ event.preventDefault();
290
+
291
+ return false;
292
+ }
293
+ } else if (keyCode === keys.upArrow && hasActiveResult) {
294
+ module.verbose('Up key pressed, changing active result');
295
+ newIndex = currentIndex - 1 < 0
296
+ ? currentIndex
297
+ : currentIndex - 1;
298
+ $category
299
+ .removeClass(className.active);
300
+ $result
301
+ .removeClass(className.active)
302
+ .eq(newIndex)
303
+ .addClass(className.active)
304
+ .closest($category)
305
+ .addClass(className.active);
306
+ module.ensureVisible($result.eq(newIndex));
307
+ event.preventDefault();
308
+ } else if (keyCode === keys.downArrow) {
309
+ module.verbose('Down key pressed, changing active result');
310
+ newIndex = currentIndex + 1 >= resultSize
311
+ ? currentIndex
312
+ : currentIndex + 1;
313
+ $category
314
+ .removeClass(className.active);
315
+ $result
316
+ .removeClass(className.active)
317
+ .eq(newIndex)
318
+ .addClass(className.active)
319
+ .closest($category)
320
+ .addClass(className.active);
321
+ module.ensureVisible($result.eq(newIndex));
322
+ event.preventDefault();
323
+ }
324
+ } else if (keyCode === keys.enter) {
325
+ module.verbose('Enter key pressed, executing query');
326
+ module.query();
327
+ module.set.buttonPressed();
328
+ $prompt.one('keyup', module.remove.buttonFocus);
329
+ }
330
+ },
331
+
332
+ setup: {
333
+ api: function (searchTerm, callback) {
334
+ const apiSettings = {
335
+ debug: settings.debug,
336
+ on: false,
337
+ cache: settings.cache,
338
+ action: 'search',
339
+ urlData: {
340
+ query: searchTerm,
341
+ },
342
+ };
343
+ const apiCallbacks = {
344
+ onSuccess: function (response, $module, xhr) {
345
+ module.parse.response.call(element, response, searchTerm);
346
+ callback();
347
+ if (settings.apiSettings && typeof settings.apiSettings.onSuccess === 'function') {
348
+ settings.apiSettings.onSuccess.call(this, response, $module, xhr);
349
+ }
350
+ },
351
+ onFailure: function (response, $module, xhr) {
352
+ module.displayMessage(error.serverError);
353
+ callback();
354
+ if (settings.apiSettings && typeof settings.apiSettings.onFailure === 'function') {
355
+ settings.apiSettings.onFailure.call(this, response, $module, xhr);
356
+ }
357
+ },
358
+ onAbort: function (status, $module, xhr) {
359
+ if (settings.apiSettings && typeof settings.apiSettings.onAbort === 'function') {
360
+ settings.apiSettings.onAbort.call(this, status, $module, xhr);
361
+ }
362
+ },
363
+ onError: function (errorMessage, $module, xhr) {
364
+ module.error();
365
+ if (settings.apiSettings && typeof settings.apiSettings.onError === 'function') {
366
+ settings.apiSettings.onError.call(this, errorMessage, $module, xhr);
367
+ }
368
+ },
369
+ };
370
+ $.extend(true, apiSettings, settings.apiSettings, apiCallbacks);
371
+ module.verbose('Setting up API request', apiSettings);
372
+ $module.api(apiSettings);
373
+ },
374
+ },
375
+
376
+ can: {
377
+ useAPI: function () {
378
+ return $.fn.api !== undefined;
379
+ },
380
+ show: function () {
381
+ return module.is.focused() && !module.is.visible() && !module.is.empty();
382
+ },
383
+ transition: function () {
384
+ return settings.transition && $.fn.transition !== undefined;
385
+ },
386
+ },
387
+
388
+ is: {
389
+ animating: function () {
390
+ return $results.hasClass(className.animating);
391
+ },
392
+ chrome: function () {
393
+ return !!window.chrome && !window.StyleMedia;
394
+ },
395
+ hidden: function () {
396
+ return $results.hasClass(className.hidden);
397
+ },
398
+ inMessage: function (event) {
399
+ if (!event.target) {
400
+ return;
401
+ }
402
+ const $target = $(event.target);
403
+ const isInDOM = document.documentElement.contains(event.target);
404
+
405
+ return isInDOM && $target.closest(selector.message).length > 0;
406
+ },
407
+ empty: function () {
408
+ return $results.html() === '';
409
+ },
410
+ visible: function () {
411
+ return $results.filter(':visible').length > 0;
412
+ },
413
+ focused: function () {
414
+ return $prompt.filter(':focus').length > 0;
415
+ },
416
+ },
417
+
418
+ get: {
419
+ settings: function () {
420
+ if ($.isPlainObject(parameters) && parameters.searchFullText) {
421
+ settings.fullTextSearch = parameters.searchFullText;
422
+ module.error(settings.error.oldSearchSyntax, element);
423
+ }
424
+ },
425
+ inputEvent: function () {
426
+ const prompt = $prompt[0];
427
+ const inputEvent = prompt !== undefined && prompt.oninput !== undefined
428
+ ? 'input'
429
+ : (prompt !== undefined && prompt.onpropertychange !== undefined
430
+ ? 'propertychange'
431
+ : 'keyup');
432
+
433
+ return inputEvent;
434
+ },
435
+ value: function () {
436
+ return $prompt.val();
437
+ },
438
+ results: function () {
439
+ return $module.data(metadata.results);
440
+ },
441
+ result: function (value = module.get.value(), results = module.get.results()) {
442
+ let result = false;
443
+ if (settings.type === 'category') {
444
+ module.debug('Finding result that matches', value);
445
+ $.each(results, function (index, category) {
446
+ if (Array.isArray(category.results)) {
447
+ result = module.search.object(value, category.results)[0];
448
+ // don't continue searching if a result is found
449
+ if (result) {
450
+ return false;
451
+ }
452
+ }
453
+ });
454
+ } else {
455
+ module.debug('Finding result in results object', value);
456
+ result = module.search.object(value, results)[0];
457
+ }
458
+
459
+ return result || false;
460
+ },
461
+ },
462
+
463
+ select: {
464
+ firstResult: function () {
465
+ module.verbose('Selecting first result');
466
+ $result.first().addClass(className.active);
467
+ },
468
+ },
469
+
470
+ set: {
471
+ focus: function () {
472
+ $module.addClass(className.focus);
473
+ },
474
+ loading: function () {
475
+ $module.addClass(className.loading);
476
+ },
477
+ value: function (value) {
478
+ module.verbose('Setting search input value', value);
479
+ $prompt
480
+ .val(value);
481
+ },
482
+ type: function (type = settings.type) {
483
+ if (className[type]) {
484
+ $module.addClass(className[type]);
485
+ }
486
+ },
487
+ buttonPressed: function () {
488
+ $searchButton.addClass(className.pressed);
489
+ },
490
+ },
491
+
492
+ remove: {
493
+ loading: function () {
494
+ $module.removeClass(className.loading);
495
+ },
496
+ focus: function () {
497
+ $module.removeClass(className.focus);
498
+ },
499
+ buttonPressed: function () {
500
+ $searchButton.removeClass(className.pressed);
501
+ },
502
+ diacritics: function (text) {
503
+ return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036F]/g, '') : text;
504
+ },
505
+ },
506
+
507
+ query: function (callback = function () {}) {
508
+ callback = isFunction(callback)
509
+ ? callback
510
+ : function () {};
511
+ const searchTerm = module.get.value();
512
+ const cache = module.read.cache(searchTerm);
513
+ if (module.has.minimumCharacters()) {
514
+ if (cache) {
515
+ module.debug('Reading result from cache', searchTerm);
516
+ module.save.results(cache.results);
517
+ settings.onResults.call(element, cache.results, true);
518
+ module.addResults(cache.html);
519
+ module.inject.id(cache.results);
520
+ callback();
521
+ } else {
522
+ module.debug('Querying for', searchTerm);
523
+ if ($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
524
+ module.search.local(searchTerm);
525
+ callback();
526
+ } else if (module.can.useAPI()) {
527
+ module.search.remote(searchTerm, callback);
528
+ } else {
529
+ module.error(error.source);
530
+ callback();
531
+ }
532
+ }
533
+ settings.onSearchQuery.call(element, searchTerm);
534
+ } else {
535
+ module.hideResults();
536
+ }
537
+ },
538
+
539
+ search: {
540
+ local: function (searchTerm) {
541
+ let results = module.search.object(searchTerm, settings.source);
542
+ module.set.loading();
543
+ module.save.results(results);
544
+ module.debug('Returned full local search results', results);
545
+ if (settings.maxResults > 0) {
546
+ module.debug('Using specified max results', results);
547
+ results = results.slice(0, settings.maxResults);
548
+ }
549
+ if (settings.type === 'category') {
550
+ results = module.create.categoryResults(results);
551
+ }
552
+ const searchHTML = module.generateResults({
553
+ results: results,
554
+ });
555
+ module.remove.loading();
556
+ module.addResults(searchHTML);
557
+ module.inject.id(results);
558
+ module.write.cache(searchTerm, {
559
+ html: searchHTML,
560
+ results: results,
561
+ });
562
+ },
563
+ remote: function (searchTerm, callback) {
564
+ callback = isFunction(callback)
565
+ ? callback
566
+ : function () {};
567
+ if ($module.api('is loading')) {
568
+ $module.api('abort');
569
+ }
570
+ module.setup.api(searchTerm, callback);
571
+ $module
572
+ .api('query');
573
+ },
574
+ object: function (searchTerm, source = settings.source, searchFields = settings.searchFields) {
575
+ searchTerm = module.remove.diacritics(String(searchTerm));
576
+ const results = [];
577
+ const exactResults = [];
578
+ const fuzzyResults = [];
579
+ const searchExp = searchTerm.replace(regExp.escape, '\\$&');
580
+ const matchRegExp = new RegExp(regExp.beginsWith + searchExp, settings.ignoreSearchCase ? 'i' : '');
581
+
582
+ // avoid duplicates when pushing results
583
+ const addResult = function (array, result) {
584
+ const notResult = !results.includes(result);
585
+ const notFuzzyResult = !fuzzyResults.includes(result);
586
+ const notExactResults = !exactResults.includes(result);
587
+ if (notResult && notFuzzyResult && notExactResults) {
588
+ array.push(result);
589
+ }
590
+ };
591
+
592
+ // search fields should be an array to loop correctly
593
+ if (!Array.isArray(searchFields)) {
594
+ searchFields = [searchFields];
595
+ }
596
+
597
+ // exit conditions if no source
598
+ if (source === undefined || source === false) {
599
+ module.error(error.source);
600
+
601
+ return [];
602
+ }
603
+ // iterate through search fields looking for matches
604
+ const lastSearchFieldIndex = searchFields.length - 1;
605
+ $.each(source, function (label, content) {
606
+ const concatenatedContent = [];
607
+ $.each(searchFields, function (index, field) {
608
+ const fieldExists = typeof content[field] === 'string' || typeof content[field] === 'number';
609
+ if (fieldExists) {
610
+ let text;
611
+ text = typeof content[field] === 'string'
612
+ ? module.remove.diacritics(content[field])
613
+ : content[field].toString();
614
+ text = $('<div/>', { html: text }).text().trim();
615
+ if (settings.fullTextSearch === 'all') {
616
+ concatenatedContent.push(text);
617
+ if (index < lastSearchFieldIndex) {
618
+ return true;
619
+ }
620
+ text = concatenatedContent.join(' ');
621
+ }
622
+ if (settings.fullTextSearch !== 'all' && text.search(matchRegExp) !== -1) {
623
+ // content starts with value (first in results)
624
+ addResult(results, content);
625
+ } else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
626
+ addResult(exactResults, content);
627
+ } else if (settings.fullTextSearch === 'some' && module.wordSearch(searchTerm, text)) {
628
+ addResult(exactResults, content);
629
+ } else if (settings.fullTextSearch === 'all' && module.wordSearch(searchTerm, text, true)) {
630
+ addResult(exactResults, content);
631
+ } else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
632
+ // content fuzzy matches (last in results)
633
+ addResult(fuzzyResults, content);
634
+ }
635
+ }
636
+ });
637
+ });
638
+
639
+ return [...results, ...exactResults, ...fuzzyResults];
640
+ },
641
+ },
642
+ exactSearch: function (query, term) {
643
+ if (settings.ignoreSearchCase) {
644
+ query = query.toLowerCase();
645
+ term = term.toLowerCase();
646
+ }
647
+
648
+ return term.includes(query);
649
+ },
650
+ wordSearch: function (query, term, matchAll) {
651
+ const allWords = query.split(/\s+/);
652
+ let found = false;
653
+ for (const w of allWords) {
654
+ found = module.exactSearch(w, term);
655
+ if ((!found && matchAll) || (found && !matchAll)) {
656
+ break;
657
+ }
658
+ }
659
+
660
+ return found;
661
+ },
662
+ fuzzySearch: function (query, term) {
663
+ const termLength = term.length;
664
+ const queryLength = query.length;
665
+ if (typeof query !== 'string') {
666
+ return false;
667
+ }
668
+ if (settings.ignoreSearchCase) {
669
+ query = query.toLowerCase();
670
+ term = term.toLowerCase();
671
+ }
672
+ if (queryLength > termLength) {
673
+ return false;
674
+ }
675
+ if (queryLength === termLength) {
676
+ return query === term;
677
+ }
678
+ for (let characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
679
+ let continueSearch = false;
680
+ const queryCharacter = query.codePointAt(characterIndex);
681
+ while (nextCharacterIndex < termLength) {
682
+ if (term.codePointAt(nextCharacterIndex++) === queryCharacter) {
683
+ continueSearch = true;
684
+
685
+ break;
686
+ }
687
+ }
688
+
689
+ if (!continueSearch) {
690
+ return false;
691
+ }
692
+ }
693
+
694
+ return true;
695
+ },
696
+
697
+ parse: {
698
+ response: function (response, searchTerm) {
699
+ if (Array.isArray(response)) {
700
+ const o = {};
701
+ o[fields.results] = response;
702
+ response = o;
703
+ }
704
+ const searchHTML = module.generateResults(response);
705
+ module.verbose('Parsing server response', response);
706
+ if (response !== undefined && searchTerm !== undefined && response[fields.results] !== undefined) {
707
+ module.addResults(searchHTML);
708
+ module.inject.id(response[fields.results]);
709
+ module.write.cache(searchTerm, {
710
+ html: searchHTML,
711
+ results: response[fields.results],
712
+ });
713
+ module.save.results(response[fields.results]);
714
+ }
715
+ },
716
+ },
717
+
718
+ cancel: {
719
+ query: function () {
720
+ if (module.can.useAPI()) {
721
+ $module.api('abort');
722
+ }
723
+ },
724
+ },
725
+
726
+ has: {
727
+ minimumCharacters: function () {
728
+ const searchTerm = module.get.value();
729
+ const numCharacters = searchTerm.length;
730
+
731
+ return numCharacters >= settings.minCharacters;
732
+ },
733
+ results: function () {
734
+ if ($results.length === 0) {
735
+ return false;
736
+ }
737
+ const html = $results.html();
738
+
739
+ return html !== '';
740
+ },
741
+ },
742
+
743
+ clear: {
744
+ cache: function (value) {
745
+ const cache = $module.data(metadata.cache);
746
+ if (!value) {
747
+ module.debug('Clearing cache', value);
748
+ $module.removeData(metadata.cache);
749
+ } else if (value && cache && cache[value]) {
750
+ module.debug('Removing value from cache', value);
751
+ delete cache[value];
752
+ $module.data(metadata.cache, cache);
753
+ }
754
+ },
755
+ value: function () {
756
+ module.set.value('');
757
+ },
758
+ },
759
+
760
+ read: {
761
+ cache: function (name) {
762
+ const cache = $module.data(metadata.cache);
763
+ if (settings.cache) {
764
+ module.verbose('Checking cache for generated html for query', name);
765
+
766
+ return (typeof cache === 'object') && (cache[name] !== undefined)
767
+ ? cache[name]
768
+ : false;
769
+ }
770
+
771
+ return false;
772
+ },
773
+ },
774
+
775
+ create: {
776
+ categoryResults: function (results) {
777
+ const categoryResults = {};
778
+ $.each(results, function (index, result) {
779
+ if (!result.category) {
780
+ return;
781
+ }
782
+ if (categoryResults[result.category] === undefined) {
783
+ module.verbose('Creating new category of results', result.category);
784
+ categoryResults[result.category] = {
785
+ name: result.category,
786
+ results: [result],
787
+ };
788
+ } else {
789
+ categoryResults[result.category].results.push(result);
790
+ }
791
+ });
792
+
793
+ return categoryResults;
794
+ },
795
+ id: function (resultIndex, categoryIndex) {
796
+ const resultID = resultIndex + 1; // not zero indexed
797
+ let letterID;
798
+ let id;
799
+ if (categoryIndex !== undefined) {
800
+ // start char code for "A"
801
+ letterID = String.fromCodePoint(97 + categoryIndex);
802
+ id = letterID + resultID;
803
+ module.verbose('Creating category result id', id);
804
+ } else {
805
+ id = resultID;
806
+ module.verbose('Creating result id', id);
807
+ }
808
+
809
+ return id;
810
+ },
811
+ results: function () {
812
+ if ($results.length === 0) {
813
+ $results = $('<div />')
814
+ .addClass(className.results)
815
+ .appendTo($module);
816
+ }
817
+ },
818
+ },
819
+
820
+ inject: {
821
+ result: function (result, resultIndex, categoryIndex) {
822
+ module.verbose('Injecting result into results');
823
+ const $selectedResult = categoryIndex !== undefined
824
+ ? $results
825
+ .children().eq(categoryIndex)
826
+ .children(selector.results)
827
+ .first()
828
+ .children(selector.result)
829
+ .eq(resultIndex)
830
+ : $results
831
+ .children(selector.result).eq(resultIndex);
832
+ module.verbose('Injecting results metadata', $selectedResult);
833
+ $selectedResult
834
+ .data(metadata.result, result);
835
+ },
836
+ id: function (results) {
837
+ module.debug('Injecting unique ids into results');
838
+ // since results may be an object, we must use counters
839
+ let categoryIndex = 0;
840
+ let resultIndex = 0;
841
+ if (settings.type === 'category') {
842
+ // iterate through each category result
843
+ $.each(results, function (index, category) {
844
+ if (category.results.length > 0) {
845
+ resultIndex = 0;
846
+ $.each(category.results, function (index, result) {
847
+ if (result.id === undefined) {
848
+ result.id = module.create.id(resultIndex, categoryIndex);
849
+ }
850
+ module.inject.result(result, resultIndex, categoryIndex);
851
+ resultIndex++;
852
+ });
853
+ categoryIndex++;
854
+ }
855
+ });
856
+ } else {
857
+ // top level
858
+ $.each(results, function (index, result) {
859
+ if (result.id === undefined) {
860
+ result.id = module.create.id(resultIndex);
861
+ }
862
+ module.inject.result(result, resultIndex);
863
+ resultIndex++;
864
+ });
865
+ }
866
+
867
+ return results;
868
+ },
869
+ },
870
+
871
+ save: {
872
+ results: function (results) {
873
+ module.verbose('Saving current search results to metadata', results);
874
+ $module.data(metadata.results, results);
875
+ },
876
+ },
877
+
878
+ write: {
879
+ cache: function (name, value) {
880
+ const cache = $module.data(metadata.cache) !== undefined
881
+ ? $module.data(metadata.cache)
882
+ : {};
883
+ if (settings.cache) {
884
+ module.verbose('Writing generated html to cache', name, value);
885
+ cache[name] = value;
886
+ $module
887
+ .data(metadata.cache, cache);
888
+ }
889
+ },
890
+ },
891
+
892
+ addResults: function (html) {
893
+ if (isFunction(settings.onResultsAdd) && settings.onResultsAdd.call($results, html) === false) {
894
+ module.debug('onResultsAdd callback cancelled default action');
895
+
896
+ return false;
897
+ }
898
+ if (html) {
899
+ $results
900
+ .html(html);
901
+ module.refreshResults();
902
+ if (settings.selectFirstResult) {
903
+ module.select.firstResult();
904
+ }
905
+ module.showResults();
906
+ } else {
907
+ module.hideResults(function () {
908
+ $results.empty();
909
+ });
910
+ }
911
+ },
912
+
913
+ showResults: function (callback) {
914
+ callback = isFunction(callback)
915
+ ? callback
916
+ : function () {};
917
+ if (resultsDismissed) {
918
+ return;
919
+ }
920
+ if (!module.is.visible() && module.has.results()) {
921
+ if (module.can.transition()) {
922
+ module.debug('Showing results with css animations');
923
+ $results
924
+ .transition({
925
+ animation: settings.transition + ' in',
926
+ debug: settings.debug,
927
+ verbose: settings.verbose,
928
+ silent: settings.silent,
929
+ duration: settings.duration,
930
+ onShow: function () {
931
+ const $firstResult = $module.find(selector.result).eq(0);
932
+ module.ensureVisible($firstResult);
933
+ },
934
+ onComplete: function () {
935
+ callback();
936
+ },
937
+ queue: true,
938
+ });
939
+ } else {
940
+ module.debug('Showing results with javascript');
941
+ $results
942
+ .stop()
943
+ .fadeIn(settings.duration, settings.easing);
944
+ }
945
+ settings.onResultsOpen.call($results);
946
+ }
947
+ },
948
+ hideResults: function (callback) {
949
+ callback = isFunction(callback)
950
+ ? callback
951
+ : function () {};
952
+ if (module.is.visible()) {
953
+ if (module.can.transition()) {
954
+ module.debug('Hiding results with css animations');
955
+ $results
956
+ .transition({
957
+ animation: settings.transition + ' out',
958
+ debug: settings.debug,
959
+ verbose: settings.verbose,
960
+ silent: settings.silent,
961
+ duration: settings.duration,
962
+ onComplete: function () {
963
+ callback();
964
+ },
965
+ queue: true,
966
+ });
967
+ } else {
968
+ module.debug('Hiding results with javascript');
969
+ $results
970
+ .stop()
971
+ .fadeOut(settings.duration, settings.easing);
972
+ }
973
+ settings.onResultsClose.call($results);
974
+ }
975
+ },
976
+
977
+ generateResults: function (response) {
978
+ module.debug('Generating html from response', response);
979
+ const template = settings.templates[settings.type];
980
+ const isProperObject = $.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results]);
981
+ const isProperArray = Array.isArray(response[fields.results]) && response[fields.results].length > 0;
982
+ let html = '';
983
+ if (isProperObject || isProperArray) {
984
+ if (settings.maxResults > 0) {
985
+ if (isProperObject) {
986
+ if (settings.type === 'standard') {
987
+ module.error(error.maxResults);
988
+ }
989
+ } else {
990
+ response[fields.results] = response[fields.results].slice(0, settings.maxResults);
991
+ }
992
+ }
993
+ if (settings.highlightMatches) {
994
+ const results = response[fields.results];
995
+ const regExpIgnore = settings.ignoreSearchCase ? 'i' : '';
996
+ const querySplit = [...module.get.value()];
997
+ const diacriticReg = settings.ignoreDiacritics ? '[\u0300-\u036F]?' : '';
998
+ const htmlReg = '(?![^<]*>)';
999
+ const markedRegExp = new RegExp(htmlReg + '(' + querySplit.join(diacriticReg + ')(.*?)' + htmlReg + '(') + diacriticReg + ')', regExpIgnore);
1000
+ const markedReplacer = function (...args) {
1001
+ args = args.slice(1, querySplit.length * 2).map(function (x, i) {
1002
+ return i & 1 ? x : '<mark>' + x + '</mark>'; // eslint-disable-line no-bitwise
1003
+ });
1004
+
1005
+ return args.join('');
1006
+ };
1007
+ $.each(results, function (label, content) {
1008
+ $.each(settings.searchFields, function (index, field) {
1009
+ const fieldExists = typeof content[field] === 'string' || typeof content[field] === 'number';
1010
+ if (fieldExists) {
1011
+ let markedHTML = typeof content[field] === 'string'
1012
+ ? content[field]
1013
+ : content[field].toString();
1014
+ if (settings.ignoreDiacritics) {
1015
+ markedHTML = markedHTML.normalize('NFD');
1016
+ }
1017
+ markedHTML = markedHTML.replace(/<\/?mark>/g, '');
1018
+ response[fields.results][label][field] = markedHTML.replace(markedRegExp, markedReplacer);
1019
+ }
1020
+ });
1021
+ });
1022
+ }
1023
+ if (isFunction(template)) {
1024
+ html = template(response, settings);
1025
+ } else {
1026
+ module.error(error.noTemplate, false);
1027
+ }
1028
+ } else if (settings.showNoResults) {
1029
+ html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
1030
+ }
1031
+ settings.onResults.call(element, response);
1032
+
1033
+ return html;
1034
+ },
1035
+
1036
+ displayMessage: function (text, type = 'standard', header = false) {
1037
+ module.debug('Displaying message', text, type, header);
1038
+ module.addResults(settings.templates.message(text, type, header));
1039
+
1040
+ return settings.templates.message(text, type, header);
1041
+ },
1042
+
1043
+ setting: function (name, value) {
1044
+ if ($.isPlainObject(name)) {
1045
+ $.extend(true, settings, name);
1046
+ } else if (value !== undefined) {
1047
+ settings[name] = value;
1048
+ } else {
1049
+ return settings[name];
1050
+ }
1051
+ },
1052
+ internal: function (name, value) {
1053
+ if ($.isPlainObject(name)) {
1054
+ $.extend(true, module, name);
1055
+ } else if (value !== undefined) {
1056
+ module[name] = value;
1057
+ } else {
1058
+ return module[name];
1059
+ }
1060
+ },
1061
+ debug: function (...args) {
1062
+ if (!settings.silent && settings.debug) {
1063
+ if (settings.performance) {
1064
+ module.performance.log(args);
1065
+ } else {
1066
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1067
+ module.debug.apply(console, args);
1068
+ }
1069
+ }
1070
+ },
1071
+ verbose: function (...args) {
1072
+ if (!settings.silent && settings.verbose && settings.debug) {
1073
+ if (settings.performance) {
1074
+ module.performance.log(args);
1075
+ } else {
1076
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1077
+ module.verbose.apply(console, args);
1078
+ }
1079
+ }
1080
+ },
1081
+ error: function (...args) {
1082
+ if (!settings.silent) {
1083
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1084
+ module.error.apply(console, args);
1085
+ }
1086
+ },
1087
+ performance: {
1088
+ log: function (message) {
1089
+ let currentTime;
1090
+ let executionTime;
1091
+ let previousTime;
1092
+ if (settings.performance) {
1093
+ currentTime = Date.now();
1094
+ previousTime = time || currentTime;
1095
+ executionTime = currentTime - previousTime;
1096
+ time = currentTime;
1097
+ performance.push({
1098
+ Name: message[0],
1099
+ Arguments: message.slice(1),
1100
+ Element: element,
1101
+ 'Execution Time': executionTime,
1102
+ });
1103
+ }
1104
+ clearTimeout(module.performance.timer);
1105
+ module.performance.timer = setTimeout(function () {
1106
+ module.performance.display();
1107
+ }, 500);
1108
+ },
1109
+ display: function () {
1110
+ let title = settings.name + ':';
1111
+ let totalTime = 0;
1112
+ time = false;
1113
+ clearTimeout(module.performance.timer);
1114
+ $.each(performance, function (index, data) {
1115
+ totalTime += data['Execution Time'];
1116
+ });
1117
+ title += ' ' + totalTime + 'ms';
1118
+ if ($allModules.length > 1) {
1119
+ title += ' (' + $allModules.length + ')';
1120
+ }
1121
+ if (performance.length > 0) {
1122
+ console.groupCollapsed(title);
1123
+ console.table(performance);
1124
+ console.groupEnd();
1125
+ }
1126
+ performance = [];
1127
+ },
1128
+ },
1129
+ invoke: function (query, passedArguments = queryArguments, context = element) {
1130
+ let object = instance;
1131
+ let maxDepth;
1132
+ let found;
1133
+ let response;
1134
+ if (typeof query === 'string' && object !== undefined) {
1135
+ query = query.split(/[ .]/);
1136
+ maxDepth = query.length - 1;
1137
+ $.each(query, function (depth, value) {
1138
+ const camelCaseValue = depth !== maxDepth
1139
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1140
+ : query;
1141
+ if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) {
1142
+ object = object[camelCaseValue];
1143
+ } else if (object[camelCaseValue] !== undefined) {
1144
+ found = object[camelCaseValue];
1145
+
1146
+ return false;
1147
+ } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) {
1148
+ object = object[value];
1149
+ } else if (object[value] !== undefined) {
1150
+ found = object[value];
1151
+
1152
+ return false;
1153
+ } else {
1154
+ module.error(error.method, query);
1155
+
1156
+ return false;
1157
+ }
1158
+ });
1159
+ }
1160
+ if (isFunction(found)) {
1161
+ response = found.apply(context, passedArguments);
1162
+ } else if (found !== undefined) {
1163
+ response = found;
1164
+ }
1165
+ if (Array.isArray(returnedValue)) {
1166
+ returnedValue.push(response);
1167
+ } else if (returnedValue !== undefined) {
1168
+ returnedValue = [returnedValue, response];
1169
+ } else if (response !== undefined) {
1170
+ returnedValue = response;
1171
+ }
1172
+
1173
+ return found;
1174
+ },
1175
+ };
1176
+ if (methodInvoked) {
1177
+ if (instance === undefined) {
1178
+ module.initialize();
1179
+ }
1180
+ module.invoke(parameters);
1181
+ } else {
1182
+ if (instance !== undefined) {
1183
+ instance.invoke('destroy');
1184
+ }
1185
+ module.initialize();
1186
+ }
1187
+ });
1188
+
1189
+ return returnedValue !== undefined
1190
+ ? returnedValue
1191
+ : this;
1192
+ };
1193
+
1194
+ $.fn.search.settings = {
1195
+
1196
+ name: 'Search',
1197
+ namespace: 'search',
1198
+
1199
+ silent: false,
1200
+ debug: false,
1201
+ verbose: false,
1202
+ performance: true,
1203
+
1204
+ // template to use (specified in settings.templates)
1205
+ type: 'standard',
1206
+
1207
+ // minimum characters required to search
1208
+ minCharacters: 1,
1209
+
1210
+ // whether to select the first result after searching automatically
1211
+ selectFirstResult: false,
1212
+
1213
+ // API config
1214
+ apiSettings: false,
1215
+
1216
+ // object to search
1217
+ source: false,
1218
+
1219
+ // Whether search should query current term on focus
1220
+ searchOnFocus: true,
1221
+
1222
+ // fields to search
1223
+ searchFields: [
1224
+ 'id',
1225
+ 'title',
1226
+ 'description',
1227
+ ],
1228
+
1229
+ // field to display in standard results template
1230
+ displayField: '',
1231
+
1232
+ // search anywhere in value (set to 'exact' to require exact matches
1233
+ fullTextSearch: 'exact',
1234
+
1235
+ // Whether search result should highlight matching strings
1236
+ highlightMatches: false,
1237
+
1238
+ // match results also if they contain diacritics of the same base character (for example, searching for "a" will also match "á" or "â" or "à", etc...)
1239
+ ignoreDiacritics: false,
1240
+
1241
+ // whether to consider case sensitivity on local searching
1242
+ ignoreSearchCase: true,
1243
+
1244
+ // whether to add events to prompt automatically
1245
+ automatic: true,
1246
+
1247
+ // delay before hiding the menu after blur
1248
+ hideDelay: 0,
1249
+
1250
+ // delay before searching
1251
+ searchDelay: 200,
1252
+
1253
+ // maximum results returned from search
1254
+ maxResults: 7,
1255
+
1256
+ // whether to store lookups in local cache
1257
+ cache: true,
1258
+
1259
+ // whether no results errors should be shown
1260
+ showNoResults: true,
1261
+
1262
+ // preserve possible html of resultset values
1263
+ preserveHTML: false,
1264
+
1265
+ // transition settings
1266
+ transition: 'scale',
1267
+ duration: 200,
1268
+ easing: 'easeOutExpo',
1269
+
1270
+ // callbacks
1271
+ onSelect: false,
1272
+ onResultsAdd: false,
1273
+
1274
+ onSearchQuery: function (query) {},
1275
+ onResults: function (response, fromCache) {},
1276
+
1277
+ onResultsOpen: function () {},
1278
+ onResultsClose: function () {},
1279
+
1280
+ className: {
1281
+ animating: 'animating',
1282
+ active: 'active',
1283
+ category: 'category',
1284
+ empty: 'empty',
1285
+ focus: 'focus',
1286
+ hidden: 'hidden',
1287
+ loading: 'loading',
1288
+ results: 'results',
1289
+ pressed: 'down',
1290
+ },
1291
+
1292
+ error: {
1293
+ source: 'Cannot search. No source used, and Fomantic API module was not included',
1294
+ noResultsHeader: 'No Results',
1295
+ noResults: 'Your search returned no results',
1296
+ noTemplate: 'A valid template name was not specified.',
1297
+ oldSearchSyntax: 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
1298
+ serverError: 'There was an issue querying the server.',
1299
+ maxResults: 'Results must be an array to use maxResults setting',
1300
+ method: 'The method you called is not defined.',
1301
+ },
1302
+
1303
+ metadata: {
1304
+ cache: 'cache',
1305
+ results: 'results',
1306
+ result: 'result',
1307
+ },
1308
+
1309
+ regExp: {
1310
+ escape: /[$()*+./?[\\\]^{|}-]/g,
1311
+ beginsWith: '(?:\\s|^)',
1312
+ },
1313
+
1314
+ // maps api response attributes to internal representation
1315
+ fields: {
1316
+ categories: 'results', // array of categories (category view)
1317
+ categoryName: 'name', // name of category (category view)
1318
+ categoryResults: 'results', // array of results (category view)
1319
+ description: 'description', // result description
1320
+ image: 'image', // result image
1321
+ alt: 'alt', // result alt text for image
1322
+ price: 'price', // result price
1323
+ results: 'results', // array of results (standard)
1324
+ title: 'title', // result title
1325
+ url: 'url', // result url
1326
+ id: 'id', // HTML 'id' attribute
1327
+ class: 'class', // Class specific to each result to add to the HTML 'class' attribute.
1328
+ action: 'action', // "view more" object name
1329
+ actionText: 'text', // "view more" text
1330
+ actionURL: 'url', // "view more" url
1331
+ },
1332
+
1333
+ selector: {
1334
+ prompt: '.prompt',
1335
+ remove: '> .icon.input > .remove.icon',
1336
+ searchButton: '.search.button',
1337
+ results: '.results',
1338
+ message: '.results > .message',
1339
+ category: '.category',
1340
+ result: '.result',
1341
+ title: '.title, .name',
1342
+ },
1343
+
1344
+ templates: {
1345
+ escape: function (string, settings) {
1346
+ if (settings !== undefined && settings.preserveHTML) {
1347
+ return string;
1348
+ }
1349
+
1350
+ const escapeMap = {
1351
+ '"': '&quot;',
1352
+ '&': '&amp;',
1353
+ "'": '&apos;',
1354
+ '<': '&lt;',
1355
+ '>': '&gt;',
1356
+ };
1357
+
1358
+ string = String(string).replace(/["&'<>]/g, (chr) => escapeMap[chr]);
1359
+
1360
+ // FUI controlled HTML is still allowed
1361
+ string = string.replace(/&lt;(\/)*mark&gt;/g, '<$1mark>');
1362
+
1363
+ return string;
1364
+ },
1365
+ message: function (message, type, header) {
1366
+ let html = '';
1367
+ if (message !== undefined && type !== undefined) {
1368
+ html += ''
1369
+ + '<div class="message ' + type + '">';
1370
+ if (header) {
1371
+ html += ''
1372
+ + '<div class="header">' + header + '</div>';
1373
+ }
1374
+ html += ' <div class="description">' + message + '</div>';
1375
+ html += '</div>';
1376
+ }
1377
+
1378
+ return html;
1379
+ },
1380
+ category: function (response, settings) {
1381
+ let html = '';
1382
+ const fields = settings.fields;
1383
+ const escape = settings.templates.escape;
1384
+ if (response[fields.categoryResults] !== undefined) {
1385
+ // each category
1386
+ $.each(response[fields.categoryResults], function (index, category) {
1387
+ if (category[fields.results] !== undefined && category.results.length > 0) {
1388
+ html += '<div class="category">';
1389
+
1390
+ if (category[fields.categoryName] !== undefined) {
1391
+ html += '<div class="name">' + escape(category[fields.categoryName], settings) + '</div>';
1392
+ }
1393
+
1394
+ // each item inside category
1395
+ html += '<div class="results">';
1396
+ $.each(category.results, function (index, result) {
1397
+ html += result[fields.url]
1398
+ ? '<a href="' + result[fields.url].replaceAll('"', '') + '" '
1399
+ : '<div ';
1400
+
1401
+ html += result[fields.id] !== undefined
1402
+ ? ' id="' + result[fields.id].replaceAll('"', '') + '" '
1403
+ : '';
1404
+
1405
+ html += result[fields.class] !== undefined
1406
+ ? ' class="result ' + result[fields.class].replaceAll('"', '') + '">'
1407
+ : ' class="result">';
1408
+
1409
+ if (result[fields.image] !== undefined) {
1410
+ html += ''
1411
+ + '<div class="image">'
1412
+ + ' <img src="' + result[fields.image].replaceAll('"', '') + '"' + (result[fields.alt] ? ' alt="' + result[fields.alt].replaceAll('"', '') + '"' : '') + '>'
1413
+ + '</div>';
1414
+ }
1415
+ html += '<div class="content">';
1416
+ if (result[fields.price] !== undefined) {
1417
+ html += '<div class="price">' + escape(result[fields.price], settings) + '</div>';
1418
+ }
1419
+ if (result[fields.title] !== undefined) {
1420
+ html += '<div class="title">' + escape(result[fields.title], settings) + '</div>';
1421
+ }
1422
+ if (result[fields.description] !== undefined) {
1423
+ html += '<div class="description">' + escape(result[fields.description], settings) + '</div>';
1424
+ }
1425
+ html += ''
1426
+ + '</div>';
1427
+ html += result[fields.url]
1428
+ ? '</a>'
1429
+ : '</div>';
1430
+ });
1431
+ html += '</div>';
1432
+ html += ''
1433
+ + '</div>';
1434
+ }
1435
+ });
1436
+ if (response[fields.action]) {
1437
+ html += fields.actionURL === false
1438
+ ? ''
1439
+ + '<div class="action">'
1440
+ + escape(response[fields.action][fields.actionText], settings)
1441
+ + '</div>'
1442
+ : ''
1443
+ + '<a href="' + response[fields.action][fields.actionURL].replaceAll('"', '') + '" class="action">'
1444
+ + escape(response[fields.action][fields.actionText], settings)
1445
+ + '</a>';
1446
+ }
1447
+
1448
+ return html;
1449
+ }
1450
+
1451
+ return false;
1452
+ },
1453
+ standard: function (response, settings) {
1454
+ let html = '';
1455
+ const fields = settings.fields;
1456
+ const escape = settings.templates.escape;
1457
+ if (response[fields.results] !== undefined) {
1458
+ // each result
1459
+ $.each(response[fields.results], function (index, result) {
1460
+ html += result[fields.url]
1461
+ ? '<a href="' + result[fields.url].replaceAll('"', '') + '" '
1462
+ : '<div ';
1463
+
1464
+ html += result[fields.id] !== undefined
1465
+ ? 'id="' + result[fields.id].replaceAll('"', '') + '" '
1466
+ : '';
1467
+
1468
+ html += result[fields.class] !== undefined
1469
+ ? 'class="result ' + result[fields.class].replaceAll('"', '') + '">'
1470
+ : 'class="result">';
1471
+
1472
+ if (result[fields.image] !== undefined) {
1473
+ html += ''
1474
+ + '<div class="image">'
1475
+ + ' <img src="' + result[fields.image].replaceAll('"', '') + '"' + (result[fields.alt] ? ' alt="' + result[fields.alt].replaceAll('"', '') + '"' : '') + '>'
1476
+ + '</div>';
1477
+ }
1478
+ html += '<div class="content">';
1479
+ if (result[fields.price] !== undefined) {
1480
+ html += '<div class="price">' + escape(result[fields.price], settings) + '</div>';
1481
+ }
1482
+ if (result[fields.title] !== undefined) {
1483
+ html += '<div class="title">' + escape(result[fields.title], settings) + '</div>';
1484
+ }
1485
+ if (result[fields.description] !== undefined) {
1486
+ html += '<div class="description">' + escape(result[fields.description], settings) + '</div>';
1487
+ }
1488
+ html += ''
1489
+ + '</div>';
1490
+ html += result[fields.url]
1491
+ ? '</a>'
1492
+ : '</div>';
1493
+ });
1494
+ if (response[fields.action]) {
1495
+ html += fields.actionURL === false
1496
+ ? ''
1497
+ + '<div class="action">'
1498
+ + escape(response[fields.action][fields.actionText], settings)
1499
+ + '</div>'
1500
+ : ''
1501
+ + '<a href="' + response[fields.action][fields.actionURL].replaceAll('"', '') + '" class="action">'
1502
+ + escape(response[fields.action][fields.actionText], settings)
1503
+ + '</a>';
1504
+ }
1505
+
1506
+ return html;
1507
+ }
1508
+
1509
+ return false;
1510
+ },
1511
+ },
1512
+ };
1513
+
1514
+ $.extend($.easing, {
1515
+ easeOutExpo: function (x) {
1516
+ return x === 1 ? 1 : 1 - 2 ** (-10 * x);
1517
+ },
1518
+ });
1519
+ })(jQuery, window, document);