datatables.net-datetime 1.1.2 → 1.2.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.
@@ -0,0 +1,1556 @@
1
+ /*! DateTime picker for DataTables.net v1.2.0
2
+ *
3
+ * © SpryMedia Ltd, all rights reserved.
4
+ * License: MIT datatables.net/license/mit
5
+ */
6
+
7
+ import $ from 'jquery';
8
+
9
+
10
+
11
+ /**
12
+ * @summary DateTime picker for DataTables.net
13
+ * @version 1.2.0
14
+ * @file dataTables.dateTime.js
15
+ * @author SpryMedia Ltd
16
+ * @contact www.datatables.net/contact
17
+ */
18
+
19
+ // Supported formatting and parsing libraries:
20
+ // * Moment
21
+ // * Luxon
22
+ // * DayJS
23
+ var dateLib;
24
+
25
+ /*
26
+ * This file provides a DateTime GUI picker (calendar and time input). Only the
27
+ * format YYYY-MM-DD is supported without additional software, but the end user
28
+ * experience can be greatly enhanced by including the momentjs, dayjs or luxon library
29
+ * which provide date / time parsing and formatting options.
30
+ *
31
+ * This functionality is required because the HTML5 date and datetime input
32
+ * types are not widely supported in desktop browsers.
33
+ *
34
+ * Constructed by using:
35
+ *
36
+ * new DateTime( input, opts )
37
+ *
38
+ * where `input` is the HTML input element to use and `opts` is an object of
39
+ * options based on the `DateTime.defaults` object.
40
+ */
41
+ var DateTime = function ( input, opts ) {
42
+ // Attempt to auto detect the formatting library (if there is one). Having it in
43
+ // the constructor allows load order independence.
44
+ if (typeof dateLib === 'undefined') {
45
+ dateLib = window.moment
46
+ ? window.moment
47
+ : window.dayjs
48
+ ? window.dayjs
49
+ : window.luxon
50
+ ? window.luxon
51
+ : null;
52
+ }
53
+
54
+ this.c = $.extend( true, {}, DateTime.defaults, opts );
55
+ var classPrefix = this.c.classPrefix;
56
+ var i18n = this.c.i18n;
57
+
58
+ // Only IS8601 dates are supported without moment, dayjs or luxon
59
+ if ( ! dateLib && this.c.format !== 'YYYY-MM-DD' ) {
60
+ throw "DateTime: Without momentjs, dayjs or luxon only the format 'YYYY-MM-DD' can be used";
61
+ }
62
+
63
+ // Min and max need to be `Date` objects in the config
64
+ if (typeof this.c.minDate === 'string') {
65
+ this.c.minDate = new Date(this.c.minDate);
66
+ }
67
+ if (typeof this.c.maxDate === 'string') {
68
+ this.c.maxDate = new Date(this.c.maxDate);
69
+ }
70
+
71
+ var timeBlock = function ( type ) {
72
+ return '<div class="'+classPrefix+'-timeblock">'+
73
+ '</div>';
74
+ };
75
+
76
+ var gap = function () {
77
+ return '<span>:</span>';
78
+ };
79
+
80
+ // DOM structure
81
+ var structure = $(
82
+ '<div class="'+classPrefix+'">'+
83
+ '<div class="'+classPrefix+'-date">'+
84
+ '<div class="'+classPrefix+'-title">'+
85
+ '<div class="'+classPrefix+'-iconLeft">'+
86
+ '<button type="button" title="'+i18n.previous+'">'+i18n.previous+'</button>'+
87
+ '</div>'+
88
+ '<div class="'+classPrefix+'-iconRight">'+
89
+ '<button type="button" title="'+i18n.next+'">'+i18n.next+'</button>'+
90
+ '</div>'+
91
+ '<div class="'+classPrefix+'-label">'+
92
+ '<span></span>'+
93
+ '<select class="'+classPrefix+'-month"></select>'+
94
+ '</div>'+
95
+ '<div class="'+classPrefix+'-label">'+
96
+ '<span></span>'+
97
+ '<select class="'+classPrefix+'-year"></select>'+
98
+ '</div>'+
99
+ '</div>'+
100
+ '<div class="'+classPrefix+'-buttons">'+
101
+ '<a class="'+classPrefix+'-clear">'+i18n.clear+'</a>'+
102
+ '<a class="'+classPrefix+'-today">'+i18n.today+'</a>'+
103
+ '</div>'+
104
+ '<div class="'+classPrefix+'-calendar"></div>'+
105
+ '</div>'+
106
+ '<div class="'+classPrefix+'-time">'+
107
+ '<div class="'+classPrefix+'-hours"></div>'+
108
+ '<div class="'+classPrefix+'-minutes"></div>'+
109
+ '<div class="'+classPrefix+'-seconds"></div>'+
110
+ '</div>'+
111
+ '<div class="'+classPrefix+'-error"></div>'+
112
+ '</div>'
113
+ );
114
+
115
+ this.dom = {
116
+ container: structure,
117
+ date: structure.find( '.'+classPrefix+'-date' ),
118
+ title: structure.find( '.'+classPrefix+'-title' ),
119
+ calendar: structure.find( '.'+classPrefix+'-calendar' ),
120
+ time: structure.find( '.'+classPrefix+'-time' ),
121
+ error: structure.find( '.'+classPrefix+'-error' ),
122
+ buttons: structure.find( '.'+classPrefix+'-buttons' ),
123
+ clear: structure.find( '.'+classPrefix+'-clear' ),
124
+ today: structure.find( '.'+classPrefix+'-today' ),
125
+ input: $(input)
126
+ };
127
+
128
+ this.s = {
129
+ /** @type {Date} Date value that the picker has currently selected */
130
+ d: null,
131
+
132
+ /** @type {Date} Date of the calendar - might not match the value */
133
+ display: null,
134
+
135
+ /** @type {number} Used to select minutes in a range where the range base is itself unavailable */
136
+ minutesRange: null,
137
+
138
+ /** @type {number} Used to select minutes in a range where the range base is itself unavailable */
139
+ secondsRange: null,
140
+
141
+ /** @type {String} Unique namespace string for this instance */
142
+ namespace: 'dateime-'+(DateTime._instance++),
143
+
144
+ /** @type {Object} Parts of the picker that should be shown */
145
+ parts: {
146
+ date: this.c.format.match( /[YMD]|L(?!T)|l/ ) !== null,
147
+ time: this.c.format.match( /[Hhm]|LT|LTS/ ) !== null,
148
+ seconds: this.c.format.indexOf( 's' ) !== -1,
149
+ hours12: this.c.format.match( /[haA]/ ) !== null
150
+ }
151
+ };
152
+
153
+ this.dom.container
154
+ .append( this.dom.date )
155
+ .append( this.dom.time )
156
+ .append( this.dom.error );
157
+
158
+ this.dom.date
159
+ .append( this.dom.title )
160
+ .append( this.dom.buttons )
161
+ .append( this.dom.calendar );
162
+
163
+ this._constructor();
164
+ };
165
+
166
+ $.extend( DateTime.prototype, {
167
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
168
+ * Public
169
+ */
170
+
171
+ /**
172
+ * Destroy the control
173
+ */
174
+ destroy: function () {
175
+ this._hide(true);
176
+ this.dom.container.off().empty();
177
+ this.dom.input
178
+ .removeAttr('autocomplete')
179
+ .off('.datetime');
180
+ },
181
+
182
+ errorMsg: function ( msg ) {
183
+ var error = this.dom.error;
184
+
185
+ if ( msg ) {
186
+ error.html( msg );
187
+ }
188
+ else {
189
+ error.empty();
190
+ }
191
+
192
+ return this;
193
+ },
194
+
195
+ hide: function () {
196
+ this._hide();
197
+
198
+ return this;
199
+ },
200
+
201
+ max: function ( date ) {
202
+ this.c.maxDate = typeof date === 'string'
203
+ ? new Date(date)
204
+ : date;
205
+
206
+ this._optionsTitle();
207
+ this._setCalander();
208
+
209
+ return this;
210
+ },
211
+
212
+ min: function ( date ) {
213
+ this.c.minDate = typeof date === 'string'
214
+ ? new Date(date)
215
+ : date;
216
+
217
+ this._optionsTitle();
218
+ this._setCalander();
219
+
220
+ return this;
221
+ },
222
+
223
+ /**
224
+ * Check if an element belongs to this control
225
+ *
226
+ * @param {node} node Element to check
227
+ * @return {boolean} true if owned by this control, false otherwise
228
+ */
229
+ owns: function ( node ) {
230
+ return $(node).parents().filter( this.dom.container ).length > 0;
231
+ },
232
+
233
+ /**
234
+ * Get / set the value
235
+ *
236
+ * @param {string|Date} set Value to set
237
+ * @param {boolean} [write=true] Flag to indicate if the formatted value
238
+ * should be written into the input element
239
+ */
240
+ val: function ( set, write ) {
241
+ if ( set === undefined ) {
242
+ return this.s.d;
243
+ }
244
+
245
+ if ( set instanceof Date ) {
246
+ this.s.d = this._dateToUtc( set );
247
+ }
248
+ else if ( set === null || set === '' ) {
249
+ this.s.d = null;
250
+ }
251
+ else if ( set === '--now' ) {
252
+ this.s.d = this._dateToUtc(new Date());
253
+ }
254
+ else if ( typeof set === 'string' ) {
255
+ // luxon uses different method names so need to be able to call them
256
+ if(dateLib && dateLib == window.luxon) {
257
+ var luxDT = dateLib.DateTime.fromFormat(set, this.c.format)
258
+ this.s.d = luxDT.isValid ? luxDT.toJSDate() : null;
259
+ }
260
+ else if ( dateLib ) {
261
+ // Use moment, dayjs or luxon if possible (even for ISO8601 strings, since it
262
+ // will correctly handle 0000-00-00 and the like)
263
+ var m = dateLib.utc( set, this.c.format, this.c.locale, this.c.strict );
264
+ this.s.d = m.isValid() ? m.toDate() : null;
265
+ }
266
+ else {
267
+ // Else must be using ISO8601 without a date library (constructor would
268
+ // have thrown an error otherwise)
269
+ var match = set.match(/(\d{4})\-(\d{2})\-(\d{2})/ );
270
+ this.s.d = match ?
271
+ new Date( Date.UTC(match[1], match[2]-1, match[3]) ) :
272
+ null;
273
+ }
274
+ }
275
+
276
+ if ( write || write === undefined ) {
277
+ if ( this.s.d ) {
278
+ this._writeOutput();
279
+ }
280
+ else {
281
+ // The input value was not valid...
282
+ this.dom.input.val( set );
283
+ }
284
+ }
285
+
286
+ // Need something to display
287
+ this.s.display = this.s.d
288
+ ? new Date( this.s.d.toString() )
289
+ : new Date();
290
+
291
+ // Set the day of the month to be 1 so changing between months doesn't
292
+ // run into issues when going from day 31 to 28 (for example)
293
+ this.s.display.setUTCDate( 1 );
294
+
295
+ // Update the display elements for the new value
296
+ this._setTitle();
297
+ this._setCalander();
298
+ this._setTime();
299
+
300
+ return this;
301
+ },
302
+
303
+
304
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
305
+ * Constructor
306
+ */
307
+
308
+ /**
309
+ * Build the control and assign initial event handlers
310
+ *
311
+ * @private
312
+ */
313
+ _constructor: function () {
314
+ var that = this;
315
+ var classPrefix = this.c.classPrefix;
316
+ var last = this.dom.input.val();
317
+
318
+ var onChange = function () {
319
+ var curr = that.dom.input.val();
320
+
321
+ if (curr !== last) {
322
+ that.c.onChange.call( that, curr, that.s.d, that.dom.input );
323
+ last = curr;
324
+ }
325
+ };
326
+
327
+ if ( ! this.s.parts.date ) {
328
+ this.dom.date.css( 'display', 'none' );
329
+ }
330
+
331
+ if ( ! this.s.parts.time ) {
332
+ this.dom.time.css( 'display', 'none' );
333
+ }
334
+
335
+ if ( ! this.s.parts.seconds ) {
336
+ this.dom.time.children('div.'+classPrefix+'-seconds').remove();
337
+ this.dom.time.children('span').eq(1).remove();
338
+ }
339
+
340
+ if ( ! this.c.buttons.clear ) {
341
+ this.dom.clear.css( 'display', 'none' );
342
+ }
343
+
344
+ if ( ! this.c.buttons.today ) {
345
+ this.dom.today.css( 'display', 'none' );
346
+ }
347
+
348
+ // Render the options
349
+ this._optionsTitle();
350
+
351
+ $(document).on('i18n.dt', function (e, settings) {
352
+ if (settings.oLanguage.datetime) {
353
+ $.extend(true, that.c.i18n, settings.oLanguage.datetime);
354
+ that._optionsTitle();
355
+ }
356
+ });
357
+
358
+ // When attached to a hidden input, we always show the input picker, and
359
+ // do so inline
360
+ if (this.dom.input.attr('type') === 'hidden') {
361
+ this.dom.container.addClass('inline');
362
+ this.c.attachTo = 'input';
363
+
364
+ this.val( this.dom.input.val(), false );
365
+ this._show();
366
+ }
367
+
368
+ // Set the initial value
369
+ if (last) {
370
+ this.val( last, false );
371
+ }
372
+
373
+ // Trigger the display of the widget when clicking or focusing on the
374
+ // input element
375
+ this.dom.input
376
+ .attr('autocomplete', 'off')
377
+ .on('focus.datetime click.datetime', function () {
378
+ // If already visible - don't do anything
379
+ if ( that.dom.container.is(':visible') || that.dom.input.is(':disabled') ) {
380
+ return;
381
+ }
382
+
383
+ // In case the value has changed by text
384
+ that.val( that.dom.input.val(), false );
385
+
386
+ that._show();
387
+ } )
388
+ .on('keyup.datetime', function () {
389
+ // Update the calendar's displayed value as the user types
390
+ if ( that.dom.container.is(':visible') ) {
391
+ that.val( that.dom.input.val(), false );
392
+ }
393
+ } );
394
+
395
+ // Main event handlers for input in the widget
396
+ this.dom.container
397
+ .on( 'change', 'select', function () {
398
+ var select = $(this);
399
+ var val = select.val();
400
+
401
+ if ( select.hasClass(classPrefix+'-month') ) {
402
+ // Month select
403
+ that._correctMonth( that.s.display, val );
404
+ that._setTitle();
405
+ that._setCalander();
406
+ }
407
+ else if ( select.hasClass(classPrefix+'-year') ) {
408
+ // Year select
409
+ that.s.display.setUTCFullYear( val );
410
+ that._setTitle();
411
+ that._setCalander();
412
+ }
413
+ else if ( select.hasClass(classPrefix+'-hours') || select.hasClass(classPrefix+'-ampm') ) {
414
+ // Hours - need to take account of AM/PM input if present
415
+ if ( that.s.parts.hours12 ) {
416
+ var hours = $(that.dom.container).find('.'+classPrefix+'-hours').val() * 1;
417
+ var pm = $(that.dom.container).find('.'+classPrefix+'-ampm').val() === 'pm';
418
+
419
+ that.s.d.setUTCHours( hours === 12 && !pm ?
420
+ 0 :
421
+ pm && hours !== 12 ?
422
+ hours + 12 :
423
+ hours
424
+ );
425
+ }
426
+ else {
427
+ that.s.d.setUTCHours( val );
428
+ }
429
+
430
+ that._setTime();
431
+ that._writeOutput( true );
432
+
433
+ onChange();
434
+ }
435
+ else if ( select.hasClass(classPrefix+'-minutes') ) {
436
+ // Minutes select
437
+ that.s.d.setUTCMinutes( val );
438
+ that._setTime();
439
+ that._writeOutput( true );
440
+
441
+ onChange();
442
+ }
443
+ else if ( select.hasClass(classPrefix+'-seconds') ) {
444
+ // Seconds select
445
+ that.s.d.setSeconds( val );
446
+ that._setTime();
447
+ that._writeOutput( true );
448
+
449
+ onChange();
450
+ }
451
+
452
+ that.dom.input.focus();
453
+ that._position();
454
+ } )
455
+ .on( 'click', function (e) {
456
+ var d = that.s.d;
457
+ var nodeName = e.target.nodeName.toLowerCase();
458
+ var target = nodeName === 'span' ?
459
+ e.target.parentNode :
460
+ e.target;
461
+
462
+ nodeName = target.nodeName.toLowerCase();
463
+
464
+ if ( nodeName === 'select' ) {
465
+ return;
466
+ }
467
+
468
+ e.stopPropagation();
469
+
470
+ if ( nodeName === 'a' ) {
471
+ e.preventDefault();
472
+
473
+ if ($(target).hasClass(classPrefix+'-clear')) {
474
+ // Clear the value and don't change the display
475
+ that.s.d = null;
476
+ that.dom.input.val('');
477
+ that._writeOutput();
478
+ that._setCalander();
479
+ that._setTime();
480
+
481
+ onChange();
482
+ }
483
+ else if ($(target).hasClass(classPrefix+'-today')) {
484
+ // Don't change the value, but jump to the month
485
+ // containing today
486
+ that.s.display = new Date();
487
+
488
+ that._setTitle();
489
+ that._setCalander();
490
+ }
491
+ }
492
+ if ( nodeName === 'button' ) {
493
+ var button = $(target);
494
+ var parent = button.parent();
495
+
496
+ if ( parent.hasClass('disabled') && ! parent.hasClass('range') ) {
497
+ button.blur();
498
+ return;
499
+ }
500
+
501
+ if ( parent.hasClass(classPrefix+'-iconLeft') ) {
502
+ // Previous month
503
+ that.s.display.setUTCMonth( that.s.display.getUTCMonth()-1 );
504
+ that._setTitle();
505
+ that._setCalander();
506
+
507
+ that.dom.input.focus();
508
+ }
509
+ else if ( parent.hasClass(classPrefix+'-iconRight') ) {
510
+ // Next month
511
+ that._correctMonth( that.s.display, that.s.display.getUTCMonth()+1 );
512
+ that._setTitle();
513
+ that._setCalander();
514
+
515
+ that.dom.input.focus();
516
+ }
517
+ else if ( button.parents('.'+classPrefix+'-time').length ) {
518
+ var val = button.data('value');
519
+ var unit = button.data('unit');
520
+
521
+ d = that._needValue();
522
+
523
+ if ( unit === 'minutes' ) {
524
+ if ( parent.hasClass('disabled') && parent.hasClass('range') ) {
525
+ that.s.minutesRange = val;
526
+ that._setTime();
527
+ return;
528
+ }
529
+ else {
530
+ that.s.minutesRange = null;
531
+ }
532
+ }
533
+
534
+ if ( unit === 'seconds' ) {
535
+ if ( parent.hasClass('disabled') && parent.hasClass('range') ) {
536
+ that.s.secondsRange = val;
537
+ that._setTime();
538
+ return;
539
+ }
540
+ else {
541
+ that.s.secondsRange = null;
542
+ }
543
+ }
544
+
545
+ // Specific to hours for 12h clock
546
+ if ( val === 'am' ) {
547
+ if ( d.getUTCHours() >= 12 ) {
548
+ val = d.getUTCHours() - 12;
549
+ }
550
+ else {
551
+ return;
552
+ }
553
+ }
554
+ else if ( val === 'pm' ) {
555
+ if ( d.getUTCHours() < 12 ) {
556
+ val = d.getUTCHours() + 12;
557
+ }
558
+ else {
559
+ return;
560
+ }
561
+ }
562
+
563
+ var set = unit === 'hours' ?
564
+ 'setUTCHours' :
565
+ unit === 'minutes' ?
566
+ 'setUTCMinutes' :
567
+ 'setSeconds';
568
+
569
+ d[set]( val );
570
+ that._setCalander();
571
+ that._setTime();
572
+ that._writeOutput( true );
573
+ onChange();
574
+ }
575
+ else {
576
+ // Calendar click
577
+ d = that._needValue();
578
+
579
+ // Can't be certain that the current day will exist in
580
+ // the new month, and likewise don't know that the
581
+ // new day will exist in the old month, But 1 always
582
+ // does, so we can change the month without worry of a
583
+ // recalculation being done automatically by `Date`
584
+ d.setUTCDate( 1 );
585
+ d.setUTCFullYear( button.data('year') );
586
+ d.setUTCMonth( button.data('month') );
587
+ d.setUTCDate( button.data('day') );
588
+
589
+ that._writeOutput( true );
590
+
591
+ // Don't hide if there is a time picker, since we want to
592
+ // be able to select a time as well.
593
+ if ( ! that.s.parts.time ) {
594
+ // This is annoying but IE has some kind of async
595
+ // behaviour with focus and the focus from the above
596
+ // write would occur after this hide - resulting in the
597
+ // calendar opening immediately
598
+ setTimeout( function () {
599
+ that._hide();
600
+ }, 10 );
601
+ }
602
+ else {
603
+ that._setCalander();
604
+ that._setTime();
605
+ }
606
+
607
+ onChange();
608
+ }
609
+ }
610
+ else {
611
+ // Click anywhere else in the widget - return focus to the
612
+ // input element
613
+ that.dom.input.focus();
614
+ }
615
+ } );
616
+ },
617
+
618
+
619
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
620
+ * Private
621
+ */
622
+
623
+ /**
624
+ * Compare the date part only of two dates - this is made super easy by the
625
+ * toDateString method!
626
+ *
627
+ * @param {Date} a Date 1
628
+ * @param {Date} b Date 2
629
+ * @private
630
+ */
631
+ _compareDates: function( a, b ) {
632
+ // Can't use toDateString as that converts to local time
633
+ // luxon uses different method names so need to be able to call them
634
+ return dateLib && dateLib == window.luxon
635
+ ? dateLib.DateTime.fromJSDate(a).toISODate() === dateLib.DateTime.fromJSDate(b).toISODate()
636
+ : this._dateToUtcString(a) === this._dateToUtcString(b);
637
+ },
638
+
639
+ /**
640
+ * When changing month, take account of the fact that some months don't have
641
+ * the same number of days. For example going from January to February you
642
+ * can have the 31st of Jan selected and just add a month since the date
643
+ * would still be 31, and thus drop you into March.
644
+ *
645
+ * @param {Date} date Date - will be modified
646
+ * @param {integer} month Month to set
647
+ * @private
648
+ */
649
+ _correctMonth: function ( date, month ) {
650
+ var days = this._daysInMonth( date.getUTCFullYear(), month );
651
+ var correctDays = date.getUTCDate() > days;
652
+
653
+ date.setUTCMonth( month );
654
+
655
+ if ( correctDays ) {
656
+ date.setUTCDate( days );
657
+ date.setUTCMonth( month );
658
+ }
659
+ },
660
+
661
+ /**
662
+ * Get the number of days in a method. Based on
663
+ * http://stackoverflow.com/a/4881951 by Matti Virkkunen
664
+ *
665
+ * @param {integer} year Year
666
+ * @param {integer} month Month (starting at 0)
667
+ * @private
668
+ */
669
+ _daysInMonth: function ( year, month ) {
670
+ //
671
+ var isLeap = ((year % 4) === 0 && ((year % 100) !== 0 || (year % 400) === 0));
672
+ var months = [31, (isLeap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
673
+
674
+ return months[month];
675
+ },
676
+
677
+ /**
678
+ * Create a new date object which has the UTC values set to the local time.
679
+ * This allows the local time to be used directly for the library which
680
+ * always bases its calculations and display on UTC.
681
+ *
682
+ * @param {Date} s Date to "convert"
683
+ * @return {Date} Shifted date
684
+ */
685
+ _dateToUtc: function ( s ) {
686
+ return new Date( Date.UTC(
687
+ s.getFullYear(), s.getMonth(), s.getDate(),
688
+ s.getHours(), s.getMinutes(), s.getSeconds()
689
+ ) );
690
+ },
691
+
692
+ /**
693
+ * Create a UTC ISO8601 date part from a date object
694
+ *
695
+ * @param {Date} d Date to "convert"
696
+ * @return {string} ISO formatted date
697
+ */
698
+ _dateToUtcString: function ( d ) {
699
+ // luxon uses different method names so need to be able to call them
700
+ return dateLib && dateLib == window.luxon
701
+ ? dateLib.DateTime.fromJSDate(d).toISODate()
702
+ : d.getUTCFullYear()+'-'+
703
+ this._pad(d.getUTCMonth()+1)+'-'+
704
+ this._pad(d.getUTCDate());
705
+ },
706
+
707
+ /**
708
+ * Hide the control and remove events related to its display
709
+ *
710
+ * @private
711
+ */
712
+ _hide: function (destroy) {
713
+ if (! destroy && this.dom.input.attr('type') === 'hidden') {
714
+ return;
715
+ }
716
+
717
+ var namespace = this.s.namespace;
718
+
719
+ this.dom.container.detach();
720
+
721
+ $(window).off( '.'+namespace );
722
+ $(document).off( 'keydown.'+namespace );
723
+ $('div.dataTables_scrollBody').off( 'scroll.'+namespace );
724
+ $('div.DTE_Body_Content').off( 'scroll.'+namespace );
725
+ $('body').off( 'click.'+namespace );
726
+ $(this.dom.input[0].offsetParent).off('.'+namespace);
727
+ },
728
+
729
+ /**
730
+ * Convert a 24 hour value to a 12 hour value
731
+ *
732
+ * @param {integer} val 24 hour value
733
+ * @return {integer} 12 hour value
734
+ * @private
735
+ */
736
+ _hours24To12: function ( val ) {
737
+ return val === 0 ?
738
+ 12 :
739
+ val > 12 ?
740
+ val - 12 :
741
+ val;
742
+ },
743
+
744
+ /**
745
+ * Generate the HTML for a single day in the calendar - this is basically
746
+ * and HTML cell with a button that has data attributes so we know what was
747
+ * clicked on (if it is clicked on) and a bunch of classes for styling.
748
+ *
749
+ * @param {object} day Day object from the `_htmlMonth` method
750
+ * @return {string} HTML cell
751
+ */
752
+ _htmlDay: function( day )
753
+ {
754
+ if ( day.empty ) {
755
+ return '<td class="empty"></td>';
756
+ }
757
+
758
+ var classes = [ 'selectable' ];
759
+ var classPrefix = this.c.classPrefix;
760
+
761
+ if ( day.disabled ) {
762
+ classes.push( 'disabled' );
763
+ }
764
+
765
+ if ( day.today ) {
766
+ classes.push( 'now' );
767
+ }
768
+
769
+ if ( day.selected ) {
770
+ classes.push( 'selected' );
771
+ }
772
+
773
+ return '<td data-day="' + day.day + '" class="' + classes.join(' ') + '">' +
774
+ '<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" ' +'data-year="' + day.year + '" data-month="' + day.month + '" data-day="' + day.day + '">' +
775
+ '<span>'+day.day+'</span>'+
776
+ '</button>' +
777
+ '</td>';
778
+ },
779
+
780
+
781
+ /**
782
+ * Create the HTML for a month to be displayed in the calendar table.
783
+ *
784
+ * Based upon the logic used in Pikaday - MIT licensed
785
+ * Copyright (c) 2014 David Bushell
786
+ * https://github.com/dbushell/Pikaday
787
+ *
788
+ * @param {integer} year Year
789
+ * @param {integer} month Month (starting at 0)
790
+ * @return {string} Calendar month HTML
791
+ * @private
792
+ */
793
+ _htmlMonth: function ( year, month ) {
794
+ var now = this._dateToUtc( new Date() ),
795
+ days = this._daysInMonth( year, month ),
796
+ before = new Date( Date.UTC(year, month, 1) ).getUTCDay(),
797
+ data = [],
798
+ row = [];
799
+
800
+ if ( this.c.firstDay > 0 ) {
801
+ before -= this.c.firstDay;
802
+
803
+ if (before < 0) {
804
+ before += 7;
805
+ }
806
+ }
807
+
808
+ var cells = days + before,
809
+ after = cells;
810
+
811
+ while ( after > 7 ) {
812
+ after -= 7;
813
+ }
814
+
815
+ cells += 7 - after;
816
+
817
+ var minDate = this.c.minDate;
818
+ var maxDate = this.c.maxDate;
819
+
820
+ if ( minDate ) {
821
+ minDate.setUTCHours(0);
822
+ minDate.setUTCMinutes(0);
823
+ minDate.setSeconds(0);
824
+ }
825
+
826
+ if ( maxDate ) {
827
+ maxDate.setUTCHours(23);
828
+ maxDate.setUTCMinutes(59);
829
+ maxDate.setSeconds(59);
830
+ }
831
+
832
+ for ( var i=0, r=0 ; i<cells ; i++ ) {
833
+ var day = new Date( Date.UTC(year, month, 1 + (i - before)) ),
834
+ selected = this.s.d ? this._compareDates(day, this.s.d) : false,
835
+ today = this._compareDates(day, now),
836
+ empty = i < before || i >= (days + before),
837
+ disabled = (minDate && day < minDate) ||
838
+ (maxDate && day > maxDate);
839
+
840
+ var disableDays = this.c.disableDays;
841
+ if ( Array.isArray( disableDays ) && $.inArray( day.getUTCDay(), disableDays ) !== -1 ) {
842
+ disabled = true;
843
+ }
844
+ else if ( typeof disableDays === 'function' && disableDays( day ) === true ) {
845
+ disabled = true;
846
+ }
847
+
848
+ var dayConfig = {
849
+ day: 1 + (i - before),
850
+ month: month,
851
+ year: year,
852
+ selected: selected,
853
+ today: today,
854
+ disabled: disabled,
855
+ empty: empty
856
+ };
857
+
858
+ row.push( this._htmlDay(dayConfig) );
859
+
860
+ if ( ++r === 7 ) {
861
+ if ( this.c.showWeekNumber ) {
862
+ row.unshift( this._htmlWeekOfYear(i - before, month, year) );
863
+ }
864
+
865
+ data.push( '<tr>'+row.join('')+'</tr>' );
866
+ row = [];
867
+ r = 0;
868
+ }
869
+ }
870
+
871
+ var classPrefix = this.c.classPrefix;
872
+ var className = classPrefix+'-table';
873
+ if ( this.c.showWeekNumber ) {
874
+ className += ' weekNumber';
875
+ }
876
+
877
+ // Show / hide month icons based on min/max
878
+ if ( minDate ) {
879
+ var underMin = minDate >= new Date( Date.UTC(year, month, 1, 0, 0, 0 ) );
880
+
881
+ this.dom.title.find('div.'+classPrefix+'-iconLeft')
882
+ .css( 'display', underMin ? 'none' : 'block' );
883
+ }
884
+
885
+ if ( maxDate ) {
886
+ var overMax = maxDate < new Date( Date.UTC(year, month+1, 1, 0, 0, 0 ) );
887
+
888
+ this.dom.title.find('div.'+classPrefix+'-iconRight')
889
+ .css( 'display', overMax ? 'none' : 'block' );
890
+ }
891
+
892
+ return '<table class="'+className+'">' +
893
+ '<thead>'+
894
+ this._htmlMonthHead() +
895
+ '</thead>'+
896
+ '<tbody>'+
897
+ data.join('') +
898
+ '</tbody>'+
899
+ '</table>';
900
+ },
901
+
902
+ /**
903
+ * Create the calendar table's header (week days)
904
+ *
905
+ * @return {string} HTML cells for the row
906
+ * @private
907
+ */
908
+ _htmlMonthHead: function () {
909
+ var a = [];
910
+ var firstDay = this.c.firstDay;
911
+ var i18n = this.c.i18n;
912
+
913
+ // Take account of the first day shift
914
+ var dayName = function ( day ) {
915
+ day += firstDay;
916
+
917
+ while (day >= 7) {
918
+ day -= 7;
919
+ }
920
+
921
+ return i18n.weekdays[day];
922
+ };
923
+
924
+ // Empty cell in the header
925
+ if ( this.c.showWeekNumber ) {
926
+ a.push( '<th></th>' );
927
+ }
928
+
929
+ for ( var i=0 ; i<7 ; i++ ) {
930
+ a.push( '<th>'+dayName( i )+'</th>' );
931
+ }
932
+
933
+ return a.join('');
934
+ },
935
+
936
+ /**
937
+ * Create a cell that contains week of the year - ISO8601
938
+ *
939
+ * Based on https://stackoverflow.com/questions/6117814/ and
940
+ * http://techblog.procurios.nl/k/n618/news/view/33796/14863/
941
+ *
942
+ * @param {integer} d Day of month
943
+ * @param {integer} m Month of year (zero index)
944
+ * @param {integer} y Year
945
+ * @return {string}
946
+ * @private
947
+ */
948
+ _htmlWeekOfYear: function ( d, m, y ) {
949
+ var date = new Date( y, m, d, 0, 0, 0, 0 );
950
+
951
+ // First week of the year always has 4th January in it
952
+ date.setDate( date.getDate() + 4 - (date.getDay() || 7) );
953
+
954
+ var oneJan = new Date( y, 0, 1 );
955
+ var weekNum = Math.ceil( ( ( (date - oneJan) / 86400000) + 1)/7 );
956
+
957
+ return '<td class="'+this.c.classPrefix+'-week">' + weekNum + '</td>';
958
+ },
959
+
960
+ /**
961
+ * Check if the instance has a date object value - it might be null.
962
+ * If is doesn't set one to now.
963
+ * @returns A Date object
964
+ * @private
965
+ */
966
+ _needValue: function () {
967
+ if ( ! this.s.d ) {
968
+ this.s.d = this._dateToUtc( new Date() );
969
+
970
+ if (! this.s.parts.time) {
971
+ this.s.d.setUTCHours(0);
972
+ this.s.d.setUTCMinutes(0);
973
+ this.s.d.setSeconds(0);
974
+ this.s.d.setMilliseconds(0);
975
+ }
976
+ }
977
+
978
+ return this.s.d;
979
+ },
980
+
981
+ /**
982
+ * Create option elements from a range in an array
983
+ *
984
+ * @param {string} selector Class name unique to the select element to use
985
+ * @param {array} values Array of values
986
+ * @param {array} [labels] Array of labels. If given must be the same
987
+ * length as the values parameter.
988
+ * @private
989
+ */
990
+ _options: function ( selector, values, labels ) {
991
+ if ( ! labels ) {
992
+ labels = values;
993
+ }
994
+
995
+ var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector);
996
+ select.empty();
997
+
998
+ for ( var i=0, ien=values.length ; i<ien ; i++ ) {
999
+ select.append( '<option value="'+values[i]+'">'+labels[i]+'</option>' );
1000
+ }
1001
+ },
1002
+
1003
+ /**
1004
+ * Set an option and update the option's span pair (since the select element
1005
+ * has opacity 0 for styling)
1006
+ *
1007
+ * @param {string} selector Class name unique to the select element to use
1008
+ * @param {*} val Value to set
1009
+ * @private
1010
+ */
1011
+ _optionSet: function ( selector, val ) {
1012
+ var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector);
1013
+ var span = select.parent().children('span');
1014
+
1015
+ select.val( val );
1016
+
1017
+ var selected = select.find('option:selected');
1018
+ span.html( selected.length !== 0 ?
1019
+ selected.text() :
1020
+ this.c.i18n.unknown
1021
+ );
1022
+ },
1023
+
1024
+ /**
1025
+ * Create time options list.
1026
+ *
1027
+ * @param {string} unit Time unit - hours, minutes or seconds
1028
+ * @param {integer} count Count range - 12, 24 or 60
1029
+ * @param {integer} val Existing value for this unit
1030
+ * @param {integer[]} allowed Values allow for selection
1031
+ * @param {integer} range Override range
1032
+ * @private
1033
+ */
1034
+ _optionsTime: function ( unit, count, val, allowed, range ) {
1035
+ var classPrefix = this.c.classPrefix;
1036
+ var container = this.dom.container.find('div.'+classPrefix+'-'+unit);
1037
+ var i, j;
1038
+ var render = count === 12 ?
1039
+ function (i) { return i; } :
1040
+ this._pad;
1041
+ var classPrefix = this.c.classPrefix;
1042
+ var className = classPrefix+'-table';
1043
+ var i18n = this.c.i18n;
1044
+
1045
+ if ( ! container.length ) {
1046
+ return;
1047
+ }
1048
+
1049
+ var a = '';
1050
+ var span = 10;
1051
+ var button = function (value, label, className) {
1052
+ // Shift the value for PM
1053
+ if ( count === 12 && typeof value === 'number' ) {
1054
+ if (val >= 12 ) {
1055
+ value += 12;
1056
+ }
1057
+
1058
+ if (value == 12) {
1059
+ value = 0;
1060
+ }
1061
+ else if (value == 24) {
1062
+ value = 12;
1063
+ }
1064
+ }
1065
+
1066
+ var selected = val === value || (value === 'am' && val < 12) || (value === 'pm' && val >= 12) ?
1067
+ 'selected' :
1068
+ '';
1069
+
1070
+ if (typeof value === 'number' && allowed && $.inArray(value, allowed) === -1) {
1071
+ selected += ' disabled';
1072
+ }
1073
+
1074
+ if ( className ) {
1075
+ selected += ' '+className;
1076
+ }
1077
+
1078
+ return '<td class="selectable '+selected+'">' +
1079
+ '<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" data-unit="'+unit+'" data-value="'+value+ '">' +
1080
+ '<span>'+label+'</span>'+
1081
+ '</button>' +
1082
+ '</td>';
1083
+ }
1084
+
1085
+ if ( count === 12 ) {
1086
+ // Hours with AM/PM
1087
+ a += '<tr>';
1088
+
1089
+ for ( i=1 ; i<=6 ; i++ ) {
1090
+ a += button(i, render(i));
1091
+ }
1092
+ a += button('am', i18n.amPm[0]);
1093
+
1094
+ a += '</tr>';
1095
+ a += '<tr>';
1096
+
1097
+ for ( i=7 ; i<=12 ; i++ ) {
1098
+ a += button(i, render(i));
1099
+ }
1100
+ a += button('pm', i18n.amPm[1]);
1101
+ a += '</tr>';
1102
+
1103
+ span = 7;
1104
+ }
1105
+ else if ( count === 24 ) {
1106
+ // Hours - 24
1107
+ var c = 0;
1108
+ for (j=0 ; j<4 ; j++ ) {
1109
+ a += '<tr>';
1110
+ for ( i=0 ; i<6 ; i++ ) {
1111
+ a += button(c, render(c));
1112
+ c++;
1113
+ }
1114
+ a += '</tr>';
1115
+ }
1116
+
1117
+ span = 6;
1118
+ }
1119
+ else {
1120
+ // Minutes and seconds
1121
+ a += '<tr>';
1122
+ for (j=0 ; j<60 ; j+=10 ) {
1123
+ a += button(j, render(j), 'range');
1124
+ }
1125
+ a += '</tr>';
1126
+
1127
+ // Slight hack to allow for the different number of columns
1128
+ a += '</tbody></thead><table class="'+className+' '+className+'-nospace"><tbody>';
1129
+
1130
+ var start = range !== null
1131
+ ? range
1132
+ : val === -1
1133
+ ? 0
1134
+ : Math.floor( val / 10 )*10;
1135
+
1136
+ a += '<tr>';
1137
+ for (j=start+1 ; j<start+10 ; j++ ) {
1138
+ a += button(j, render(j));
1139
+ }
1140
+ a += '</tr>';
1141
+
1142
+ span = 6;
1143
+ }
1144
+
1145
+ container
1146
+ .empty()
1147
+ .append(
1148
+ '<table class="'+className+'">'+
1149
+ '<thead><tr><th colspan="'+span+'">'+
1150
+ i18n[unit] +
1151
+ '</th></tr></thead>'+
1152
+ '<tbody>'+
1153
+ a+
1154
+ '</tbody>'+
1155
+ '</table>'
1156
+ );
1157
+ },
1158
+
1159
+ /**
1160
+ * Create the options for the month and year
1161
+ *
1162
+ * @param {integer} year Year
1163
+ * @param {integer} month Month (starting at 0)
1164
+ * @private
1165
+ */
1166
+ _optionsTitle: function () {
1167
+ var i18n = this.c.i18n;
1168
+ var min = this.c.minDate;
1169
+ var max = this.c.maxDate;
1170
+ var minYear = min ? min.getFullYear() : null;
1171
+ var maxYear = max ? max.getFullYear() : null;
1172
+
1173
+ var i = minYear !== null ? minYear : new Date().getFullYear() - this.c.yearRange;
1174
+ var j = maxYear !== null ? maxYear : new Date().getFullYear() + this.c.yearRange;
1175
+
1176
+ this._options( 'month', this._range( 0, 11 ), i18n.months );
1177
+ this._options( 'year', this._range( i, j ) );
1178
+ },
1179
+
1180
+ /**
1181
+ * Simple two digit pad
1182
+ *
1183
+ * @param {integer} i Value that might need padding
1184
+ * @return {string|integer} Padded value
1185
+ * @private
1186
+ */
1187
+ _pad: function ( i ) {
1188
+ return i<10 ? '0'+i : i;
1189
+ },
1190
+
1191
+ /**
1192
+ * Position the calendar to look attached to the input element
1193
+ * @private
1194
+ */
1195
+ _position: function () {
1196
+ var offset = this.c.attachTo === 'input' ? this.dom.input.position() : this.dom.input.offset();
1197
+ var container = this.dom.container;
1198
+ var inputHeight = this.dom.input.outerHeight();
1199
+
1200
+ if (container.hasClass('inline')) {
1201
+ container.insertAfter( this.dom.input );
1202
+ return;
1203
+ }
1204
+
1205
+ if ( this.s.parts.date && this.s.parts.time && $(window).width() > 550 ) {
1206
+ container.addClass('horizontal');
1207
+ }
1208
+ else {
1209
+ container.removeClass('horizontal');
1210
+ }
1211
+
1212
+ if(this.c.attachTo === 'input') {
1213
+ container
1214
+ .css( {
1215
+ top: offset.top + inputHeight,
1216
+ left: offset.left
1217
+ } )
1218
+ .insertAfter( this.dom.input );
1219
+ }
1220
+ else {
1221
+ container
1222
+ .css( {
1223
+ top: offset.top + inputHeight,
1224
+ left: offset.left
1225
+ } )
1226
+ .appendTo( 'body' );
1227
+ }
1228
+
1229
+ var calHeight = container.outerHeight();
1230
+ var calWidth = container.outerWidth();
1231
+ var scrollTop = $(window).scrollTop();
1232
+
1233
+ // Correct to the bottom
1234
+ if ( offset.top + inputHeight + calHeight - scrollTop > $(window).height() ) {
1235
+ var newTop = offset.top - calHeight;
1236
+
1237
+ container.css( 'top', newTop < 0 ? 0 : newTop );
1238
+ }
1239
+
1240
+ // Correct to the right
1241
+ if ( calWidth + offset.left > $(window).width() ) {
1242
+ var newLeft = $(window).width() - calWidth;
1243
+
1244
+ // Account for elements which are inside a position absolute element
1245
+ if (this.c.attachTo === 'input') {
1246
+ newLeft -= $(container).offsetParent().offset().left;
1247
+ }
1248
+
1249
+ container.css( 'left', newLeft < 0 ? 0 : newLeft );
1250
+ }
1251
+ },
1252
+
1253
+ /**
1254
+ * Create a simple array with a range of values
1255
+ *
1256
+ * @param {integer} start Start value (inclusive)
1257
+ * @param {integer} end End value (inclusive)
1258
+ * @param {integer} [inc=1] Increment value
1259
+ * @return {array} Created array
1260
+ * @private
1261
+ */
1262
+ _range: function ( start, end, inc ) {
1263
+ var a = [];
1264
+
1265
+ if ( ! inc ) {
1266
+ inc = 1;
1267
+ }
1268
+
1269
+ for ( var i=start ; i<=end ; i+=inc ) {
1270
+ a.push( i );
1271
+ }
1272
+
1273
+ return a;
1274
+ },
1275
+
1276
+ /**
1277
+ * Redraw the calendar based on the display date - this is a destructive
1278
+ * operation
1279
+ *
1280
+ * @private
1281
+ */
1282
+ _setCalander: function () {
1283
+ if ( this.s.display ) {
1284
+ this.dom.calendar
1285
+ .empty()
1286
+ .append( this._htmlMonth(
1287
+ this.s.display.getUTCFullYear(),
1288
+ this.s.display.getUTCMonth()
1289
+ ) );
1290
+ }
1291
+ },
1292
+
1293
+ /**
1294
+ * Set the month and year for the calendar based on the current display date
1295
+ *
1296
+ * @private
1297
+ */
1298
+ _setTitle: function () {
1299
+ this._optionSet( 'month', this.s.display.getUTCMonth() );
1300
+ this._optionSet( 'year', this.s.display.getUTCFullYear() );
1301
+ },
1302
+
1303
+ /**
1304
+ * Set the time based on the current value of the widget
1305
+ *
1306
+ * @private
1307
+ */
1308
+ _setTime: function () {
1309
+ var that = this;
1310
+ var d = this.s.d;
1311
+
1312
+ // luxon uses different method names so need to be able to call them. This happens a few time later in this method too
1313
+ var luxDT = null
1314
+ if (dateLib && dateLib == window.luxon) {
1315
+ luxDT = dateLib.DateTime.fromJSDate(d);
1316
+ }
1317
+
1318
+ var hours = luxDT != null
1319
+ ? luxDT.hour
1320
+ : d
1321
+ ? d.getUTCHours()
1322
+ : -1;
1323
+
1324
+ var allowed = function ( prop ) { // Backwards compt with `Increment` option
1325
+ return that.c[prop+'Available'] ?
1326
+ that.c[prop+'Available'] :
1327
+ that._range( 0, 59, that.c[prop+'Increment'] );
1328
+ }
1329
+
1330
+ this._optionsTime( 'hours', this.s.parts.hours12 ? 12 : 24, hours, this.c.hoursAvailable )
1331
+ this._optionsTime(
1332
+ 'minutes',
1333
+ 60,
1334
+ luxDT != null
1335
+ ? luxDT.minute
1336
+ : d
1337
+ ? d.getUTCMinutes()
1338
+ : -1,
1339
+ allowed('minutes'),
1340
+ this.s.minutesRange
1341
+ );
1342
+ this._optionsTime(
1343
+ 'seconds',
1344
+ 60,
1345
+ luxDT != null
1346
+ ? luxDT.second
1347
+ : d
1348
+ ? d.getSeconds()
1349
+ : -1,
1350
+ allowed('seconds'),
1351
+ this.s.secondsRange
1352
+ );
1353
+ },
1354
+
1355
+ /**
1356
+ * Show the widget and add events to the document required only while it
1357
+ * is displayed
1358
+ *
1359
+ * @private
1360
+ */
1361
+ _show: function () {
1362
+ var that = this;
1363
+ var namespace = this.s.namespace;
1364
+
1365
+ this._position();
1366
+
1367
+ // Need to reposition on scroll
1368
+ $(window).on( 'scroll.'+namespace+' resize.'+namespace, function () {
1369
+ that._position();
1370
+ } );
1371
+
1372
+ $('div.DTE_Body_Content').on( 'scroll.'+namespace, function () {
1373
+ that._position();
1374
+ } );
1375
+
1376
+ $('div.dataTables_scrollBody').on( 'scroll.'+namespace, function () {
1377
+ that._position();
1378
+ } );
1379
+
1380
+ var offsetParent = this.dom.input[0].offsetParent;
1381
+
1382
+ if ( offsetParent !== document.body ) {
1383
+ $(offsetParent).on( 'scroll.'+namespace, function () {
1384
+ that._position();
1385
+ } );
1386
+ }
1387
+
1388
+ // On tab focus will move to a different field (no keyboard navigation
1389
+ // in the date picker - this might need to be changed).
1390
+ $(document).on( 'keydown.'+namespace, function (e) {
1391
+ if (
1392
+ e.keyCode === 9 || // tab
1393
+ e.keyCode === 27 || // esc
1394
+ e.keyCode === 13 // return
1395
+ ) {
1396
+ that._hide();
1397
+ }
1398
+ } );
1399
+
1400
+ // Hide if clicking outside of the widget - but in a different click
1401
+ // event from the one that was used to trigger the show (bubble and
1402
+ // inline)
1403
+ setTimeout( function () {
1404
+ $('body').on( 'click.'+namespace, function (e) {
1405
+ var parents = $(e.target).parents();
1406
+
1407
+ if ( ! parents.filter( that.dom.container ).length && e.target !== that.dom.input[0] ) {
1408
+ that._hide();
1409
+ }
1410
+ } );
1411
+ }, 10 );
1412
+ },
1413
+
1414
+ /**
1415
+ * Write the formatted string to the input element this control is attached
1416
+ * to
1417
+ *
1418
+ * @private
1419
+ */
1420
+ _writeOutput: function ( focus ) {
1421
+ var date = this.s.d;
1422
+ var out = '';
1423
+
1424
+ // Use moment, dayjs or luxon if possible - otherwise it must be ISO8601 (or the
1425
+ // constructor would have thrown an error)
1426
+ // luxon uses different method names so need to be able to call them.
1427
+ if (date) {
1428
+ out = dateLib && dateLib == window.luxon
1429
+ ? dateLib.DateTime.fromJSDate(this.s.d).toFormat(this.c.format)
1430
+ : dateLib ?
1431
+ dateLib.utc( date, undefined, this.c.locale, this.c.strict ).format( this.c.format ) :
1432
+ date.getUTCFullYear() +'-'+
1433
+ this._pad(date.getUTCMonth() + 1) +'-'+
1434
+ this._pad(date.getUTCDate());
1435
+ }
1436
+
1437
+ this.dom.input
1438
+ .val( out )
1439
+ .trigger('change', {write: date});
1440
+
1441
+ if ( this.dom.input.attr('type') === 'hidden' ) {
1442
+ this.val(out, false);
1443
+ }
1444
+
1445
+ if ( focus ) {
1446
+ this.dom.input.focus();
1447
+ }
1448
+ }
1449
+ } );
1450
+
1451
+ /**
1452
+ * Use a specificmoment compatible date library
1453
+ */
1454
+ DateTime.use = function (lib) {
1455
+ dateLib = lib;
1456
+ };
1457
+
1458
+ /**
1459
+ * For generating unique namespaces
1460
+ *
1461
+ * @type {Number}
1462
+ * @private
1463
+ */
1464
+ DateTime._instance = 0;
1465
+
1466
+ /**
1467
+ * Defaults for the date time picker
1468
+ *
1469
+ * @type {Object}
1470
+ */
1471
+ DateTime.defaults = {
1472
+ attachTo: 'body',
1473
+
1474
+ buttons: {
1475
+ clear: false,
1476
+ today: false
1477
+ },
1478
+
1479
+ // Not documented - could be an internal property
1480
+ classPrefix: 'dt-datetime',
1481
+
1482
+ // function or array of ints
1483
+ disableDays: null,
1484
+
1485
+ // first day of the week (0: Sunday, 1: Monday, etc)
1486
+ firstDay: 1,
1487
+
1488
+ format: 'YYYY-MM-DD',
1489
+
1490
+ hoursAvailable: null,
1491
+
1492
+ i18n: {
1493
+ clear: 'Clear',
1494
+ previous: 'Previous',
1495
+ next: 'Next',
1496
+ months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
1497
+ weekdays: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
1498
+ amPm: [ 'am', 'pm' ],
1499
+ hours: 'Hour',
1500
+ minutes: 'Minute',
1501
+ seconds: 'Second',
1502
+ unknown: '-',
1503
+ today: 'Today'
1504
+ },
1505
+
1506
+ maxDate: null,
1507
+
1508
+ minDate: null,
1509
+
1510
+ minutesAvailable: null,
1511
+
1512
+ minutesIncrement: 1, // deprecated
1513
+
1514
+ strict: true,
1515
+
1516
+ locale: 'en',
1517
+
1518
+ onChange: function () {},
1519
+
1520
+ secondsAvailable: null,
1521
+
1522
+ secondsIncrement: 1, // deprecated
1523
+
1524
+ // show the ISO week number at the head of the row
1525
+ showWeekNumber: false,
1526
+
1527
+ // overruled by max / min date
1528
+ yearRange: 25
1529
+ };
1530
+
1531
+ DateTime.version = '1.2.0';
1532
+
1533
+ // Global export - if no conflicts
1534
+ if (! window.DateTime) {
1535
+ window.DateTime = DateTime;
1536
+ }
1537
+
1538
+ // Make available via jQuery
1539
+ $.fn.dtDateTime = function (options) {
1540
+ return this.each(function() {
1541
+ new DateTime(this, options);
1542
+ });
1543
+ }
1544
+
1545
+ // Attach to DataTables if present
1546
+ if ($.fn.dataTable) {
1547
+ $.fn.dataTable.DateTime = DateTime;
1548
+ $.fn.DataTable.DateTime = DateTime;
1549
+
1550
+ if ($.fn.dataTable.Editor) {
1551
+ $.fn.dataTable.Editor.DateTime = DateTime;
1552
+ }
1553
+ }
1554
+
1555
+
1556
+ export default DateTime;