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,1960 @@
1
+ /*!
2
+ * # Fomantic-UI 2.9.4 - Calendar
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.calendar = function (...args) {
23
+ const $allModules = $(this);
24
+ const $document = $(document);
25
+
26
+ let time = Date.now();
27
+ let performance = [];
28
+
29
+ const parameters = args[0];
30
+ const methodInvoked = typeof parameters === 'string';
31
+ const queryArguments = args.slice(1);
32
+ let returnedValue;
33
+ const timeGapTable = {
34
+ 5: { row: 4, column: 3 },
35
+ 10: { row: 3, column: 2 },
36
+ 15: { row: 2, column: 2 },
37
+ 20: { row: 3, column: 1 },
38
+ 30: { row: 2, column: 1 },
39
+ };
40
+ const numberText = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'];
41
+
42
+ $allModules.each(function () {
43
+ const settings = $.isPlainObject(parameters)
44
+ ? $.extend(true, {}, $.fn.calendar.settings, parameters)
45
+ : $.extend({}, $.fn.calendar.settings);
46
+
47
+ const className = settings.className;
48
+ const namespace = settings.namespace;
49
+ const selector = settings.selector;
50
+ const formatter = settings.formatter;
51
+ const parser = settings.parser;
52
+ const metadata = settings.metadata;
53
+ const timeGap = timeGapTable[settings.minTimeGap];
54
+ const error = settings.error;
55
+
56
+ const eventNamespace = '.' + namespace;
57
+ const moduleNamespace = 'module-' + namespace;
58
+
59
+ const $module = $(this);
60
+ const $input = $module.find(selector.input);
61
+ let $activator = $module.find(selector.activator);
62
+
63
+ const element = this;
64
+ let instance = $module.data(moduleNamespace);
65
+ let $container = instance && instance.popupId ? $document.find('#' + instance.popupId) : $module.find(selector.popup);
66
+
67
+ let isTouch;
68
+ let isTouchDown = false;
69
+ const isInverted = $module.hasClass(className.inverted);
70
+ let focusDateUsedForRange = false;
71
+ let selectionComplete = false;
72
+ let classObserver;
73
+
74
+ const module = {
75
+
76
+ initialize: function () {
77
+ module.debug('Initializing calendar for', element, $module);
78
+
79
+ isTouch = module.get.isTouch();
80
+ module.setup.config();
81
+ module.setup.popup();
82
+ module.setup.inline();
83
+ module.setup.input();
84
+ module.setup.date();
85
+ module.create.calendar();
86
+
87
+ module.bind.events();
88
+ module.observeChanges();
89
+ module.instantiate();
90
+ },
91
+
92
+ instantiate: function () {
93
+ module.verbose('Storing instance of calendar');
94
+ instance = module;
95
+ $module.data(moduleNamespace, instance);
96
+ },
97
+
98
+ destroy: function () {
99
+ module.verbose('Destroying previous calendar for', element);
100
+ $module.removeData([
101
+ metadata.date,
102
+ metadata.focusDate,
103
+ metadata.startDate,
104
+ metadata.endDate,
105
+ metadata.minDate,
106
+ metadata.maxDate,
107
+ metadata.mode,
108
+ metadata.monthOffset,
109
+ moduleNamespace,
110
+ ]);
111
+ module.unbind.events();
112
+ module.disconnect.classObserver();
113
+ },
114
+
115
+ setup: {
116
+ config: function () {
117
+ if (module.get.minDate() !== null) {
118
+ module.set.minDate($module.data(metadata.minDate));
119
+ }
120
+ if (module.get.maxDate() !== null) {
121
+ module.set.maxDate($module.data(metadata.maxDate));
122
+ }
123
+ module.setting('type', module.get.type());
124
+ module.setting('on', settings.on || 'click');
125
+ },
126
+ popup: function () {
127
+ if (settings.inline) {
128
+ return;
129
+ }
130
+ if ($activator.length === 0) {
131
+ $activator = $module.children().first();
132
+ if ($activator.length === 0) {
133
+ return;
134
+ }
135
+ }
136
+ if ($.fn.popup === undefined) {
137
+ module.error(error.popup);
138
+
139
+ return;
140
+ }
141
+ if ($container.length === 0) {
142
+ if (settings.context) {
143
+ module.popupId = namespace + '_popup_' + (Math.random().toString(16) + '000000000').slice(2, 10);
144
+ $container = $('<div/>', { id: module.popupId }).addClass(className.popup).appendTo($document.find(settings.context));
145
+ } else {
146
+ // prepend the popup element to the activator's parent so that it has less chance of messing with
147
+ // the styling (e.g., input action button needs to be the last child to have the correct border radius)
148
+ const $activatorParent = $activator.parent();
149
+ const domPositionFunction = $activatorParent.closest(selector.append).length > 0 ? 'appendTo' : 'prependTo';
150
+ $container = $('<div/>').addClass(className.popup)[domPositionFunction]($activatorParent);
151
+ }
152
+ }
153
+ $container.addClass(className.calendar);
154
+ if (isInverted) {
155
+ $container.addClass(className.inverted);
156
+ }
157
+ let onVisible = function (...args) {
158
+ module.refreshTooltips();
159
+
160
+ return settings.onVisible.apply($container, args);
161
+ };
162
+ const onHidden = function (...args) {
163
+ module.blur();
164
+
165
+ return settings.onHidden.apply($container, args);
166
+ };
167
+ if ($input.length === 0) {
168
+ // no input, $container has to handle focus/blur
169
+ $container.attr('tabindex', '0');
170
+ onVisible = function (...args) {
171
+ module.refreshTooltips();
172
+ module.focus();
173
+
174
+ return settings.onVisible.apply($container, args);
175
+ };
176
+ }
177
+ const onShow = function (...args) {
178
+ // reset the focus date onShow
179
+ module.set.focusDate(module.get.date());
180
+ module.set.mode(module.get.validatedMode(settings.startMode));
181
+
182
+ return settings.onShow.apply($container, args);
183
+ };
184
+ const on = module.setting('on');
185
+ const options = $.extend({}, settings.popupOptions, {
186
+ popup: $container,
187
+ movePopup: !settings.context,
188
+ on: on,
189
+ hoverable: on === 'hover',
190
+ closable: on === 'click',
191
+ onShow: onShow,
192
+ onVisible: onVisible,
193
+ onHide: settings.onHide,
194
+ onHidden: onHidden,
195
+ });
196
+ module.popup(options);
197
+ },
198
+ inline: function () {
199
+ if ($activator.length > 0 && !settings.inline) {
200
+ return;
201
+ }
202
+ settings.inline = true;
203
+ $container = $('<div/>').addClass(className.calendar).appendTo($module);
204
+ if ($input.length === 0) {
205
+ $container.attr('tabindex', '0');
206
+ }
207
+ },
208
+ input: function () {
209
+ if (settings.touchReadonly && $input.length > 0 && isTouch) {
210
+ $input.prop('readonly', true);
211
+ }
212
+ module.check.disabled();
213
+ },
214
+ date: function () {
215
+ let date;
216
+ if (settings.initialDate) {
217
+ date = parser.date(settings.initialDate, settings);
218
+ } else if ($module.data(metadata.date) !== undefined) {
219
+ date = parser.date($module.data(metadata.date), settings);
220
+ } else if ($input.length > 0) {
221
+ date = parser.date($input.val(), settings);
222
+ }
223
+ module.set.date(date, settings.formatInput, false);
224
+ module.set.mode(module.get.mode(), false);
225
+ },
226
+ },
227
+
228
+ trigger: {
229
+ change: function () {
230
+ const inputElement = $input[0];
231
+ if (inputElement) {
232
+ const event = new Event('change', { bubbles: true });
233
+ module.verbose('Triggering native change event');
234
+ inputElement.dispatchEvent(event);
235
+ }
236
+ },
237
+ },
238
+
239
+ create: {
240
+ calendar: function () {
241
+ let i;
242
+ let r;
243
+ let c;
244
+ let p;
245
+ let row;
246
+ let cell;
247
+ let pageGrid;
248
+
249
+ const mode = module.get.mode();
250
+ const today = new Date();
251
+ const date = module.get.date();
252
+ let focusDate = module.get.focusDate();
253
+ const display = module.helper.dateInRange(focusDate || date || parser.date(settings.initialDate, settings) || today);
254
+
255
+ if (!focusDate) {
256
+ focusDate = display;
257
+ module.set.focusDate(focusDate, false, false);
258
+ }
259
+
260
+ const isYear = mode === 'year';
261
+ const isMonth = mode === 'month';
262
+ const isDay = mode === 'day';
263
+ const isHour = mode === 'hour';
264
+ const isMinute = mode === 'minute';
265
+ const isTimeOnly = settings.type === 'time';
266
+
267
+ const multiMonth = Math.max(settings.multiMonth, 1);
268
+ const monthOffset = !isDay ? 0 : module.get.monthOffset();
269
+
270
+ const minute = display.getMinutes();
271
+ const hour = display.getHours();
272
+ const day = display.getDate();
273
+ const startMonth = display.getMonth() + monthOffset;
274
+ const year = display.getFullYear();
275
+
276
+ const columns = isDay
277
+ ? (settings.showWeekNumbers ? 8 : 7)
278
+ : (isHour ? 4 : timeGap.column);
279
+ let rows = isDay || isHour ? 6 : timeGap.row;
280
+ const pages = isDay ? multiMonth : 1;
281
+
282
+ let container = $container;
283
+ const tooltipPosition = container.hasClass('left') ? 'right center' : 'left center';
284
+ container.empty();
285
+ if (pages > 1) {
286
+ pageGrid = $('<div/>').addClass(className.grid).appendTo(container);
287
+ }
288
+
289
+ for (p = 0; p < pages; p++) {
290
+ if (pages > 1) {
291
+ const pageColumn = $('<div/>').addClass(className.column).appendTo(pageGrid);
292
+ container = pageColumn;
293
+ }
294
+
295
+ const month = startMonth + p;
296
+ const firstMonthDayColumn = (new Date(year, month, 1).getDay() - (settings.firstDayOfWeek % 7) + 7) % 7;
297
+ if (!settings.constantHeight && isDay) {
298
+ const requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn;
299
+ rows = Math.ceil(requiredCells / 7);
300
+ }
301
+
302
+ const yearChange = isYear ? 10 : (isMonth ? 1 : 0);
303
+ const monthChange = isDay ? 1 : 0;
304
+ const dayChange = isHour || isMinute ? 1 : 0;
305
+ const prevNextDay = isHour || isMinute ? day : 1;
306
+ const prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour);
307
+ const nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour);
308
+ const prevLast = isYear
309
+ ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0)
310
+ : (isMonth
311
+ ? new Date(year, 0, 0)
312
+ : (isDay // eslint-disable-line unicorn/no-nested-ternary
313
+ ? new Date(year, month, 0)
314
+ : new Date(year, month, day, -1)));
315
+ const nextFirst = isYear
316
+ ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1)
317
+ : (isMonth
318
+ ? new Date(year + 1, 0, 1)
319
+ : (isDay // eslint-disable-line unicorn/no-nested-ternary
320
+ ? new Date(year, month + 1, 1)
321
+ : new Date(year, month, day + 1)));
322
+
323
+ let tempMode = mode;
324
+ if (isDay && settings.showWeekNumbers) {
325
+ tempMode += ' andweek';
326
+ }
327
+ const table = $('<table/>').addClass(className.table).addClass(tempMode).addClass(numberText[columns] + ' column')
328
+ .appendTo(container);
329
+ if (isInverted) {
330
+ table.addClass(className.inverted);
331
+ }
332
+ let textColumns = columns;
333
+ // no header for time-only mode
334
+ if (!isTimeOnly) {
335
+ const thead = $('<thead/>').appendTo(table);
336
+
337
+ row = $('<tr/>').appendTo(thead);
338
+ cell = $('<th/>').attr('colspan', '' + columns).appendTo(row);
339
+
340
+ const headerDate = isYear || isMonth
341
+ ? new Date(year, 0, 1)
342
+ : (isDay
343
+ ? new Date(year, month, 1)
344
+ : new Date(year, month, day, hour, minute));
345
+ const headerText = $('<span/>').addClass(className.link).appendTo(cell);
346
+ headerText.text(module.helper.dateFormat(formatter[mode + 'Header'], headerDate));
347
+ const newMode = isMonth
348
+ ? (settings.disableYear ? 'day' : 'year')
349
+ : (isDay
350
+ ? (settings.disableMonth ? 'year' : 'month') // eslint-disable-line unicorn/no-nested-ternary
351
+ : 'day');
352
+ headerText.data(metadata.mode, newMode);
353
+
354
+ if (p === 0) {
355
+ const prev = $('<span/>').addClass(className.prev).appendTo(cell);
356
+ prev.data(metadata.focusDate, prevDate);
357
+ prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode));
358
+ $('<i/>').addClass(className.prevIcon).appendTo(prev);
359
+ }
360
+
361
+ if (p === pages - 1) {
362
+ const next = $('<span/>').addClass(className.next).appendTo(cell);
363
+ next.data(metadata.focusDate, nextDate);
364
+ next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode));
365
+ $('<i/>').addClass(className.nextIcon).appendTo(next);
366
+ }
367
+ if (isDay) {
368
+ row = $('<tr/>').appendTo(thead);
369
+ if (settings.showWeekNumbers) {
370
+ cell = $('<th/>').appendTo(row);
371
+ cell.text(settings.text.weekNo);
372
+ cell.addClass(className.weekCell);
373
+ textColumns--;
374
+ }
375
+ for (i = 0; i < textColumns; i++) {
376
+ cell = $('<th/>').appendTo(row);
377
+ cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings));
378
+ }
379
+ }
380
+ }
381
+
382
+ const tbody = $('<tbody/>').appendTo(table);
383
+ i = isYear
384
+ ? Math.ceil(year / 10) * 10 - 9
385
+ : (isDay ? 1 - firstMonthDayColumn : 0);
386
+ for (r = 0; r < rows; r++) {
387
+ row = $('<tr/>').appendTo(tbody);
388
+ if (isDay && settings.showWeekNumbers) {
389
+ cell = $('<th/>').appendTo(row);
390
+ cell.text(module.get.weekOfYear(year, month, i + 1 - settings.firstDayOfWeek));
391
+ cell.addClass(className.weekCell);
392
+ }
393
+ for (c = 0; c < textColumns; c++, i++) {
394
+ const cellDate = isYear
395
+ ? new Date(i, month, 1, hour, minute)
396
+ : (isMonth
397
+ ? new Date(year, i, 1, hour, minute)
398
+ : (isDay // eslint-disable-line unicorn/no-nested-ternary
399
+ ? new Date(year, month, i, hour, minute)
400
+ : (isHour
401
+ ? new Date(year, month, day, i)
402
+ : new Date(year, month, day, hour, i * settings.minTimeGap))));
403
+ const cellText = isYear
404
+ ? i
405
+ : (isMonth
406
+ ? settings.text.monthsShort[i]
407
+ : (isDay // eslint-disable-line unicorn/no-nested-ternary
408
+ ? cellDate.getDate()
409
+ : module.helper.dateFormat(formatter.cellTime, cellDate)));
410
+ cell = $('<td/>').addClass(className.cell).appendTo(row);
411
+ cell.text(cellText);
412
+ cell.data(metadata.date, cellDate);
413
+ const adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12);
414
+ const disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode);
415
+ let eventDate;
416
+ if (disabled) {
417
+ const disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates);
418
+ if (disabledDate !== null && disabledDate[metadata.message]) {
419
+ cell.attr('data-tooltip', disabledDate[metadata.message]);
420
+ cell.attr('data-position', disabledDate[metadata.position] || tooltipPosition);
421
+ if (disabledDate[metadata.inverted] || (isInverted && disabledDate[metadata.inverted] === undefined)) {
422
+ cell.attr('data-inverted', '');
423
+ }
424
+ if (disabledDate[metadata.variation]) {
425
+ cell.attr('data-variation', disabledDate[metadata.variation]);
426
+ }
427
+ }
428
+ if (mode === 'hour') {
429
+ const disabledHour = module.helper.findHourAsObject(cellDate, mode, settings.disabledHours);
430
+ if (disabledHour !== null && disabledHour[metadata.message]) {
431
+ cell.attr('data-tooltip', disabledHour[metadata.message]);
432
+ cell.attr('data-position', disabledHour[metadata.position] || tooltipPosition);
433
+ if (disabledHour[metadata.inverted] || (isInverted && disabledHour[metadata.inverted] === undefined)) {
434
+ cell.attr('data-inverted', '');
435
+ }
436
+ if (disabledHour[metadata.variation]) {
437
+ cell.attr('data-variation', disabledHour[metadata.variation]);
438
+ }
439
+ }
440
+ }
441
+ } else {
442
+ eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates);
443
+ if (eventDate !== null) {
444
+ cell.addClass(eventDate[metadata.class] || settings.eventClass);
445
+ if (eventDate[metadata.message]) {
446
+ cell.attr('data-tooltip', eventDate[metadata.message]);
447
+ cell.attr('data-position', eventDate[metadata.position] || tooltipPosition);
448
+ if (eventDate[metadata.inverted] || (isInverted && eventDate[metadata.inverted] === undefined)) {
449
+ cell.attr('data-inverted', '');
450
+ }
451
+ if (eventDate[metadata.variation]) {
452
+ cell.attr('data-variation', eventDate[metadata.variation]);
453
+ }
454
+ }
455
+ }
456
+ }
457
+ const active = module.helper.dateEqual(cellDate, date, mode);
458
+ const isToday = module.helper.dateEqual(cellDate, today, mode);
459
+ cell.toggleClass(className.adjacentCell, adjacent && !eventDate);
460
+ cell.toggleClass(className.disabledCell, disabled);
461
+ cell.toggleClass(className.activeCell, active && !(adjacent && disabled));
462
+ if (!isHour && !isMinute) {
463
+ cell.toggleClass(className.todayCell, !adjacent && isToday);
464
+ }
465
+
466
+ // Allow for external modifications of each cell
467
+ const cellOptions = {
468
+ mode: mode,
469
+ adjacent: adjacent,
470
+ disabled: disabled,
471
+ active: active,
472
+ today: isToday,
473
+ };
474
+ formatter.cell(cell, cellDate, cellOptions);
475
+
476
+ if (module.helper.dateEqual(cellDate, focusDate, mode)) {
477
+ // ensure that the focus date is exactly equal to the cell date
478
+ // so that, if selected, the correct value is set
479
+ module.set.focusDate(cellDate, false, false);
480
+ }
481
+ }
482
+ }
483
+
484
+ if (settings.today) {
485
+ const todayRow = $('<tr/>').appendTo(tbody);
486
+ const todayButton = $('<td/>').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow);
487
+ todayButton.text(formatter.today(settings));
488
+ todayButton.data(metadata.date, today);
489
+ }
490
+
491
+ module.update.focus(false, table);
492
+
493
+ if (settings.inline) {
494
+ module.refreshTooltips();
495
+ }
496
+ }
497
+ },
498
+ },
499
+
500
+ update: {
501
+ focus: function (updateRange, container = $container) {
502
+ const mode = module.get.mode();
503
+ const date = module.get.date();
504
+ const focusDate = module.get.focusDate();
505
+ const startDate = module.get.startDate();
506
+ const endDate = module.get.endDate();
507
+ const rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null);
508
+
509
+ container.find('td').each(function () {
510
+ const $cell = $(this);
511
+ const cellDate = $cell.data(metadata.date);
512
+ if (!cellDate) {
513
+ return;
514
+ }
515
+ const disabled = $cell.hasClass(className.disabledCell);
516
+ const active = $cell.hasClass(className.activeCell);
517
+ const adjacent = $cell.hasClass(className.adjacentCell);
518
+ const focused = module.helper.dateEqual(cellDate, focusDate, mode);
519
+ const inRange = !rangeDate
520
+ ? false
521
+ : (!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate))
522
+ || (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate));
523
+ $cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled);
524
+
525
+ if (module.helper.isTodayButton($cell)) {
526
+ return;
527
+ }
528
+ $cell.toggleClass(className.rangeCell, inRange && !active && !disabled);
529
+ });
530
+ },
531
+ },
532
+
533
+ refresh: function () {
534
+ module.create.calendar();
535
+ },
536
+
537
+ refreshTooltips: function () {
538
+ const winWidth = $(window).width();
539
+ $container.find('td[data-position]').each(function () {
540
+ const $cell = $(this);
541
+ const tooltipWidth = window.getComputedStyle($cell[0], '::after').width.replace(/[^\d.]/g, '');
542
+ const tooltipPosition = $cell.attr('data-position');
543
+ // use a fallback width of 250 (calendar width) for IE/Edge (which return "auto")
544
+ const calcPosition = (winWidth - $cell.width() - (Number.parseInt(tooltipWidth, 10) || 250)) > $cell.offset().left ? 'right' : 'left';
545
+ if (!tooltipPosition.includes(calcPosition)) {
546
+ $cell.attr('data-position', tooltipPosition.replace(/(left|right)/, calcPosition));
547
+ }
548
+ });
549
+ },
550
+
551
+ bind: {
552
+ events: function () {
553
+ module.debug('Binding events');
554
+ $container.on('mousedown' + eventNamespace, module.event.mousedown);
555
+ $container.on('touchstart' + eventNamespace, module.event.mousedown);
556
+ $container.on('mouseup' + eventNamespace, module.event.mouseup);
557
+ $container.on('touchend' + eventNamespace, module.event.mouseup);
558
+ $container.on('mouseover' + eventNamespace, module.event.mouseover);
559
+ if ($input.length > 0) {
560
+ $input.on('input' + eventNamespace, module.event.inputChange);
561
+ $input.on('focus' + eventNamespace, module.event.inputFocus);
562
+ $input.on('blur' + eventNamespace, module.event.inputBlur);
563
+ $input.on('keydown' + eventNamespace, module.event.keydown);
564
+ } else {
565
+ $container.on('keydown' + eventNamespace, module.event.keydown);
566
+ }
567
+ },
568
+ },
569
+
570
+ unbind: {
571
+ events: function () {
572
+ module.debug('Unbinding events');
573
+ $container.off(eventNamespace);
574
+ if ($input.length > 0) {
575
+ $input.off(eventNamespace);
576
+ }
577
+ },
578
+ },
579
+
580
+ event: {
581
+ mouseover: function (event) {
582
+ const target = $(event.target);
583
+ const date = target.data(metadata.date);
584
+ const mousedown = event.buttons === 1;
585
+ if (date) {
586
+ module.set.focusDate(date, false, true, mousedown);
587
+ }
588
+ },
589
+ mousedown: function (event) {
590
+ if ($input.length > 0) {
591
+ // prevent the mousedown on the calendar causing the input to lose focus
592
+ event.preventDefault();
593
+ }
594
+ isTouchDown = event.type.includes('touch');
595
+ const target = $(event.target);
596
+ const date = target.data(metadata.date);
597
+ if (date) {
598
+ module.set.focusDate(date, false, true, true);
599
+ }
600
+ },
601
+ mouseup: function (event) {
602
+ // ensure input has focus so that it receives keydown events for calendar navigation
603
+ module.focus();
604
+ event.preventDefault();
605
+ event.stopPropagation();
606
+ isTouchDown = false;
607
+ let target = $(event.target);
608
+ if (target.hasClass('disabled')) {
609
+ return;
610
+ }
611
+ const parent = target.parent();
612
+ if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) {
613
+ // clicked on a child element, switch to parent (used when clicking directly on the prev/next <i> icon element)
614
+ target = parent;
615
+ }
616
+ const date = target.data(metadata.date);
617
+ const focusDate = target.data(metadata.focusDate);
618
+ const mode = target.data(metadata.mode);
619
+ if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) {
620
+ const forceSet = target.hasClass(className.today);
621
+ module.selectDate(date, forceSet);
622
+ } else if (focusDate) {
623
+ module.set.focusDate(focusDate);
624
+ } else if (mode) {
625
+ module.set.mode(mode);
626
+ }
627
+ },
628
+ keydown: function (event) {
629
+ const keyCode = event.which;
630
+ if (keyCode === 9) {
631
+ // tab
632
+ module.popup('hide');
633
+ }
634
+
635
+ if (module.popup('is visible')) {
636
+ const mode = module.get.mode();
637
+ switch (keyCode) {
638
+ // arrow keys
639
+ case 37:
640
+ case 38:
641
+ case 39:
642
+ case 40: {
643
+ const bigIncrement = mode === 'day'
644
+ ? 7
645
+ : (mode === 'hour'
646
+ ? 4
647
+ : (mode === 'minute' ? timeGap.column : 3)); // eslint-disable-line unicorn/no-nested-ternary
648
+ let increment = keyCode === 37
649
+ ? -1
650
+ : (keyCode === 38
651
+ ? -bigIncrement
652
+ : (keyCode === 39 ? 1 : bigIncrement)); // eslint-disable-line unicorn/no-nested-ternary
653
+ increment *= mode === 'minute' ? settings.minTimeGap : 1;
654
+ const focusDate = module.get.focusDate() || module.get.date() || new Date();
655
+ const year = focusDate.getFullYear() + (mode === 'year' ? increment : 0);
656
+ const month = focusDate.getMonth() + (mode === 'month' ? increment : 0);
657
+ const day = focusDate.getDate() + (mode === 'day' ? increment : 0);
658
+ const hour = focusDate.getHours() + (mode === 'hour' ? increment : 0);
659
+ const minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0);
660
+ let newFocusDate = new Date(year, month, day, hour, minute);
661
+ if (settings.type === 'time') {
662
+ newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate);
663
+ }
664
+ if (module.helper.isDateInRange(newFocusDate, mode)) {
665
+ module.set.focusDate(newFocusDate);
666
+ }
667
+
668
+ break;
669
+ }
670
+ // enter key
671
+ case 13: {
672
+ const date = module.get.focusDate();
673
+ if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode) && settings.onSelect.call(element, date, module.get.mode()) !== false) {
674
+ module.selectDate(date);
675
+ }
676
+ // disable form submission:
677
+ event.preventDefault();
678
+ event.stopPropagation();
679
+
680
+ break;
681
+ }
682
+ // escape key
683
+ case 27: {
684
+ module.popup('hide');
685
+ event.stopPropagation();
686
+
687
+ break;
688
+ }
689
+ // no default
690
+ }
691
+ }
692
+
693
+ if (keyCode === 38 || keyCode === 40) {
694
+ // arrow-up || arrow-down
695
+ event.preventDefault(); // don't scroll
696
+ module.popup('show');
697
+ }
698
+ },
699
+ inputChange: function () {
700
+ const val = $input.val();
701
+ const date = parser.date(val, settings);
702
+ module.set.date(date, false);
703
+ },
704
+ inputFocus: function () {
705
+ $container.addClass(className.active);
706
+ },
707
+ inputBlur: function () {
708
+ $container.removeClass(className.active);
709
+ if (settings.formatInput) {
710
+ const date = module.get.date();
711
+ const text = module.helper.dateFormat(formatter[settings.type], date);
712
+ $input.val(text);
713
+ }
714
+ if (selectionComplete) {
715
+ module.trigger.change();
716
+ selectionComplete = false;
717
+ }
718
+ },
719
+ class: {
720
+ mutation: function (mutations) {
721
+ for (const mutation of mutations) {
722
+ if (mutation.attributeName === 'class') {
723
+ module.check.disabled();
724
+ }
725
+ }
726
+ },
727
+ },
728
+ },
729
+
730
+ observeChanges: function () {
731
+ classObserver = new MutationObserver(module.event.class.mutation);
732
+ module.debug('Setting up mutation observer', classObserver);
733
+ module.observe.class();
734
+ },
735
+
736
+ disconnect: {
737
+ classObserver: function () {
738
+ if ($input.length > 0 && classObserver) {
739
+ classObserver.disconnect();
740
+ }
741
+ },
742
+ },
743
+
744
+ observe: {
745
+ class: function () {
746
+ if ($input.length > 0 && classObserver) {
747
+ classObserver.observe($module[0], {
748
+ attributes: true,
749
+ });
750
+ }
751
+ },
752
+ },
753
+
754
+ is: {
755
+ disabled: function () {
756
+ return $module.hasClass(className.disabled);
757
+ },
758
+ },
759
+
760
+ check: {
761
+ disabled: function () {
762
+ $input.attr('tabindex', module.is.disabled() ? -1 : 0);
763
+ },
764
+ },
765
+
766
+ get: {
767
+ weekOfYear: function (weekYear, weekMonth, weekDay) {
768
+ // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
769
+ const ms1d = 24 * 3600 * 1000;
770
+ const ms7d = 7 * ms1d;
771
+ const DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d; // an absolute day number
772
+ const AWN = Math.floor(DC3 / 7); // an absolute week number
773
+ const Wyr = new Date(AWN * ms7d).getUTCFullYear();
774
+
775
+ return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
776
+ },
777
+ formattedDate: function (format, date) {
778
+ return module.helper.dateFormat(format || formatter[settings.type], date || module.get.date());
779
+ },
780
+ date: function (format) {
781
+ return module.helper.dateObjectOrFormatted(format, $module.data(metadata.date));
782
+ },
783
+ inputDate: function () {
784
+ return $input.val();
785
+ },
786
+ focusDate: function (format) {
787
+ return module.helper.dateObjectOrFormatted(format, $module.data(metadata.focusDate));
788
+ },
789
+ startDate: function (format) {
790
+ const startModule = module.get.calendarModule(settings.startCalendar);
791
+
792
+ if (startModule) {
793
+ return startModule.get.date(format);
794
+ }
795
+
796
+ return module.helper.dateObjectOrFormatted(format, $module.data(metadata.startDate));
797
+ },
798
+ endDate: function (format) {
799
+ const endModule = module.get.calendarModule(settings.endCalendar);
800
+
801
+ if (endModule) {
802
+ return endModule.get.date(format);
803
+ }
804
+
805
+ return module.helper.dateObjectOrFormatted(format, $module.data(metadata.endDate));
806
+ },
807
+ minDate: function () {
808
+ return $module.data(metadata.minDate) || null;
809
+ },
810
+ maxDate: function () {
811
+ return $module.data(metadata.maxDate) || null;
812
+ },
813
+ monthOffset: function () {
814
+ return $module.data(metadata.monthOffset) || settings.monthOffset || 0;
815
+ },
816
+ mode: function () {
817
+ // only returns valid modes for the current settings
818
+ const mode = $module.data(metadata.mode) || settings.startMode;
819
+
820
+ return module.get.validatedMode(mode);
821
+ },
822
+ validatedMode: function (mode) {
823
+ if (module.get.validModes().includes(mode)) {
824
+ return mode;
825
+ }
826
+
827
+ return settings.type === 'time'
828
+ ? 'hour'
829
+ : (settings.type === 'month'
830
+ ? 'month'
831
+ : (settings.type === 'year' ? 'year' : 'day')); // eslint-disable-line unicorn/no-nested-ternary
832
+ },
833
+ type: function () {
834
+ return $module.data(metadata.type) || settings.type;
835
+ },
836
+ validModes: function () {
837
+ const validModes = [];
838
+ if (settings.type !== 'time') {
839
+ if (!settings.disableYear || settings.type === 'year') {
840
+ validModes.push('year');
841
+ }
842
+ if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') {
843
+ validModes.push('month');
844
+ }
845
+ if (settings.type.includes('date')) {
846
+ validModes.push('day');
847
+ }
848
+ }
849
+ if (settings.type.includes('time')) {
850
+ validModes.push('hour');
851
+ if (!settings.disableMinute) {
852
+ validModes.push('minute');
853
+ }
854
+ }
855
+
856
+ return validModes;
857
+ },
858
+ isTouch: function () {
859
+ try {
860
+ document.createEvent('TouchEvent');
861
+
862
+ return true;
863
+ } catch {
864
+ return false;
865
+ }
866
+ },
867
+ calendarModule: function (selector) {
868
+ if (!selector) {
869
+ return null;
870
+ }
871
+ if (!(selector instanceof $)) {
872
+ selector = $document.find(selector).first();
873
+ }
874
+
875
+ // assume range related calendars are using the same namespace
876
+ return selector.data(moduleNamespace);
877
+ },
878
+ },
879
+
880
+ set: {
881
+ date: function (date, updateInput, fireChange) {
882
+ updateInput = updateInput !== false;
883
+ fireChange = fireChange !== false;
884
+ date = module.helper.sanitiseDate(date);
885
+ date = module.helper.dateInRange(date);
886
+
887
+ const mode = module.get.mode();
888
+ const text = module.helper.dateFormat(formatter[settings.type], date);
889
+
890
+ if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) {
891
+ return false;
892
+ }
893
+
894
+ module.set.focusDate(date);
895
+
896
+ if (settings.isDisabled(date, mode)) {
897
+ return false;
898
+ }
899
+
900
+ const endDate = module.get.endDate();
901
+ if (!!endDate && !!date && date > endDate) {
902
+ // selected date is greater than end date in range, so clear end date
903
+ module.set.endDate();
904
+ }
905
+ module.set.dataKeyValue(metadata.date, date);
906
+
907
+ if (updateInput && $input.length > 0) {
908
+ $input.val(text);
909
+ }
910
+
911
+ if (fireChange) {
912
+ settings.onChange.call(element, date, text, mode);
913
+ }
914
+ },
915
+ startDate: function (date, refreshCalendar) {
916
+ date = module.helper.sanitiseDate(date);
917
+ const startModule = module.get.calendarModule(settings.startCalendar);
918
+ if (startModule) {
919
+ startModule.set.date(date);
920
+ }
921
+ module.set.dataKeyValue(metadata.startDate, date, refreshCalendar);
922
+ },
923
+ endDate: function (date, refreshCalendar) {
924
+ date = module.helper.sanitiseDate(date);
925
+ const endModule = module.get.calendarModule(settings.endCalendar);
926
+ if (endModule) {
927
+ endModule.set.date(date);
928
+ }
929
+ module.set.dataKeyValue(metadata.endDate, date, refreshCalendar);
930
+ },
931
+ focusDate: function (date, refreshCalendar, updateFocus, updateRange) {
932
+ date = module.helper.sanitiseDate(date);
933
+ date = module.helper.dateInRange(date);
934
+ const isDay = module.get.mode() === 'day';
935
+ const oldFocusDate = module.get.focusDate();
936
+ if (isDay && date && oldFocusDate) {
937
+ const yearDelta = date.getFullYear() - oldFocusDate.getFullYear();
938
+ const monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth();
939
+ if (monthDelta) {
940
+ const monthOffset = module.get.monthOffset() - monthDelta;
941
+ module.set.monthOffset(monthOffset, false);
942
+ }
943
+ }
944
+ const changed = module.set.dataKeyValue(metadata.focusDate, date, !!date && refreshCalendar);
945
+ updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange;
946
+ focusDateUsedForRange = updateRange;
947
+ if (updateFocus) {
948
+ module.update.focus(updateRange);
949
+ }
950
+ },
951
+ minDate: function (date) {
952
+ date = module.helper.sanitiseDate(date);
953
+ if (settings.maxDate !== null && settings.maxDate <= date) {
954
+ module.verbose('Unable to set minDate variable bigger that maxDate variable', date, settings.maxDate);
955
+ } else {
956
+ module.setting('minDate', date);
957
+ module.set.dataKeyValue(metadata.minDate, date);
958
+ }
959
+ },
960
+ maxDate: function (date) {
961
+ date = module.helper.sanitiseDate(date);
962
+ if (settings.minDate !== null && settings.minDate >= date) {
963
+ module.verbose('Unable to set maxDate variable lower that minDate variable', date, settings.minDate);
964
+ } else {
965
+ module.setting('maxDate', date);
966
+ module.set.dataKeyValue(metadata.maxDate, date);
967
+ }
968
+ },
969
+ monthOffset: function (monthOffset, refreshCalendar) {
970
+ const multiMonth = Math.max(settings.multiMonth, 1);
971
+ monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset));
972
+ module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar);
973
+ },
974
+ mode: function (mode, refreshCalendar) {
975
+ module.set.dataKeyValue(metadata.mode, mode, refreshCalendar);
976
+ },
977
+ dataKeyValue: function (key, value, refreshCalendar) {
978
+ const oldValue = $module.data(key);
979
+ const equal = oldValue === value || (oldValue <= value && oldValue >= value); // equality test for dates and string objects
980
+ if (value) {
981
+ $module.data(key, value);
982
+ } else {
983
+ $module.removeData(key);
984
+ }
985
+ refreshCalendar = refreshCalendar !== false && !equal;
986
+ if (refreshCalendar) {
987
+ module.refresh();
988
+ }
989
+
990
+ return !equal;
991
+ },
992
+ },
993
+
994
+ selectDate: function (date, forceSet) {
995
+ module.verbose('New date selection', date);
996
+ const mode = module.get.mode();
997
+ const complete = forceSet || mode === 'minute'
998
+ || (settings.disableMinute && mode === 'hour')
999
+ || (settings.type === 'date' && mode === 'day')
1000
+ || (settings.type === 'month' && mode === 'month')
1001
+ || (settings.type === 'year' && mode === 'year');
1002
+ if (complete) {
1003
+ const canceled = module.set.date(date) === false;
1004
+ if (!canceled) {
1005
+ selectionComplete = true;
1006
+ if (settings.closable) {
1007
+ module.popup('hide');
1008
+ // if this is a range calendar, focus the container or input. This will open the popup from its event listeners.
1009
+ const endModule = module.get.calendarModule(settings.endCalendar);
1010
+ if (endModule) {
1011
+ endModule.refresh();
1012
+ if (endModule.setting('on') !== 'focus') {
1013
+ endModule.popup('show');
1014
+ }
1015
+ endModule.focus();
1016
+ }
1017
+ }
1018
+ }
1019
+ } else {
1020
+ const newMode = mode === 'year'
1021
+ ? (!settings.disableMonth ? 'month' : 'day')
1022
+ : (mode === 'month'
1023
+ ? 'day'
1024
+ : (mode === 'day' ? 'hour' : 'minute')); // eslint-disable-line unicorn/no-nested-ternary
1025
+ module.set.mode(newMode);
1026
+ if (mode === 'hour' || (mode === 'day' && module.get.date())) {
1027
+ // the user has chosen enough to consider a valid date/time has been chosen
1028
+ module.set.date(date, true, false);
1029
+ } else {
1030
+ module.set.focusDate(date);
1031
+ }
1032
+ }
1033
+ },
1034
+
1035
+ changeDate: function (date) {
1036
+ module.set.date(date);
1037
+ },
1038
+
1039
+ clear: function () {
1040
+ module.set.date();
1041
+ },
1042
+
1043
+ popup: function (...args) {
1044
+ return $activator.popup(...args);
1045
+ },
1046
+
1047
+ focus: function () {
1048
+ if ($input.length > 0) {
1049
+ $input.trigger('focus');
1050
+ } else {
1051
+ $container.trigger('focus');
1052
+ }
1053
+ },
1054
+ blur: function () {
1055
+ if ($input.length > 0) {
1056
+ $input.trigger('blur');
1057
+ } else {
1058
+ $container.trigger('blur');
1059
+ }
1060
+ },
1061
+
1062
+ helper: {
1063
+ dateFormat: function (format, date) {
1064
+ if (!(date instanceof Date)) {
1065
+ return '';
1066
+ }
1067
+ if (typeof format === 'function') {
1068
+ return format.call(module, date, settings);
1069
+ }
1070
+
1071
+ const D = date.getDate();
1072
+ const M = date.getMonth();
1073
+ const Y = date.getFullYear();
1074
+ const d = date.getDay();
1075
+ const H = date.getHours();
1076
+ const m = date.getMinutes();
1077
+ const s = date.getSeconds();
1078
+ const w = module.get.weekOfYear(Y, M, D + 1 - settings.firstDayOfWeek);
1079
+ const h = H % 12 || 12;
1080
+ const a = H < 12 ? settings.text.am.toLowerCase() : settings.text.pm.toLowerCase();
1081
+ const tokens = {
1082
+ D: D,
1083
+ DD: ('0' + D).slice(-2),
1084
+ M: M + 1,
1085
+ MM: ('0' + (M + 1)).slice(-2),
1086
+ MMM: settings.text.monthsShort[M],
1087
+ MMMM: settings.text.months[M],
1088
+ Y: Y,
1089
+ YY: String(Y).slice(2),
1090
+ YYYY: Y,
1091
+ d: d,
1092
+ dd: settings.text.dayNamesShort[d].slice(0, 2),
1093
+ ddd: settings.text.dayNamesShort[d],
1094
+ dddd: settings.text.dayNames[d],
1095
+ h: h,
1096
+ hh: ('0' + h).slice(-2),
1097
+ H: H,
1098
+ HH: ('0' + H).slice(-2),
1099
+ m: m,
1100
+ mm: ('0' + m).slice(-2),
1101
+ s: s,
1102
+ ss: ('0' + s).slice(-2),
1103
+ a: a,
1104
+ A: a.toUpperCase(),
1105
+ S: ['th', 'st', 'nd', 'rd'][(D % 10) > 3 ? 0 : ((D % 100) - (D % 10) === 10 ? 0 : D % 10)],
1106
+ w: w,
1107
+ ww: ('0' + w).slice(-2),
1108
+ };
1109
+
1110
+ return format.replace(settings.regExp.token, function (match) {
1111
+ if (match in tokens) {
1112
+ return tokens[match];
1113
+ }
1114
+
1115
+ return match.slice(1, -1);
1116
+ });
1117
+ },
1118
+ dateObjectOrFormatted: function (format = '', date = null) {
1119
+ date = module.helper.sanitiseDate(date) || null;
1120
+
1121
+ if (!date) {
1122
+ return null;
1123
+ }
1124
+
1125
+ if (format === '') {
1126
+ return date;
1127
+ }
1128
+
1129
+ return module.helper.dateFormat(format, date);
1130
+ },
1131
+ isDisabled: function (date, mode) {
1132
+ return (mode === 'day' || mode === 'month' || mode === 'year' || mode === 'hour') && (((mode === 'day' && settings.disabledDaysOfWeek.includes(date.getDay())) || settings.disabledDates.some(function (d) {
1133
+ let blocked = false;
1134
+
1135
+ if (typeof d === 'string') {
1136
+ d = module.helper.sanitiseDate(d);
1137
+ }
1138
+ if (d instanceof Date) {
1139
+ blocked = module.helper.dateEqual(date, d, mode);
1140
+ } else if (d !== null && typeof d === 'object') {
1141
+ if (d[metadata.year]) {
1142
+ if (typeof d[metadata.year] === 'number') {
1143
+ blocked = date.getFullYear() === d[metadata.year];
1144
+ } else if (Array.isArray(d[metadata.year])) {
1145
+ blocked = d[metadata.year].includes(date.getFullYear());
1146
+ }
1147
+ } else if (d[metadata.month]) {
1148
+ if (typeof d[metadata.month] === 'number') {
1149
+ blocked = date.getMonth() === d[metadata.month];
1150
+ } else if (Array.isArray(d[metadata.month])) {
1151
+ blocked = d[metadata.month].includes(date.getMonth());
1152
+ } else if (d[metadata.month] instanceof Date) {
1153
+ const sdate = module.helper.sanitiseDate(d[metadata.month]);
1154
+
1155
+ blocked = (date.getMonth() === sdate.getMonth()) && (date.getFullYear() === sdate.getFullYear());
1156
+ }
1157
+ } else if (d[metadata.date] && mode === 'day') {
1158
+ if (d[metadata.date] instanceof Date) {
1159
+ blocked = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
1160
+ } else if (Array.isArray(d[metadata.date])) {
1161
+ blocked = d[metadata.date].some(function (idate) {
1162
+ return module.helper.dateEqual(date, idate, mode);
1163
+ });
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ return blocked;
1169
+ })) || (mode === 'hour' && settings.disabledHours.some(function (d) {
1170
+ let blocked = false;
1171
+
1172
+ if (typeof d === 'string') {
1173
+ d = module.helper.sanitiseDate(d);
1174
+ }
1175
+ if (d instanceof Date) {
1176
+ blocked = module.helper.dateEqual(date, d, mode);
1177
+ } else if (typeof d === 'number') {
1178
+ blocked = date.getHours() === d;
1179
+ } else if (d !== null && typeof d === 'object') {
1180
+ if (d[metadata.date]) {
1181
+ if (d[metadata.date] instanceof Date) {
1182
+ blocked = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]));
1183
+ } else if (Array.isArray(d[metadata.date])) {
1184
+ blocked = d[metadata.date].some(function (idate) {
1185
+ return module.helper.dateEqual(date, idate, mode);
1186
+ });
1187
+ }
1188
+ }
1189
+
1190
+ if (d[metadata.days]) {
1191
+ if (typeof d[metadata.days] === 'number') {
1192
+ blocked = date.getDay() === d[metadata.days];
1193
+ } else if (Array.isArray(d[metadata.days])) {
1194
+ blocked = d[metadata.days].includes(date.getDay());
1195
+ }
1196
+ }
1197
+
1198
+ if (d[metadata.hours]) {
1199
+ if (typeof d[metadata.hours] === 'number') {
1200
+ blocked = blocked && date.getHours() === d[metadata.hours];
1201
+ } else if (Array.isArray(d[metadata.hours])) {
1202
+ blocked = blocked && d[metadata.hours].includes(date.getHours());
1203
+ }
1204
+ }
1205
+ }
1206
+
1207
+ return blocked;
1208
+ })));
1209
+ },
1210
+ isEnabled: function (date, mode) {
1211
+ if (mode === 'day') {
1212
+ return settings.enabledDates.length === 0 || settings.enabledDates.some(function (d) {
1213
+ let enabled = false;
1214
+
1215
+ if (typeof d === 'string') {
1216
+ d = module.helper.sanitiseDate(d);
1217
+ }
1218
+ if (d instanceof Date) {
1219
+ enabled = module.helper.dateEqual(date, d, mode);
1220
+ } else if (d !== null && typeof d === 'object' && d[metadata.date]) {
1221
+ enabled = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
1222
+ }
1223
+
1224
+ return enabled;
1225
+ });
1226
+ }
1227
+
1228
+ return true;
1229
+ },
1230
+ findDayAsObject: function (date, mode, dates) {
1231
+ if (mode === 'day' || mode === 'month' || mode === 'year') {
1232
+ for (let d of dates) {
1233
+ if (typeof d === 'string') {
1234
+ d = module.helper.sanitiseDate(d);
1235
+ }
1236
+ if (d instanceof Date && module.helper.dateEqual(date, d, mode)) {
1237
+ const dateObject = {};
1238
+ dateObject[metadata.date] = d;
1239
+
1240
+ return dateObject;
1241
+ }
1242
+ if (d !== null && typeof d === 'object') {
1243
+ if (d[metadata.year]) {
1244
+ if (typeof d[metadata.year] === 'number' && date.getFullYear() === d[metadata.year]) {
1245
+ return d;
1246
+ }
1247
+ if (Array.isArray(d[metadata.year]) && d[metadata.year].includes(date.getFullYear())) {
1248
+ return d;
1249
+ }
1250
+ } else if (d[metadata.month]) {
1251
+ if (typeof d[metadata.month] === 'number' && date.getMonth() === d[metadata.month]) {
1252
+ return d;
1253
+ }
1254
+ if (Array.isArray(d[metadata.month])) {
1255
+ if (d[metadata.month].includes(date.getMonth())) {
1256
+ return d;
1257
+ }
1258
+ } else if (d[metadata.month] instanceof Date) {
1259
+ const sdate = module.helper.sanitiseDate(d[metadata.month]);
1260
+ if ((date.getMonth() === sdate.getMonth()) && (date.getFullYear() === sdate.getFullYear())) {
1261
+ return d;
1262
+ }
1263
+ }
1264
+ } else if (d[metadata.date] && mode === 'day') {
1265
+ if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode)) {
1266
+ return d;
1267
+ }
1268
+ if (Array.isArray(d[metadata.date]) && d[metadata.date].some(function (idate) {
1269
+ return module.helper.dateEqual(date, idate, mode);
1270
+ })) {
1271
+ return d;
1272
+ }
1273
+ }
1274
+ }
1275
+ }
1276
+ }
1277
+
1278
+ return null;
1279
+ },
1280
+ findHourAsObject: function (date, mode, hours) {
1281
+ if (mode === 'hour') {
1282
+ const hourCheck = function (date, d) {
1283
+ if (d[metadata.hours]) {
1284
+ if (typeof d[metadata.hours] === 'number' && date.getHours() === d[metadata.hours]) {
1285
+ return d;
1286
+ }
1287
+ if (Array.isArray(d[metadata.hours]) && d[metadata.hours].includes(date.getHours())) {
1288
+ return d;
1289
+ }
1290
+ }
1291
+ };
1292
+ for (const d of hours) {
1293
+ if (typeof d === 'number' && date.getHours() === d) {
1294
+ return null;
1295
+ }
1296
+ if (d !== null && typeof d === 'object') {
1297
+ if (d[metadata.days] && hourCheck(date, d)) {
1298
+ if (typeof d[metadata.days] === 'number' && date.getDay() === d[metadata.days]) {
1299
+ return d;
1300
+ }
1301
+ if (Array.isArray(d[metadata.days]) && d[metadata.days].includes(date.getDay())) {
1302
+ return d;
1303
+ }
1304
+ } else if (d[metadata.date] && hourCheck(date, d)) {
1305
+ if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]))) {
1306
+ return d;
1307
+ }
1308
+ if (Array.isArray(d[metadata.date]) && d[metadata.date].some(function (idate) {
1309
+ return module.helper.dateEqual(date, idate, mode);
1310
+ })) {
1311
+ return d;
1312
+ }
1313
+ } else if (hourCheck(date, d)) {
1314
+ return d;
1315
+ }
1316
+ }
1317
+ }
1318
+ }
1319
+
1320
+ return null;
1321
+ },
1322
+ sanitiseDate: function (date) {
1323
+ if (!(date instanceof Date)) {
1324
+ date = parser.date('' + date, settings);
1325
+ }
1326
+ if (!date || Number.isNaN(date.getTime())) {
1327
+ return null;
1328
+ }
1329
+
1330
+ return date;
1331
+ },
1332
+ dateDiff: function (date1, date2, mode) {
1333
+ if (!mode) {
1334
+ mode = 'day';
1335
+ }
1336
+
1337
+ const isTimeOnly = settings.type === 'time';
1338
+ const isYear = mode === 'year';
1339
+ const isYearOrMonth = isYear || mode === 'month';
1340
+ const isMinute = mode === 'minute';
1341
+ const isHourOrMinute = isMinute || mode === 'hour';
1342
+ // only care about a minute accuracy of settings.minTimeGap
1343
+ date1 = new Date(
1344
+ isTimeOnly ? 2000 : date1.getFullYear(),
1345
+ isTimeOnly ? 0 : (isYear ? 0 : date1.getMonth()),
1346
+ isTimeOnly ? 1 : (isYearOrMonth ? 1 : date1.getDate()),
1347
+ !isHourOrMinute ? 0 : date1.getHours(),
1348
+ !isMinute ? 0 : settings.minTimeGap * Math.floor(date1.getMinutes() / settings.minTimeGap)
1349
+ );
1350
+ date2 = new Date(
1351
+ isTimeOnly ? 2000 : date2.getFullYear(),
1352
+ isTimeOnly ? 0 : (isYear ? 0 : date2.getMonth()),
1353
+ isTimeOnly ? 1 : (isYearOrMonth ? 1 : date2.getDate()),
1354
+ !isHourOrMinute ? 0 : date2.getHours(),
1355
+ !isMinute ? 0 : settings.minTimeGap * Math.floor(date2.getMinutes() / settings.minTimeGap)
1356
+ );
1357
+
1358
+ return date2.getTime() - date1.getTime();
1359
+ },
1360
+ dateEqual: function (date1, date2, mode) {
1361
+ return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0;
1362
+ },
1363
+ isDateInRange: function (date, mode, minDate, maxDate) {
1364
+ if (!minDate && !maxDate) {
1365
+ const startDate = module.get.startDate();
1366
+ minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
1367
+ maxDate = settings.maxDate;
1368
+ }
1369
+ minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
1370
+
1371
+ return !(!date
1372
+ || (minDate && module.helper.dateDiff(date, minDate, mode) > 0)
1373
+ || (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0));
1374
+ },
1375
+ dateInRange: function (date, minDate, maxDate) {
1376
+ if (!minDate && !maxDate) {
1377
+ const startDate = module.get.startDate();
1378
+ minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
1379
+ maxDate = settings.maxDate;
1380
+ }
1381
+ minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
1382
+ const isTimeOnly = settings.type === 'time';
1383
+
1384
+ return !date
1385
+ ? date
1386
+ : (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0
1387
+ ? (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) // eslint-disable-line unicorn/no-nested-ternary
1388
+ : (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0 // eslint-disable-line unicorn/no-nested-ternary
1389
+ ? (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate)
1390
+ : date));
1391
+ },
1392
+ mergeDateTime: function (date, time) {
1393
+ return !date || !time
1394
+ ? time
1395
+ : new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes());
1396
+ },
1397
+ isTodayButton: function (element) {
1398
+ return element.text() === settings.text.today;
1399
+ },
1400
+ },
1401
+
1402
+ setting: function (name, value) {
1403
+ module.debug('Changing setting', name, value);
1404
+ if ($.isPlainObject(name)) {
1405
+ $.extend(true, settings, name);
1406
+ } else if (value !== undefined) {
1407
+ if ($.isPlainObject(settings[name])) {
1408
+ $.extend(true, settings[name], value);
1409
+ } else {
1410
+ settings[name] = value;
1411
+ }
1412
+ } else {
1413
+ return settings[name];
1414
+ }
1415
+ },
1416
+ internal: function (name, value) {
1417
+ if ($.isPlainObject(name)) {
1418
+ $.extend(true, module, name);
1419
+ } else if (value !== undefined) {
1420
+ module[name] = value;
1421
+ } else {
1422
+ return module[name];
1423
+ }
1424
+ },
1425
+ debug: function (...args) {
1426
+ if (!settings.silent && settings.debug) {
1427
+ if (settings.performance) {
1428
+ module.performance.log(args);
1429
+ } else {
1430
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1431
+ module.debug.apply(console, args);
1432
+ }
1433
+ }
1434
+ },
1435
+ verbose: function (...args) {
1436
+ if (!settings.silent && settings.verbose && settings.debug) {
1437
+ if (settings.performance) {
1438
+ module.performance.log(args);
1439
+ } else {
1440
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1441
+ module.verbose.apply(console, args);
1442
+ }
1443
+ }
1444
+ },
1445
+ error: function (...args) {
1446
+ if (!settings.silent) {
1447
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1448
+ module.error.apply(console, args);
1449
+ }
1450
+ },
1451
+ performance: {
1452
+ log: function (message) {
1453
+ let currentTime;
1454
+ let executionTime;
1455
+ let previousTime;
1456
+ if (settings.performance) {
1457
+ currentTime = Date.now();
1458
+ previousTime = time || currentTime;
1459
+ executionTime = currentTime - previousTime;
1460
+ time = currentTime;
1461
+ performance.push({
1462
+ Name: message[0],
1463
+ Arguments: message.slice(1),
1464
+ Element: element,
1465
+ 'Execution Time': executionTime,
1466
+ });
1467
+ }
1468
+ clearTimeout(module.performance.timer);
1469
+ module.performance.timer = setTimeout(function () {
1470
+ module.performance.display();
1471
+ }, 500);
1472
+ },
1473
+ display: function () {
1474
+ let title = settings.name + ':';
1475
+ let totalTime = 0;
1476
+ time = false;
1477
+ clearTimeout(module.performance.timer);
1478
+ $.each(performance, function (index, data) {
1479
+ totalTime += data['Execution Time'];
1480
+ });
1481
+ title += ' ' + totalTime + 'ms';
1482
+ if (performance.length > 0) {
1483
+ console.groupCollapsed(title);
1484
+ console.table(performance);
1485
+ console.groupEnd();
1486
+ }
1487
+ performance = [];
1488
+ },
1489
+ },
1490
+ invoke: function (query, passedArguments = queryArguments, context = element) {
1491
+ let object = instance;
1492
+ let maxDepth;
1493
+ let found;
1494
+ let response;
1495
+ if (typeof query === 'string' && object !== undefined) {
1496
+ query = query.split(/[ .]/);
1497
+ maxDepth = query.length - 1;
1498
+ $.each(query, function (depth, value) {
1499
+ const camelCaseValue = depth !== maxDepth
1500
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1501
+ : query;
1502
+ if ($.isPlainObject(object[camelCaseValue]) && (depth !== maxDepth)) {
1503
+ object = object[camelCaseValue];
1504
+ } else if (object[camelCaseValue] !== undefined) {
1505
+ found = object[camelCaseValue];
1506
+
1507
+ return false;
1508
+ } else if ($.isPlainObject(object[value]) && (depth !== maxDepth)) {
1509
+ object = object[value];
1510
+ } else if (object[value] !== undefined) {
1511
+ found = object[value];
1512
+
1513
+ return false;
1514
+ } else {
1515
+ module.error(error.method, query);
1516
+
1517
+ return false;
1518
+ }
1519
+ });
1520
+ }
1521
+ if (isFunction(found)) {
1522
+ response = found.apply(context, passedArguments);
1523
+ } else if (found !== undefined) {
1524
+ response = found;
1525
+ }
1526
+ if (Array.isArray(returnedValue)) {
1527
+ returnedValue.push(response);
1528
+ } else if (returnedValue !== undefined) {
1529
+ returnedValue = [returnedValue, response];
1530
+ } else if (response !== undefined) {
1531
+ returnedValue = response;
1532
+ }
1533
+
1534
+ return found;
1535
+ },
1536
+ };
1537
+
1538
+ if (methodInvoked) {
1539
+ if (instance === undefined) {
1540
+ module.initialize();
1541
+ }
1542
+ module.invoke(parameters);
1543
+ } else {
1544
+ if (instance !== undefined) {
1545
+ instance.invoke('destroy');
1546
+ }
1547
+ module.initialize();
1548
+ }
1549
+ });
1550
+
1551
+ return returnedValue !== undefined
1552
+ ? returnedValue
1553
+ : this;
1554
+ };
1555
+
1556
+ $.fn.calendar.settings = {
1557
+
1558
+ name: 'Calendar',
1559
+ namespace: 'calendar',
1560
+
1561
+ silent: false,
1562
+ debug: false,
1563
+ verbose: false,
1564
+ performance: true,
1565
+
1566
+ context: false,
1567
+
1568
+ type: 'datetime', // picker type. can be 'datetime', 'date', 'time', 'month', or 'year'
1569
+ firstDayOfWeek: 0, // day for first day column (0 = Sunday)
1570
+ constantHeight: true, // add rows to shorter months to keep day calendar height consistent (6 rows)
1571
+ today: false, // show a 'today/now' button at the bottom of the calendar
1572
+ closable: true, // close the popup after selecting a date/time
1573
+ monthFirst: false, // month before day when parsing date from a text
1574
+ touchReadonly: true, // set input to readonly on touch devices
1575
+ inline: false, // create the calendar inline instead of inside a popup
1576
+ on: null, // when to show the popup (defaults to 'focus' for input, 'click' for others)
1577
+ initialDate: null, // date to display initially when no date is selected (null = now)
1578
+ startMode: false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day')
1579
+ minDate: null, // minimum date/time that can be selected, dates/times before are disabled
1580
+ maxDate: null, // maximum date/time that can be selected, dates/times after are disabled
1581
+ disableYear: false, // disable year selection mode
1582
+ disableMonth: false, // disable month selection mode
1583
+ disableMinute: false, // disable minute selection mode
1584
+ formatInput: true, // format the input text upon input blur and module creation
1585
+ startCalendar: null, // jquery object or selector for another calendar that represents the start date of a date range
1586
+ endCalendar: null, // jquery object or selector for another calendar that represents the end date of a date range
1587
+ multiMonth: 1, // show multiple months when in 'day' mode
1588
+ monthOffset: 0, // position current month by offset when multimonth > 1
1589
+ minTimeGap: 5,
1590
+ showWeekNumbers: false, // show Number of Weeks at the very first column of a dayView
1591
+ disabledHours: [], // specific hour(s) which won't be selectable and contain additional information.
1592
+ disabledDates: [], // specific day(s) which won't be selectable and contain additional information.
1593
+ disabledDaysOfWeek: [], // day(s) which won't be selectable(s) (0 = Sunday)
1594
+ enabledDates: [], // specific day(s) which will be selectable, all other days will be disabled
1595
+ eventDates: [], // specific day(s) which will be shown in a different color and using tooltips
1596
+ centuryBreak: 60, // starting short year until 99 where it will be assumed to belong to the last century
1597
+ currentCentury: 2000, // century to be added to 2-digit years (00 to {centuryBreak}-1)
1598
+ selectAdjacentDays: false, // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable.
1599
+ // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden)
1600
+ popupOptions: {
1601
+ position: 'bottom left',
1602
+ lastResort: 'bottom left',
1603
+ prefer: 'opposite',
1604
+ observeChanges: false,
1605
+ hideOnScroll: false,
1606
+ },
1607
+
1608
+ text: {
1609
+ days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
1610
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
1611
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1612
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
1613
+ monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
1614
+ today: 'Today',
1615
+ now: 'Now',
1616
+ am: 'AM',
1617
+ pm: 'PM',
1618
+ weekNo: 'Week',
1619
+ },
1620
+
1621
+ formatter: {
1622
+ yearHeader: function (date, settings) {
1623
+ const decadeYear = Math.ceil(date.getFullYear() / 10) * 10;
1624
+
1625
+ return (decadeYear - 9) + ' - ' + (decadeYear + 2);
1626
+ },
1627
+ monthHeader: 'YYYY',
1628
+ dayHeader: 'MMMM YYYY',
1629
+ hourHeader: 'MMMM D, YYYY',
1630
+ minuteHeader: 'MMMM D, YYYY',
1631
+ dayColumnHeader: function (day, settings) {
1632
+ return settings.text.days[day];
1633
+ },
1634
+ datetime: 'MMMM D, YYYY h:mm A',
1635
+ date: 'MMMM D, YYYY',
1636
+ time: 'h:mm A',
1637
+ cellTime: 'h:mm A',
1638
+ month: 'MMMM YYYY',
1639
+ year: 'YYYY',
1640
+ today: function (settings) {
1641
+ return settings.type === 'date' ? settings.text.today : settings.text.now;
1642
+ },
1643
+ cell: function (cell, date, cellOptions) {},
1644
+ },
1645
+
1646
+ parser: {
1647
+ date: function (text, settings) {
1648
+ if (text instanceof Date) {
1649
+ return text;
1650
+ }
1651
+ if (!text) {
1652
+ return null;
1653
+ }
1654
+ text = String(text).trim().replace(/([./:-])\s+/g, '$1').replace(/\s+([./:-])/g, '$1')
1655
+ .replace(/\s+/g, ' ');
1656
+ if (text.length === 0) {
1657
+ return null;
1658
+ }
1659
+ if (/^\d{4}(?:[./-]\d{1,2}){2}$/.test(text)) {
1660
+ text = text.replace(/[./-]/g, '/') + ' 00:00:00';
1661
+ }
1662
+ // Reverse date and month in some cases
1663
+ text = settings.monthFirst || !/^\d{1,2}[./-]/.test(text) ? text : text.replace(/[./-]/g, '/').replace(/(\d+)\/(\d+)/, '$2/$1');
1664
+ const textDate = new Date(text);
1665
+ const numberOnly = text.match(/^\d+$/) !== null;
1666
+ const isShortYear = text.match(/^(?:\d{1,2}[./-]){2}\d{1,2}$/) !== null;
1667
+ if (!isShortYear && !numberOnly && !Number.isNaN(textDate.getDate())) {
1668
+ return textDate;
1669
+ }
1670
+ text = text.toLowerCase();
1671
+
1672
+ let i;
1673
+ let j;
1674
+ let k;
1675
+ let minute = -1;
1676
+ let hour = -1;
1677
+ let day = -1;
1678
+ let month = -1;
1679
+ let year = -1;
1680
+ let isAm;
1681
+
1682
+ const isTimeOnly = settings.type === 'time';
1683
+ const isDateOnly = !settings.type.includes('time');
1684
+
1685
+ const words = text.split(settings.regExp.dateWords);
1686
+ const numbers = text.split(settings.regExp.dateNumbers);
1687
+ let number;
1688
+
1689
+ let parts;
1690
+ let monthString;
1691
+
1692
+ if (!isDateOnly) {
1693
+ // am/pm
1694
+ isAm = words.includes(settings.text.am.toLowerCase())
1695
+ ? true
1696
+ : (words.includes(settings.text.pm.toLowerCase()) ? false : undefined);
1697
+
1698
+ // time with ':'
1699
+ for (i = 0; i < numbers.length; i++) {
1700
+ number = numbers[i];
1701
+ if (number.includes(':')) {
1702
+ if (hour < 0 || minute < 0) {
1703
+ parts = number.split(':');
1704
+ for (k = 0; k < Math.min(2, parts.length); k++) {
1705
+ j = Number.parseInt(parts[k], 10);
1706
+ if (Number.isNaN(j)) {
1707
+ j = 0;
1708
+ }
1709
+ if (k === 0) {
1710
+ hour = j % 24;
1711
+ } else {
1712
+ minute = j % 60;
1713
+ }
1714
+ }
1715
+ }
1716
+ numbers.splice(i, 1);
1717
+ }
1718
+ }
1719
+ }
1720
+
1721
+ if (!isTimeOnly) {
1722
+ // textual month
1723
+ for (const word of words) {
1724
+ if (word.length > 0) {
1725
+ for (j = 0; j < settings.text.months.length; j++) {
1726
+ monthString = settings.text.months[j];
1727
+ monthString = monthString.slice(0, word.length).toLowerCase();
1728
+ if (monthString === word) {
1729
+ month = j + 1;
1730
+
1731
+ break;
1732
+ }
1733
+ }
1734
+ if (month >= 0) {
1735
+ break;
1736
+ }
1737
+ }
1738
+ }
1739
+
1740
+ // year > settings.centuryBreak
1741
+ for (i = 0; i < numbers.length; i++) {
1742
+ j = Number.parseInt(numbers[i], 10);
1743
+ if (!Number.isNaN(j) && j >= settings.centuryBreak && i === numbers.length - 1) {
1744
+ if (j <= 99) {
1745
+ j += settings.currentCentury - 100;
1746
+ }
1747
+ year = j;
1748
+ numbers.splice(i, 1);
1749
+
1750
+ break;
1751
+ }
1752
+ }
1753
+
1754
+ // numeric month
1755
+ if (month < 0) {
1756
+ for (i = 0; i < numbers.length; i++) {
1757
+ k = i > 1 || settings.monthFirst
1758
+ ? i
1759
+ : (i === 1 ? 0 : 1);
1760
+ j = Number.parseInt(numbers[k], 10);
1761
+ if (!Number.isNaN(j) && j >= 1 && j <= 12) {
1762
+ month = j;
1763
+ numbers.splice(k, 1);
1764
+
1765
+ break;
1766
+ }
1767
+ }
1768
+ }
1769
+
1770
+ // day
1771
+ for (i = 0; i < numbers.length; i++) {
1772
+ j = Number.parseInt(numbers[i], 10);
1773
+ if (!Number.isNaN(j) && j >= 1 && j <= 31) {
1774
+ day = j;
1775
+ numbers.splice(i, 1);
1776
+
1777
+ break;
1778
+ }
1779
+ }
1780
+
1781
+ // year <= settings.centuryBreak
1782
+ if (year < 0) {
1783
+ for (i = numbers.length - 1; i >= 0; i--) {
1784
+ j = Number.parseInt(numbers[i], 10);
1785
+ if (!Number.isNaN(j)) {
1786
+ if (j <= 99) {
1787
+ j += settings.currentCentury;
1788
+ }
1789
+ year = j;
1790
+ numbers.splice(i, 1);
1791
+
1792
+ break;
1793
+ }
1794
+ }
1795
+ }
1796
+ }
1797
+
1798
+ if (!isDateOnly) {
1799
+ // hour
1800
+ if (hour < 0) {
1801
+ for (i = 0; i < numbers.length; i++) {
1802
+ j = Number.parseInt(numbers[i], 10);
1803
+ if (!Number.isNaN(j) && j >= 0 && j <= 23) {
1804
+ hour = j;
1805
+ numbers.splice(i, 1);
1806
+
1807
+ break;
1808
+ }
1809
+ }
1810
+ }
1811
+
1812
+ // minute
1813
+ if (minute < 0) {
1814
+ for (i = 0; i < numbers.length; i++) {
1815
+ j = Number.parseInt(numbers[i], 10);
1816
+ if (!Number.isNaN(j) && j >= 0 && j <= 59) {
1817
+ minute = j;
1818
+ numbers.splice(i, 1);
1819
+
1820
+ break;
1821
+ }
1822
+ }
1823
+ }
1824
+ }
1825
+
1826
+ if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) {
1827
+ return null;
1828
+ }
1829
+
1830
+ if (minute < 0) {
1831
+ minute = 0;
1832
+ }
1833
+ if (hour < 0) {
1834
+ hour = 0;
1835
+ }
1836
+ if (day < 0) {
1837
+ day = 1;
1838
+ }
1839
+ if (month < 0) {
1840
+ month = 1;
1841
+ }
1842
+ if (year < 0) {
1843
+ year = new Date().getFullYear();
1844
+ }
1845
+
1846
+ if (isAm !== undefined) {
1847
+ if (isAm) {
1848
+ if (hour === 12) {
1849
+ hour = 0;
1850
+ }
1851
+ } else if (hour < 12) {
1852
+ hour += 12;
1853
+ }
1854
+ }
1855
+
1856
+ let date = new Date(year, month - 1, day, hour, minute);
1857
+ if (date.getMonth() !== month - 1 || date.getFullYear() !== year) {
1858
+ // month or year don't match up, switch to the last day of the month
1859
+ date = new Date(year, month, 0, hour, minute);
1860
+ }
1861
+
1862
+ return Number.isNaN(date.getTime()) ? null : date;
1863
+ },
1864
+ },
1865
+
1866
+ // callback before date is changed, return false to cancel the change
1867
+ onBeforeChange: function (date, text, mode) {
1868
+ return true;
1869
+ },
1870
+
1871
+ // callback when date changes
1872
+ onChange: function (date, text, mode) {},
1873
+
1874
+ // callback before show animation, return false to prevent show
1875
+ onShow: function () {},
1876
+
1877
+ // callback after show animation
1878
+ onVisible: function () {},
1879
+
1880
+ // callback before hide animation, return false to prevent hide
1881
+ onHide: function () {},
1882
+
1883
+ // callback after hide animation
1884
+ onHidden: function () {},
1885
+
1886
+ // callback before item is selected, return false to prevent selection
1887
+ onSelect: function (date, mode) {},
1888
+
1889
+ // is the given date disabled?
1890
+ isDisabled: function (date, mode) {
1891
+ return false;
1892
+ },
1893
+
1894
+ selector: {
1895
+ popup: '.ui.popup',
1896
+ input: 'input',
1897
+ activator: 'input',
1898
+ append: '.inline.field,.inline.fields',
1899
+ },
1900
+
1901
+ regExp: {
1902
+ dateWords: /[^A-Za-z\u00C0-\u024F]+/g,
1903
+ dateNumbers: /[^\d:]+/g,
1904
+ token: /d{1,4}|D{1,2}|M{1,4}|YY(?:YY)?|([Hhmsw])\1?|[ASYa]|"[^"]*"|'[^']*'/g,
1905
+ },
1906
+
1907
+ error: {
1908
+ popup: 'UI Popup, a required component is not included in this page',
1909
+ method: 'The method you called is not defined.',
1910
+ },
1911
+
1912
+ className: {
1913
+ calendar: 'calendar',
1914
+ active: 'active',
1915
+ popup: 'ui popup',
1916
+ grid: 'ui equal width grid',
1917
+ column: 'column',
1918
+ table: 'ui celled center aligned unstackable table',
1919
+ inverted: 'inverted',
1920
+ prev: 'prev link',
1921
+ next: 'next link',
1922
+ prevIcon: 'chevron left icon',
1923
+ nextIcon: 'chevron right icon',
1924
+ link: 'link',
1925
+ cell: 'link',
1926
+ disabledCell: 'disabled',
1927
+ weekCell: 'disabled',
1928
+ adjacentCell: 'adjacent',
1929
+ activeCell: 'active',
1930
+ rangeCell: 'range',
1931
+ focusCell: 'focus',
1932
+ todayCell: 'today',
1933
+ today: 'today link',
1934
+ disabled: 'disabled',
1935
+ },
1936
+
1937
+ metadata: {
1938
+ date: 'date',
1939
+ focusDate: 'focusDate',
1940
+ startDate: 'startDate',
1941
+ endDate: 'endDate',
1942
+ minDate: 'minDate',
1943
+ maxDate: 'maxDate',
1944
+ mode: 'mode',
1945
+ type: 'type',
1946
+ monthOffset: 'monthOffset',
1947
+ message: 'message',
1948
+ class: 'class',
1949
+ inverted: 'inverted',
1950
+ variation: 'variation',
1951
+ position: 'position',
1952
+ month: 'month',
1953
+ year: 'year',
1954
+ hours: 'hours',
1955
+ days: 'days',
1956
+ },
1957
+
1958
+ eventClass: 'blue',
1959
+ };
1960
+ })(jQuery, window, document);