Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
malwaredllc
GitHub Repository: malwaredllc/byob
Path: blob/master/web-gui/buildyourownbotnet/assets/js/daterangepicker/daterangepicker.js
1293 views
1
/**
2
* @version: 1.3.21
3
* @author: Dan Grossman http://www.dangrossman.info/
4
* @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved.
5
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
6
* @website: https://www.improvely.com/
7
*/
8
9
(function(root, factory) {
10
11
if (typeof define === 'function' && define.amd) {
12
define(['moment', 'jquery', 'exports'], function(momentjs, $, exports) {
13
root.daterangepicker = factory(root, exports, momentjs, $);
14
});
15
16
} else if (typeof exports !== 'undefined') {
17
var momentjs = require('moment');
18
var jQuery;
19
try {
20
jQuery = require('jquery');
21
} catch (err) {
22
jQuery = window.jQuery;
23
if (!jQuery) throw new Error('jQuery dependency not found');
24
}
25
26
factory(root, exports, momentjs, jQuery);
27
28
// Finally, as a browser global.
29
} else {
30
root.daterangepicker = factory(root, {}, root.moment, (root.jQuery || root.Zepto || root.ender || root.$));
31
}
32
33
}(this, function(root, daterangepicker, moment, $) {
34
35
var DateRangePicker = function (element, options, cb) {
36
37
// by default, the daterangepicker element is placed at the bottom of HTML body
38
this.parentEl = 'body';
39
40
//element that triggered the date range picker
41
this.element = $(element);
42
43
//tracks visible state
44
this.isShowing = false;
45
46
//create the picker HTML object
47
var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
48
'<div class="calendar first left"></div>' +
49
'<div class="calendar second right"></div>' +
50
'<div class="ranges">' +
51
'<div class="range_inputs">' +
52
'<div class="daterangepicker_start_input">' +
53
'<label for="daterangepicker_start"></label>' +
54
'<input class="input-mini" type="text" name="daterangepicker_start" value="" />' +
55
'</div>' +
56
'<div class="daterangepicker_end_input">' +
57
'<label for="daterangepicker_end"></label>' +
58
'<input class="input-mini" type="text" name="daterangepicker_end" value="" />' +
59
'</div>' +
60
'<button class="applyBtn" disabled="disabled"></button>&nbsp;' +
61
'<button class="cancelBtn"></button>' +
62
'</div>' +
63
'</div>' +
64
'</div>';
65
66
//custom options
67
if (typeof options !== 'object' || options === null)
68
options = {};
69
70
this.parentEl = (typeof options === 'object' && options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
71
this.container = $(DRPTemplate).appendTo(this.parentEl);
72
73
this.setOptions(options, cb);
74
75
//event listeners
76
this.container.find('.calendar')
77
.on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
78
.on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
79
.on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
80
.on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
81
.on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
82
.on('change.daterangepicker', 'select.yearselect', $.proxy(this.updateMonthYear, this))
83
.on('change.daterangepicker', 'select.monthselect', $.proxy(this.updateMonthYear, this))
84
.on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.updateTime, this));
85
86
this.container.find('.ranges')
87
.on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
88
.on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
89
.on('click.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
90
.on('change.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsChanged, this))
91
.on('keydown.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsKeydown, this))
92
.on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
93
.on('mouseenter.daterangepicker', 'li', $.proxy(this.enterRange, this))
94
.on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
95
96
if (this.element.is('input')) {
97
this.element.on({
98
'click.daterangepicker': $.proxy(this.show, this),
99
'focus.daterangepicker': $.proxy(this.show, this),
100
'keyup.daterangepicker': $.proxy(this.updateFromControl, this),
101
'keydown.daterangepicker': $.proxy(this.keydown, this)
102
});
103
} else {
104
this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
105
}
106
107
};
108
109
DateRangePicker.prototype = {
110
111
constructor: DateRangePicker,
112
113
setOptions: function(options, callback) {
114
115
this.startDate = moment().startOf('day');
116
this.endDate = moment().endOf('day');
117
this.timeZone = moment().utcOffset();
118
this.minDate = false;
119
this.maxDate = false;
120
this.dateLimit = false;
121
122
this.showDropdowns = false;
123
this.showWeekNumbers = false;
124
this.timePicker = false;
125
this.timePickerSeconds = false;
126
this.timePickerIncrement = 30;
127
this.timePicker12Hour = true;
128
this.singleDatePicker = false;
129
this.ranges = {};
130
131
this.opens = 'right';
132
if (this.element.hasClass('pull-right'))
133
this.opens = 'left';
134
135
this.drops = 'down';
136
if (this.element.hasClass('dropup'))
137
this.drops = 'up';
138
139
this.buttonClasses = ['btn', 'btn-small btn-sm'];
140
this.applyClass = 'btn-success';
141
this.cancelClass = 'btn-default';
142
143
this.format = 'MM/DD/YYYY';
144
this.separator = ' - ';
145
146
this.locale = {
147
applyLabel: 'Apply',
148
cancelLabel: 'Cancel',
149
fromLabel: 'From',
150
toLabel: 'To',
151
weekLabel: 'W',
152
customRangeLabel: 'Custom Range',
153
daysOfWeek: moment.weekdaysMin(),
154
monthNames: moment.monthsShort(),
155
firstDay: moment.localeData()._week.dow
156
};
157
158
this.cb = function () { };
159
160
if (typeof options.format === 'string')
161
this.format = options.format;
162
163
if (typeof options.separator === 'string')
164
this.separator = options.separator;
165
166
if (typeof options.startDate === 'string')
167
this.startDate = moment(options.startDate, this.format);
168
169
if (typeof options.endDate === 'string')
170
this.endDate = moment(options.endDate, this.format);
171
172
if (typeof options.minDate === 'string')
173
this.minDate = moment(options.minDate, this.format);
174
175
if (typeof options.maxDate === 'string')
176
this.maxDate = moment(options.maxDate, this.format);
177
178
if (typeof options.startDate === 'object')
179
this.startDate = moment(options.startDate);
180
181
if (typeof options.endDate === 'object')
182
this.endDate = moment(options.endDate);
183
184
if (typeof options.minDate === 'object')
185
this.minDate = moment(options.minDate);
186
187
if (typeof options.maxDate === 'object')
188
this.maxDate = moment(options.maxDate);
189
190
if (typeof options.applyClass === 'string')
191
this.applyClass = options.applyClass;
192
193
if (typeof options.cancelClass === 'string')
194
this.cancelClass = options.cancelClass;
195
196
if (typeof options.dateLimit === 'object')
197
this.dateLimit = options.dateLimit;
198
199
if (typeof options.locale === 'object') {
200
201
if (typeof options.locale.daysOfWeek === 'object') {
202
// Create a copy of daysOfWeek to avoid modification of original
203
// options object for reusability in multiple daterangepicker instances
204
this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
205
}
206
207
if (typeof options.locale.monthNames === 'object') {
208
this.locale.monthNames = options.locale.monthNames.slice();
209
}
210
211
if (typeof options.locale.firstDay === 'number') {
212
this.locale.firstDay = options.locale.firstDay;
213
}
214
215
if (typeof options.locale.applyLabel === 'string') {
216
this.locale.applyLabel = options.locale.applyLabel;
217
}
218
219
if (typeof options.locale.cancelLabel === 'string') {
220
this.locale.cancelLabel = options.locale.cancelLabel;
221
}
222
223
if (typeof options.locale.fromLabel === 'string') {
224
this.locale.fromLabel = options.locale.fromLabel;
225
}
226
227
if (typeof options.locale.toLabel === 'string') {
228
this.locale.toLabel = options.locale.toLabel;
229
}
230
231
if (typeof options.locale.weekLabel === 'string') {
232
this.locale.weekLabel = options.locale.weekLabel;
233
}
234
235
if (typeof options.locale.customRangeLabel === 'string') {
236
this.locale.customRangeLabel = options.locale.customRangeLabel;
237
}
238
}
239
240
if (typeof options.opens === 'string')
241
this.opens = options.opens;
242
243
if (typeof options.drops === 'string')
244
this.drops = options.drops;
245
246
if (typeof options.showWeekNumbers === 'boolean') {
247
this.showWeekNumbers = options.showWeekNumbers;
248
}
249
250
if (typeof options.buttonClasses === 'string') {
251
this.buttonClasses = [options.buttonClasses];
252
}
253
254
if (typeof options.buttonClasses === 'object') {
255
this.buttonClasses = options.buttonClasses;
256
}
257
258
if (typeof options.showDropdowns === 'boolean') {
259
this.showDropdowns = options.showDropdowns;
260
}
261
262
if (typeof options.singleDatePicker === 'boolean') {
263
this.singleDatePicker = options.singleDatePicker;
264
if (this.singleDatePicker) {
265
this.endDate = this.startDate.clone();
266
}
267
}
268
269
if (typeof options.timePicker === 'boolean') {
270
this.timePicker = options.timePicker;
271
}
272
273
if (typeof options.timePickerSeconds === 'boolean') {
274
this.timePickerSeconds = options.timePickerSeconds;
275
}
276
277
if (typeof options.timePickerIncrement === 'number') {
278
this.timePickerIncrement = options.timePickerIncrement;
279
}
280
281
if (typeof options.timePicker12Hour === 'boolean') {
282
this.timePicker12Hour = options.timePicker12Hour;
283
}
284
285
// update day names order to firstDay
286
if (this.locale.firstDay != 0) {
287
var iterator = this.locale.firstDay;
288
while (iterator > 0) {
289
this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
290
iterator--;
291
}
292
}
293
294
var start, end, range;
295
296
//if no start/end dates set, check if an input element contains initial values
297
if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
298
if ($(this.element).is('input[type=text]')) {
299
var val = $(this.element).val(),
300
split = val.split(this.separator);
301
302
start = end = null;
303
304
if (split.length == 2) {
305
start = moment(split[0], this.format);
306
end = moment(split[1], this.format);
307
} else if (this.singleDatePicker && val !== "") {
308
start = moment(val, this.format);
309
end = moment(val, this.format);
310
}
311
if (start !== null && end !== null) {
312
this.startDate = start;
313
this.endDate = end;
314
}
315
}
316
}
317
318
// bind the time zone used to build the calendar to either the timeZone passed in through the options or the zone of the startDate (which will be the local time zone by default)
319
if (typeof options.timeZone === 'string' || typeof options.timeZone === 'number') {
320
if (typeof options.timeZone === 'string' && typeof moment.tz !== 'undefined') {
321
this.timeZone = moment.tz.zone(options.timeZone).parse(new Date) * -1; // Offset is positive if the timezone is behind UTC and negative if it is ahead.
322
} else {
323
this.timeZone = options.timeZone;
324
}
325
this.startDate.utcOffset(this.timeZone);
326
this.endDate.utcOffset(this.timeZone);
327
} else {
328
this.timeZone = moment(this.startDate).utcOffset();
329
}
330
331
if (typeof options.ranges === 'object') {
332
for (range in options.ranges) {
333
334
if (typeof options.ranges[range][0] === 'string')
335
start = moment(options.ranges[range][0], this.format);
336
else
337
start = moment(options.ranges[range][0]);
338
339
if (typeof options.ranges[range][1] === 'string')
340
end = moment(options.ranges[range][1], this.format);
341
else
342
end = moment(options.ranges[range][1]);
343
344
// If we have a min/max date set, bound this range
345
// to it, but only if it would otherwise fall
346
// outside of the min/max.
347
if (this.minDate && start.isBefore(this.minDate))
348
start = moment(this.minDate);
349
350
if (this.maxDate && end.isAfter(this.maxDate))
351
end = moment(this.maxDate);
352
353
// If the end of the range is before the minimum (if min is set) OR
354
// the start of the range is after the max (also if set) don't display this
355
// range option.
356
if ((this.minDate && end.isBefore(this.minDate)) || (this.maxDate && start.isAfter(this.maxDate))) {
357
continue;
358
}
359
360
this.ranges[range] = [start, end];
361
}
362
363
var list = '<ul>';
364
for (range in this.ranges) {
365
list += '<li>' + range + '</li>';
366
}
367
list += '<li>' + this.locale.customRangeLabel + '</li>';
368
list += '</ul>';
369
this.container.find('.ranges ul').remove();
370
this.container.find('.ranges').prepend(list);
371
}
372
373
if (typeof callback === 'function') {
374
this.cb = callback;
375
}
376
377
if (!this.timePicker) {
378
this.startDate = this.startDate.startOf('day');
379
this.endDate = this.endDate.endOf('day');
380
}
381
382
if (this.singleDatePicker) {
383
this.opens = 'right';
384
this.container.addClass('single');
385
this.container.find('.calendar.right').show();
386
this.container.find('.calendar.left').hide();
387
if (!this.timePicker) {
388
this.container.find('.ranges').hide();
389
} else {
390
this.container.find('.ranges .daterangepicker_start_input, .ranges .daterangepicker_end_input').hide();
391
}
392
if (!this.container.find('.calendar.right').hasClass('single'))
393
this.container.find('.calendar.right').addClass('single');
394
} else {
395
this.container.removeClass('single');
396
this.container.find('.calendar.right').removeClass('single');
397
this.container.find('.ranges').show();
398
}
399
400
this.oldStartDate = this.startDate.clone();
401
this.oldEndDate = this.endDate.clone();
402
this.oldChosenLabel = this.chosenLabel;
403
404
this.leftCalendar = {
405
month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute(), this.startDate.second()]),
406
calendar: []
407
};
408
409
this.rightCalendar = {
410
month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute(), this.endDate.second()]),
411
calendar: []
412
};
413
414
if (this.opens == 'right' || this.opens == 'center') {
415
//swap calendar positions
416
var first = this.container.find('.calendar.first');
417
var second = this.container.find('.calendar.second');
418
419
if (second.hasClass('single')) {
420
second.removeClass('single');
421
first.addClass('single');
422
}
423
424
first.removeClass('left').addClass('right');
425
second.removeClass('right').addClass('left');
426
427
if (this.singleDatePicker) {
428
first.show();
429
second.hide();
430
}
431
}
432
433
if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
434
this.container.addClass('show-calendar');
435
}
436
437
this.container.removeClass('opensleft opensright').addClass('opens' + this.opens);
438
439
this.updateView();
440
this.updateCalendars();
441
442
//apply CSS classes and labels to buttons
443
var c = this.container;
444
$.each(this.buttonClasses, function (idx, val) {
445
c.find('button').addClass(val);
446
});
447
this.container.find('.daterangepicker_start_input label').html(this.locale.fromLabel);
448
this.container.find('.daterangepicker_end_input label').html(this.locale.toLabel);
449
if (this.applyClass.length)
450
this.container.find('.applyBtn').addClass(this.applyClass);
451
if (this.cancelClass.length)
452
this.container.find('.cancelBtn').addClass(this.cancelClass);
453
this.container.find('.applyBtn').html(this.locale.applyLabel);
454
this.container.find('.cancelBtn').html(this.locale.cancelLabel);
455
},
456
457
setStartDate: function(startDate) {
458
if (typeof startDate === 'string')
459
this.startDate = moment(startDate, this.format).utcOffset(this.timeZone);
460
461
if (typeof startDate === 'object')
462
this.startDate = moment(startDate);
463
464
if (!this.timePicker)
465
this.startDate = this.startDate.startOf('day');
466
467
this.oldStartDate = this.startDate.clone();
468
469
this.updateView();
470
this.updateCalendars();
471
this.updateInputText();
472
},
473
474
setEndDate: function(endDate) {
475
if (typeof endDate === 'string')
476
this.endDate = moment(endDate, this.format).utcOffset(this.timeZone);
477
478
if (typeof endDate === 'object')
479
this.endDate = moment(endDate);
480
481
if (!this.timePicker)
482
this.endDate = this.endDate.endOf('day');
483
484
this.oldEndDate = this.endDate.clone();
485
486
this.updateView();
487
this.updateCalendars();
488
this.updateInputText();
489
},
490
491
updateView: function () {
492
this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
493
this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
494
this.updateFormInputs();
495
},
496
497
updateFormInputs: function () {
498
this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
499
this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
500
501
if (this.startDate.isSame(this.endDate) || this.startDate.isBefore(this.endDate)) {
502
this.container.find('button.applyBtn').removeAttr('disabled');
503
} else {
504
this.container.find('button.applyBtn').attr('disabled', 'disabled');
505
}
506
},
507
508
updateFromControl: function () {
509
if (!this.element.is('input')) return;
510
if (!this.element.val().length) return;
511
512
var dateString = this.element.val().split(this.separator),
513
start = null,
514
end = null;
515
516
if(dateString.length === 2) {
517
start = moment(dateString[0], this.format).utcOffset(this.timeZone);
518
end = moment(dateString[1], this.format).utcOffset(this.timeZone);
519
}
520
521
if (this.singleDatePicker || start === null || end === null) {
522
start = moment(this.element.val(), this.format).utcOffset(this.timeZone);
523
end = start;
524
}
525
526
if (end.isBefore(start)) return;
527
528
this.oldStartDate = this.startDate.clone();
529
this.oldEndDate = this.endDate.clone();
530
531
this.startDate = start;
532
this.endDate = end;
533
534
if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
535
this.notify();
536
537
this.updateCalendars();
538
},
539
540
keydown: function (e) {
541
//hide on tab or enter
542
if ((e.keyCode === 9) || (e.keyCode === 13)) {
543
this.hide();
544
}
545
},
546
547
notify: function () {
548
this.updateView();
549
this.cb(this.startDate, this.endDate, this.chosenLabel);
550
},
551
552
move: function () {
553
var parentOffset = { top: 0, left: 0 },
554
containerTop;
555
var parentRightEdge = $(window).width();
556
if (!this.parentEl.is('body')) {
557
parentOffset = {
558
top: this.parentEl.offset().top - this.parentEl.scrollTop(),
559
left: this.parentEl.offset().left - this.parentEl.scrollLeft()
560
};
561
parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
562
}
563
564
if (this.drops == 'up')
565
containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
566
else
567
containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
568
this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup');
569
570
if (this.opens == 'left') {
571
this.container.css({
572
top: containerTop,
573
right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),
574
left: 'auto'
575
});
576
if (this.container.offset().left < 0) {
577
this.container.css({
578
right: 'auto',
579
left: 9
580
});
581
}
582
} else if (this.opens == 'center') {
583
this.container.css({
584
top: containerTop,
585
left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
586
- this.container.outerWidth() / 2,
587
right: 'auto'
588
});
589
if (this.container.offset().left < 0) {
590
this.container.css({
591
right: 'auto',
592
left: 9
593
});
594
}
595
} else {
596
this.container.css({
597
top: containerTop,
598
left: this.element.offset().left - parentOffset.left,
599
right: 'auto'
600
});
601
if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
602
this.container.css({
603
left: 'auto',
604
right: 0
605
});
606
}
607
}
608
},
609
610
toggle: function (e) {
611
if (this.element.hasClass('active')) {
612
this.hide();
613
} else {
614
this.show();
615
}
616
},
617
618
show: function (e) {
619
if (this.isShowing) return;
620
621
this.element.addClass('active');
622
this.container.show();
623
this.move();
624
625
// Create a click proxy that is private to this instance of datepicker, for unbinding
626
this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this);
627
// Bind global datepicker mousedown for hiding and
628
$(document)
629
.on('mousedown.daterangepicker', this._outsideClickProxy)
630
// also support mobile devices
631
.on('touchend.daterangepicker', this._outsideClickProxy)
632
// also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
633
.on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
634
// and also close when focus changes to outside the picker (eg. tabbing between controls)
635
.on('focusin.daterangepicker', this._outsideClickProxy);
636
637
this.isShowing = true;
638
this.element.trigger('show.daterangepicker', this);
639
},
640
641
outsideClick: function (e) {
642
var target = $(e.target);
643
// if the page is clicked anywhere except within the daterangerpicker/button
644
// itself then call this.hide()
645
if (
646
// ie modal dialog fix
647
e.type == "focusin" ||
648
target.closest(this.element).length ||
649
target.closest(this.container).length ||
650
target.closest('.calendar-date').length
651
) return;
652
this.hide();
653
},
654
655
hide: function (e) {
656
if (!this.isShowing) return;
657
658
$(document)
659
.off('.daterangepicker');
660
661
this.element.removeClass('active');
662
this.container.hide();
663
664
if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
665
this.notify();
666
667
this.oldStartDate = this.startDate.clone();
668
this.oldEndDate = this.endDate.clone();
669
670
this.isShowing = false;
671
this.element.trigger('hide.daterangepicker', this);
672
},
673
674
enterRange: function (e) {
675
// mouse pointer has entered a range label
676
var label = e.target.innerHTML;
677
if (label == this.locale.customRangeLabel) {
678
this.updateView();
679
} else {
680
var dates = this.ranges[label];
681
this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.format));
682
this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.format));
683
}
684
},
685
686
showCalendars: function() {
687
this.container.addClass('show-calendar');
688
this.move();
689
this.element.trigger('showCalendar.daterangepicker', this);
690
},
691
692
hideCalendars: function() {
693
this.container.removeClass('show-calendar');
694
this.element.trigger('hideCalendar.daterangepicker', this);
695
},
696
697
// when a date is typed into the start to end date textboxes
698
inputsChanged: function (e) {
699
var el = $(e.target);
700
var date = moment(el.val(), this.format);
701
if (!date.isValid()) return;
702
703
var startDate, endDate;
704
if (el.attr('name') === 'daterangepicker_start') {
705
startDate = (false !== this.minDate && date.isBefore(this.minDate)) ? this.minDate : date;
706
endDate = this.endDate;
707
} else {
708
startDate = this.startDate;
709
endDate = (false !== this.maxDate && date.isAfter(this.maxDate)) ? this.maxDate : date;
710
}
711
this.setCustomDates(startDate, endDate);
712
},
713
714
inputsKeydown: function(e) {
715
if (e.keyCode === 13) {
716
this.inputsChanged(e);
717
this.notify();
718
}
719
},
720
721
updateInputText: function() {
722
if (this.element.is('input') && !this.singleDatePicker) {
723
this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
724
this.element.trigger('change');
725
} else if (this.element.is('input')) {
726
this.element.val(this.endDate.format(this.format));
727
this.element.trigger('change');
728
}
729
},
730
731
clickRange: function (e) {
732
var label = e.target.innerHTML;
733
this.chosenLabel = label;
734
if (label == this.locale.customRangeLabel) {
735
this.showCalendars();
736
} else {
737
var dates = this.ranges[label];
738
739
this.startDate = dates[0];
740
this.endDate = dates[1];
741
742
if (!this.timePicker) {
743
this.startDate.startOf('day');
744
this.endDate.endOf('day');
745
}
746
747
this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
748
this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
749
this.updateCalendars();
750
751
this.updateInputText();
752
753
this.hideCalendars();
754
this.hide();
755
this.element.trigger('apply.daterangepicker', this);
756
}
757
},
758
759
clickPrev: function (e) {
760
var cal = $(e.target).parents('.calendar');
761
if (cal.hasClass('left')) {
762
this.leftCalendar.month.subtract(1, 'month');
763
} else {
764
this.rightCalendar.month.subtract(1, 'month');
765
}
766
this.updateCalendars();
767
},
768
769
clickNext: function (e) {
770
var cal = $(e.target).parents('.calendar');
771
if (cal.hasClass('left')) {
772
this.leftCalendar.month.add(1, 'month');
773
} else {
774
this.rightCalendar.month.add(1, 'month');
775
}
776
this.updateCalendars();
777
},
778
779
hoverDate: function (e) {
780
var title = $(e.target).attr('data-title');
781
var row = title.substr(1, 1);
782
var col = title.substr(3, 1);
783
var cal = $(e.target).parents('.calendar');
784
785
if (cal.hasClass('left')) {
786
this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].format(this.format));
787
} else {
788
this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].format(this.format));
789
}
790
},
791
792
setCustomDates: function(startDate, endDate) {
793
this.chosenLabel = this.locale.customRangeLabel;
794
if (startDate.isAfter(endDate)) {
795
var difference = this.endDate.diff(this.startDate);
796
endDate = moment(startDate).add(difference, 'ms');
797
if (this.maxDate && endDate.isAfter(this.maxDate)) {
798
endDate = this.maxDate.clone();
799
}
800
}
801
this.startDate = startDate;
802
this.endDate = endDate;
803
804
this.updateView();
805
this.updateCalendars();
806
},
807
808
clickDate: function (e) {
809
var title = $(e.target).attr('data-title');
810
var row = title.substr(1, 1);
811
var col = title.substr(3, 1);
812
var cal = $(e.target).parents('.calendar');
813
814
var startDate, endDate;
815
if (cal.hasClass('left')) {
816
startDate = this.leftCalendar.calendar[row][col];
817
endDate = this.endDate;
818
if (typeof this.dateLimit === 'object') {
819
var maxDate = moment(startDate).add(this.dateLimit).startOf('day');
820
if (endDate.isAfter(maxDate)) {
821
endDate = maxDate;
822
}
823
}
824
} else {
825
startDate = this.startDate;
826
endDate = this.rightCalendar.calendar[row][col];
827
if (typeof this.dateLimit === 'object') {
828
var minDate = moment(endDate).subtract(this.dateLimit).startOf('day');
829
if (startDate.isBefore(minDate)) {
830
startDate = minDate;
831
}
832
}
833
}
834
835
if (this.singleDatePicker && cal.hasClass('left')) {
836
endDate = startDate.clone();
837
} else if (this.singleDatePicker && cal.hasClass('right')) {
838
startDate = endDate.clone();
839
}
840
841
cal.find('td').removeClass('active');
842
843
$(e.target).addClass('active');
844
845
this.setCustomDates(startDate, endDate);
846
847
if (!this.timePicker)
848
endDate.endOf('day');
849
850
if (this.singleDatePicker && !this.timePicker)
851
this.clickApply();
852
},
853
854
clickApply: function (e) {
855
this.updateInputText();
856
this.hide();
857
this.element.trigger('apply.daterangepicker', this);
858
},
859
860
clickCancel: function (e) {
861
this.startDate = this.oldStartDate;
862
this.endDate = this.oldEndDate;
863
this.chosenLabel = this.oldChosenLabel;
864
this.updateView();
865
this.updateCalendars();
866
this.hide();
867
this.element.trigger('cancel.daterangepicker', this);
868
},
869
870
updateMonthYear: function (e) {
871
var isLeft = $(e.target).closest('.calendar').hasClass('left'),
872
leftOrRight = isLeft ? 'left' : 'right',
873
cal = this.container.find('.calendar.'+leftOrRight);
874
875
// Month must be Number for new moment versions
876
var month = parseInt(cal.find('.monthselect').val(), 10);
877
var year = cal.find('.yearselect').val();
878
879
if (!isLeft && !this.singleDatePicker) {
880
if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {
881
month = this.startDate.month();
882
year = this.startDate.year();
883
}
884
}
885
886
if (this.minDate) {
887
if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {
888
month = this.minDate.month();
889
year = this.minDate.year();
890
}
891
}
892
893
if (this.maxDate) {
894
if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {
895
month = this.maxDate.month();
896
year = this.maxDate.year();
897
}
898
}
899
900
901
this[leftOrRight+'Calendar'].month.month(month).year(year);
902
this.updateCalendars();
903
},
904
905
updateTime: function(e) {
906
907
var cal = $(e.target).closest('.calendar'),
908
isLeft = cal.hasClass('left');
909
910
var hour = parseInt(cal.find('.hourselect').val(), 10);
911
var minute = parseInt(cal.find('.minuteselect').val(), 10);
912
var second = 0;
913
914
if (this.timePickerSeconds) {
915
second = parseInt(cal.find('.secondselect').val(), 10);
916
}
917
918
if (this.timePicker12Hour) {
919
var ampm = cal.find('.ampmselect').val();
920
if (ampm === 'PM' && hour < 12)
921
hour += 12;
922
if (ampm === 'AM' && hour === 12)
923
hour = 0;
924
}
925
926
if (isLeft) {
927
var start = this.startDate.clone();
928
start.hour(hour);
929
start.minute(minute);
930
start.second(second);
931
this.startDate = start;
932
this.leftCalendar.month.hour(hour).minute(minute).second(second);
933
if (this.singleDatePicker)
934
this.endDate = start.clone();
935
} else {
936
var end = this.endDate.clone();
937
end.hour(hour);
938
end.minute(minute);
939
end.second(second);
940
this.endDate = end;
941
if (this.singleDatePicker)
942
this.startDate = end.clone();
943
this.rightCalendar.month.hour(hour).minute(minute).second(second);
944
}
945
946
this.updateView();
947
this.updateCalendars();
948
},
949
950
updateCalendars: function () {
951
this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.month(), this.leftCalendar.month.year(), this.leftCalendar.month.hour(), this.leftCalendar.month.minute(), this.leftCalendar.month.second(), 'left');
952
this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.month(), this.rightCalendar.month.year(), this.rightCalendar.month.hour(), this.rightCalendar.month.minute(), this.rightCalendar.month.second(), 'right');
953
this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate, 'left'));
954
this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.singleDatePicker ? this.minDate : this.startDate, this.maxDate, 'right'));
955
956
this.container.find('.ranges li').removeClass('active');
957
var customRange = true;
958
var i = 0;
959
for (var range in this.ranges) {
960
if (this.timePicker) {
961
if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
962
customRange = false;
963
this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
964
.addClass('active').html();
965
}
966
} else {
967
//ignore times when comparing dates if time picker is not enabled
968
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')) {
969
customRange = false;
970
this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
971
.addClass('active').html();
972
}
973
}
974
i++;
975
}
976
if (customRange) {
977
this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
978
this.showCalendars();
979
}
980
},
981
982
buildCalendar: function (month, year, hour, minute, second, side) {
983
var daysInMonth = moment([year, month]).daysInMonth();
984
var firstDay = moment([year, month, 1]);
985
var lastDay = moment([year, month, daysInMonth]);
986
var lastMonth = moment(firstDay).subtract(1, 'month').month();
987
var lastYear = moment(firstDay).subtract(1, 'month').year();
988
989
var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
990
991
var dayOfWeek = firstDay.day();
992
993
var i;
994
995
//initialize a 6 rows x 7 columns array for the calendar
996
var calendar = [];
997
calendar.firstDay = firstDay;
998
calendar.lastDay = lastDay;
999
1000
for (i = 0; i < 6; i++) {
1001
calendar[i] = [];
1002
}
1003
1004
//populate the calendar with date objects
1005
var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
1006
if (startDay > daysInLastMonth)
1007
startDay -= 7;
1008
1009
if (dayOfWeek == this.locale.firstDay)
1010
startDay = daysInLastMonth - 6;
1011
1012
var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]).utcOffset(this.timeZone);
1013
1014
var col, row;
1015
for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
1016
if (i > 0 && col % 7 === 0) {
1017
col = 0;
1018
row++;
1019
}
1020
calendar[row][col] = curDate.clone().hour(hour);
1021
curDate.hour(12);
1022
1023
if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
1024
calendar[row][col] = this.minDate.clone();
1025
}
1026
1027
if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
1028
calendar[row][col] = this.maxDate.clone();
1029
}
1030
1031
}
1032
1033
return calendar;
1034
},
1035
1036
renderDropdowns: function (selected, minDate, maxDate) {
1037
var currentMonth = selected.month();
1038
var currentYear = selected.year();
1039
var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
1040
var minYear = (minDate && minDate.year()) || (currentYear - 50);
1041
1042
var monthHtml = '<select class="monthselect">';
1043
var inMinYear = currentYear == minYear;
1044
var inMaxYear = currentYear == maxYear;
1045
1046
for (var m = 0; m < 12; m++) {
1047
if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
1048
monthHtml += "<option value='" + m + "'" +
1049
(m === currentMonth ? " selected='selected'" : "") +
1050
">" + this.locale.monthNames[m] + "</option>";
1051
}
1052
}
1053
monthHtml += "</select>";
1054
1055
var yearHtml = '<select class="yearselect">';
1056
1057
for (var y = minYear; y <= maxYear; y++) {
1058
yearHtml += '<option value="' + y + '"' +
1059
(y === currentYear ? ' selected="selected"' : '') +
1060
'>' + y + '</option>';
1061
}
1062
1063
yearHtml += '</select>';
1064
1065
return monthHtml + yearHtml;
1066
},
1067
1068
renderCalendar: function (calendar, selected, minDate, maxDate, side) {
1069
1070
var html = '<div class="calendar-date">';
1071
html += '<table class="table-condensed">';
1072
html += '<thead>';
1073
html += '<tr>';
1074
1075
// add empty cell for week number
1076
if (this.showWeekNumbers)
1077
html += '<th></th>';
1078
1079
if (!minDate || minDate.isBefore(calendar.firstDay)) {
1080
html += '<th class="prev available"><i class="fa fa-arrow-left icon icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
1081
} else {
1082
html += '<th></th>';
1083
}
1084
1085
var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
1086
1087
if (this.showDropdowns) {
1088
dateHtml = this.renderDropdowns(calendar[1][1], minDate, maxDate);
1089
}
1090
1091
html += '<th colspan="5" class="month">' + dateHtml + '</th>';
1092
if (!maxDate || maxDate.isAfter(calendar.lastDay)) {
1093
html += '<th class="next available"><i class="fa fa-arrow-right icon icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
1094
} else {
1095
html += '<th></th>';
1096
}
1097
1098
html += '</tr>';
1099
html += '<tr>';
1100
1101
// add week number label
1102
if (this.showWeekNumbers)
1103
html += '<th class="week">' + this.locale.weekLabel + '</th>';
1104
1105
$.each(this.locale.daysOfWeek, function (index, dayOfWeek) {
1106
html += '<th>' + dayOfWeek + '</th>';
1107
});
1108
1109
html += '</tr>';
1110
html += '</thead>';
1111
html += '<tbody>';
1112
1113
for (var row = 0; row < 6; row++) {
1114
html += '<tr>';
1115
1116
// add week number
1117
if (this.showWeekNumbers)
1118
html += '<td class="week">' + calendar[row][0].week() + '</td>';
1119
1120
for (var col = 0; col < 7; col++) {
1121
var cname = 'available ';
1122
cname += (calendar[row][col].month() == calendar[1][1].month()) ? '' : 'off';
1123
1124
if ((minDate && calendar[row][col].isBefore(minDate, 'day')) || (maxDate && calendar[row][col].isAfter(maxDate, 'day'))) {
1125
cname = ' off disabled ';
1126
} else if (calendar[row][col].format('YYYY-MM-DD') == selected.format('YYYY-MM-DD')) {
1127
cname += ' active ';
1128
if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) {
1129
cname += ' start-date ';
1130
}
1131
if (calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) {
1132
cname += ' end-date ';
1133
}
1134
} else if (calendar[row][col] >= this.startDate && calendar[row][col] <= this.endDate) {
1135
cname += ' in-range ';
1136
if (calendar[row][col].isSame(this.startDate)) { cname += ' start-date '; }
1137
if (calendar[row][col].isSame(this.endDate)) { cname += ' end-date '; }
1138
}
1139
1140
var title = 'r' + row + 'c' + col;
1141
html += '<td class="' + cname.replace(/\s+/g, ' ').replace(/^\s?(.*?)\s?$/, '$1') + '" data-title="' + title + '">' + calendar[row][col].date() + '</td>';
1142
}
1143
html += '</tr>';
1144
}
1145
1146
html += '</tbody>';
1147
html += '</table>';
1148
html += '</div>';
1149
1150
var i;
1151
if (this.timePicker) {
1152
1153
html += '<div class="calendar-time">';
1154
html += '<select class="hourselect">';
1155
1156
// Disallow selections before the minDate or after the maxDate
1157
var min_hour = 0;
1158
var max_hour = 23;
1159
1160
if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == minDate.format('YYYY-MM-DD')) {
1161
min_hour = minDate.hour();
1162
if (selected.hour() < min_hour)
1163
selected.hour(min_hour);
1164
if (this.timePicker12Hour && min_hour >= 12 && selected.hour() >= 12)
1165
min_hour -= 12;
1166
if (this.timePicker12Hour && min_hour == 12)
1167
min_hour = 1;
1168
}
1169
1170
if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == maxDate.format('YYYY-MM-DD')) {
1171
max_hour = maxDate.hour();
1172
if (selected.hour() > max_hour)
1173
selected.hour(max_hour);
1174
if (this.timePicker12Hour && max_hour >= 12 && selected.hour() >= 12)
1175
max_hour -= 12;
1176
}
1177
1178
var start = 0;
1179
var end = 23;
1180
var selected_hour = selected.hour();
1181
if (this.timePicker12Hour) {
1182
start = 1;
1183
end = 12;
1184
if (selected_hour >= 12)
1185
selected_hour -= 12;
1186
if (selected_hour === 0)
1187
selected_hour = 12;
1188
}
1189
1190
for (i = start; i <= end; i++) {
1191
1192
if (i == selected_hour) {
1193
html += '<option value="' + i + '" selected="selected">' + i + '</option>';
1194
} else if (i < min_hour || i > max_hour) {
1195
html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>';
1196
} else {
1197
html += '<option value="' + i + '">' + i + '</option>';
1198
}
1199
}
1200
1201
html += '</select> : ';
1202
1203
html += '<select class="minuteselect">';
1204
1205
// Disallow selections before the minDate or after the maxDate
1206
var min_minute = 0;
1207
var max_minute = 59;
1208
1209
if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD h A') == minDate.format('YYYY-MM-DD h A')) {
1210
min_minute = minDate.minute();
1211
if (selected.minute() < min_minute)
1212
selected.minute(min_minute);
1213
}
1214
1215
if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD h A') == maxDate.format('YYYY-MM-DD h A')) {
1216
max_minute = maxDate.minute();
1217
if (selected.minute() > max_minute)
1218
selected.minute(max_minute);
1219
}
1220
1221
for (i = 0; i < 60; i += this.timePickerIncrement) {
1222
var num = i;
1223
if (num < 10)
1224
num = '0' + num;
1225
if (i == selected.minute()) {
1226
html += '<option value="' + i + '" selected="selected">' + num + '</option>';
1227
} else if (i < min_minute || i > max_minute) {
1228
html += '<option value="' + i + '" disabled="disabled" class="disabled">' + num + '</option>';
1229
} else {
1230
html += '<option value="' + i + '">' + num + '</option>';
1231
}
1232
}
1233
1234
html += '</select> ';
1235
1236
if (this.timePickerSeconds) {
1237
html += ': <select class="secondselect">';
1238
1239
for (i = 0; i < 60; i += this.timePickerIncrement) {
1240
var num = i;
1241
if (num < 10)
1242
num = '0' + num;
1243
if (i == selected.second()) {
1244
html += '<option value="' + i + '" selected="selected">' + num + '</option>';
1245
} else {
1246
html += '<option value="' + i + '">' + num + '</option>';
1247
}
1248
}
1249
1250
html += '</select>';
1251
}
1252
1253
if (this.timePicker12Hour) {
1254
html += '<select class="ampmselect">';
1255
1256
// Disallow selection before the minDate or after the maxDate
1257
var am_html = '';
1258
var pm_html = '';
1259
1260
if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == minDate.format('YYYY-MM-DD') && minDate.hour() >= 12) {
1261
am_html = ' disabled="disabled" class="disabled"';
1262
}
1263
1264
if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == maxDate.format('YYYY-MM-DD') && maxDate.hour() < 12) {
1265
pm_html = ' disabled="disabled" class="disabled"';
1266
}
1267
1268
if (selected.hour() >= 12) {
1269
html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>';
1270
} else {
1271
html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>';
1272
}
1273
html += '</select>';
1274
}
1275
1276
html += '</div>';
1277
1278
}
1279
1280
return html;
1281
1282
},
1283
1284
remove: function() {
1285
1286
this.container.remove();
1287
this.element.off('.daterangepicker');
1288
this.element.removeData('daterangepicker');
1289
1290
}
1291
1292
};
1293
1294
$.fn.daterangepicker = function (options, cb) {
1295
this.each(function () {
1296
var el = $(this);
1297
if (el.data('daterangepicker'))
1298
el.data('daterangepicker').remove();
1299
el.data('daterangepicker', new DateRangePicker(el, options, cb));
1300
});
1301
return this;
1302
};
1303
1304
}));
1305
1306