]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/static/AdminLTE-2.3.7/plugins/daterangepicker/daterangepicker.js
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/
9 (function(root
, factory
) {
11 if (typeof define
=== 'function' && define
.amd
) {
12 define(['moment', 'jquery', 'exports'], function(momentjs
, $, exports
) {
13 root
.daterangepicker
= factory(root
, exports
, momentjs
, $);
16 } else if (typeof exports
!== 'undefined') {
17 var momentjs
= require('moment');
18 var jQuery
= (typeof window
!= 'undefined') ? window
.jQuery
: undefined; //isomorphic issue
21 jQuery
= require('jquery');
22 if (!jQuery
.fn
) jQuery
.fn
= {}; //isomorphic issue
24 if (!jQuery
) throw new Error('jQuery dependency not found');
28 factory(root
, exports
, momentjs
, jQuery
);
30 // Finally, as a browser global.
32 root
.daterangepicker
= factory(root
, {}, root
.moment
|| moment
, (root
.jQuery
|| root
.Zepto
|| root
.ender
|| root
.$));
35 }(this || {}, function(root
, daterangepicker
, moment
, $) { // 'this' doesn't exist on a server
37 var DateRangePicker = function(element
, options
, cb
) {
39 //default settings for options
40 this.parentEl
= 'body';
41 this.element
= $(element
);
42 this.startDate
= moment().startOf('day');
43 this.endDate
= moment().endOf('day');
46 this.dateLimit
= false;
47 this.autoApply
= false;
48 this.singleDatePicker
= false;
49 this.showDropdowns
= false;
50 this.showWeekNumbers
= false;
51 this.showISOWeekNumbers
= false;
52 this.timePicker
= false;
53 this.timePicker24Hour
= false;
54 this.timePickerIncrement
= 1;
55 this.timePickerSeconds
= false;
56 this.linkedCalendars
= true;
57 this.autoUpdateInput
= true;
58 this.alwaysShowCalendars
= false;
62 if (this.element
.hasClass('pull-right'))
66 if (this.element
.hasClass('dropup'))
69 this.buttonClasses
= 'btn btn-sm';
70 this.applyClass
= 'btn-success';
71 this.cancelClass
= 'btn-default';
77 cancelLabel
: 'Cancel',
79 customRangeLabel
: 'Custom Range',
80 daysOfWeek
: moment
.weekdaysMin(),
81 monthNames
: moment
.monthsShort(),
82 firstDay
: moment
.localeData().firstDayOfWeek()
85 this.callback = function() { };
87 //some state information
88 this.isShowing
= false;
89 this.leftCalendar
= {};
90 this.rightCalendar
= {};
92 //custom options from user
93 if (typeof options
!== 'object' || options
=== null)
96 //allow setting options with data attributes
97 //data-api options will be overwritten with custom javascript options
98 options
= $.extend(this.element
.data(), options
);
100 //html template for the picker UI
101 if (typeof options
.template
!== 'string' && !(options
.template
instanceof $))
102 options
.template
= '<div class="daterangepicker dropdown-menu">' +
103 '<div class="calendar left">' +
104 '<div class="daterangepicker_input">' +
105 '<input class="input-mini" type="text" name="daterangepicker_start" value="" />' +
106 '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
107 '<div class="calendar-time">' +
109 '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
112 '<div class="calendar-table"></div>' +
114 '<div class="calendar right">' +
115 '<div class="daterangepicker_input">' +
116 '<input class="input-mini" type="text" name="daterangepicker_end" value="" />' +
117 '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
118 '<div class="calendar-time">' +
120 '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
123 '<div class="calendar-table"></div>' +
125 '<div class="ranges">' +
126 '<div class="range_inputs">' +
127 '<button class="applyBtn" disabled="disabled" type="button"></button> ' +
128 '<button class="cancelBtn" type="button"></button>' +
133 this.parentEl
= (options
.parentEl
&& $(options
.parentEl
).length
) ? $(options
.parentEl
) : $(this.parentEl
);
134 this.container
= $(options
.template
).appendTo(this.parentEl
);
137 // handle all the possible options overriding defaults
140 if (typeof options
.locale
=== 'object') {
142 if (typeof options
.locale
.format
=== 'string')
143 this.locale
.format
= options
.locale
.format
;
145 if (typeof options
.locale
.separator
=== 'string')
146 this.locale
.separator
= options
.locale
.separator
;
148 if (typeof options
.locale
.daysOfWeek
=== 'object')
149 this.locale
.daysOfWeek
= options
.locale
.daysOfWeek
.slice();
151 if (typeof options
.locale
.monthNames
=== 'object')
152 this.locale
.monthNames
= options
.locale
.monthNames
.slice();
154 if (typeof options
.locale
.firstDay
=== 'number')
155 this.locale
.firstDay
= options
.locale
.firstDay
;
157 if (typeof options
.locale
.applyLabel
=== 'string')
158 this.locale
.applyLabel
= options
.locale
.applyLabel
;
160 if (typeof options
.locale
.cancelLabel
=== 'string')
161 this.locale
.cancelLabel
= options
.locale
.cancelLabel
;
163 if (typeof options
.locale
.weekLabel
=== 'string')
164 this.locale
.weekLabel
= options
.locale
.weekLabel
;
166 if (typeof options
.locale
.customRangeLabel
=== 'string')
167 this.locale
.customRangeLabel
= options
.locale
.customRangeLabel
;
171 if (typeof options
.startDate
=== 'string')
172 this.startDate
= moment(options
.startDate
, this.locale
.format
);
174 if (typeof options
.endDate
=== 'string')
175 this.endDate
= moment(options
.endDate
, this.locale
.format
);
177 if (typeof options
.minDate
=== 'string')
178 this.minDate
= moment(options
.minDate
, this.locale
.format
);
180 if (typeof options
.maxDate
=== 'string')
181 this.maxDate
= moment(options
.maxDate
, this.locale
.format
);
183 if (typeof options
.startDate
=== 'object')
184 this.startDate
= moment(options
.startDate
);
186 if (typeof options
.endDate
=== 'object')
187 this.endDate
= moment(options
.endDate
);
189 if (typeof options
.minDate
=== 'object')
190 this.minDate
= moment(options
.minDate
);
192 if (typeof options
.maxDate
=== 'object')
193 this.maxDate
= moment(options
.maxDate
);
195 // sanity check for bad options
196 if (this.minDate
&& this.startDate
.isBefore(this.minDate
))
197 this.startDate
= this.minDate
.clone();
199 // sanity check for bad options
200 if (this.maxDate
&& this.endDate
.isAfter(this.maxDate
))
201 this.endDate
= this.maxDate
.clone();
203 if (typeof options
.applyClass
=== 'string')
204 this.applyClass
= options
.applyClass
;
206 if (typeof options
.cancelClass
=== 'string')
207 this.cancelClass
= options
.cancelClass
;
209 if (typeof options
.dateLimit
=== 'object')
210 this.dateLimit
= options
.dateLimit
;
212 if (typeof options
.opens
=== 'string')
213 this.opens
= options
.opens
;
215 if (typeof options
.drops
=== 'string')
216 this.drops
= options
.drops
;
218 if (typeof options
.showWeekNumbers
=== 'boolean')
219 this.showWeekNumbers
= options
.showWeekNumbers
;
221 if (typeof options
.showISOWeekNumbers
=== 'boolean')
222 this.showISOWeekNumbers
= options
.showISOWeekNumbers
;
224 if (typeof options
.buttonClasses
=== 'string')
225 this.buttonClasses
= options
.buttonClasses
;
227 if (typeof options
.buttonClasses
=== 'object')
228 this.buttonClasses
= options
.buttonClasses
.join(' ');
230 if (typeof options
.showDropdowns
=== 'boolean')
231 this.showDropdowns
= options
.showDropdowns
;
233 if (typeof options
.singleDatePicker
=== 'boolean') {
234 this.singleDatePicker
= options
.singleDatePicker
;
235 if (this.singleDatePicker
)
236 this.endDate
= this.startDate
.clone();
239 if (typeof options
.timePicker
=== 'boolean')
240 this.timePicker
= options
.timePicker
;
242 if (typeof options
.timePickerSeconds
=== 'boolean')
243 this.timePickerSeconds
= options
.timePickerSeconds
;
245 if (typeof options
.timePickerIncrement
=== 'number')
246 this.timePickerIncrement
= options
.timePickerIncrement
;
248 if (typeof options
.timePicker24Hour
=== 'boolean')
249 this.timePicker24Hour
= options
.timePicker24Hour
;
251 if (typeof options
.autoApply
=== 'boolean')
252 this.autoApply
= options
.autoApply
;
254 if (typeof options
.autoUpdateInput
=== 'boolean')
255 this.autoUpdateInput
= options
.autoUpdateInput
;
257 if (typeof options
.linkedCalendars
=== 'boolean')
258 this.linkedCalendars
= options
.linkedCalendars
;
260 if (typeof options
.isInvalidDate
=== 'function')
261 this.isInvalidDate
= options
.isInvalidDate
;
263 if (typeof options
.alwaysShowCalendars
=== 'boolean')
264 this.alwaysShowCalendars
= options
.alwaysShowCalendars
;
266 // update day names order to firstDay
267 if (this.locale
.firstDay
!= 0) {
268 var iterator
= this.locale
.firstDay
;
269 while (iterator
> 0) {
270 this.locale
.daysOfWeek
.push(this.locale
.daysOfWeek
.shift());
275 var start
, end
, range
;
277 //if no start/end dates set, check if an input element contains initial values
278 if (typeof options
.startDate
=== 'undefined' && typeof options
.endDate
=== 'undefined') {
279 if ($(this.element
).is('input[type=text]')) {
280 var val
= $(this.element
).val(),
281 split
= val
.split(this.locale
.separator
);
285 if (split
.length
== 2) {
286 start
= moment(split
[0], this.locale
.format
);
287 end
= moment(split
[1], this.locale
.format
);
288 } else if (this.singleDatePicker
&& val
!== "") {
289 start
= moment(val
, this.locale
.format
);
290 end
= moment(val
, this.locale
.format
);
292 if (start
!== null && end
!== null) {
293 this.setStartDate(start
);
294 this.setEndDate(end
);
299 if (typeof options
.ranges
=== 'object') {
300 for (range
in options
.ranges
) {
302 if (typeof options
.ranges
[range
][0] === 'string')
303 start
= moment(options
.ranges
[range
][0], this.locale
.format
);
305 start
= moment(options
.ranges
[range
][0]);
307 if (typeof options
.ranges
[range
][1] === 'string')
308 end
= moment(options
.ranges
[range
][1], this.locale
.format
);
310 end
= moment(options
.ranges
[range
][1]);
312 // If the start or end date exceed those allowed by the minDate or dateLimit
313 // options, shorten the range to the allowable period.
314 if (this.minDate
&& start
.isBefore(this.minDate
))
315 start
= this.minDate
.clone();
317 var maxDate
= this.maxDate
;
318 if (this.dateLimit
&& start
.clone().add(this.dateLimit
).isAfter(maxDate
))
319 maxDate
= start
.clone().add(this.dateLimit
);
320 if (maxDate
&& end
.isAfter(maxDate
))
321 end
= maxDate
.clone();
323 // If the end of the range is before the minimum or the start of the range is
324 // after the maximum, don't display this range option at all.
325 if ((this.minDate
&& end
.isBefore(this.minDate
)) || (maxDate
&& start
.isAfter(maxDate
)))
328 //Support unicode chars in the range names.
329 var elem
= document
.createElement('textarea');
330 elem
.innerHTML
= range
;
331 var rangeHtml
= elem
.value
;
333 this.ranges
[rangeHtml
] = [start
, end
];
337 for (range
in this.ranges
) {
338 list
+= '<li>' + range
+ '</li>';
340 list
+= '<li>' + this.locale
.customRangeLabel
+ '</li>';
342 this.container
.find('.ranges').prepend(list
);
345 if (typeof cb
=== 'function') {
349 if (!this.timePicker
) {
350 this.startDate
= this.startDate
.startOf('day');
351 this.endDate
= this.endDate
.endOf('day');
352 this.container
.find('.calendar-time').hide();
355 //can't be used together for now
356 if (this.timePicker
&& this.autoApply
)
357 this.autoApply
= false;
359 if (this.autoApply
&& typeof options
.ranges
!== 'object') {
360 this.container
.find('.ranges').hide();
361 } else if (this.autoApply
) {
362 this.container
.find('.applyBtn, .cancelBtn').addClass('hide');
365 if (this.singleDatePicker
) {
366 this.container
.addClass('single');
367 this.container
.find('.calendar.left').addClass('single');
368 this.container
.find('.calendar.left').show();
369 this.container
.find('.calendar.right').hide();
370 this.container
.find('.daterangepicker_input input, .daterangepicker_input i').hide();
371 if (!this.timePicker
) {
372 this.container
.find('.ranges').hide();
376 if ((typeof options
.ranges
=== 'undefined' && !this.singleDatePicker
) || this.alwaysShowCalendars
) {
377 this.container
.addClass('show-calendar');
380 this.container
.addClass('opens' + this.opens
);
382 //swap the position of the predefined ranges if opens right
383 if (typeof options
.ranges
!== 'undefined' && this.opens
== 'right') {
384 var ranges
= this.container
.find('.ranges');
385 var html
= ranges
.clone();
387 this.container
.find('.calendar.left').parent().prepend(html
);
390 //apply CSS classes and labels to buttons
391 this.container
.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses
);
392 if (this.applyClass
.length
)
393 this.container
.find('.applyBtn').addClass(this.applyClass
);
394 if (this.cancelClass
.length
)
395 this.container
.find('.cancelBtn').addClass(this.cancelClass
);
396 this.container
.find('.applyBtn').html(this.locale
.applyLabel
);
397 this.container
.find('.cancelBtn').html(this.locale
.cancelLabel
);
403 this.container
.find('.calendar')
404 .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev
, this))
405 .on('click.daterangepicker', '.next', $.proxy(this.clickNext
, this))
406 .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate
, this))
407 .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate
, this))
408 .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs
, this))
409 .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged
, this))
410 .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged
, this))
411 .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged
, this))
412 .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars
, this))
413 //.on('keyup.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))
414 .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged
, this));
416 this.container
.find('.ranges')
417 .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply
, this))
418 .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel
, this))
419 .on('click.daterangepicker', 'li', $.proxy(this.clickRange
, this))
420 .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange
, this))
421 .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs
, this));
423 if (this.element
.is('input')) {
425 'click.daterangepicker': $.proxy(this.show
, this),
426 'focus.daterangepicker': $.proxy(this.show
, this),
427 'keyup.daterangepicker': $.proxy(this.elementChanged
, this),
428 'keydown.daterangepicker': $.proxy(this.keydown
, this)
431 this.element
.on('click.daterangepicker', $.proxy(this.toggle
, this));
435 // if attached to a text input, set the initial value
438 if (this.element
.is('input') && !this.singleDatePicker
&& this.autoUpdateInput
) {
439 this.element
.val(this.startDate
.format(this.locale
.format
) + this.locale
.separator
+ this.endDate
.format(this.locale
.format
));
440 this.element
.trigger('change');
441 } else if (this.element
.is('input') && this.autoUpdateInput
) {
442 this.element
.val(this.startDate
.format(this.locale
.format
));
443 this.element
.trigger('change');
448 DateRangePicker
.prototype = {
450 constructor: DateRangePicker
,
452 setStartDate: function(startDate
) {
453 if (typeof startDate
=== 'string')
454 this.startDate
= moment(startDate
, this.locale
.format
);
456 if (typeof startDate
=== 'object')
457 this.startDate
= moment(startDate
);
459 if (!this.timePicker
)
460 this.startDate
= this.startDate
.startOf('day');
462 if (this.timePicker
&& this.timePickerIncrement
)
463 this.startDate
.minute(Math
.round(this.startDate
.minute() / this.timePickerIncrement
) * this.timePickerIncrement
);
465 if (this.minDate
&& this.startDate
.isBefore(this.minDate
))
466 this.startDate
= this.minDate
;
468 if (this.maxDate
&& this.startDate
.isAfter(this.maxDate
))
469 this.startDate
= this.maxDate
;
472 this.updateElement();
474 this.updateMonthsInView();
477 setEndDate: function(endDate
) {
478 if (typeof endDate
=== 'string')
479 this.endDate
= moment(endDate
, this.locale
.format
);
481 if (typeof endDate
=== 'object')
482 this.endDate
= moment(endDate
);
484 if (!this.timePicker
)
485 this.endDate
= this.endDate
.endOf('day');
487 if (this.timePicker
&& this.timePickerIncrement
)
488 this.endDate
.minute(Math
.round(this.endDate
.minute() / this.timePickerIncrement
) * this.timePickerIncrement
);
490 if (this.endDate
.isBefore(this.startDate
))
491 this.endDate
= this.startDate
.clone();
493 if (this.maxDate
&& this.endDate
.isAfter(this.maxDate
))
494 this.endDate
= this.maxDate
;
496 if (this.dateLimit
&& this.startDate
.clone().add(this.dateLimit
).isBefore(this.endDate
))
497 this.endDate
= this.startDate
.clone().add(this.dateLimit
);
499 this.previousRightTime
= this.endDate
.clone();
502 this.updateElement();
504 this.updateMonthsInView();
507 isInvalidDate: function() {
511 updateView: function() {
512 if (this.timePicker
) {
513 this.renderTimePicker('left');
514 this.renderTimePicker('right');
516 this.container
.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled');
518 this.container
.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled');
522 this.container
.find('input[name="daterangepicker_end"]').removeClass('active');
523 this.container
.find('input[name="daterangepicker_start"]').addClass('active');
525 this.container
.find('input[name="daterangepicker_end"]').addClass('active');
526 this.container
.find('input[name="daterangepicker_start"]').removeClass('active');
528 this.updateMonthsInView();
529 this.updateCalendars();
530 this.updateFormInputs();
533 updateMonthsInView: function() {
536 //if both dates are visible already, do nothing
537 if (!this.singleDatePicker
&& this.leftCalendar
.month
&& this.rightCalendar
.month
&&
538 (this.startDate
.format('YYYY-MM') == this.leftCalendar
.month
.format('YYYY-MM') || this.startDate
.format('YYYY-MM') == this.rightCalendar
.month
.format('YYYY-MM'))
540 (this.endDate
.format('YYYY-MM') == this.leftCalendar
.month
.format('YYYY-MM') || this.endDate
.format('YYYY-MM') == this.rightCalendar
.month
.format('YYYY-MM'))
545 this.leftCalendar
.month
= this.startDate
.clone().date(2);
546 if (!this.linkedCalendars
&& (this.endDate
.month() != this.startDate
.month() || this.endDate
.year() != this.startDate
.year())) {
547 this.rightCalendar
.month
= this.endDate
.clone().date(2);
549 this.rightCalendar
.month
= this.startDate
.clone().date(2).add(1, 'month');
553 if (this.leftCalendar
.month
.format('YYYY-MM') != this.startDate
.format('YYYY-MM') && this.rightCalendar
.month
.format('YYYY-MM') != this.startDate
.format('YYYY-MM')) {
554 this.leftCalendar
.month
= this.startDate
.clone().date(2);
555 this.rightCalendar
.month
= this.startDate
.clone().date(2).add(1, 'month');
560 updateCalendars: function() {
562 if (this.timePicker
) {
563 var hour
, minute
, second
;
565 hour
= parseInt(this.container
.find('.left .hourselect').val(), 10);
566 minute
= parseInt(this.container
.find('.left .minuteselect').val(), 10);
567 second
= this.timePickerSeconds
? parseInt(this.container
.find('.left .secondselect').val(), 10) : 0;
568 if (!this.timePicker24Hour
) {
569 var ampm
= this.container
.find('.left .ampmselect').val();
570 if (ampm
=== 'PM' && hour
< 12)
572 if (ampm
=== 'AM' && hour
=== 12)
576 hour
= parseInt(this.container
.find('.right .hourselect').val(), 10);
577 minute
= parseInt(this.container
.find('.right .minuteselect').val(), 10);
578 second
= this.timePickerSeconds
? parseInt(this.container
.find('.right .secondselect').val(), 10) : 0;
579 if (!this.timePicker24Hour
) {
580 var ampm
= this.container
.find('.right .ampmselect').val();
581 if (ampm
=== 'PM' && hour
< 12)
583 if (ampm
=== 'AM' && hour
=== 12)
587 this.leftCalendar
.month
.hour(hour
).minute(minute
).second(second
);
588 this.rightCalendar
.month
.hour(hour
).minute(minute
).second(second
);
591 this.renderCalendar('left');
592 this.renderCalendar('right');
594 //highlight any predefined range matching the current start and end dates
595 this.container
.find('.ranges li').removeClass('active');
596 if (this.endDate
== null) return;
598 this.calculateChosenLabel();
601 renderCalendar: function(side
) {
604 // Build the matrix of dates that will populate the calendar
607 var calendar
= side
== 'left' ? this.leftCalendar
: this.rightCalendar
;
608 var month
= calendar
.month
.month();
609 var year
= calendar
.month
.year();
610 var hour
= calendar
.month
.hour();
611 var minute
= calendar
.month
.minute();
612 var second
= calendar
.month
.second();
613 var daysInMonth
= moment([year
, month
]).daysInMonth();
614 var firstDay
= moment([year
, month
, 1]);
615 var lastDay
= moment([year
, month
, daysInMonth
]);
616 var lastMonth
= moment(firstDay
).subtract(1, 'month').month();
617 var lastYear
= moment(firstDay
).subtract(1, 'month').year();
618 var daysInLastMonth
= moment([lastYear
, lastMonth
]).daysInMonth();
619 var dayOfWeek
= firstDay
.day();
621 //initialize a 6 rows x 7 columns array for the calendar
623 calendar
.firstDay
= firstDay
;
624 calendar
.lastDay
= lastDay
;
626 for (var i
= 0; i
< 6; i
++) {
630 //populate the calendar with date objects
631 var startDay
= daysInLastMonth
- dayOfWeek
+ this.locale
.firstDay
+ 1;
632 if (startDay
> daysInLastMonth
)
635 if (dayOfWeek
== this.locale
.firstDay
)
636 startDay
= daysInLastMonth
- 6;
638 var curDate
= moment([lastYear
, lastMonth
, startDay
, 12, minute
, second
]);
641 for (var i
= 0, col
= 0, row
= 0; i
< 42; i
++, col
++, curDate
= moment(curDate
).add(24, 'hour')) {
642 if (i
> 0 && col
% 7 === 0) {
646 calendar
[row
][col
] = curDate
.clone().hour(hour
).minute(minute
).second(second
);
649 if (this.minDate
&& calendar
[row
][col
].format('YYYY-MM-DD') == this.minDate
.format('YYYY-MM-DD') && calendar
[row
][col
].isBefore(this.minDate
) && side
== 'left') {
650 calendar
[row
][col
] = this.minDate
.clone();
653 if (this.maxDate
&& calendar
[row
][col
].format('YYYY-MM-DD') == this.maxDate
.format('YYYY-MM-DD') && calendar
[row
][col
].isAfter(this.maxDate
) && side
== 'right') {
654 calendar
[row
][col
] = this.maxDate
.clone();
659 //make the calendar object available to hoverDate/clickDate
660 if (side
== 'left') {
661 this.leftCalendar
.calendar
= calendar
;
663 this.rightCalendar
.calendar
= calendar
;
667 // Display the calendar
670 var minDate
= side
== 'left' ? this.minDate
: this.startDate
;
671 var maxDate
= this.maxDate
;
672 var selected
= side
== 'left' ? this.startDate
: this.endDate
;
674 var html
= '<table class="table-condensed">';
678 // add empty cell for week number
679 if (this.showWeekNumbers
|| this.showISOWeekNumbers
)
682 if ((!minDate
|| minDate
.isBefore(calendar
.firstDay
)) && (!this.linkedCalendars
|| side
== 'left')) {
683 html
+= '<th class="prev available"><i class="fa fa-chevron-left glyphicon glyphicon-chevron-left"></i></th>';
688 var dateHtml
= this.locale
.monthNames
[calendar
[1][1].month()] + calendar
[1][1].format(" YYYY");
690 if (this.showDropdowns
) {
691 var currentMonth
= calendar
[1][1].month();
692 var currentYear
= calendar
[1][1].year();
693 var maxYear
= (maxDate
&& maxDate
.year()) || (currentYear
+ 5);
694 var minYear
= (minDate
&& minDate
.year()) || (currentYear
- 50);
695 var inMinYear
= currentYear
== minYear
;
696 var inMaxYear
= currentYear
== maxYear
;
698 var monthHtml
= '<select class="monthselect">';
699 for (var m
= 0; m
< 12; m
++) {
700 if ((!inMinYear
|| m
>= minDate
.month()) && (!inMaxYear
|| m
<= maxDate
.month())) {
701 monthHtml
+= "<option value='" + m
+ "'" +
702 (m
=== currentMonth
? " selected='selected'" : "") +
703 ">" + this.locale
.monthNames
[m
] + "</option>";
705 monthHtml
+= "<option value='" + m
+ "'" +
706 (m
=== currentMonth
? " selected='selected'" : "") +
707 " disabled='disabled'>" + this.locale
.monthNames
[m
] + "</option>";
710 monthHtml
+= "</select>";
712 var yearHtml
= '<select class="yearselect">';
713 for (var y
= minYear
; y
<= maxYear
; y
++) {
714 yearHtml
+= '<option value="' + y
+ '"' +
715 (y
=== currentYear
? ' selected="selected"' : '') +
716 '>' + y
+ '</option>';
718 yearHtml
+= '</select>';
720 dateHtml
= monthHtml
+ yearHtml
;
723 html
+= '<th colspan="5" class="month">' + dateHtml
+ '</th>';
724 if ((!maxDate
|| maxDate
.isAfter(calendar
.lastDay
)) && (!this.linkedCalendars
|| side
== 'right' || this.singleDatePicker
)) {
725 html
+= '<th class="next available"><i class="fa fa-chevron-right glyphicon glyphicon-chevron-right"></i></th>';
733 // add week number label
734 if (this.showWeekNumbers
|| this.showISOWeekNumbers
)
735 html
+= '<th class="week">' + this.locale
.weekLabel
+ '</th>';
737 $.each(this.locale
.daysOfWeek
, function(index
, dayOfWeek
) {
738 html
+= '<th>' + dayOfWeek
+ '</th>';
745 //adjust maxDate to reflect the dateLimit setting in order to
746 //grey out end dates beyond the dateLimit
747 if (this.endDate
== null && this.dateLimit
) {
748 var maxLimit
= this.startDate
.clone().add(this.dateLimit
).endOf('day');
749 if (!maxDate
|| maxLimit
.isBefore(maxDate
)) {
754 for (var row
= 0; row
< 6; row
++) {
758 if (this.showWeekNumbers
)
759 html
+= '<td class="week">' + calendar
[row
][0].week() + '</td>';
760 else if (this.showISOWeekNumbers
)
761 html
+= '<td class="week">' + calendar
[row
][0].isoWeek() + '</td>';
763 for (var col
= 0; col
< 7; col
++) {
767 //highlight today's date
768 if (calendar
[row
][col
].isSame(new Date(), "day"))
769 classes
.push('today');
772 if (calendar
[row
][col
].isoWeekday() > 5)
773 classes
.push('weekend');
775 //grey out the dates in other months displayed at beginning and end of this calendar
776 if (calendar
[row
][col
].month() != calendar
[1][1].month())
779 //don't allow selection of dates before the minimum date
780 if (this.minDate
&& calendar
[row
][col
].isBefore(this.minDate
, 'day'))
781 classes
.push('off', 'disabled');
783 //don't allow selection of dates after the maximum date
784 if (maxDate
&& calendar
[row
][col
].isAfter(maxDate
, 'day'))
785 classes
.push('off', 'disabled');
787 //don't allow selection of date if a custom function decides it's invalid
788 if (this.isInvalidDate(calendar
[row
][col
]))
789 classes
.push('off', 'disabled');
791 //highlight the currently selected start date
792 if (calendar
[row
][col
].format('YYYY-MM-DD') == this.startDate
.format('YYYY-MM-DD'))
793 classes
.push('active', 'start-date');
795 //highlight the currently selected end date
796 if (this.endDate
!= null && calendar
[row
][col
].format('YYYY-MM-DD') == this.endDate
.format('YYYY-MM-DD'))
797 classes
.push('active', 'end-date');
799 //highlight dates in-between the selected dates
800 if (this.endDate
!= null && calendar
[row
][col
] > this.startDate
&& calendar
[row
][col
] < this.endDate
)
801 classes
.push('in-range');
803 var cname
= '', disabled
= false;
804 for (var i
= 0; i
< classes
.length
; i
++) {
805 cname
+= classes
[i
] + ' ';
806 if (classes
[i
] == 'disabled')
810 cname
+= 'available';
812 html
+= '<td class="' + cname
.replace(/^\s+|\s+$/g, '') + '" data-title="' + 'r' + row
+ 'c' + col
+ '">' + calendar
[row
][col
].date() + '</td>';
821 this.container
.find('.calendar.' + side
+ ' .calendar-table').html(html
);
825 renderTimePicker: function(side
) {
827 var html
, selected
, minDate
, maxDate
= this.maxDate
;
829 if (this.dateLimit
&& (!this.maxDate
|| this.startDate
.clone().add(this.dateLimit
).isAfter(this.maxDate
)))
830 maxDate
= this.startDate
.clone().add(this.dateLimit
);
832 if (side
== 'left') {
833 selected
= this.startDate
.clone();
834 minDate
= this.minDate
;
835 } else if (side
== 'right') {
836 selected
= this.endDate
? this.endDate
.clone() : this.previousRightTime
.clone();
837 minDate
= this.startDate
;
839 //Preserve the time already selected
840 var timeSelector
= this.container
.find('.calendar.right .calendar-time div');
841 if (timeSelector
.html() != '') {
843 selected
.hour(timeSelector
.find('.hourselect option:selected').val() || selected
.hour());
844 selected
.minute(timeSelector
.find('.minuteselect option:selected').val() || selected
.minute());
845 selected
.second(timeSelector
.find('.secondselect option:selected').val() || selected
.second());
847 if (!this.timePicker24Hour
) {
848 var ampm
= timeSelector
.find('.ampmselect option:selected').val();
849 if (ampm
=== 'PM' && selected
.hour() < 12)
850 selected
.hour(selected
.hour() + 12);
851 if (ampm
=== 'AM' && selected
.hour() === 12)
855 if (selected
.isBefore(this.startDate
))
856 selected
= this.startDate
.clone();
858 if (selected
.isAfter(maxDate
))
859 selected
= maxDate
.clone();
868 html
= '<select class="hourselect">';
870 var start
= this.timePicker24Hour
? 0 : 1;
871 var end
= this.timePicker24Hour
? 23 : 12;
873 for (var i
= start
; i
<= end
; i
++) {
875 if (!this.timePicker24Hour
)
876 i_in_24
= selected
.hour() >= 12 ? (i
== 12 ? 12 : i
+ 12) : (i
== 12 ? 0 : i
);
878 var time
= selected
.clone().hour(i_in_24
);
879 var disabled
= false;
880 if (minDate
&& time
.minute(59).isBefore(minDate
))
882 if (maxDate
&& time
.minute(0).isAfter(maxDate
))
885 if (i_in_24
== selected
.hour() && !disabled
) {
886 html
+= '<option value="' + i
+ '" selected="selected">' + i
+ '</option>';
887 } else if (disabled
) {
888 html
+= '<option value="' + i
+ '" disabled="disabled" class="disabled">' + i
+ '</option>';
890 html
+= '<option value="' + i
+ '">' + i
+ '</option>';
894 html
+= '</select> ';
900 html
+= ': <select class="minuteselect">';
902 for (var i
= 0; i
< 60; i
+= this.timePickerIncrement
) {
903 var padded
= i
< 10 ? '0' + i
: i
;
904 var time
= selected
.clone().minute(i
);
906 var disabled
= false;
907 if (minDate
&& time
.second(59).isBefore(minDate
))
909 if (maxDate
&& time
.second(0).isAfter(maxDate
))
912 if (selected
.minute() == i
&& !disabled
) {
913 html
+= '<option value="' + i
+ '" selected="selected">' + padded
+ '</option>';
914 } else if (disabled
) {
915 html
+= '<option value="' + i
+ '" disabled="disabled" class="disabled">' + padded
+ '</option>';
917 html
+= '<option value="' + i
+ '">' + padded
+ '</option>';
921 html
+= '</select> ';
927 if (this.timePickerSeconds
) {
928 html
+= ': <select class="secondselect">';
930 for (var i
= 0; i
< 60; i
++) {
931 var padded
= i
< 10 ? '0' + i
: i
;
932 var time
= selected
.clone().second(i
);
934 var disabled
= false;
935 if (minDate
&& time
.isBefore(minDate
))
937 if (maxDate
&& time
.isAfter(maxDate
))
940 if (selected
.second() == i
&& !disabled
) {
941 html
+= '<option value="' + i
+ '" selected="selected">' + padded
+ '</option>';
942 } else if (disabled
) {
943 html
+= '<option value="' + i
+ '" disabled="disabled" class="disabled">' + padded
+ '</option>';
945 html
+= '<option value="' + i
+ '">' + padded
+ '</option>';
949 html
+= '</select> ';
956 if (!this.timePicker24Hour
) {
957 html
+= '<select class="ampmselect">';
962 if (minDate
&& selected
.clone().hour(12).minute(0).second(0).isBefore(minDate
))
963 am_html
= ' disabled="disabled" class="disabled"';
965 if (maxDate
&& selected
.clone().hour(0).minute(0).second(0).isAfter(maxDate
))
966 pm_html
= ' disabled="disabled" class="disabled"';
968 if (selected
.hour() >= 12) {
969 html
+= '<option value="AM"' + am_html
+ '>AM</option><option value="PM" selected="selected"' + pm_html
+ '>PM</option>';
971 html
+= '<option value="AM" selected="selected"' + am_html
+ '>AM</option><option value="PM"' + pm_html
+ '>PM</option>';
977 this.container
.find('.calendar.' + side
+ ' .calendar-time div').html(html
);
981 updateFormInputs: function() {
983 //ignore mouse movements while an above-calendar text input has focus
984 if (this.container
.find('input[name=daterangepicker_start]').is(":focus") || this.container
.find('input[name=daterangepicker_end]').is(":focus"))
987 this.container
.find('input[name=daterangepicker_start]').val(this.startDate
.format(this.locale
.format
));
989 this.container
.find('input[name=daterangepicker_end]').val(this.endDate
.format(this.locale
.format
));
991 if (this.singleDatePicker
|| (this.endDate
&& (this.startDate
.isBefore(this.endDate
) || this.startDate
.isSame(this.endDate
)))) {
992 this.container
.find('button.applyBtn').removeAttr('disabled');
994 this.container
.find('button.applyBtn').attr('disabled', 'disabled');
1000 var parentOffset
= { top
: 0, left
: 0 },
1002 var parentRightEdge
= $(window
).width();
1003 if (!this.parentEl
.is('body')) {
1005 top
: this.parentEl
.offset().top
- this.parentEl
.scrollTop(),
1006 left
: this.parentEl
.offset().left
- this.parentEl
.scrollLeft()
1008 parentRightEdge
= this.parentEl
[0].clientWidth
+ this.parentEl
.offset().left
;
1011 if (this.drops
== 'up')
1012 containerTop
= this.element
.offset().top
- this.container
.outerHeight() - parentOffset
.top
;
1014 containerTop
= this.element
.offset().top
+ this.element
.outerHeight() - parentOffset
.top
;
1015 this.container
[this.drops
== 'up' ? 'addClass' : 'removeClass']('dropup');
1017 if (this.opens
== 'left') {
1018 this.container
.css({
1020 right
: parentRightEdge
- this.element
.offset().left
- this.element
.outerWidth(),
1023 if (this.container
.offset().left
< 0) {
1024 this.container
.css({
1029 } else if (this.opens
== 'center') {
1030 this.container
.css({
1032 left
: this.element
.offset().left
- parentOffset
.left
+ this.element
.outerWidth() / 2
1033 - this.container
.outerWidth() / 2,
1036 if (this.container
.offset().left
< 0) {
1037 this.container
.css({
1043 this.container
.css({
1045 left
: this.element
.offset().left
- parentOffset
.left
,
1048 if (this.container
.offset().left
+ this.container
.outerWidth() > $(window
).width()) {
1049 this.container
.css({
1058 if (this.isShowing
) return;
1060 // Create a click proxy that is private to this instance of datepicker, for unbinding
1061 this._outsideClickProxy
= $.proxy(function(e
) { this.outsideClick(e
); }, this);
1063 // Bind global datepicker mousedown for hiding and
1065 .on('mousedown.daterangepicker', this._outsideClickProxy
)
1066 // also support mobile devices
1067 .on('touchend.daterangepicker', this._outsideClickProxy
)
1068 // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
1069 .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy
)
1070 // and also close when focus changes to outside the picker (eg. tabbing between controls)
1071 .on('focusin.daterangepicker', this._outsideClickProxy
);
1073 // Reposition the picker if the window is resized while it's open
1074 $(window
).on('resize.daterangepicker', $.proxy(function(e
) { this.move(e
); }, this));
1076 this.oldStartDate
= this.startDate
.clone();
1077 this.oldEndDate
= this.endDate
.clone();
1078 this.previousRightTime
= this.endDate
.clone();
1081 this.container
.show();
1083 this.element
.trigger('show.daterangepicker', this);
1084 this.isShowing
= true;
1088 if (!this.isShowing
) return;
1090 //incomplete date selection, revert to last values
1091 if (!this.endDate
) {
1092 this.startDate
= this.oldStartDate
.clone();
1093 this.endDate
= this.oldEndDate
.clone();
1096 //if a new date range was selected, invoke the user callback function
1097 if (!this.startDate
.isSame(this.oldStartDate
) || !this.endDate
.isSame(this.oldEndDate
))
1098 this.callback(this.startDate
, this.endDate
, this.chosenLabel
);
1100 //if picker is attached to a text input, update it
1101 this.updateElement();
1103 $(document
).off('.daterangepicker');
1104 $(window
).off('.daterangepicker');
1105 this.container
.hide();
1106 this.element
.trigger('hide.daterangepicker', this);
1107 this.isShowing
= false;
1110 toggle: function(e
) {
1111 if (this.isShowing
) {
1118 outsideClick: function(e
) {
1119 var target
= $(e
.target
);
1120 // if the page is clicked anywhere except within the daterangerpicker/button
1121 // itself then call this.hide()
1123 // ie modal dialog fix
1124 e
.type
== "focusin" ||
1125 target
.closest(this.element
).length
||
1126 target
.closest(this.container
).length
||
1127 target
.closest('.calendar-table').length
1132 showCalendars: function() {
1133 this.container
.addClass('show-calendar');
1135 this.element
.trigger('showCalendar.daterangepicker', this);
1138 hideCalendars: function() {
1139 this.container
.removeClass('show-calendar');
1140 this.element
.trigger('hideCalendar.daterangepicker', this);
1143 hoverRange: function(e
) {
1145 //ignore mouse movements while an above-calendar text input has focus
1146 if (this.container
.find('input[name=daterangepicker_start]').is(":focus") || this.container
.find('input[name=daterangepicker_end]').is(":focus"))
1149 var label
= e
.target
.innerHTML
;
1150 if (label
== this.locale
.customRangeLabel
) {
1153 var dates
= this.ranges
[label
];
1154 this.container
.find('input[name=daterangepicker_start]').val(dates
[0].format(this.locale
.format
));
1155 this.container
.find('input[name=daterangepicker_end]').val(dates
[1].format(this.locale
.format
));
1160 clickRange: function(e
) {
1161 var label
= e
.target
.innerHTML
;
1162 this.chosenLabel
= label
;
1163 if (label
== this.locale
.customRangeLabel
) {
1164 this.showCalendars();
1166 var dates
= this.ranges
[label
];
1167 this.startDate
= dates
[0];
1168 this.endDate
= dates
[1];
1170 if (!this.timePicker
) {
1171 this.startDate
.startOf('day');
1172 this.endDate
.endOf('day');
1175 if (!this.alwaysShowCalendars
)
1176 this.hideCalendars();
1181 clickPrev: function(e
) {
1182 var cal
= $(e
.target
).parents('.calendar');
1183 if (cal
.hasClass('left')) {
1184 this.leftCalendar
.month
.subtract(1, 'month');
1185 if (this.linkedCalendars
)
1186 this.rightCalendar
.month
.subtract(1, 'month');
1188 this.rightCalendar
.month
.subtract(1, 'month');
1190 this.updateCalendars();
1193 clickNext: function(e
) {
1194 var cal
= $(e
.target
).parents('.calendar');
1195 if (cal
.hasClass('left')) {
1196 this.leftCalendar
.month
.add(1, 'month');
1198 this.rightCalendar
.month
.add(1, 'month');
1199 if (this.linkedCalendars
)
1200 this.leftCalendar
.month
.add(1, 'month');
1202 this.updateCalendars();
1205 hoverDate: function(e
) {
1207 //ignore mouse movements while an above-calendar text input has focus
1208 if (this.container
.find('input[name=daterangepicker_start]').is(":focus") || this.container
.find('input[name=daterangepicker_end]').is(":focus"))
1211 //ignore dates that can't be selected
1212 if (!$(e
.target
).hasClass('available')) return;
1214 //have the text inputs above calendars reflect the date being hovered over
1215 var title
= $(e
.target
).attr('data-title');
1216 var row
= title
.substr(1, 1);
1217 var col
= title
.substr(3, 1);
1218 var cal
= $(e
.target
).parents('.calendar');
1219 var date
= cal
.hasClass('left') ? this.leftCalendar
.calendar
[row
][col
] : this.rightCalendar
.calendar
[row
][col
];
1222 this.container
.find('input[name=daterangepicker_start]').val(date
.format(this.locale
.format
));
1224 this.container
.find('input[name=daterangepicker_end]').val(date
.format(this.locale
.format
));
1227 //highlight the dates between the start date and the date being hovered as a potential end date
1228 var leftCalendar
= this.leftCalendar
;
1229 var rightCalendar
= this.rightCalendar
;
1230 var startDate
= this.startDate
;
1231 if (!this.endDate
) {
1232 this.container
.find('.calendar td').each(function(index
, el
) {
1234 //skip week numbers, only look at dates
1235 if ($(el
).hasClass('week')) return;
1237 var title
= $(el
).attr('data-title');
1238 var row
= title
.substr(1, 1);
1239 var col
= title
.substr(3, 1);
1240 var cal
= $(el
).parents('.calendar');
1241 var dt
= cal
.hasClass('left') ? leftCalendar
.calendar
[row
][col
] : rightCalendar
.calendar
[row
][col
];
1243 if (dt
.isAfter(startDate
) && dt
.isBefore(date
)) {
1244 $(el
).addClass('in-range');
1246 $(el
).removeClass('in-range');
1254 clickDate: function(e
) {
1256 if (!$(e
.target
).hasClass('available')) return;
1258 var title
= $(e
.target
).attr('data-title');
1259 var row
= title
.substr(1, 1);
1260 var col
= title
.substr(3, 1);
1261 var cal
= $(e
.target
).parents('.calendar');
1262 var date
= cal
.hasClass('left') ? this.leftCalendar
.calendar
[row
][col
] : this.rightCalendar
.calendar
[row
][col
];
1265 // this function needs to do a few things:
1266 // * alternate between selecting a start and end date for the range,
1267 // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date
1268 // * if autoapply is enabled, and an end date was chosen, apply the selection
1269 // * if single date picker mode, and time picker isn't enabled, apply the selection immediately
1272 if (this.endDate
|| date
.isBefore(this.startDate
, 'day')) {
1273 if (this.timePicker
) {
1274 var hour
= parseInt(this.container
.find('.left .hourselect').val(), 10);
1275 if (!this.timePicker24Hour
) {
1276 var ampm
= this.container
.find('.left .ampmselect').val();
1277 if (ampm
=== 'PM' && hour
< 12)
1279 if (ampm
=== 'AM' && hour
=== 12)
1282 var minute
= parseInt(this.container
.find('.left .minuteselect').val(), 10);
1283 var second
= this.timePickerSeconds
? parseInt(this.container
.find('.left .secondselect').val(), 10) : 0;
1284 date
= date
.clone().hour(hour
).minute(minute
).second(second
);
1286 this.endDate
= null;
1287 this.setStartDate(date
.clone());
1288 } else if (!this.endDate
&& date
.isBefore(this.startDate
)) {
1289 //special case: clicking the same date for start/end,
1290 //but the time of the end date is before the start date
1291 this.setEndDate(this.startDate
.clone());
1293 if (this.timePicker
) {
1294 var hour
= parseInt(this.container
.find('.right .hourselect').val(), 10);
1295 if (!this.timePicker24Hour
) {
1296 var ampm
= this.container
.find('.right .ampmselect').val();
1297 if (ampm
=== 'PM' && hour
< 12)
1299 if (ampm
=== 'AM' && hour
=== 12)
1302 var minute
= parseInt(this.container
.find('.right .minuteselect').val(), 10);
1303 var second
= this.timePickerSeconds
? parseInt(this.container
.find('.right .secondselect').val(), 10) : 0;
1304 date
= date
.clone().hour(hour
).minute(minute
).second(second
);
1306 this.setEndDate(date
.clone());
1307 if (this.autoApply
) {
1308 this.calculateChosenLabel();
1313 if (this.singleDatePicker
) {
1314 this.setEndDate(this.startDate
);
1315 if (!this.timePicker
)
1323 calculateChosenLabel: function() {
1324 var customRange
= true;
1326 for (var range
in this.ranges
) {
1327 if (this.timePicker
) {
1328 if (this.startDate
.isSame(this.ranges
[range
][0]) && this.endDate
.isSame(this.ranges
[range
][1])) {
1329 customRange
= false;
1330 this.chosenLabel
= this.container
.find('.ranges li:eq(' + i
+ ')').addClass('active').html();
1334 //ignore times when comparing dates if time picker is not enabled
1335 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')) {
1336 customRange
= false;
1337 this.chosenLabel
= this.container
.find('.ranges li:eq(' + i
+ ')').addClass('active').html();
1344 this.chosenLabel
= this.container
.find('.ranges li:last').addClass('active').html();
1345 this.showCalendars();
1349 clickApply: function(e
) {
1351 this.element
.trigger('apply.daterangepicker', this);
1354 clickCancel: function(e
) {
1355 this.startDate
= this.oldStartDate
;
1356 this.endDate
= this.oldEndDate
;
1358 this.element
.trigger('cancel.daterangepicker', this);
1361 monthOrYearChanged: function(e
) {
1362 var isLeft
= $(e
.target
).closest('.calendar').hasClass('left'),
1363 leftOrRight
= isLeft
? 'left' : 'right',
1364 cal
= this.container
.find('.calendar.'+leftOrRight
);
1366 // Month must be Number for new moment versions
1367 var month
= parseInt(cal
.find('.monthselect').val(), 10);
1368 var year
= cal
.find('.yearselect').val();
1371 if (year
< this.startDate
.year() || (year
== this.startDate
.year() && month
< this.startDate
.month())) {
1372 month
= this.startDate
.month();
1373 year
= this.startDate
.year();
1378 if (year
< this.minDate
.year() || (year
== this.minDate
.year() && month
< this.minDate
.month())) {
1379 month
= this.minDate
.month();
1380 year
= this.minDate
.year();
1385 if (year
> this.maxDate
.year() || (year
== this.maxDate
.year() && month
> this.maxDate
.month())) {
1386 month
= this.maxDate
.month();
1387 year
= this.maxDate
.year();
1392 this.leftCalendar
.month
.month(month
).year(year
);
1393 if (this.linkedCalendars
)
1394 this.rightCalendar
.month
= this.leftCalendar
.month
.clone().add(1, 'month');
1396 this.rightCalendar
.month
.month(month
).year(year
);
1397 if (this.linkedCalendars
)
1398 this.leftCalendar
.month
= this.rightCalendar
.month
.clone().subtract(1, 'month');
1400 this.updateCalendars();
1403 timeChanged: function(e
) {
1405 var cal
= $(e
.target
).closest('.calendar'),
1406 isLeft
= cal
.hasClass('left');
1408 var hour
= parseInt(cal
.find('.hourselect').val(), 10);
1409 var minute
= parseInt(cal
.find('.minuteselect').val(), 10);
1410 var second
= this.timePickerSeconds
? parseInt(cal
.find('.secondselect').val(), 10) : 0;
1412 if (!this.timePicker24Hour
) {
1413 var ampm
= cal
.find('.ampmselect').val();
1414 if (ampm
=== 'PM' && hour
< 12)
1416 if (ampm
=== 'AM' && hour
=== 12)
1421 var start
= this.startDate
.clone();
1423 start
.minute(minute
);
1424 start
.second(second
);
1425 this.setStartDate(start
);
1426 if (this.singleDatePicker
) {
1427 this.endDate
= this.startDate
.clone();
1428 } else if (this.endDate
&& this.endDate
.format('YYYY-MM-DD') == start
.format('YYYY-MM-DD') && this.endDate
.isBefore(start
)) {
1429 this.setEndDate(start
.clone());
1431 } else if (this.endDate
) {
1432 var end
= this.endDate
.clone();
1436 this.setEndDate(end
);
1439 //update the calendars so all clickable dates reflect the new time component
1440 this.updateCalendars();
1442 //update the form inputs above the calendars with the new time
1443 this.updateFormInputs();
1445 //re-render the time pickers because changing one selection can affect what's enabled in another
1446 this.renderTimePicker('left');
1447 this.renderTimePicker('right');
1451 formInputsChanged: function(e
) {
1452 var isRight
= $(e
.target
).closest('.calendar').hasClass('right');
1453 var start
= moment(this.container
.find('input[name="daterangepicker_start"]').val(), this.locale
.format
);
1454 var end
= moment(this.container
.find('input[name="daterangepicker_end"]').val(), this.locale
.format
);
1456 if (start
.isValid() && end
.isValid()) {
1458 if (isRight
&& end
.isBefore(start
))
1459 start
= end
.clone();
1461 this.setStartDate(start
);
1462 this.setEndDate(end
);
1465 this.container
.find('input[name="daterangepicker_start"]').val(this.startDate
.format(this.locale
.format
));
1467 this.container
.find('input[name="daterangepicker_end"]').val(this.endDate
.format(this.locale
.format
));
1472 this.updateCalendars();
1473 if (this.timePicker
) {
1474 this.renderTimePicker('left');
1475 this.renderTimePicker('right');
1479 elementChanged: function() {
1480 if (!this.element
.is('input')) return;
1481 if (!this.element
.val().length
) return;
1482 if (this.element
.val().length
< this.locale
.format
.length
) return;
1484 var dateString
= this.element
.val().split(this.locale
.separator
),
1488 if (dateString
.length
=== 2) {
1489 start
= moment(dateString
[0], this.locale
.format
);
1490 end
= moment(dateString
[1], this.locale
.format
);
1493 if (this.singleDatePicker
|| start
=== null || end
=== null) {
1494 start
= moment(this.element
.val(), this.locale
.format
);
1498 if (!start
.isValid() || !end
.isValid()) return;
1500 this.setStartDate(start
);
1501 this.setEndDate(end
);
1505 keydown: function(e
) {
1506 //hide on tab or enter
1507 if ((e
.keyCode
=== 9) || (e
.keyCode
=== 13)) {
1512 updateElement: function() {
1513 if (this.element
.is('input') && !this.singleDatePicker
&& this.autoUpdateInput
) {
1514 this.element
.val(this.startDate
.format(this.locale
.format
) + this.locale
.separator
+ this.endDate
.format(this.locale
.format
));
1515 this.element
.trigger('change');
1516 } else if (this.element
.is('input') && this.autoUpdateInput
) {
1517 this.element
.val(this.startDate
.format(this.locale
.format
));
1518 this.element
.trigger('change');
1522 remove: function() {
1523 this.container
.remove();
1524 this.element
.off('.daterangepicker');
1525 this.element
.removeData();
1530 $.fn
.daterangepicker = function(options
, callback
) {
1531 this.each(function() {
1533 if (el
.data('daterangepicker'))
1534 el
.data('daterangepicker').remove();
1535 el
.data('daterangepicker', new DateRangePicker(el
, options
, callback
));
1540 return DateRangePicker
;