PageSourceSearch

https://www.lusa.pt/portal/templates/auto_load/bootstrap-daterangepicker/daterangepicker.js?v=4_10_23_1

js lusa.pt collected 2026-10-01 10:20:59 UTC 76,232 bytes, 1,749 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.