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