懒羊羊
2023-08-30 1ac2bc1590406d9babec036e154d8d08f34a6aa1
提交 | 用户 | 时间
1ac2bc 1 /**
2  * @version: 1.3.7
3  * @author: Dan Grossman http://www.dangrossman.info/
4  * @date: 2014-04-29
5  * @copyright: Copyright (c) 2012-2014 Dan Grossman. All rights reserved.
6  * @license: Licensed under Apache License v2.0. See http://www.apache.org/licenses/LICENSE-2.0
7  * @website: http://www.improvely.com/
8  */
9 !function ($, moment) {
10
11     var DateRangePicker = function (element, options, cb) {
12
13         // by default, the daterangepicker element is placed at the bottom of HTML body
14         this.parentEl = 'body';
15
16         //element that triggered the date range picker
17         this.element = $(element);
18
19         //create the picker HTML object
20         var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
21             '<div class="calendar left"></div>' +
22             '<div class="calendar right"></div>' +
23             '<div class="ranges">' +
24             '<div class="range_inputs">' +
25             '<div class="daterangepicker_start_input">' +
26             '<label for="daterangepicker_start"></label>' +
27             '<input class="input-mini" type="text" name="daterangepicker_start" value="" disabled="disabled" />' +
28             '</div>' +
29             '<div class="daterangepicker_end_input">' +
30             '<label for="daterangepicker_end"></label>' +
31             '<input class="input-mini" type="text" name="daterangepicker_end" value="" disabled="disabled" />' +
32             '</div>' +
33             '<button class="applyBtn" disabled="disabled"></button>&nbsp;' +
34             '<button class="cancelBtn"></button>' +
35             '</div>' +
36             '</div>' +
37             '</div>';
38
39         //custom options
40         if (typeof options !== 'object' || options === null)
41             options = {};
42
43         this.parentEl = (typeof options === 'object' && options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
44         this.container = $(DRPTemplate).appendTo(this.parentEl);
45
46         this.setOptions(options, cb);
47
48         //apply CSS classes and labels to buttons
49         var c = this.container;
50         $.each(this.buttonClasses, function (idx, val) {
51             c.find('button').addClass(val);
52         });
53         this.container.find('.daterangepicker_start_input label').html(this.locale.fromLabel);
54         this.container.find('.daterangepicker_end_input label').html(this.locale.toLabel);
55         if (this.applyClass.length)
56             this.container.find('.applyBtn').addClass(this.applyClass);
57         if (this.cancelClass.length)
58             this.container.find('.cancelBtn').addClass(this.cancelClass);
59         this.container.find('.applyBtn').html(this.locale.applyLabel);
60         this.container.find('.cancelBtn').html(this.locale.cancelLabel);
61
62         //event listeners
63
64         this.container.find('.calendar')
65             .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
66             .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
67             .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
68             .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.enterDate, this))
69             .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
70             .on('change.daterangepicker', 'select.yearselect', $.proxy(this.updateMonthYear, this))
71             .on('change.daterangepicker', 'select.monthselect', $.proxy(this.updateMonthYear, this))
72             .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.ampmselect', $.proxy(this.updateTime, this));
73
74         this.container.find('.ranges')
75             .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
76             .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
77             .on('click.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
78             .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
79             .on('mouseenter.daterangepicker', 'li', $.proxy(this.enterRange, this))
80             .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
81
82         if (this.element.is('input')) {
83             this.element.on({
84                 'click.daterangepicker': $.proxy(this.show, this),
85                 'focus.daterangepicker': $.proxy(this.show, this),
86                 'keyup.daterangepicker': $.proxy(this.updateFromControl, this)
87             });
88         } else {
89             this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
90         }
91
92     };
93
94     DateRangePicker.prototype = {
95
96         constructor: DateRangePicker,
97
98         setOptions: function(options, callback) {
99
100             this.startDate = moment().startOf('day');
101             this.endDate = moment().endOf('day');
102             this.minDate = false;
103             this.maxDate = false;
104             this.dateLimit = false;
105
106             this.showDropdowns = false;
107             this.showWeekNumbers = false;
108             this.timePicker = false;
109             this.timePickerIncrement = 30;
110             this.timePicker12Hour = true;
111             this.singleDatePicker = false;
112             this.ranges = {};
113
114             this.opens = 'right';
115             if (this.element.hasClass('pull-right'))
116                 this.opens = 'left';
117
118             this.buttonClasses = ['btn', 'btn-small'];
119             this.applyClass = 'btn-success';
120             this.cancelClass = 'btn-default';
121
122             this.format = 'MM/DD/YYYY';
123             this.separator = ' - ';
124
125             this.locale = {
126                 applyLabel: 'Apply',
127                 cancelLabel: 'Cancel',
128                 fromLabel: 'From',
129                 toLabel: 'To',
130                 weekLabel: 'W',
131                 customRangeLabel: 'Custom Range',
132                 daysOfWeek: moment()._lang._weekdaysMin.slice(),
133                 monthNames: moment()._lang._monthsShort.slice(),
134                 firstDay: 0
135             };
136
137             this.cb = function () { };
138
139             if (typeof options.format === 'string')
140                 this.format = options.format;
141
142             if (typeof options.separator === 'string')
143                 this.separator = options.separator;
144
145             if (typeof options.startDate === 'string')
146                 this.startDate = moment(options.startDate, this.format);
147
148             if (typeof options.endDate === 'string')
149                 this.endDate = moment(options.endDate, this.format);
150
151             if (typeof options.minDate === 'string')
152                 this.minDate = moment(options.minDate, this.format);
153
154             if (typeof options.maxDate === 'string')
155                 this.maxDate = moment(options.maxDate, this.format);
156
157             if (typeof options.startDate === 'object')
158                 this.startDate = moment(options.startDate);
159
160             if (typeof options.endDate === 'object')
161                 this.endDate = moment(options.endDate);
162
163             if (typeof options.minDate === 'object')
164                 this.minDate = moment(options.minDate);
165
166             if (typeof options.maxDate === 'object')
167                 this.maxDate = moment(options.maxDate);
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             // update day names order to firstDay
179             if (typeof options.locale === 'object') {
180
181                 if (typeof options.locale.daysOfWeek === 'object') {
182                     // Create a copy of daysOfWeek to avoid modification of original
183                     // options object for reusability in multiple daterangepicker instances
184                     this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
185                 }
186
187                 if (typeof options.locale.monthNames === 'object') {
188                     this.locale.monthNames = options.locale.monthNames.slice();
189                 }
190
191                 if (typeof options.locale.firstDay === 'number') {
192                     this.locale.firstDay = options.locale.firstDay;
193                     var iterator = options.locale.firstDay;
194                     while (iterator > 0) {
195                         this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
196                         iterator--;
197                     }
198                 }
199
200                 if (typeof options.locale.applyLabel === 'string') {
201                     this.locale.applyLabel = options.locale.applyLabel;
202                 }
203
204                 if (typeof options.locale.cancelLabel === 'string') {
205                     this.locale.cancelLabel = options.locale.cancelLabel;
206                 }
207
208                 if (typeof options.locale.fromLabel === 'string') {
209                     this.locale.fromLabel = options.locale.fromLabel;
210                 }
211
212                 if (typeof options.locale.toLabel === 'string') {
213                     this.locale.toLabel = options.locale.toLabel;
214                 }
215
216                 if (typeof options.locale.weekLabel === 'string') {
217                     this.locale.weekLabel = options.locale.weekLabel;
218                 }
219
220                 if (typeof options.locale.customRangeLabel === 'string') {
221                     this.locale.customRangeLabel = options.locale.customRangeLabel;
222                 }
223             }
224
225             if (typeof options.opens === 'string')
226                 this.opens = options.opens;
227
228             if (typeof options.showWeekNumbers === 'boolean') {
229                 this.showWeekNumbers = options.showWeekNumbers;
230             }
231
232             if (typeof options.buttonClasses === 'string') {
233                 this.buttonClasses = [options.buttonClasses];
234             }
235
236             if (typeof options.buttonClasses === 'object') {
237                 this.buttonClasses = options.buttonClasses;
238             }
239
240             if (typeof options.showDropdowns === 'boolean') {
241                 this.showDropdowns = options.showDropdowns;
242             }
243
244             if (typeof options.singleDatePicker === 'boolean') {
245                 this.singleDatePicker = options.singleDatePicker;
246             }
247
248             if (typeof options.timePicker === 'boolean') {
249                 this.timePicker = options.timePicker;
250             }
251
252             if (typeof options.timePickerIncrement === 'number') {
253                 this.timePickerIncrement = options.timePickerIncrement;
254             }
255
256             if (typeof options.timePicker12Hour === 'boolean') {
257                 this.timePicker12Hour = options.timePicker12Hour;
258             }
259
260             var start, end, range;
261
262             //if no start/end dates set, check if an input element contains initial values
263             if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
264                 if ($(this.element).is('input[type=text]')) {
265                     var val = $(this.element).val();
266                     var split = val.split(this.separator);
267                     start = end = null;
268                     if (split.length == 2) {
269                         start = moment(split[0], this.format);
270                         end = moment(split[1], this.format);
271                     } else if (this.singleDatePicker) {
272                         start = moment(val, this.format);
273                         end = moment(val, this.format);
274                     }
275                     if (start !== null && end !== null) {
276                         this.startDate = start;
277                         this.endDate = end;
278                     }
279                 }
280             }
281
282             if (typeof options.ranges === 'object') {
283                 for (range in options.ranges) {
284
285                     start = moment(options.ranges[range][0]);
286                     end = moment(options.ranges[range][1]);
287
288                     // If we have a min/max date set, bound this range
289                     // to it, but only if it would otherwise fall
290                     // outside of the min/max.
291                     if (this.minDate && start.isBefore(this.minDate))
292                         start = moment(this.minDate);
293
294                     if (this.maxDate && end.isAfter(this.maxDate))
295                         end = moment(this.maxDate);
296
297                     // If the end of the range is before the minimum (if min is set) OR
298                     // the start of the range is after the max (also if set) don't display this
299                     // range option.
300                     if ((this.minDate && end.isBefore(this.minDate)) || (this.maxDate && start.isAfter(this.maxDate))) {
301                         continue;
302                     }
303
304                     this.ranges[range] = [start, end];
305                 }
306
307                 var list = '<ul>';
308                 for (range in this.ranges) {
309                     list += '<li>' + range + '</li>';
310                 }
311                 list += '<li>' + this.locale.customRangeLabel + '</li>';
312                 list += '</ul>';
313                 this.container.find('.ranges ul').remove();
314                 this.container.find('.ranges').prepend(list);
315             }
316
317             if (typeof callback === 'function') {
318                 this.cb = callback;
319             }
320
321             if (!this.timePicker) {
322                 this.startDate = this.startDate.startOf('day');
323                 this.endDate = this.endDate.endOf('day');
324             }
325
326             if (this.singleDatePicker) {
327                 this.opens = 'right';
328                 this.container.find('.calendar.right').show();
329                 this.container.find('.calendar.left').hide();
330                 this.container.find('.ranges').hide();
331                 if (!this.container.find('.calendar.right').hasClass('single'))
332                     this.container.find('.calendar.right').addClass('single');
333             } else {
334                 this.container.find('.calendar.right').removeClass('single');
335                 this.container.find('.ranges').show();
336             }
337
338             this.oldStartDate = this.startDate.clone();
339             this.oldEndDate = this.endDate.clone();
340             this.oldChosenLabel = this.chosenLabel;
341
342             this.leftCalendar = {
343                 month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute()]),
344                 calendar: []
345             };
346
347             this.rightCalendar = {
348                 month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute()]),
349                 calendar: []
350             };
351
352             if (this.opens == 'right') {
353                 //swap calendar positions
354                 var left = this.container.find('.calendar.left');
355                 var right = this.container.find('.calendar.right');
356                 left.removeClass('left').addClass('right');
357                 right.removeClass('right').addClass('left');
358             }
359
360             if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
361                 this.container.addClass('show-calendar');
362             }
363
364             this.container.addClass('opens' + this.opens);
365
366             this.updateView();
367             this.updateCalendars();
368
369         },
370
371         setStartDate: function(startDate) {
372             if (typeof startDate === 'string')
373                 this.startDate = moment(startDate, this.format);
374
375             if (typeof startDate === 'object')
376                 this.startDate = moment(startDate);
377
378             if (!this.timePicker)
379                 this.startDate = this.startDate.startOf('day');
380
381             this.oldStartDate = this.startDate.clone();
382
383             this.updateView();
384             this.updateCalendars();
385         },
386
387         setEndDate: function(endDate) {
388             if (typeof endDate === 'string')
389                 this.endDate = moment(endDate, this.format);
390
391             if (typeof endDate === 'object')
392                 this.endDate = moment(endDate);
393
394             if (!this.timePicker)
395                 this.endDate = this.endDate.endOf('day');
396
397             this.oldEndDate = this.endDate.clone();
398
399             this.updateView();
400             this.updateCalendars();
401         },
402
403         updateView: function () {
404             this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
405             this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
406             this.updateFormInputs();
407         },
408
409         updateFormInputs: function () {
410             this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
411             this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
412
413             if (this.startDate.isSame(this.endDate) || this.startDate.isBefore(this.endDate)) {
414                 this.container.find('button.applyBtn').removeAttr('disabled');
415             } else {
416                 this.container.find('button.applyBtn').attr('disabled', 'disabled');
417             }
418         },
419
420         updateFromControl: function () {
421             if (!this.element.is('input')) return;
422             if (!this.element.val().length) return;
423
424             var dateString = this.element.val().split(this.separator),
425                 start = null,
426                 end = null;
427
428             if(dateString.length === 2) {
429                 start = moment(dateString[0], this.format);
430                 end = moment(dateString[1], this.format);
431             }
432
433             if (this.singleDatePicker || start === null || end === null) {
434                 start = moment(this.element.val(), this.format);
435                 end = start;
436             }
437
438             if (end.isBefore(start)) return;
439
440             this.oldStartDate = this.startDate.clone();
441             this.oldEndDate = this.endDate.clone();
442
443             this.startDate = start;
444             this.endDate = end;
445
446             if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
447                 this.notify();
448
449             this.updateCalendars();
450         },
451
452         notify: function () {
453             this.updateView();
454             this.cb(this.startDate, this.endDate, this.chosenLabel);
455         },
456
457         move: function () {
458             var parentOffset = { top: 0, left: 0 };
459             if (!this.parentEl.is('body')) {
460                 parentOffset = {
461                     top: this.parentEl.offset().top - this.parentEl.scrollTop(),
462                     left: this.parentEl.offset().left - this.parentEl.scrollLeft()
463                 };
464             }
465
466             if (this.opens == 'left') {
467                 this.container.css({
468                     top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
469                     right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
470                     left: 'auto'
471                 });
472                 if (this.container.offset().left < 0) {
473                     this.container.css({
474                         right: 'auto',
475                         left: 9
476                     });
477                 }
478             } else {
479                 this.container.css({
480                     top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
481                     left: this.element.offset().left - parentOffset.left,
482                     right: 'auto'
483                 });
484                 if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
485                     this.container.css({
486                         left: 'auto',
487                         right: 0
488                     });
489                 }
490             }
491         },
492
493         toggle: function (e) {
494             if (this.element.hasClass('active')) {
495                 this.hide();
496             } else {
497                 this.show();
498             }
499         },
500
501         show: function (e) {
502             this.element.addClass('active');
503             this.container.show();
504             this.move();
505
506             // Create a click proxy that is private to this instance of datepicker, for unbinding
507             this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this);
508             // Bind global datepicker mousedown for hiding and
509             $(document)
510                 .on('mousedown.daterangepicker', this._outsideClickProxy)
511                 // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
512                 .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
513                 // and also close when focus changes to outside the picker (eg. tabbing between controls)
514                 .on('focusin.daterangepicker', this._outsideClickProxy);
515
516             this.element.trigger('show.daterangepicker', this);
517         },
518
519         outsideClick: function (e) {
520             var target = $(e.target);
521             // if the page is clicked anywhere except within the daterangerpicker/button
522             // itself then call this.hide()
523             if (
524                 target.closest(this.element).length ||
525                 target.closest(this.container).length ||
526                 target.closest('.calendar-date').length
527                 ) return;
528             this.hide();
529         },
530
531         hide: function (e) {
532             $(document)
533                 .off('mousedown.daterangepicker', this._outsideClickProxy)
534                 .off('click.daterangepicker', this._outsideClickProxy)
535                 .off('focusin.daterangepicker', this._outsideClickProxy);
536
537             this.element.removeClass('active');
538             this.container.hide();
539
540             if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
541                 this.notify();
542
543             this.oldStartDate = this.startDate.clone();
544             this.oldEndDate = this.endDate.clone();
545
546             this.element.trigger('hide.daterangepicker', this);
547         },
548
549         enterRange: function (e) {
550             // mouse pointer has entered a range label
551             var label = e.target.innerHTML;
552             if (label == this.locale.customRangeLabel) {
553                 this.updateView();
554             } else {
555                 var dates = this.ranges[label];
556                 this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.format));
557                 this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.format));
558             }
559         },
560
561         showCalendars: function() {
562             this.container.addClass('show-calendar');
563             this.move();
564         },
565
566         hideCalendars: function() {
567             this.container.removeClass('show-calendar');
568         },
569
570         updateInputText: function() {
571             if (this.element.is('input') && !this.singleDatePicker) {
572                 this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
573             } else if (this.element.is('input')) {
574                 this.element.val(this.startDate.format(this.format));
575             }
576         },
577
578         clickRange: function (e) {
579             var label = e.target.innerHTML;
580             this.chosenLabel = label;
581             if (label == this.locale.customRangeLabel) {
582                 this.showCalendars();
583             } else {
584                 var dates = this.ranges[label];
585
586                 this.startDate = dates[0];
587                 this.endDate = dates[1];
588
589                 if (!this.timePicker) {
590                     this.startDate.startOf('day');
591                     this.endDate.endOf('day');
592                 }
593
594                 this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
595                 this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
596                 this.updateCalendars();
597
598                 this.updateInputText();
599
600                 this.hideCalendars();
601                 this.hide();
602                 this.element.trigger('apply.daterangepicker', this);
603             }
604         },
605
606         clickPrev: function (e) {
607             var cal = $(e.target).parents('.calendar');
608             if (cal.hasClass('left')) {
609                 this.leftCalendar.month.subtract('month', 1);
610             } else {
611                 this.rightCalendar.month.subtract('month', 1);
612             }
613             this.updateCalendars();
614         },
615
616         clickNext: function (e) {
617             var cal = $(e.target).parents('.calendar');
618             if (cal.hasClass('left')) {
619                 this.leftCalendar.month.add('month', 1);
620             } else {
621                 this.rightCalendar.month.add('month', 1);
622             }
623             this.updateCalendars();
624         },
625
626         enterDate: function (e) {
627
628             var title = $(e.target).attr('data-title');
629             var row = title.substr(1, 1);
630             var col = title.substr(3, 1);
631             var cal = $(e.target).parents('.calendar');
632
633             if (cal.hasClass('left')) {
634                 this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].format(this.format));
635             } else {
636                 this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].format(this.format));
637             }
638
639         },
640
641         clickDate: function (e) {
642             var title = $(e.target).attr('data-title');
643             var row = title.substr(1, 1);
644             var col = title.substr(3, 1);
645             var cal = $(e.target).parents('.calendar');
646
647             var startDate, endDate;
648             if (cal.hasClass('left')) {
649                 startDate = this.leftCalendar.calendar[row][col];
650                 endDate = this.endDate;
651                 if (typeof this.dateLimit === 'object') {
652                     var maxDate = moment(startDate).add(this.dateLimit).startOf('day');
653                     if (endDate.isAfter(maxDate)) {
654                         endDate = maxDate;
655                     }
656                 }
657             } else {
658                 startDate = this.startDate;
659                 endDate = this.rightCalendar.calendar[row][col];
660                 if (typeof this.dateLimit === 'object') {
661                     var minDate = moment(endDate).subtract(this.dateLimit).startOf('day');
662                     if (startDate.isBefore(minDate)) {
663                         startDate = minDate;
664                     }
665                 }
666             }
667
668             if (this.singleDatePicker && cal.hasClass('left')) {
669                 endDate = startDate.clone();
670             } else if (this.singleDatePicker && cal.hasClass('right')) {
671                 startDate = endDate.clone();
672             }
673
674             cal.find('td').removeClass('active');
675
676             if (startDate.isSame(endDate) || startDate.isBefore(endDate)) {
677                 $(e.target).addClass('active');
678                 this.startDate = startDate;
679                 this.endDate = endDate;
680                 this.chosenLabel = this.locale.customRangeLabel;
681             } else if (startDate.isAfter(endDate)) {
682                 $(e.target).addClass('active');
683                 var difference = this.endDate.diff(this.startDate);
684                 this.startDate = startDate;
685                 this.endDate = moment(startDate).add('ms', difference);
686                 this.chosenLabel = this.locale.customRangeLabel;
687             }
688
689             this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
690             this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
691             this.updateCalendars();
692
693             if (!this.timePicker)
694                 endDate.endOf('day');
695
696             if (this.singleDatePicker)
697                 this.clickApply();
698         },
699
700         clickApply: function (e) {
701             this.updateInputText();
702             this.hide();
703             this.element.trigger('apply.daterangepicker', this);
704         },
705
706         clickCancel: function (e) {
707             this.startDate = this.oldStartDate;
708             this.endDate = this.oldEndDate;
709             this.chosenLabel = this.oldChosenLabel;
710             this.updateView();
711             this.updateCalendars();
712             this.hide();
713             this.element.trigger('cancel.daterangepicker', this);
714         },
715
716         updateMonthYear: function (e) {
717             var isLeft = $(e.target).closest('.calendar').hasClass('left'),
718                 leftOrRight = isLeft ? 'left' : 'right',
719                 cal = this.container.find('.calendar.'+leftOrRight);
720
721             // Month must be Number for new moment versions
722             var month = parseInt(cal.find('.monthselect').val(), 10);
723             var year = cal.find('.yearselect').val();
724
725             this[leftOrRight+'Calendar'].month.month(month).year(year);
726             this.updateCalendars();
727         },
728
729         updateTime: function(e) {
730
731             var cal = $(e.target).closest('.calendar'),
732                 isLeft = cal.hasClass('left');
733
734             var hour = parseInt(cal.find('.hourselect').val(), 10);
735             var minute = parseInt(cal.find('.minuteselect').val(), 10);
736
737             if (this.timePicker12Hour) {
738                 var ampm = cal.find('.ampmselect').val();
739                 if (ampm === 'PM' && hour < 12)
740                     hour += 12;
741                 if (ampm === 'AM' && hour === 12)
742                     hour = 0;
743             }
744
745             if (isLeft) {
746                 var start = this.startDate.clone();
747                 start.hour(hour);
748                 start.minute(minute);
749                 this.startDate = start;
750                 this.leftCalendar.month.hour(hour).minute(minute);
751             } else {
752                 var end = this.endDate.clone();
753                 end.hour(hour);
754                 end.minute(minute);
755                 this.endDate = end;
756                 this.rightCalendar.month.hour(hour).minute(minute);
757             }
758
759             this.updateCalendars();
760         },
761
762         updateCalendars: function () {
763             this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.month(), this.leftCalendar.month.year(), this.leftCalendar.month.hour(), this.leftCalendar.month.minute(), 'left');
764             this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.month(), this.rightCalendar.month.year(), this.rightCalendar.month.hour(), this.rightCalendar.month.minute(), 'right');
765             this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate));
766             this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.startDate, this.maxDate));
767
768             this.container.find('.ranges li').removeClass('active');
769             var customRange = true;
770             var i = 0;
771             for (var range in this.ranges) {
772                 if (this.timePicker) {
773                     if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
774                         customRange = false;
775                         this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
776                             .addClass('active').html();
777                     }
778                 } else {
779                     //ignore times when comparing dates if time picker is not enabled
780                     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')) {
781                         customRange = false;
782                         this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
783                             .addClass('active').html();
784                     }
785                 }
786                 i++;
787             }
788             if (customRange) {
789                 this.chosenLabel = this.container.find('.ranges li:last')
790                     .addClass('active').html();
791             }
792         },
793
794         buildCalendar: function (month, year, hour, minute, side) {
795             var firstDay = moment([year, month, 1]);
796             var lastMonth = moment(firstDay).subtract('month', 1).month();
797             var lastYear = moment(firstDay).subtract('month', 1).year();
798
799             var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
800
801             var dayOfWeek = firstDay.day();
802
803             var i;
804
805             //initialize a 6 rows x 7 columns array for the calendar
806             var calendar = [];
807             for (i = 0; i < 6; i++) {
808                 calendar[i] = [];
809             }
810
811             //populate the calendar with date objects
812             var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
813             if (startDay > daysInLastMonth)
814                 startDay -= 7;
815
816             if (dayOfWeek == this.locale.firstDay)
817                 startDay = daysInLastMonth - 6;
818
819             var curDate = moment([lastYear, lastMonth, startDay, 12, minute]);
820             var col, row;
821             for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add('hour', 24)) {
822                 if (i > 0 && col % 7 === 0) {
823                     col = 0;
824                     row++;
825                 }
826                 calendar[row][col] = curDate.clone().hour(hour);
827                 curDate.hour(12);
828             }
829
830             return calendar;
831         },
832
833         renderDropdowns: function (selected, minDate, maxDate) {
834             var currentMonth = selected.month();
835             var monthHtml = '<select class="monthselect">';
836             var inMinYear = false;
837             var inMaxYear = false;
838
839             for (var m = 0; m < 12; m++) {
840                 if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
841                     monthHtml += "<option value='" + m + "'" +
842                         (m === currentMonth ? " selected='selected'" : "") +
843                         ">" + this.locale.monthNames[m] + "</option>";
844                 }
845             }
846             monthHtml += "</select>";
847
848             var currentYear = selected.year();
849             var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
850             var minYear = (minDate && minDate.year()) || (currentYear - 50);
851             var yearHtml = '<select class="yearselect">';
852
853             for (var y = minYear; y <= maxYear; y++) {
854                 yearHtml += '<option value="' + y + '"' +
855                     (y === currentYear ? ' selected="selected"' : '') +
856                     '>' + y + '</option>';
857             }
858
859             yearHtml += '</select>';
860
861             return monthHtml + yearHtml;
862         },
863
864         renderCalendar: function (calendar, selected, minDate, maxDate) {
865
866             var html = '<div class="calendar-date">';
867             html += '<table class="table-condensed">';
868             html += '<thead>';
869             html += '<tr>';
870
871             // add empty cell for week number
872             if (this.showWeekNumbers)
873                 html += '<th></th>';
874
875             if (!minDate || minDate.isBefore(calendar[1][1])) {
876                 html += '<th class="prev available"><i class="fa fa-arrow-left icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
877             } else {
878                 html += '<th></th>';
879             }
880
881             var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
882
883             if (this.showDropdowns) {
884                 dateHtml = this.renderDropdowns(calendar[1][1], minDate, maxDate);
885             }
886
887             html += '<th colspan="5" class="month">' + dateHtml + '</th>';
888             if (!maxDate || maxDate.isAfter(calendar[1][1])) {
889                 html += '<th class="next available"><i class="fa fa-arrow-right icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
890             } else {
891                 html += '<th></th>';
892             }
893
894             html += '</tr>';
895             html += '<tr>';
896
897             // add week number label
898             if (this.showWeekNumbers)
899                 html += '<th class="week">' + this.locale.weekLabel + '</th>';
900
901             $.each(this.locale.daysOfWeek, function (index, dayOfWeek) {
902                 html += '<th>' + dayOfWeek + '</th>';
903             });
904
905             html += '</tr>';
906             html += '</thead>';
907             html += '<tbody>';
908
909             for (var row = 0; row < 6; row++) {
910                 html += '<tr>';
911
912                 // add week number
913                 if (this.showWeekNumbers)
914                     html += '<td class="week">' + calendar[row][0].week() + '</td>';
915
916                 for (var col = 0; col < 7; col++) {
917                     var cname = 'available ';
918                     cname += (calendar[row][col].month() == calendar[1][1].month()) ? '' : 'off';
919
920                     if ((minDate && calendar[row][col].isBefore(minDate, 'day')) || (maxDate && calendar[row][col].isAfter(maxDate, 'day'))) {
921                         cname = ' off disabled ';
922                     } else if (calendar[row][col].format('YYYY-MM-DD') == selected.format('YYYY-MM-DD')) {
923                         cname += ' active ';
924                         if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) {
925                             cname += ' start-date ';
926                         }
927                         if (calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) {
928                             cname += ' end-date ';
929                         }
930                     } else if (calendar[row][col] >= this.startDate && calendar[row][col] <= this.endDate) {
931                         cname += ' in-range ';
932                         if (calendar[row][col].isSame(this.startDate)) { cname += ' start-date '; }
933                         if (calendar[row][col].isSame(this.endDate)) { cname += ' end-date '; }
934                     }
935
936                     var title = 'r' + row + 'c' + col;
937                     html += '<td class="' + cname.replace(/\s+/g, ' ').replace(/^\s?(.*?)\s?$/, '$1') + '" data-title="' + title + '">' + calendar[row][col].date() + '</td>';
938                 }
939                 html += '</tr>';
940             }
941
942             html += '</tbody>';
943             html += '</table>';
944             html += '</div>';
945
946             var i;
947             if (this.timePicker) {
948
949                 html += '<div class="calendar-time">';
950                 html += '<select class="hourselect">';
951                 var start = 0;
952                 var end = 23;
953                 var selected_hour = selected.hour();
954                 if (this.timePicker12Hour) {
955                     start = 1;
956                     end = 12;
957                     if (selected_hour >= 12)
958                         selected_hour -= 12;
959                     if (selected_hour === 0)
960                         selected_hour = 12;
961                 }
962
963                 for (i = start; i <= end; i++) {
964                     if (i == selected_hour) {
965                         html += '<option value="' + i + '" selected="selected">' + i + '</option>';
966                     } else {
967                         html += '<option value="' + i + '">' + i + '</option>';
968                     }
969                 }
970
971                 html += '</select> : ';
972
973                 html += '<select class="minuteselect">';
974
975                 for (i = 0; i < 60; i += this.timePickerIncrement) {
976                     var num = i;
977                     if (num < 10)
978                         num = '0' + num;
979                     if (i == selected.minute()) {
980                         html += '<option value="' + i + '" selected="selected">' + num + '</option>';
981                     } else {
982                         html += '<option value="' + i + '">' + num + '</option>';
983                     }
984                 }
985
986                 html += '</select> ';
987
988                 if (this.timePicker12Hour) {
989                     html += '<select class="ampmselect">';
990                     if (selected.hour() >= 12) {
991                         html += '<option value="AM">AM</option><option value="PM" selected="selected">PM</option>';
992                     } else {
993                         html += '<option value="AM" selected="selected">AM</option><option value="PM">PM</option>';
994                     }
995                     html += '</select>';
996                 }
997
998                 html += '</div>';
999
1000             }
1001
1002             return html;
1003
1004         },
1005
1006         remove: function() {
1007
1008             this.container.remove();
1009             this.element.off('.daterangepicker');
1010             this.element.removeData('daterangepicker');
1011
1012         }
1013
1014     };
1015
1016     $.fn.daterangepicker = function (options, cb) {
1017         this.each(function () {
1018             var el = $(this);
1019             if (el.data('daterangepicker'))
1020                 el.data('daterangepicker').remove();
1021             el.data('daterangepicker', new DateRangePicker(el, options, cb));
1022         });
1023         return this;
1024     };
1025
1026 }(window.jQuery, window.moment);