Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ignitetch
GitHub Repository: ignitetch/advphishing
Path: blob/master/sites/tiktok/vendor/daterangepicker/daterangepicker.js
740 views
1
/**
2
* @version: 2.1.25
3
* @author: Dan Grossman http://www.dangrossman.info/
4
* @copyright: Copyright (c) 2012-2017 Dan Grossman. All rights reserved.
5
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
6
* @website: http://www.daterangepicker.com/
7
*/
8
// Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js
9
(function (root, factory) {
10
if (typeof define === 'function' && define.amd) {
11
// AMD. Make globaly available as well
12
define(['moment', 'jquery'], function (moment, jquery) {
13
if (!jquery.fn) jquery.fn = {}; // webpack server rendering
14
return (root.daterangepicker = factory(moment, jquery));
15
});
16
} else if (typeof module === 'object' && module.exports) {
17
// Node / Browserify
18
//isomorphic issue
19
var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined;
20
if (!jQuery) {
21
jQuery = require('jquery');
22
if (!jQuery.fn) jQuery.fn = {};
23
}
24
var moment = (typeof window != 'undefined' && typeof window.moment != 'undefined') ? window.moment : require('moment');
25
module.exports = factory(moment, jQuery);
26
} else {
27
// Browser globals
28
root.daterangepicker = factory(root.moment, root.jQuery);
29
}
30
}(this, function(moment, $) {
31
var DateRangePicker = function(element, options, cb) {
32
33
//default settings for options
34
this.parentEl = 'body';
35
this.element = $(element);
36
this.startDate = moment().startOf('day');
37
this.endDate = moment().endOf('day');
38
this.minDate = false;
39
this.maxDate = false;
40
this.dateLimit = false;
41
this.autoApply = false;
42
this.singleDatePicker = false;
43
this.showDropdowns = false;
44
this.showWeekNumbers = false;
45
this.showISOWeekNumbers = false;
46
this.showCustomRangeLabel = true;
47
this.timePicker = false;
48
this.timePicker24Hour = false;
49
this.timePickerIncrement = 1;
50
this.timePickerSeconds = false;
51
this.linkedCalendars = true;
52
this.autoUpdateInput = true;
53
this.alwaysShowCalendars = false;
54
this.ranges = {};
55
56
this.opens = 'right';
57
if (this.element.hasClass('pull-right'))
58
this.opens = 'left';
59
60
this.drops = 'down';
61
if (this.element.hasClass('dropup'))
62
this.drops = 'up';
63
64
this.buttonClasses = 'btn btn-sm';
65
this.applyClass = 'btn-success';
66
this.cancelClass = 'btn-default';
67
68
this.locale = {
69
direction: 'ltr',
70
format: moment.localeData().longDateFormat('L'),
71
separator: ' - ',
72
applyLabel: 'Apply',
73
cancelLabel: 'Cancel',
74
weekLabel: 'W',
75
customRangeLabel: 'Custom Range',
76
daysOfWeek: moment.weekdaysMin(),
77
monthNames: moment.monthsShort(),
78
firstDay: moment.localeData().firstDayOfWeek()
79
};
80
81
this.callback = function() { };
82
83
//some state information
84
this.isShowing = false;
85
this.leftCalendar = {};
86
this.rightCalendar = {};
87
88
//custom options from user
89
if (typeof options !== 'object' || options === null)
90
options = {};
91
92
//allow setting options with data attributes
93
//data-api options will be overwritten with custom javascript options
94
options = $.extend(this.element.data(), options);
95
96
//html template for the picker UI
97
if (typeof options.template !== 'string' && !(options.template instanceof $))
98
options.template = '<div class="daterangepicker dropdown-menu">' +
99
'<div class="calendar left">' +
100
'<div class="daterangepicker_input">' +
101
'<input class="input-mini form-control" type="text" name="daterangepicker_start" value="" />' +
102
'<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
103
'<div class="calendar-time">' +
104
'<div></div>' +
105
'<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
106
'</div>' +
107
'</div>' +
108
'<div class="calendar-table"></div>' +
109
'</div>' +
110
'<div class="calendar right">' +
111
'<div class="daterangepicker_input">' +
112
'<input class="input-mini form-control" type="text" name="daterangepicker_end" value="" />' +
113
'<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
114
'<div class="calendar-time">' +
115
'<div></div>' +
116
'<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
117
'</div>' +
118
'</div>' +
119
'<div class="calendar-table"></div>' +
120
'</div>' +
121
'<div class="ranges">' +
122
'<div class="range_inputs">' +
123
'<button class="applyBtn" disabled="disabled" type="button"></button> ' +
124
'<button class="cancelBtn" type="button"></button>' +
125
'</div>' +
126
'</div>' +
127
'</div>';
128
129
this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
130
this.container = $(options.template).appendTo(this.parentEl);
131
132
//
133
// handle all the possible options overriding defaults
134
//
135
136
if (typeof options.locale === 'object') {
137
138
if (typeof options.locale.direction === 'string')
139
this.locale.direction = options.locale.direction;
140
141
if (typeof options.locale.format === 'string')
142
this.locale.format = options.locale.format;
143
144
if (typeof options.locale.separator === 'string')
145
this.locale.separator = options.locale.separator;
146
147
if (typeof options.locale.daysOfWeek === 'object')
148
this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
149
150
if (typeof options.locale.monthNames === 'object')
151
this.locale.monthNames = options.locale.monthNames.slice();
152
153
if (typeof options.locale.firstDay === 'number')
154
this.locale.firstDay = options.locale.firstDay;
155
156
if (typeof options.locale.applyLabel === 'string')
157
this.locale.applyLabel = options.locale.applyLabel;
158
159
if (typeof options.locale.cancelLabel === 'string')
160
this.locale.cancelLabel = options.locale.cancelLabel;
161
162
if (typeof options.locale.weekLabel === 'string')
163
this.locale.weekLabel = options.locale.weekLabel;
164
165
if (typeof options.locale.customRangeLabel === 'string'){
166
//Support unicode chars in the custom range name.
167
var elem = document.createElement('textarea');
168
elem.innerHTML = options.locale.customRangeLabel;
169
var rangeHtml = elem.value;
170
this.locale.customRangeLabel = rangeHtml;
171
}
172
}
173
this.container.addClass(this.locale.direction);
174
175
if (typeof options.startDate === 'string')
176
this.startDate = moment(options.startDate, this.locale.format);
177
178
if (typeof options.endDate === 'string')
179
this.endDate = moment(options.endDate, this.locale.format);
180
181
if (typeof options.minDate === 'string')
182
this.minDate = moment(options.minDate, this.locale.format);
183
184
if (typeof options.maxDate === 'string')
185
this.maxDate = moment(options.maxDate, this.locale.format);
186
187
if (typeof options.startDate === 'object')
188
this.startDate = moment(options.startDate);
189
190
if (typeof options.endDate === 'object')
191
this.endDate = moment(options.endDate);
192
193
if (typeof options.minDate === 'object')
194
this.minDate = moment(options.minDate);
195
196
if (typeof options.maxDate === 'object')
197
this.maxDate = moment(options.maxDate);
198
199
// sanity check for bad options
200
if (this.minDate && this.startDate.isBefore(this.minDate))
201
this.startDate = this.minDate.clone();
202
203
// sanity check for bad options
204
if (this.maxDate && this.endDate.isAfter(this.maxDate))
205
this.endDate = this.maxDate.clone();
206
207
if (typeof options.applyClass === 'string')
208
this.applyClass = options.applyClass;
209
210
if (typeof options.cancelClass === 'string')
211
this.cancelClass = options.cancelClass;
212
213
if (typeof options.dateLimit === 'object')
214
this.dateLimit = options.dateLimit;
215
216
if (typeof options.opens === 'string')
217
this.opens = options.opens;
218
219
if (typeof options.drops === 'string')
220
this.drops = options.drops;
221
222
if (typeof options.showWeekNumbers === 'boolean')
223
this.showWeekNumbers = options.showWeekNumbers;
224
225
if (typeof options.showISOWeekNumbers === 'boolean')
226
this.showISOWeekNumbers = options.showISOWeekNumbers;
227
228
if (typeof options.buttonClasses === 'string')
229
this.buttonClasses = options.buttonClasses;
230
231
if (typeof options.buttonClasses === 'object')
232
this.buttonClasses = options.buttonClasses.join(' ');
233
234
if (typeof options.showDropdowns === 'boolean')
235
this.showDropdowns = options.showDropdowns;
236
237
if (typeof options.showCustomRangeLabel === 'boolean')
238
this.showCustomRangeLabel = options.showCustomRangeLabel;
239
240
if (typeof options.singleDatePicker === 'boolean') {
241
this.singleDatePicker = options.singleDatePicker;
242
if (this.singleDatePicker)
243
this.endDate = this.startDate.clone();
244
}
245
246
if (typeof options.timePicker === 'boolean')
247
this.timePicker = options.timePicker;
248
249
if (typeof options.timePickerSeconds === 'boolean')
250
this.timePickerSeconds = options.timePickerSeconds;
251
252
if (typeof options.timePickerIncrement === 'number')
253
this.timePickerIncrement = options.timePickerIncrement;
254
255
if (typeof options.timePicker24Hour === 'boolean')
256
this.timePicker24Hour = options.timePicker24Hour;
257
258
if (typeof options.autoApply === 'boolean')
259
this.autoApply = options.autoApply;
260
261
if (typeof options.autoUpdateInput === 'boolean')
262
this.autoUpdateInput = options.autoUpdateInput;
263
264
if (typeof options.linkedCalendars === 'boolean')
265
this.linkedCalendars = options.linkedCalendars;
266
267
if (typeof options.isInvalidDate === 'function')
268
this.isInvalidDate = options.isInvalidDate;
269
270
if (typeof options.isCustomDate === 'function')
271
this.isCustomDate = options.isCustomDate;
272
273
if (typeof options.alwaysShowCalendars === 'boolean')
274
this.alwaysShowCalendars = options.alwaysShowCalendars;
275
276
// update day names order to firstDay
277
if (this.locale.firstDay != 0) {
278
var iterator = this.locale.firstDay;
279
while (iterator > 0) {
280
this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
281
iterator--;
282
}
283
}
284
285
var start, end, range;
286
287
//if no start/end dates set, check if an input element contains initial values
288
if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
289
if ($(this.element).is('input[type=text]')) {
290
var val = $(this.element).val(),
291
split = val.split(this.locale.separator);
292
293
start = end = null;
294
295
if (split.length == 2) {
296
start = moment(split[0], this.locale.format);
297
end = moment(split[1], this.locale.format);
298
} else if (this.singleDatePicker && val !== "") {
299
start = moment(val, this.locale.format);
300
end = moment(val, this.locale.format);
301
}
302
if (start !== null && end !== null) {
303
this.setStartDate(start);
304
this.setEndDate(end);
305
}
306
}
307
}
308
309
if (typeof options.ranges === 'object') {
310
for (range in options.ranges) {
311
312
if (typeof options.ranges[range][0] === 'string')
313
start = moment(options.ranges[range][0], this.locale.format);
314
else
315
start = moment(options.ranges[range][0]);
316
317
if (typeof options.ranges[range][1] === 'string')
318
end = moment(options.ranges[range][1], this.locale.format);
319
else
320
end = moment(options.ranges[range][1]);
321
322
// If the start or end date exceed those allowed by the minDate or dateLimit
323
// options, shorten the range to the allowable period.
324
if (this.minDate && start.isBefore(this.minDate))
325
start = this.minDate.clone();
326
327
var maxDate = this.maxDate;
328
if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate))
329
maxDate = start.clone().add(this.dateLimit);
330
if (maxDate && end.isAfter(maxDate))
331
end = maxDate.clone();
332
333
// If the end of the range is before the minimum or the start of the range is
334
// after the maximum, don't display this range option at all.
335
if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day'))
336
|| (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))
337
continue;
338
339
//Support unicode chars in the range names.
340
var elem = document.createElement('textarea');
341
elem.innerHTML = range;
342
var rangeHtml = elem.value;
343
344
this.ranges[rangeHtml] = [start, end];
345
}
346
347
var list = '<ul>';
348
for (range in this.ranges) {
349
list += '<li data-range-key="' + range + '">' + range + '</li>';
350
}
351
if (this.showCustomRangeLabel) {
352
list += '<li data-range-key="' + this.locale.customRangeLabel + '">' + this.locale.customRangeLabel + '</li>';
353
}
354
list += '</ul>';
355
this.container.find('.ranges').prepend(list);
356
}
357
358
if (typeof cb === 'function') {
359
this.callback = cb;
360
}
361
362
if (!this.timePicker) {
363
this.startDate = this.startDate.startOf('day');
364
this.endDate = this.endDate.endOf('day');
365
this.container.find('.calendar-time').hide();
366
}
367
368
//can't be used together for now
369
if (this.timePicker && this.autoApply)
370
this.autoApply = false;
371
372
if (this.autoApply && typeof options.ranges !== 'object') {
373
this.container.find('.ranges').hide();
374
} else if (this.autoApply) {
375
this.container.find('.applyBtn, .cancelBtn').addClass('hide');
376
}
377
378
if (this.singleDatePicker) {
379
this.container.addClass('single');
380
this.container.find('.calendar.left').addClass('single');
381
this.container.find('.calendar.left').show();
382
this.container.find('.calendar.right').hide();
383
this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide();
384
if (this.timePicker) {
385
this.container.find('.ranges ul').hide();
386
} else {
387
this.container.find('.ranges').hide();
388
}
389
}
390
391
if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) {
392
this.container.addClass('show-calendar');
393
}
394
395
this.container.addClass('opens' + this.opens);
396
397
//swap the position of the predefined ranges if opens right
398
if (typeof options.ranges !== 'undefined' && this.opens == 'right') {
399
this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() );
400
}
401
402
//apply CSS classes and labels to buttons
403
this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses);
404
if (this.applyClass.length)
405
this.container.find('.applyBtn').addClass(this.applyClass);
406
if (this.cancelClass.length)
407
this.container.find('.cancelBtn').addClass(this.cancelClass);
408
this.container.find('.applyBtn').html(this.locale.applyLabel);
409
this.container.find('.cancelBtn').html(this.locale.cancelLabel);
410
411
//
412
// event listeners
413
//
414
415
this.container.find('.calendar')
416
.on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
417
.on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
418
.on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
419
.on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
420
.on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
421
.on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))
422
.on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
423
.on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))
424
.on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this))
425
.on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this))
426
.on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this))
427
.on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this));
428
429
this.container.find('.ranges')
430
.on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
431
.on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
432
.on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
433
.on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this))
434
.on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
435
436
if (this.element.is('input') || this.element.is('button')) {
437
this.element.on({
438
'click.daterangepicker': $.proxy(this.show, this),
439
'focus.daterangepicker': $.proxy(this.show, this),
440
'keyup.daterangepicker': $.proxy(this.elementChanged, this),
441
'keydown.daterangepicker': $.proxy(this.keydown, this)
442
});
443
} else {
444
this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
445
}
446
447
//
448
// if attached to a text input, set the initial value
449
//
450
451
if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
452
this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
453
this.element.trigger('change');
454
} else if (this.element.is('input') && this.autoUpdateInput) {
455
this.element.val(this.startDate.format(this.locale.format));
456
this.element.trigger('change');
457
}
458
459
};
460
461
DateRangePicker.prototype = {
462
463
constructor: DateRangePicker,
464
465
setStartDate: function(startDate) {
466
if (typeof startDate === 'string')
467
this.startDate = moment(startDate, this.locale.format);
468
469
if (typeof startDate === 'object')
470
this.startDate = moment(startDate);
471
472
if (!this.timePicker)
473
this.startDate = this.startDate.startOf('day');
474
475
if (this.timePicker && this.timePickerIncrement)
476
this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
477
478
if (this.minDate && this.startDate.isBefore(this.minDate)) {
479
this.startDate = this.minDate.clone();
480
if (this.timePicker && this.timePickerIncrement)
481
this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
482
}
483
484
if (this.maxDate && this.startDate.isAfter(this.maxDate)) {
485
this.startDate = this.maxDate.clone();
486
if (this.timePicker && this.timePickerIncrement)
487
this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
488
}
489
490
if (!this.isShowing)
491
this.updateElement();
492
493
this.updateMonthsInView();
494
},
495
496
setEndDate: function(endDate) {
497
if (typeof endDate === 'string')
498
this.endDate = moment(endDate, this.locale.format);
499
500
if (typeof endDate === 'object')
501
this.endDate = moment(endDate);
502
503
if (!this.timePicker)
504
this.endDate = this.endDate.endOf('day');
505
506
if (this.timePicker && this.timePickerIncrement)
507
this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
508
509
if (this.endDate.isBefore(this.startDate))
510
this.endDate = this.startDate.clone();
511
512
if (this.maxDate && this.endDate.isAfter(this.maxDate))
513
this.endDate = this.maxDate.clone();
514
515
if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate))
516
this.endDate = this.startDate.clone().add(this.dateLimit);
517
518
this.previousRightTime = this.endDate.clone();
519
520
if (!this.isShowing)
521
this.updateElement();
522
523
this.updateMonthsInView();
524
},
525
526
isInvalidDate: function() {
527
return false;
528
},
529
530
isCustomDate: function() {
531
return false;
532
},
533
534
updateView: function() {
535
if (this.timePicker) {
536
this.renderTimePicker('left');
537
this.renderTimePicker('right');
538
if (!this.endDate) {
539
this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled');
540
} else {
541
this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled');
542
}
543
}
544
if (this.endDate) {
545
this.container.find('input[name="daterangepicker_end"]').removeClass('active');
546
this.container.find('input[name="daterangepicker_start"]').addClass('active');
547
} else {
548
this.container.find('input[name="daterangepicker_end"]').addClass('active');
549
this.container.find('input[name="daterangepicker_start"]').removeClass('active');
550
}
551
this.updateMonthsInView();
552
this.updateCalendars();
553
this.updateFormInputs();
554
},
555
556
updateMonthsInView: function() {
557
if (this.endDate) {
558
559
//if both dates are visible already, do nothing
560
if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month &&
561
(this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
562
&&
563
(this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
564
) {
565
return;
566
}
567
568
this.leftCalendar.month = this.startDate.clone().date(2);
569
if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) {
570
this.rightCalendar.month = this.endDate.clone().date(2);
571
} else {
572
this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
573
}
574
575
} else {
576
if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) {
577
this.leftCalendar.month = this.startDate.clone().date(2);
578
this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
579
}
580
}
581
if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) {
582
this.rightCalendar.month = this.maxDate.clone().date(2);
583
this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month');
584
}
585
},
586
587
updateCalendars: function() {
588
589
if (this.timePicker) {
590
var hour, minute, second;
591
if (this.endDate) {
592
hour = parseInt(this.container.find('.left .hourselect').val(), 10);
593
minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
594
second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
595
if (!this.timePicker24Hour) {
596
var ampm = this.container.find('.left .ampmselect').val();
597
if (ampm === 'PM' && hour < 12)
598
hour += 12;
599
if (ampm === 'AM' && hour === 12)
600
hour = 0;
601
}
602
} else {
603
hour = parseInt(this.container.find('.right .hourselect').val(), 10);
604
minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
605
second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
606
if (!this.timePicker24Hour) {
607
var ampm = this.container.find('.right .ampmselect').val();
608
if (ampm === 'PM' && hour < 12)
609
hour += 12;
610
if (ampm === 'AM' && hour === 12)
611
hour = 0;
612
}
613
}
614
this.leftCalendar.month.hour(hour).minute(minute).second(second);
615
this.rightCalendar.month.hour(hour).minute(minute).second(second);
616
}
617
618
this.renderCalendar('left');
619
this.renderCalendar('right');
620
621
//highlight any predefined range matching the current start and end dates
622
this.container.find('.ranges li').removeClass('active');
623
if (this.endDate == null) return;
624
625
this.calculateChosenLabel();
626
},
627
628
renderCalendar: function(side) {
629
630
//
631
// Build the matrix of dates that will populate the calendar
632
//
633
634
var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar;
635
var month = calendar.month.month();
636
var year = calendar.month.year();
637
var hour = calendar.month.hour();
638
var minute = calendar.month.minute();
639
var second = calendar.month.second();
640
var daysInMonth = moment([year, month]).daysInMonth();
641
var firstDay = moment([year, month, 1]);
642
var lastDay = moment([year, month, daysInMonth]);
643
var lastMonth = moment(firstDay).subtract(1, 'month').month();
644
var lastYear = moment(firstDay).subtract(1, 'month').year();
645
var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
646
var dayOfWeek = firstDay.day();
647
648
//initialize a 6 rows x 7 columns array for the calendar
649
var calendar = [];
650
calendar.firstDay = firstDay;
651
calendar.lastDay = lastDay;
652
653
for (var i = 0; i < 6; i++) {
654
calendar[i] = [];
655
}
656
657
//populate the calendar with date objects
658
var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
659
if (startDay > daysInLastMonth)
660
startDay -= 7;
661
662
if (dayOfWeek == this.locale.firstDay)
663
startDay = daysInLastMonth - 6;
664
665
var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]);
666
667
var col, row;
668
for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
669
if (i > 0 && col % 7 === 0) {
670
col = 0;
671
row++;
672
}
673
calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second);
674
curDate.hour(12);
675
676
if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
677
calendar[row][col] = this.minDate.clone();
678
}
679
680
if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
681
calendar[row][col] = this.maxDate.clone();
682
}
683
684
}
685
686
//make the calendar object available to hoverDate/clickDate
687
if (side == 'left') {
688
this.leftCalendar.calendar = calendar;
689
} else {
690
this.rightCalendar.calendar = calendar;
691
}
692
693
//
694
// Display the calendar
695
//
696
697
var minDate = side == 'left' ? this.minDate : this.startDate;
698
var maxDate = this.maxDate;
699
var selected = side == 'left' ? this.startDate : this.endDate;
700
var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'};
701
702
var html = '<table class="table-condensed">';
703
html += '<thead>';
704
html += '<tr>';
705
706
// add empty cell for week number
707
if (this.showWeekNumbers || this.showISOWeekNumbers)
708
html += '<th></th>';
709
710
if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {
711
html += '<th class="prev available"><i class="fa fa-' + arrow.left + ' glyphicon glyphicon-' + arrow.left + '"></i></th>';
712
} else {
713
html += '<th></th>';
714
}
715
716
var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
717
718
if (this.showDropdowns) {
719
var currentMonth = calendar[1][1].month();
720
var currentYear = calendar[1][1].year();
721
var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
722
var minYear = (minDate && minDate.year()) || (currentYear - 50);
723
var inMinYear = currentYear == minYear;
724
var inMaxYear = currentYear == maxYear;
725
726
var monthHtml = '<select class="monthselect">';
727
for (var m = 0; m < 12; m++) {
728
if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
729
monthHtml += "<option value='" + m + "'" +
730
(m === currentMonth ? " selected='selected'" : "") +
731
">" + this.locale.monthNames[m] + "</option>";
732
} else {
733
monthHtml += "<option value='" + m + "'" +
734
(m === currentMonth ? " selected='selected'" : "") +
735
" disabled='disabled'>" + this.locale.monthNames[m] + "</option>";
736
}
737
}
738
monthHtml += "</select>";
739
740
var yearHtml = '<select class="yearselect">';
741
for (var y = minYear; y <= maxYear; y++) {
742
yearHtml += '<option value="' + y + '"' +
743
(y === currentYear ? ' selected="selected"' : '') +
744
'>' + y + '</option>';
745
}
746
yearHtml += '</select>';
747
748
dateHtml = monthHtml + yearHtml;
749
}
750
751
html += '<th colspan="5" class="month">' + dateHtml + '</th>';
752
if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {
753
html += '<th class="next available"><i class="fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '"></i></th>';
754
} else {
755
html += '<th></th>';
756
}
757
758
html += '</tr>';
759
html += '<tr>';
760
761
// add week number label
762
if (this.showWeekNumbers || this.showISOWeekNumbers)
763
html += '<th class="week">' + this.locale.weekLabel + '</th>';
764
765
$.each(this.locale.daysOfWeek, function(index, dayOfWeek) {
766
html += '<th>' + dayOfWeek + '</th>';
767
});
768
769
html += '</tr>';
770
html += '</thead>';
771
html += '<tbody>';
772
773
//adjust maxDate to reflect the dateLimit setting in order to
774
//grey out end dates beyond the dateLimit
775
if (this.endDate == null && this.dateLimit) {
776
var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day');
777
if (!maxDate || maxLimit.isBefore(maxDate)) {
778
maxDate = maxLimit;
779
}
780
}
781
782
for (var row = 0; row < 6; row++) {
783
html += '<tr>';
784
785
// add week number
786
if (this.showWeekNumbers)
787
html += '<td class="week">' + calendar[row][0].week() + '</td>';
788
else if (this.showISOWeekNumbers)
789
html += '<td class="week">' + calendar[row][0].isoWeek() + '</td>';
790
791
for (var col = 0; col < 7; col++) {
792
793
var classes = [];
794
795
//highlight today's date
796
if (calendar[row][col].isSame(new Date(), "day"))
797
classes.push('today');
798
799
//highlight weekends
800
if (calendar[row][col].isoWeekday() > 5)
801
classes.push('weekend');
802
803
//grey out the dates in other months displayed at beginning and end of this calendar
804
if (calendar[row][col].month() != calendar[1][1].month())
805
classes.push('off');
806
807
//don't allow selection of dates before the minimum date
808
if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day'))
809
classes.push('off', 'disabled');
810
811
//don't allow selection of dates after the maximum date
812
if (maxDate && calendar[row][col].isAfter(maxDate, 'day'))
813
classes.push('off', 'disabled');
814
815
//don't allow selection of date if a custom function decides it's invalid
816
if (this.isInvalidDate(calendar[row][col]))
817
classes.push('off', 'disabled');
818
819
//highlight the currently selected start date
820
if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD'))
821
classes.push('active', 'start-date');
822
823
//highlight the currently selected end date
824
if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD'))
825
classes.push('active', 'end-date');
826
827
//highlight dates in-between the selected dates
828
if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate)
829
classes.push('in-range');
830
831
//apply custom classes for this date
832
var isCustom = this.isCustomDate(calendar[row][col]);
833
if (isCustom !== false) {
834
if (typeof isCustom === 'string')
835
classes.push(isCustom);
836
else
837
Array.prototype.push.apply(classes, isCustom);
838
}
839
840
var cname = '', disabled = false;
841
for (var i = 0; i < classes.length; i++) {
842
cname += classes[i] + ' ';
843
if (classes[i] == 'disabled')
844
disabled = true;
845
}
846
if (!disabled)
847
cname += 'available';
848
849
html += '<td class="' + cname.replace(/^\s+|\s+$/g, '') + '" data-title="' + 'r' + row + 'c' + col + '">' + calendar[row][col].date() + '</td>';
850
851
}
852
html += '</tr>';
853
}
854
855
html += '</tbody>';
856
html += '</table>';
857
858
this.container.find('.calendar.' + side + ' .calendar-table').html(html);
859
860
},
861
862
renderTimePicker: function(side) {
863
864
// Don't bother updating the time picker if it's currently disabled
865
// because an end date hasn't been clicked yet
866
if (side == 'right' && !this.endDate) return;
867
868
var html, selected, minDate, maxDate = this.maxDate;
869
870
if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate)))
871
maxDate = this.startDate.clone().add(this.dateLimit);
872
873
if (side == 'left') {
874
selected = this.startDate.clone();
875
minDate = this.minDate;
876
} else if (side == 'right') {
877
selected = this.endDate.clone();
878
minDate = this.startDate;
879
880
//Preserve the time already selected
881
var timeSelector = this.container.find('.calendar.right .calendar-time div');
882
if (timeSelector.html() != '') {
883
884
selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour());
885
selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute());
886
selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second());
887
888
if (!this.timePicker24Hour) {
889
var ampm = timeSelector.find('.ampmselect option:selected').val();
890
if (ampm === 'PM' && selected.hour() < 12)
891
selected.hour(selected.hour() + 12);
892
if (ampm === 'AM' && selected.hour() === 12)
893
selected.hour(0);
894
}
895
896
}
897
898
if (selected.isBefore(this.startDate))
899
selected = this.startDate.clone();
900
901
if (maxDate && selected.isAfter(maxDate))
902
selected = maxDate.clone();
903
904
}
905
906
//
907
// hours
908
//
909
910
html = '<select class="hourselect">';
911
912
var start = this.timePicker24Hour ? 0 : 1;
913
var end = this.timePicker24Hour ? 23 : 12;
914
915
for (var i = start; i <= end; i++) {
916
var i_in_24 = i;
917
if (!this.timePicker24Hour)
918
i_in_24 = selected.hour() >= 12 ? (i == 12 ? 12 : i + 12) : (i == 12 ? 0 : i);
919
920
var time = selected.clone().hour(i_in_24);
921
var disabled = false;
922
if (minDate && time.minute(59).isBefore(minDate))
923
disabled = true;
924
if (maxDate && time.minute(0).isAfter(maxDate))
925
disabled = true;
926
927
if (i_in_24 == selected.hour() && !disabled) {
928
html += '<option value="' + i + '" selected="selected">' + i + '</option>';
929
} else if (disabled) {
930
html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>';
931
} else {
932
html += '<option value="' + i + '">' + i + '</option>';
933
}
934
}
935
936
html += '</select> ';
937
938
//
939
// minutes
940
//
941
942
html += ': <select class="minuteselect">';
943
944
for (var i = 0; i < 60; i += this.timePickerIncrement) {
945
var padded = i < 10 ? '0' + i : i;
946
var time = selected.clone().minute(i);
947
948
var disabled = false;
949
if (minDate && time.second(59).isBefore(minDate))
950
disabled = true;
951
if (maxDate && time.second(0).isAfter(maxDate))
952
disabled = true;
953
954
if (selected.minute() == i && !disabled) {
955
html += '<option value="' + i + '" selected="selected">' + padded + '</option>';
956
} else if (disabled) {
957
html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>';
958
} else {
959
html += '<option value="' + i + '">' + padded + '</option>';
960
}
961
}
962
963
html += '</select> ';
964
965
//
966
// seconds
967
//
968
969
if (this.timePickerSeconds) {
970
html += ': <select class="secondselect">';
971
972
for (var i = 0; i < 60; i++) {
973
var padded = i < 10 ? '0' + i : i;
974
var time = selected.clone().second(i);
975
976
var disabled = false;
977
if (minDate && time.isBefore(minDate))
978
disabled = true;
979
if (maxDate && time.isAfter(maxDate))
980
disabled = true;
981
982
if (selected.second() == i && !disabled) {
983
html += '<option value="' + i + '" selected="selected">' + padded + '</option>';
984
} else if (disabled) {
985
html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>';
986
} else {
987
html += '<option value="' + i + '">' + padded + '</option>';
988
}
989
}
990
991
html += '</select> ';
992
}
993
994
//
995
// AM/PM
996
//
997
998
if (!this.timePicker24Hour) {
999
html += '<select class="ampmselect">';
1000
1001
var am_html = '';
1002
var pm_html = '';
1003
1004
if (minDate && selected.clone().hour(12).minute(0).second(0).isBefore(minDate))
1005
am_html = ' disabled="disabled" class="disabled"';
1006
1007
if (maxDate && selected.clone().hour(0).minute(0).second(0).isAfter(maxDate))
1008
pm_html = ' disabled="disabled" class="disabled"';
1009
1010
if (selected.hour() >= 12) {
1011
html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>';
1012
} else {
1013
html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>';
1014
}
1015
1016
html += '</select>';
1017
}
1018
1019
this.container.find('.calendar.' + side + ' .calendar-time div').html(html);
1020
1021
},
1022
1023
updateFormInputs: function() {
1024
1025
//ignore mouse movements while an above-calendar text input has focus
1026
if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1027
return;
1028
1029
this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format));
1030
if (this.endDate)
1031
this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format));
1032
1033
if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {
1034
this.container.find('button.applyBtn').removeAttr('disabled');
1035
} else {
1036
this.container.find('button.applyBtn').attr('disabled', 'disabled');
1037
}
1038
1039
},
1040
1041
move: function() {
1042
var parentOffset = { top: 0, left: 0 },
1043
containerTop;
1044
var parentRightEdge = $(window).width();
1045
if (!this.parentEl.is('body')) {
1046
parentOffset = {
1047
top: this.parentEl.offset().top - this.parentEl.scrollTop(),
1048
left: this.parentEl.offset().left - this.parentEl.scrollLeft()
1049
};
1050
parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
1051
}
1052
1053
if (this.drops == 'up')
1054
containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
1055
else
1056
containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
1057
this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup');
1058
1059
if (this.opens == 'left') {
1060
this.container.css({
1061
top: containerTop,
1062
right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),
1063
left: 'auto'
1064
});
1065
if (this.container.offset().left < 0) {
1066
this.container.css({
1067
right: 'auto',
1068
left: 9
1069
});
1070
}
1071
} else if (this.opens == 'center') {
1072
this.container.css({
1073
top: containerTop,
1074
left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
1075
- this.container.outerWidth() / 2,
1076
right: 'auto'
1077
});
1078
if (this.container.offset().left < 0) {
1079
this.container.css({
1080
right: 'auto',
1081
left: 9
1082
});
1083
}
1084
} else {
1085
this.container.css({
1086
top: containerTop,
1087
left: this.element.offset().left - parentOffset.left,
1088
right: 'auto'
1089
});
1090
if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
1091
this.container.css({
1092
left: 'auto',
1093
right: 0
1094
});
1095
}
1096
}
1097
},
1098
1099
show: function(e) {
1100
if (this.isShowing) return;
1101
1102
// Create a click proxy that is private to this instance of datepicker, for unbinding
1103
this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this);
1104
1105
// Bind global datepicker mousedown for hiding and
1106
$(document)
1107
.on('mousedown.daterangepicker', this._outsideClickProxy)
1108
// also support mobile devices
1109
.on('touchend.daterangepicker', this._outsideClickProxy)
1110
// also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
1111
.on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
1112
// and also close when focus changes to outside the picker (eg. tabbing between controls)
1113
.on('focusin.daterangepicker', this._outsideClickProxy);
1114
1115
// Reposition the picker if the window is resized while it's open
1116
$(window).on('resize.daterangepicker', $.proxy(function(e) { this.move(e); }, this));
1117
1118
this.oldStartDate = this.startDate.clone();
1119
this.oldEndDate = this.endDate.clone();
1120
this.previousRightTime = this.endDate.clone();
1121
1122
this.updateView();
1123
this.container.show();
1124
this.move();
1125
this.element.trigger('show.daterangepicker', this);
1126
this.isShowing = true;
1127
},
1128
1129
hide: function(e) {
1130
if (!this.isShowing) return;
1131
1132
//incomplete date selection, revert to last values
1133
if (!this.endDate) {
1134
this.startDate = this.oldStartDate.clone();
1135
this.endDate = this.oldEndDate.clone();
1136
}
1137
1138
//if a new date range was selected, invoke the user callback function
1139
if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
1140
this.callback(this.startDate, this.endDate, this.chosenLabel);
1141
1142
//if picker is attached to a text input, update it
1143
this.updateElement();
1144
1145
$(document).off('.daterangepicker');
1146
$(window).off('.daterangepicker');
1147
this.container.hide();
1148
this.element.trigger('hide.daterangepicker', this);
1149
this.isShowing = false;
1150
},
1151
1152
toggle: function(e) {
1153
if (this.isShowing) {
1154
this.hide();
1155
} else {
1156
this.show();
1157
}
1158
},
1159
1160
outsideClick: function(e) {
1161
var target = $(e.target);
1162
// if the page is clicked anywhere except within the daterangerpicker/button
1163
// itself then call this.hide()
1164
if (
1165
// ie modal dialog fix
1166
e.type == "focusin" ||
1167
target.closest(this.element).length ||
1168
target.closest(this.container).length ||
1169
target.closest('.calendar-table').length
1170
) return;
1171
this.hide();
1172
this.element.trigger('outsideClick.daterangepicker', this);
1173
},
1174
1175
showCalendars: function() {
1176
this.container.addClass('show-calendar');
1177
this.move();
1178
this.element.trigger('showCalendar.daterangepicker', this);
1179
},
1180
1181
hideCalendars: function() {
1182
this.container.removeClass('show-calendar');
1183
this.element.trigger('hideCalendar.daterangepicker', this);
1184
},
1185
1186
hoverRange: function(e) {
1187
1188
//ignore mouse movements while an above-calendar text input has focus
1189
if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1190
return;
1191
1192
var label = e.target.getAttribute('data-range-key');
1193
1194
if (label == this.locale.customRangeLabel) {
1195
this.updateView();
1196
} else {
1197
var dates = this.ranges[label];
1198
this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format));
1199
this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format));
1200
}
1201
1202
},
1203
1204
clickRange: function(e) {
1205
var label = e.target.getAttribute('data-range-key');
1206
this.chosenLabel = label;
1207
if (label == this.locale.customRangeLabel) {
1208
this.showCalendars();
1209
} else {
1210
var dates = this.ranges[label];
1211
this.startDate = dates[0];
1212
this.endDate = dates[1];
1213
1214
if (!this.timePicker) {
1215
this.startDate.startOf('day');
1216
this.endDate.endOf('day');
1217
}
1218
1219
if (!this.alwaysShowCalendars)
1220
this.hideCalendars();
1221
this.clickApply();
1222
}
1223
},
1224
1225
clickPrev: function(e) {
1226
var cal = $(e.target).parents('.calendar');
1227
if (cal.hasClass('left')) {
1228
this.leftCalendar.month.subtract(1, 'month');
1229
if (this.linkedCalendars)
1230
this.rightCalendar.month.subtract(1, 'month');
1231
} else {
1232
this.rightCalendar.month.subtract(1, 'month');
1233
}
1234
this.updateCalendars();
1235
},
1236
1237
clickNext: function(e) {
1238
var cal = $(e.target).parents('.calendar');
1239
if (cal.hasClass('left')) {
1240
this.leftCalendar.month.add(1, 'month');
1241
} else {
1242
this.rightCalendar.month.add(1, 'month');
1243
if (this.linkedCalendars)
1244
this.leftCalendar.month.add(1, 'month');
1245
}
1246
this.updateCalendars();
1247
},
1248
1249
hoverDate: function(e) {
1250
1251
//ignore mouse movements while an above-calendar text input has focus
1252
//if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1253
// return;
1254
1255
//ignore dates that can't be selected
1256
if (!$(e.target).hasClass('available')) return;
1257
1258
//have the text inputs above calendars reflect the date being hovered over
1259
var title = $(e.target).attr('data-title');
1260
var row = title.substr(1, 1);
1261
var col = title.substr(3, 1);
1262
var cal = $(e.target).parents('.calendar');
1263
var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1264
1265
if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) {
1266
this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format));
1267
} else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) {
1268
this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format));
1269
}
1270
1271
//highlight the dates between the start date and the date being hovered as a potential end date
1272
var leftCalendar = this.leftCalendar;
1273
var rightCalendar = this.rightCalendar;
1274
var startDate = this.startDate;
1275
if (!this.endDate) {
1276
this.container.find('.calendar tbody td').each(function(index, el) {
1277
1278
//skip week numbers, only look at dates
1279
if ($(el).hasClass('week')) return;
1280
1281
var title = $(el).attr('data-title');
1282
var row = title.substr(1, 1);
1283
var col = title.substr(3, 1);
1284
var cal = $(el).parents('.calendar');
1285
var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
1286
1287
if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) {
1288
$(el).addClass('in-range');
1289
} else {
1290
$(el).removeClass('in-range');
1291
}
1292
1293
});
1294
}
1295
1296
},
1297
1298
clickDate: function(e) {
1299
1300
if (!$(e.target).hasClass('available')) return;
1301
1302
var title = $(e.target).attr('data-title');
1303
var row = title.substr(1, 1);
1304
var col = title.substr(3, 1);
1305
var cal = $(e.target).parents('.calendar');
1306
var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1307
1308
//
1309
// this function needs to do a few things:
1310
// * alternate between selecting a start and end date for the range,
1311
// * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date
1312
// * if autoapply is enabled, and an end date was chosen, apply the selection
1313
// * if single date picker mode, and time picker isn't enabled, apply the selection immediately
1314
// * if one of the inputs above the calendars was focused, cancel that manual input
1315
//
1316
1317
if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start
1318
if (this.timePicker) {
1319
var hour = parseInt(this.container.find('.left .hourselect').val(), 10);
1320
if (!this.timePicker24Hour) {
1321
var ampm = this.container.find('.left .ampmselect').val();
1322
if (ampm === 'PM' && hour < 12)
1323
hour += 12;
1324
if (ampm === 'AM' && hour === 12)
1325
hour = 0;
1326
}
1327
var minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
1328
var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
1329
date = date.clone().hour(hour).minute(minute).second(second);
1330
}
1331
this.endDate = null;
1332
this.setStartDate(date.clone());
1333
} else if (!this.endDate && date.isBefore(this.startDate)) {
1334
//special case: clicking the same date for start/end,
1335
//but the time of the end date is before the start date
1336
this.setEndDate(this.startDate.clone());
1337
} else { // picking end
1338
if (this.timePicker) {
1339
var hour = parseInt(this.container.find('.right .hourselect').val(), 10);
1340
if (!this.timePicker24Hour) {
1341
var ampm = this.container.find('.right .ampmselect').val();
1342
if (ampm === 'PM' && hour < 12)
1343
hour += 12;
1344
if (ampm === 'AM' && hour === 12)
1345
hour = 0;
1346
}
1347
var minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
1348
var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
1349
date = date.clone().hour(hour).minute(minute).second(second);
1350
}
1351
this.setEndDate(date.clone());
1352
if (this.autoApply) {
1353
this.calculateChosenLabel();
1354
this.clickApply();
1355
}
1356
}
1357
1358
if (this.singleDatePicker) {
1359
this.setEndDate(this.startDate);
1360
if (!this.timePicker)
1361
this.clickApply();
1362
}
1363
1364
this.updateView();
1365
1366
//This is to cancel the blur event handler if the mouse was in one of the inputs
1367
e.stopPropagation();
1368
1369
},
1370
1371
calculateChosenLabel: function () {
1372
var customRange = true;
1373
var i = 0;
1374
for (var range in this.ranges) {
1375
if (this.timePicker) {
1376
if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
1377
customRange = false;
1378
this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1379
break;
1380
}
1381
} else {
1382
//ignore times when comparing dates if time picker is not enabled
1383
if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
1384
customRange = false;
1385
this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1386
break;
1387
}
1388
}
1389
i++;
1390
}
1391
if (customRange) {
1392
if (this.showCustomRangeLabel) {
1393
this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
1394
} else {
1395
this.chosenLabel = null;
1396
}
1397
this.showCalendars();
1398
}
1399
},
1400
1401
clickApply: function(e) {
1402
this.hide();
1403
this.element.trigger('apply.daterangepicker', this);
1404
},
1405
1406
clickCancel: function(e) {
1407
this.startDate = this.oldStartDate;
1408
this.endDate = this.oldEndDate;
1409
this.hide();
1410
this.element.trigger('cancel.daterangepicker', this);
1411
},
1412
1413
monthOrYearChanged: function(e) {
1414
var isLeft = $(e.target).closest('.calendar').hasClass('left'),
1415
leftOrRight = isLeft ? 'left' : 'right',
1416
cal = this.container.find('.calendar.'+leftOrRight);
1417
1418
// Month must be Number for new moment versions
1419
var month = parseInt(cal.find('.monthselect').val(), 10);
1420
var year = cal.find('.yearselect').val();
1421
1422
if (!isLeft) {
1423
if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {
1424
month = this.startDate.month();
1425
year = this.startDate.year();
1426
}
1427
}
1428
1429
if (this.minDate) {
1430
if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {
1431
month = this.minDate.month();
1432
year = this.minDate.year();
1433
}
1434
}
1435
1436
if (this.maxDate) {
1437
if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {
1438
month = this.maxDate.month();
1439
year = this.maxDate.year();
1440
}
1441
}
1442
1443
if (isLeft) {
1444
this.leftCalendar.month.month(month).year(year);
1445
if (this.linkedCalendars)
1446
this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month');
1447
} else {
1448
this.rightCalendar.month.month(month).year(year);
1449
if (this.linkedCalendars)
1450
this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month');
1451
}
1452
this.updateCalendars();
1453
},
1454
1455
timeChanged: function(e) {
1456
1457
var cal = $(e.target).closest('.calendar'),
1458
isLeft = cal.hasClass('left');
1459
1460
var hour = parseInt(cal.find('.hourselect').val(), 10);
1461
var minute = parseInt(cal.find('.minuteselect').val(), 10);
1462
var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0;
1463
1464
if (!this.timePicker24Hour) {
1465
var ampm = cal.find('.ampmselect').val();
1466
if (ampm === 'PM' && hour < 12)
1467
hour += 12;
1468
if (ampm === 'AM' && hour === 12)
1469
hour = 0;
1470
}
1471
1472
if (isLeft) {
1473
var start = this.startDate.clone();
1474
start.hour(hour);
1475
start.minute(minute);
1476
start.second(second);
1477
this.setStartDate(start);
1478
if (this.singleDatePicker) {
1479
this.endDate = this.startDate.clone();
1480
} else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) {
1481
this.setEndDate(start.clone());
1482
}
1483
} else if (this.endDate) {
1484
var end = this.endDate.clone();
1485
end.hour(hour);
1486
end.minute(minute);
1487
end.second(second);
1488
this.setEndDate(end);
1489
}
1490
1491
//update the calendars so all clickable dates reflect the new time component
1492
this.updateCalendars();
1493
1494
//update the form inputs above the calendars with the new time
1495
this.updateFormInputs();
1496
1497
//re-render the time pickers because changing one selection can affect what's enabled in another
1498
this.renderTimePicker('left');
1499
this.renderTimePicker('right');
1500
1501
},
1502
1503
formInputsChanged: function(e) {
1504
var isRight = $(e.target).closest('.calendar').hasClass('right');
1505
var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format);
1506
var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format);
1507
1508
if (start.isValid() && end.isValid()) {
1509
1510
if (isRight && end.isBefore(start))
1511
start = end.clone();
1512
1513
this.setStartDate(start);
1514
this.setEndDate(end);
1515
1516
if (isRight) {
1517
this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format));
1518
} else {
1519
this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format));
1520
}
1521
1522
}
1523
1524
this.updateView();
1525
},
1526
1527
formInputsFocused: function(e) {
1528
1529
// Highlight the focused input
1530
this.container.find('input[name="daterangepicker_start"], input[name="daterangepicker_end"]').removeClass('active');
1531
$(e.target).addClass('active');
1532
1533
// Set the state such that if the user goes back to using a mouse,
1534
// the calendars are aware we're selecting the end of the range, not
1535
// the start. This allows someone to edit the end of a date range without
1536
// re-selecting the beginning, by clicking on the end date input then
1537
// using the calendar.
1538
var isRight = $(e.target).closest('.calendar').hasClass('right');
1539
if (isRight) {
1540
this.endDate = null;
1541
this.setStartDate(this.startDate.clone());
1542
this.updateView();
1543
}
1544
1545
},
1546
1547
formInputsBlurred: function(e) {
1548
1549
// this function has one purpose right now: if you tab from the first
1550
// text input to the second in the UI, the endDate is nulled so that
1551
// you can click another, but if you tab out without clicking anything
1552
// or changing the input value, the old endDate should be retained
1553
1554
if (!this.endDate) {
1555
var val = this.container.find('input[name="daterangepicker_end"]').val();
1556
var end = moment(val, this.locale.format);
1557
if (end.isValid()) {
1558
this.setEndDate(end);
1559
this.updateView();
1560
}
1561
}
1562
1563
},
1564
1565
elementChanged: function() {
1566
if (!this.element.is('input')) return;
1567
if (!this.element.val().length) return;
1568
if (this.element.val().length < this.locale.format.length) return;
1569
1570
var dateString = this.element.val().split(this.locale.separator),
1571
start = null,
1572
end = null;
1573
1574
if (dateString.length === 2) {
1575
start = moment(dateString[0], this.locale.format);
1576
end = moment(dateString[1], this.locale.format);
1577
}
1578
1579
if (this.singleDatePicker || start === null || end === null) {
1580
start = moment(this.element.val(), this.locale.format);
1581
end = start;
1582
}
1583
1584
if (!start.isValid() || !end.isValid()) return;
1585
1586
this.setStartDate(start);
1587
this.setEndDate(end);
1588
this.updateView();
1589
},
1590
1591
keydown: function(e) {
1592
//hide on tab or enter
1593
if ((e.keyCode === 9) || (e.keyCode === 13)) {
1594
this.hide();
1595
}
1596
},
1597
1598
updateElement: function() {
1599
if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
1600
this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
1601
this.element.trigger('change');
1602
} else if (this.element.is('input') && this.autoUpdateInput) {
1603
this.element.val(this.startDate.format(this.locale.format));
1604
this.element.trigger('change');
1605
}
1606
},
1607
1608
remove: function() {
1609
this.container.remove();
1610
this.element.off('.daterangepicker');
1611
this.element.removeData();
1612
}
1613
1614
};
1615
1616
$.fn.daterangepicker = function(options, callback) {
1617
this.each(function() {
1618
var el = $(this);
1619
if (el.data('daterangepicker'))
1620
el.data('daterangepicker').remove();
1621
el.data('daterangepicker', new DateRangePicker(el, options, callback));
1622
});
1623
return this;
1624
};
1625
1626
return DateRangePicker;
1627
1628
}));
1629
1630