PageSourceSearch

https://cr-koeln.de/assets/vendor/daterangepicker/daterangepicker.js

js cr-koeln.de collected 2026-10-07 04:55:23 UTC 67,671 bytes, 1,602 lines download raw bytes

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

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.