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,867 @@
1
+ /*!
2
+ * # Fomantic-UI 2.9.4 - Tab
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 isWindow(obj) {
15
+ return obj !== null && obj === obj.window;
16
+ }
17
+
18
+ function isFunction(obj) {
19
+ return typeof obj === 'function' && typeof obj.nodeType !== 'number';
20
+ }
21
+
22
+ window = window !== undefined && window.Math === Math
23
+ ? window
24
+ : globalThis;
25
+
26
+ $.fn.tab = function (...args) {
27
+ // use window context if none specified
28
+ const $allModules = isFunction(this)
29
+ ? $(window)
30
+ : $(this);
31
+ const $document = $(document);
32
+ let time = Date.now();
33
+ let performance = [];
34
+
35
+ const parameters = args[0];
36
+ const methodInvoked = typeof parameters === 'string';
37
+ const queryArguments = args.slice(1);
38
+ const contextCheck = function (context, win) {
39
+ let $context;
40
+ if ([window, document].includes(context)) {
41
+ $context = $(context);
42
+ } else {
43
+ $context = $(win.document).find(context);
44
+ if ($context.length === 0) {
45
+ $context = win.frameElement ? contextCheck(context, win.parent) : window;
46
+ }
47
+ }
48
+
49
+ return $context;
50
+ };
51
+ let initializedHistory = false;
52
+ let returnedValue;
53
+
54
+ $allModules.each(function () {
55
+ const settings = $.isPlainObject(parameters)
56
+ ? $.extend(true, {}, $.fn.tab.settings, parameters)
57
+ : $.extend({}, $.fn.tab.settings);
58
+
59
+ const className = settings.className;
60
+ const metadata = settings.metadata;
61
+ const selector = settings.selector;
62
+ const error = settings.error;
63
+
64
+ const eventNamespace = '.' + settings.namespace;
65
+ const moduleNamespace = 'module-' + settings.namespace;
66
+
67
+ const $module = $(this);
68
+ let $context;
69
+ let $tabs;
70
+
71
+ const cache = {};
72
+ let firstLoad = true;
73
+ let recursionDepth = 0;
74
+ const element = this;
75
+ let instance = $module.data(moduleNamespace);
76
+
77
+ let activeTabPath;
78
+ let parameterArray;
79
+
80
+ let historyEvent;
81
+
82
+ const module = {
83
+
84
+ initialize: function () {
85
+ module.debug('Initializing tab menu item', $module);
86
+ module.determineTabs();
87
+
88
+ module.debug('Determining tabs', settings.context, $tabs);
89
+ // set up automatic routing
90
+ if (settings.auto) {
91
+ module.set.auto();
92
+ }
93
+ module.bind.events();
94
+
95
+ if (settings.history && !initializedHistory) {
96
+ settings.history = module.initializeHistory();
97
+ initializedHistory = true;
98
+ }
99
+
100
+ let activeTab = module.determine.activeTab();
101
+ if (settings.autoTabActivation && instance === undefined && activeTab === null) {
102
+ activeTab = settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation;
103
+ module.debug('No active tab detected, setting tab active', activeTab);
104
+ module.changeTab(activeTab);
105
+ }
106
+ if (activeTab !== null && settings.history && settings.historyType === 'state') {
107
+ const autoUpdate = $.address.autoUpdate();
108
+ $.address.autoUpdate(false);
109
+ $.address.value(activeTab);
110
+ $.address.autoUpdate(autoUpdate);
111
+ }
112
+
113
+ module.instantiate();
114
+ },
115
+
116
+ instantiate: function () {
117
+ module.verbose('Storing instance of module', module);
118
+ instance = module;
119
+ $module
120
+ .data(moduleNamespace, module);
121
+ },
122
+
123
+ destroy: function () {
124
+ module.debug('Destroying tabs', $module);
125
+ $module
126
+ .removeData(moduleNamespace)
127
+ .off(eventNamespace);
128
+ },
129
+
130
+ bind: {
131
+ events: function () {
132
+ // if using $.tab don't add events
133
+ if (!isWindow(element)) {
134
+ module.debug('Attaching tab activation events to element', $module);
135
+ $module
136
+ .on('click' + eventNamespace, module.event.click);
137
+ }
138
+ },
139
+ },
140
+
141
+ determineTabs: function () {
142
+ let $reference;
143
+
144
+ // determine tab context
145
+ if (settings.context === 'parent') {
146
+ if ($module.closest(selector.ui).length > 0) {
147
+ $reference = $module.closest(selector.ui);
148
+ module.verbose('Using closest UI element as parent', $reference);
149
+ } else {
150
+ $reference = $module;
151
+ }
152
+ $context = $reference.parent();
153
+ module.verbose('Determined parent element for creating context', $context);
154
+ } else if (settings.context) {
155
+ $context = contextCheck(settings.context, window);
156
+ module.verbose('Using selector for tab context', settings.context, $context);
157
+ } else {
158
+ $context = $('body');
159
+ }
160
+ // find tabs
161
+ if (settings.childrenOnly) {
162
+ $tabs = $context.children(selector.tabs);
163
+ module.debug('Searching tab context children for tabs', $context, $tabs);
164
+ } else {
165
+ $tabs = $context.find(selector.tabs);
166
+ module.debug('Searching tab context for tabs', $context, $tabs);
167
+ }
168
+ },
169
+
170
+ initializeHistory: function () {
171
+ module.debug('Initializing page state');
172
+ if ($.address === undefined) {
173
+ module.error(error.state);
174
+
175
+ return false;
176
+ }
177
+ if (settings.historyType === 'state') {
178
+ module.debug('Using HTML5 to manage state');
179
+ if (settings.path !== false) {
180
+ $.address
181
+ .history(true)
182
+ .state(settings.path);
183
+ $(window).trigger('popstate');
184
+ } else {
185
+ module.error(error.path);
186
+
187
+ return false;
188
+ }
189
+ }
190
+ $.address
191
+ .bind('change', module.event.history.change);
192
+
193
+ return true;
194
+ },
195
+
196
+ event: {
197
+ click: function (event) {
198
+ const tabPath = $(this).data(metadata.tab);
199
+ if (tabPath !== undefined) {
200
+ if (settings.history) {
201
+ module.verbose('Updating page state', event);
202
+ $.address.value(tabPath);
203
+ } else {
204
+ module.verbose('Changing tab', event);
205
+ module.changeTab(tabPath);
206
+ }
207
+ event.preventDefault();
208
+ } else {
209
+ module.debug('No tab specified');
210
+ }
211
+ },
212
+ history: {
213
+ change: function (event) {
214
+ const tabPath = event.pathNames.join('/') || module.get.initialPath();
215
+ const pageTitle = settings.templates.determineTitle(tabPath) || false;
216
+ module.performance.display();
217
+ module.debug('History change event', tabPath, event);
218
+ historyEvent = event;
219
+ if (tabPath !== undefined) {
220
+ module.changeTab(tabPath);
221
+ }
222
+ if (pageTitle) {
223
+ $.address.title(pageTitle);
224
+ }
225
+ },
226
+ },
227
+ },
228
+
229
+ refresh: function () {
230
+ if (activeTabPath) {
231
+ module.debug('Refreshing tab', activeTabPath);
232
+ module.changeTab(activeTabPath);
233
+ }
234
+ },
235
+
236
+ cache: {
237
+
238
+ read: function (cacheKey) {
239
+ return cacheKey !== undefined
240
+ ? cache[cacheKey]
241
+ : false;
242
+ },
243
+ add: function (cacheKey = activeTabPath, content = '') {
244
+ module.debug('Adding cached content for', cacheKey);
245
+ cache[cacheKey] = content;
246
+ },
247
+ remove: function (cacheKey = activeTabPath) {
248
+ module.debug('Removing cached content for', cacheKey);
249
+ delete cache[cacheKey];
250
+ },
251
+ },
252
+
253
+ set: {
254
+ auto: function () {
255
+ const url = typeof settings.path === 'string'
256
+ ? settings.path.replace(/\/$/, '') + '/{$tab}'
257
+ : '/{$tab}';
258
+ module.verbose('Setting up automatic tab retrieval from server', url);
259
+ if ($.isPlainObject(settings.apiSettings)) {
260
+ settings.apiSettings.url = url;
261
+ } else {
262
+ settings.apiSettings = {
263
+ url: url,
264
+ };
265
+ }
266
+ },
267
+ loading: function (tabPath) {
268
+ const $tab = module.get.tabElement(tabPath);
269
+ const isLoading = $tab.hasClass(className.loading);
270
+ if (!isLoading) {
271
+ module.verbose('Setting loading state for', $tab);
272
+ $tab
273
+ .addClass(className.loading)
274
+ .siblings($tabs)
275
+ .removeClass(className.active + ' ' + className.loading);
276
+ if ($tab.length > 0) {
277
+ settings.onRequest.call($tab[0], tabPath);
278
+ }
279
+ }
280
+ },
281
+ state: function (state) {
282
+ $.address.value(state);
283
+ },
284
+ },
285
+
286
+ changeTab: function (tabPath) {
287
+ const pushStateAvailable = window.history && window.history.pushState;
288
+ const shouldIgnoreLoad = pushStateAvailable && settings.ignoreFirstLoad && firstLoad;
289
+ const remoteContent = settings.auto || $.isPlainObject(settings.apiSettings);
290
+ // only add the default path if not remote content
291
+ const pathArray = remoteContent && !shouldIgnoreLoad
292
+ ? module.utilities.pathToArray(tabPath)
293
+ : module.get.defaultPathArray(tabPath);
294
+ tabPath = module.utilities.arrayToPath(pathArray);
295
+ $.each(pathArray, function (index, tab) {
296
+ const currentPathArray = pathArray.slice(0, index + 1);
297
+ let currentPath = module.utilities.arrayToPath(currentPathArray);
298
+
299
+ const isTab = module.is.tab(currentPath);
300
+ const isLastIndex = index + 1 === pathArray.length;
301
+
302
+ let $tab = module.get.tabElement(currentPath);
303
+ let $anchor;
304
+ let nextPathArray;
305
+ let nextPath;
306
+ let isLastTab;
307
+ module.verbose('Looking for tab', tab);
308
+ if (isTab) {
309
+ module.verbose('Tab was found', tab);
310
+ // scope up
311
+ activeTabPath = currentPath;
312
+ parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
313
+
314
+ if (isLastIndex) {
315
+ isLastTab = true;
316
+ } else {
317
+ nextPathArray = pathArray.slice(0, index + 2);
318
+ nextPath = module.utilities.arrayToPath(nextPathArray);
319
+ isLastTab = !module.is.tab(nextPath);
320
+ if (isLastTab) {
321
+ module.verbose('Tab parameters found', nextPathArray);
322
+ }
323
+ }
324
+ if (settings.onBeforeChange.call(element, currentPath) === false) {
325
+ module.debug('onBeforeChange returned false, cancelling tab change', $tab);
326
+
327
+ return false;
328
+ }
329
+ if (isLastTab && remoteContent) {
330
+ if (!shouldIgnoreLoad) {
331
+ module.activate.navigation(currentPath);
332
+ module.fetch.content(currentPath, tabPath);
333
+ } else {
334
+ module.debug('Ignoring remote content on first tab load', currentPath);
335
+ firstLoad = false;
336
+ module.cache.add(tabPath, $tab.html());
337
+ module.activate.all(currentPath);
338
+ settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
339
+ settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
340
+ }
341
+
342
+ return false;
343
+ }
344
+
345
+ module.debug('Opened local tab', currentPath);
346
+ module.activate.all(currentPath);
347
+ if (!module.cache.read(currentPath)) {
348
+ module.cache.add(currentPath, true);
349
+ module.debug('First time tab loaded calling tab init');
350
+ settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
351
+ }
352
+ settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
353
+ } else if (tabPath.search('/') === -1 && tabPath !== '') {
354
+ // look for in page anchor
355
+ $anchor = $('#' + CSS.escape(tabPath) + ', a[name="' + CSS.escape(tabPath) + '"]');
356
+ currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
357
+ $tab = module.get.tabElement(currentPath);
358
+ // if anchor exists, use parent tab
359
+ if ($anchor && $anchor.length > 0 && currentPath) {
360
+ module.debug('Anchor link used, opening parent tab', $tab, $anchor);
361
+ if (settings.onBeforeChange.call(element, currentPath) === false) {
362
+ module.debug('onBeforeChange returned false, cancelling tab change', $tab);
363
+
364
+ return false;
365
+ }
366
+ if (!$tab.hasClass(className.active)) {
367
+ setTimeout(function () {
368
+ module.scrollTo($anchor);
369
+ }, 0);
370
+ }
371
+ module.activate.all(currentPath);
372
+ if (!module.cache.read(currentPath)) {
373
+ module.cache.add(currentPath, true);
374
+ module.debug('First time tab loaded calling tab init');
375
+ settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
376
+ }
377
+ settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
378
+
379
+ return false;
380
+ }
381
+ } else {
382
+ module.error(error.missingTab, $module, $context, currentPath);
383
+
384
+ return false;
385
+ }
386
+ });
387
+ },
388
+
389
+ scrollTo: function ($element) {
390
+ const scrollOffset = $element && $element.length > 0
391
+ ? $element.offset().top
392
+ : false;
393
+ if (scrollOffset !== false) {
394
+ module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
395
+ $document.scrollTop(scrollOffset);
396
+ }
397
+ },
398
+
399
+ update: {
400
+ content: function (tabPath, html, evaluateScripts = settings.evaluateScripts) {
401
+ const $tab = module.get.tabElement(tabPath);
402
+ const tab = $tab[0];
403
+ if (typeof settings.cacheType === 'string' && settings.cacheType.toLowerCase() === 'dom' && typeof html !== 'string') {
404
+ $tab
405
+ .empty()
406
+ .append($(html).clone(true));
407
+ } else if (evaluateScripts) {
408
+ module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
409
+ $tab.html(html);
410
+ } else {
411
+ module.debug('Updating HTML', tabPath, html);
412
+ tab.innerHTML = html;
413
+ }
414
+ },
415
+ },
416
+
417
+ fetch: {
418
+
419
+ content: function (tabPath, fullTabPath = tabPath) {
420
+ const $tab = module.get.tabElement(tabPath);
421
+ const apiSettings = {
422
+ dataType: 'html',
423
+ encodeParameters: false,
424
+ on: 'now',
425
+ cache: settings.alwaysRefresh,
426
+ onSuccess: function (response) {
427
+ if (settings.cacheType === 'response') {
428
+ module.cache.add(fullTabPath, response);
429
+ }
430
+ module.update.content(tabPath, response);
431
+ if (tabPath == activeTabPath) {
432
+ module.debug('Content loaded', tabPath);
433
+ module.activate.tab(tabPath);
434
+ } else {
435
+ module.debug('Content loaded in background', tabPath);
436
+ }
437
+ settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
438
+ settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
439
+
440
+ if (settings.loadOnce) {
441
+ module.cache.add(fullTabPath, true);
442
+ } else if (typeof settings.cacheType === 'string' && settings.cacheType.toLowerCase() === 'dom' && $tab.children().length > 0) {
443
+ setTimeout(function () {
444
+ let $clone = $tab.children().clone(true);
445
+ $clone = $clone.not('script');
446
+ module.cache.add(fullTabPath, $clone);
447
+ }, 0);
448
+ } else {
449
+ module.cache.add(fullTabPath, $tab.html());
450
+ }
451
+ },
452
+ urlData: {
453
+ tab: fullTabPath,
454
+ },
455
+ };
456
+ const request = $tab.api('get request') || false;
457
+ const existingRequest = request && request.state() === 'pending';
458
+ let requestSettings;
459
+
460
+ const cachedContent = module.cache.read(fullTabPath);
461
+
462
+ if (settings.cache && cachedContent) {
463
+ module.activate.tab(tabPath);
464
+ module.debug('Adding cached content', fullTabPath);
465
+ if (!settings.loadOnce) {
466
+ if (settings.evaluateScripts === 'once') {
467
+ module.update.content(tabPath, cachedContent, false);
468
+ } else {
469
+ module.update.content(tabPath, cachedContent);
470
+ }
471
+ }
472
+ settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
473
+ } else if (existingRequest) {
474
+ module.set.loading(tabPath);
475
+ module.debug('Content is already loading', fullTabPath);
476
+ } else if ($.api !== undefined) {
477
+ requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
478
+ module.debug('Retrieving remote content', fullTabPath, requestSettings);
479
+ module.set.loading(tabPath);
480
+ $tab.api(requestSettings);
481
+ } else {
482
+ module.error(error.api);
483
+ }
484
+ },
485
+ },
486
+
487
+ activate: {
488
+ all: function (tabPath) {
489
+ module.activate.tab(tabPath);
490
+ module.activate.navigation(tabPath);
491
+ },
492
+ tab: function (tabPath) {
493
+ const $tab = module.get.tabElement(tabPath);
494
+ const $deactiveTabs = settings.deactivate === 'siblings'
495
+ ? $tab.siblings($tabs)
496
+ : $tabs.not($tab);
497
+ const isActive = $tab.hasClass(className.active);
498
+ module.verbose('Showing tab content for', $tab);
499
+ if (!isActive) {
500
+ $tab
501
+ .addClass(className.active);
502
+ $deactiveTabs
503
+ .removeClass(className.active + ' ' + className.loading);
504
+ if ($tab.length > 0) {
505
+ settings.onVisible.call($tab[0], tabPath);
506
+ }
507
+ }
508
+ },
509
+ navigation: function (tabPath) {
510
+ const $navigation = module.get.navElement(tabPath);
511
+ const $deactiveNavigation = settings.deactivate === 'siblings'
512
+ ? $navigation.siblings($allModules)
513
+ : $allModules.not($navigation);
514
+ const isActive = $navigation.hasClass(className.active);
515
+ module.verbose('Activating tab navigation for', $navigation, tabPath);
516
+ if (!isActive) {
517
+ $navigation
518
+ .addClass(className.active);
519
+ $deactiveNavigation
520
+ .removeClass(className.active + ' ' + className.loading);
521
+ }
522
+ },
523
+ },
524
+
525
+ deactivate: {
526
+ all: function () {
527
+ module.deactivate.navigation();
528
+ module.deactivate.tabs();
529
+ },
530
+ navigation: function () {
531
+ $allModules
532
+ .removeClass(className.active);
533
+ },
534
+ tabs: function () {
535
+ $tabs
536
+ .removeClass(className.active + ' ' + className.loading);
537
+ },
538
+ },
539
+
540
+ is: {
541
+ tab: function (tabName) {
542
+ return tabName !== undefined
543
+ ? module.get.tabElement(tabName).length > 0
544
+ : false;
545
+ },
546
+ },
547
+
548
+ get: {
549
+ initialPath: function () {
550
+ return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
551
+ },
552
+ path: function () {
553
+ return $.address.value();
554
+ },
555
+ // adds default tabs to the tab path
556
+ defaultPathArray: function (tabPath) {
557
+ return module.utilities.pathToArray(module.get.defaultPath(tabPath));
558
+ },
559
+ defaultPath: function (tabPath) {
560
+ const $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + CSS.escape(tabPath) + '/"]').eq(0);
561
+ const defaultTab = $defaultNav.data(metadata.tab) || false;
562
+ if (defaultTab) {
563
+ module.debug('Found default tab', defaultTab);
564
+ if (recursionDepth < settings.maxDepth) {
565
+ recursionDepth++;
566
+
567
+ return module.get.defaultPath(defaultTab);
568
+ }
569
+ module.error(error.recursion);
570
+ } else {
571
+ module.debug('No default tabs found for', tabPath, $tabs);
572
+ }
573
+ recursionDepth = 0;
574
+
575
+ return tabPath;
576
+ },
577
+ navElement: function (tabPath = activeTabPath) {
578
+ return $allModules.filter('[data-' + metadata.tab + '="' + CSS.escape(tabPath) + '"]');
579
+ },
580
+ tabElement: function (tabPath = activeTabPath) {
581
+ const tabPathArray = module.utilities.pathToArray(tabPath);
582
+ const lastTab = module.utilities.last(tabPathArray);
583
+ const $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + CSS.escape(tabPath) + '"]');
584
+ const $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + CSS.escape(lastTab) + '"]');
585
+
586
+ return $fullPathTab.length > 0
587
+ ? $fullPathTab
588
+ : $simplePathTab;
589
+ },
590
+ tab: function () {
591
+ return activeTabPath;
592
+ },
593
+ },
594
+
595
+ determine: {
596
+ activeTab: function () {
597
+ let activeTab = null;
598
+
599
+ $tabs.each(function (_index, tab) {
600
+ const $tab = $(tab);
601
+
602
+ if ($tab.hasClass(className.active)) {
603
+ const tabPath = $(this).data(metadata.tab);
604
+ const $anchor = $allModules.filter('[data-' + metadata.tab + '="' + CSS.escape(tabPath) + '"]');
605
+
606
+ if ($anchor.hasClass(className.active)) {
607
+ activeTab = tabPath;
608
+ }
609
+ }
610
+ });
611
+
612
+ return activeTab;
613
+ },
614
+ },
615
+
616
+ utilities: {
617
+ filterArray: function (keepArray, removeArray) {
618
+ return $.grep(keepArray, function (keepValue) {
619
+ return !removeArray.includes(keepValue);
620
+ });
621
+ },
622
+ last: function (array) {
623
+ return Array.isArray(array)
624
+ ? array[array.length - 1]
625
+ : false;
626
+ },
627
+ pathToArray: function (pathName) {
628
+ if (pathName === undefined) {
629
+ pathName = activeTabPath;
630
+ }
631
+
632
+ return typeof pathName === 'string'
633
+ ? pathName.split('/')
634
+ : [pathName];
635
+ },
636
+ arrayToPath: function (pathArray) {
637
+ return Array.isArray(pathArray)
638
+ ? pathArray.join('/')
639
+ : false;
640
+ },
641
+ },
642
+
643
+ setting: function (name, value) {
644
+ module.debug('Changing setting', name, value);
645
+ if ($.isPlainObject(name)) {
646
+ $.extend(true, settings, name);
647
+ } else if (value !== undefined) {
648
+ if ($.isPlainObject(settings[name])) {
649
+ $.extend(true, settings[name], value);
650
+ } else {
651
+ settings[name] = value;
652
+ }
653
+ } else {
654
+ return settings[name];
655
+ }
656
+ },
657
+ internal: function (name, value) {
658
+ if ($.isPlainObject(name)) {
659
+ $.extend(true, module, name);
660
+ } else if (value !== undefined) {
661
+ module[name] = value;
662
+ } else {
663
+ return module[name];
664
+ }
665
+ },
666
+ debug: function (...args) {
667
+ if (!settings.silent && settings.debug) {
668
+ if (settings.performance) {
669
+ module.performance.log(args);
670
+ } else {
671
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
672
+ module.debug.apply(console, args);
673
+ }
674
+ }
675
+ },
676
+ verbose: function (...args) {
677
+ if (!settings.silent && settings.verbose && settings.debug) {
678
+ if (settings.performance) {
679
+ module.performance.log(args);
680
+ } else {
681
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
682
+ module.verbose.apply(console, args);
683
+ }
684
+ }
685
+ },
686
+ error: function (...args) {
687
+ if (!settings.silent) {
688
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
689
+ module.error.apply(console, args);
690
+ }
691
+ },
692
+ performance: {
693
+ log: function (message) {
694
+ let currentTime;
695
+ let executionTime;
696
+ let previousTime;
697
+ if (settings.performance) {
698
+ currentTime = Date.now();
699
+ previousTime = time || currentTime;
700
+ executionTime = currentTime - previousTime;
701
+ time = currentTime;
702
+ performance.push({
703
+ Name: message[0],
704
+ Arguments: message.slice(1),
705
+ Element: element,
706
+ 'Execution Time': executionTime,
707
+ });
708
+ }
709
+ clearTimeout(module.performance.timer);
710
+ module.performance.timer = setTimeout(function () {
711
+ module.performance.display();
712
+ }, 500);
713
+ },
714
+ display: function () {
715
+ let title = settings.name + ':';
716
+ let totalTime = 0;
717
+ time = false;
718
+ clearTimeout(module.performance.timer);
719
+ $.each(performance, function (index, data) {
720
+ totalTime += data['Execution Time'];
721
+ });
722
+ title += ' ' + totalTime + 'ms';
723
+ if (performance.length > 0) {
724
+ console.groupCollapsed(title);
725
+ console.table(performance);
726
+ console.groupEnd();
727
+ }
728
+ performance = [];
729
+ },
730
+ },
731
+ invoke: function (query, passedArguments = queryArguments, context = element) {
732
+ let object = instance;
733
+ let maxDepth;
734
+ let found;
735
+ let response;
736
+ if (typeof query === 'string' && object !== undefined) {
737
+ query = query.split(/[ .]/);
738
+ maxDepth = query.length - 1;
739
+ $.each(query, function (depth, value) {
740
+ const camelCaseValue = depth !== maxDepth
741
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
742
+ : query;
743
+ if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) {
744
+ object = object[camelCaseValue];
745
+ } else if (object[camelCaseValue] !== undefined) {
746
+ found = object[camelCaseValue];
747
+
748
+ return false;
749
+ } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) {
750
+ object = object[value];
751
+ } else if (object[value] !== undefined) {
752
+ found = object[value];
753
+
754
+ return false;
755
+ } else {
756
+ module.error(error.method, query);
757
+
758
+ return false;
759
+ }
760
+ });
761
+ }
762
+ if (isFunction(found)) {
763
+ response = found.apply(context, passedArguments);
764
+ } else if (found !== undefined) {
765
+ response = found;
766
+ }
767
+ if (Array.isArray(returnedValue)) {
768
+ returnedValue.push(response);
769
+ } else if (returnedValue !== undefined) {
770
+ returnedValue = [returnedValue, response];
771
+ } else if (response !== undefined) {
772
+ returnedValue = response;
773
+ }
774
+
775
+ return found;
776
+ },
777
+ };
778
+ if (methodInvoked) {
779
+ if (instance === undefined) {
780
+ module.initialize();
781
+ }
782
+ module.invoke(parameters);
783
+ } else {
784
+ if (instance !== undefined) {
785
+ instance.invoke('destroy');
786
+ }
787
+ module.initialize();
788
+ }
789
+ });
790
+
791
+ return returnedValue !== undefined
792
+ ? returnedValue
793
+ : this;
794
+ };
795
+
796
+ // shortcut for tabbed content with no defined navigation
797
+ $.tab = $.fn.tab;
798
+
799
+ $.fn.tab.settings = {
800
+
801
+ name: 'Tab',
802
+ namespace: 'tab',
803
+
804
+ silent: false,
805
+ debug: false,
806
+ verbose: false,
807
+ performance: true,
808
+
809
+ auto: false, // uses pjax style endpoints fetching content from the same url with remote-content headers
810
+ history: false, // use browser history
811
+ historyType: 'hash', // #/ or html5 state
812
+ path: false, // base path of url
813
+
814
+ context: false, // specify a context that tabs must appear inside
815
+ childrenOnly: false, // use only tabs that are children of context
816
+ maxDepth: 25, // max depth a tab can be nested
817
+
818
+ deactivate: 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
819
+
820
+ alwaysRefresh: false, // load tab content new every tab click
821
+ cache: true, // cache the content requests to pull locally
822
+ loadOnce: false, // Whether tab data should only be loaded once when using remote content
823
+ cacheType: 'response', // Whether to cache exact response, or to HTML cache contents after scripts execute
824
+ ignoreFirstLoad: false, // don't load remote content on the first load
825
+
826
+ apiSettings: false, // settings for api call
827
+ evaluateScripts: 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
828
+ autoTabActivation: true, // whether a non-existing active tab will auto activate the first available tab
829
+
830
+ onFirstLoad: function (tabPath, parameterArray, historyEvent) {}, // called first time loaded
831
+ onLoad: function (tabPath, parameterArray, historyEvent) {}, // called on every load
832
+ onVisible: function (tabPath, parameterArray, historyEvent) {}, // called every time tab visible
833
+ onRequest: function (tabPath, parameterArray, historyEvent) {}, // called every time a tab beings loading remote content
834
+ onBeforeChange: function (tabPath) {}, // called before a tab is about to be changed. Returning false will cancel the tab change
835
+
836
+ templates: {
837
+ determineTitle: function (tabArray) {}, // returns page title for the path
838
+ },
839
+
840
+ error: {
841
+ api: 'You attempted to load content without API module',
842
+ method: 'The method you called is not defined',
843
+ missingTab: 'Activated tab cannot be found. Tabs are case-sensitive.',
844
+ noContent: 'The tab you specified is missing a content url.',
845
+ path: 'History enabled, but no path was specified',
846
+ recursion: 'Max recursive depth reached',
847
+ state: 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>',
848
+ },
849
+
850
+ metadata: {
851
+ tab: 'tab',
852
+ loaded: 'loaded',
853
+ promise: 'promise',
854
+ },
855
+
856
+ className: {
857
+ loading: 'loading',
858
+ active: 'active',
859
+ },
860
+
861
+ selector: {
862
+ tabs: '.ui.tab',
863
+ ui: '.ui',
864
+ },
865
+
866
+ };
867
+ })(jQuery, window, document);