app/assets/javascripts/blazer/daterangepicker.js in blazer-2.2.6 vs app/assets/javascripts/blazer/daterangepicker.js in blazer-2.2.7

- old
+ new

@@ -1,18 +1,19 @@ /** -* @version: 2.1.27 +* @version: 3.1 * @author: Dan Grossman http://www.dangrossman.info/ -* @copyright: Copyright (c) 2012-2017 Dan Grossman. All rights reserved. +* @copyright: Copyright (c) 2012-2019 Dan Grossman. All rights reserved. * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php * @website: http://www.daterangepicker.com/ */ -// Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js +// Following the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Make globaly available as well define(['moment', 'jquery'], function (moment, jquery) { if (!jquery.fn) jquery.fn = {}; // webpack server rendering + if (typeof moment !== 'function' && moment.hasOwnProperty('default')) moment = moment['default'] return factory(moment, jquery); }); } else if (typeof module === 'object' && module.exports) { // Node / Browserify //isomorphic issue @@ -35,14 +36,16 @@ this.element = $(element); this.startDate = moment().startOf('day'); this.endDate = moment().endOf('day'); this.minDate = false; this.maxDate = false; - this.dateLimit = false; + this.maxSpan = false; this.autoApply = false; this.singleDatePicker = false; this.showDropdowns = false; + this.minYear = moment().subtract(100, 'year').format('YYYY'); + this.maxYear = moment().add(100, 'year').format('YYYY'); this.showWeekNumbers = false; this.showISOWeekNumbers = false; this.showCustomRangeLabel = true; this.timePicker = false; this.timePicker24Hour = false; @@ -60,12 +63,12 @@ this.drops = 'down'; if (this.element.hasClass('dropup')) this.drops = 'up'; this.buttonClasses = 'btn btn-sm'; - this.applyClass = 'btn-success'; - this.cancelClass = 'btn-default'; + this.applyButtonClasses = 'btn-primary'; + this.cancelButtonClasses = 'btn-default'; this.locale = { direction: 'ltr', format: moment.localeData().longDateFormat('L'), separator: ' - ', @@ -93,38 +96,25 @@ //data-api options will be overwritten with custom javascript options options = $.extend(this.element.data(), options); //html template for the picker UI if (typeof options.template !== 'string' && !(options.template instanceof $)) - options.template = '<div class="daterangepicker dropdown-menu">' + - '<div class="calendar left">' + - '<div class="daterangepicker_input">' + - '<input class="input-mini form-control" type="text" name="daterangepicker_start" value="" />' + - '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' + - '<div class="calendar-time">' + - '<div></div>' + - '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' + - '</div>' + - '</div>' + + options.template = + '<div class="daterangepicker">' + + '<div class="ranges"></div>' + + '<div class="drp-calendar left">' + '<div class="calendar-table"></div>' + + '<div class="calendar-time"></div>' + '</div>' + - '<div class="calendar right">' + - '<div class="daterangepicker_input">' + - '<input class="input-mini form-control" type="text" name="daterangepicker_end" value="" />' + - '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' + - '<div class="calendar-time">' + - '<div></div>' + - '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' + - '</div>' + - '</div>' + + '<div class="drp-calendar right">' + '<div class="calendar-table"></div>' + + '<div class="calendar-time"></div>' + '</div>' + - '<div class="ranges">' + - '<div class="range_inputs">' + - '<button class="applyBtn" disabled="disabled" type="button"></button> ' + - '<button class="cancelBtn" type="button"></button>' + - '</div>' + + '<div class="drp-buttons">' + + '<span class="drp-selected"></span>' + + '<button class="cancelBtn" type="button"></button>' + + '<button class="applyBtn" disabled="disabled" type="button"></button> ' + '</div>' + '</div>'; this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl); this.container = $(options.template).appendTo(this.parentEl); @@ -202,19 +192,28 @@ // sanity check for bad options if (this.maxDate && this.endDate.isAfter(this.maxDate)) this.endDate = this.maxDate.clone(); - if (typeof options.applyClass === 'string') - this.applyClass = options.applyClass; + if (typeof options.applyButtonClasses === 'string') + this.applyButtonClasses = options.applyButtonClasses; - if (typeof options.cancelClass === 'string') - this.cancelClass = options.cancelClass; + if (typeof options.applyClass === 'string') //backwards compat + this.applyButtonClasses = options.applyClass; - if (typeof options.dateLimit === 'object') - this.dateLimit = options.dateLimit; + if (typeof options.cancelButtonClasses === 'string') + this.cancelButtonClasses = options.cancelButtonClasses; + if (typeof options.cancelClass === 'string') //backwards compat + this.cancelButtonClasses = options.cancelClass; + + if (typeof options.maxSpan === 'object') + this.maxSpan = options.maxSpan; + + if (typeof options.dateLimit === 'object') //backwards compat + this.maxSpan = options.dateLimit; + if (typeof options.opens === 'string') this.opens = options.opens; if (typeof options.drops === 'string') this.drops = options.drops; @@ -232,10 +231,16 @@ this.buttonClasses = options.buttonClasses.join(' '); if (typeof options.showDropdowns === 'boolean') this.showDropdowns = options.showDropdowns; + if (typeof options.minYear === 'number') + this.minYear = options.minYear; + + if (typeof options.maxYear === 'number') + this.maxYear = options.maxYear; + if (typeof options.showCustomRangeLabel === 'boolean') this.showCustomRangeLabel = options.showCustomRangeLabel; if (typeof options.singleDatePicker === 'boolean') { this.singleDatePicker = options.singleDatePicker; @@ -284,11 +289,11 @@ var start, end, range; //if no start/end dates set, check if an input element contains initial values if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') { - if ($(this.element).is('input[type=text]')) { + if ($(this.element).is(':text')) { var val = $(this.element).val(), split = val.split(this.locale.separator); start = end = null; @@ -317,24 +322,24 @@ if (typeof options.ranges[range][1] === 'string') end = moment(options.ranges[range][1], this.locale.format); else end = moment(options.ranges[range][1]); - // If the start or end date exceed those allowed by the minDate or dateLimit + // If the start or end date exceed those allowed by the minDate or maxSpan // options, shorten the range to the allowable period. if (this.minDate && start.isBefore(this.minDate)) start = this.minDate.clone(); var maxDate = this.maxDate; - if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate)) - maxDate = start.clone().add(this.dateLimit); + if (this.maxSpan && maxDate && start.clone().add(this.maxSpan).isAfter(maxDate)) + maxDate = start.clone().add(this.maxSpan); if (maxDate && end.isAfter(maxDate)) end = maxDate.clone(); // If the end of the range is before the minimum or the start of the range is // after the maximum, don't display this range option at all. - if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day')) + if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day')) || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day'))) continue; //Support unicode chars in the range names. var elem = document.createElement('textarea'); @@ -367,74 +372,61 @@ //can't be used together for now if (this.timePicker && this.autoApply) this.autoApply = false; - if (this.autoApply && typeof options.ranges !== 'object') { - this.container.find('.ranges').hide(); - } else if (this.autoApply) { - this.container.find('.applyBtn, .cancelBtn').addClass('hide'); + if (this.autoApply) { + this.container.addClass('auto-apply'); } + if (typeof options.ranges === 'object') + this.container.addClass('show-ranges'); + if (this.singleDatePicker) { this.container.addClass('single'); - this.container.find('.calendar.left').addClass('single'); - this.container.find('.calendar.left').show(); - this.container.find('.calendar.right').hide(); - this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide(); - if (this.timePicker) { - this.container.find('.ranges ul').hide(); - } else { - this.container.find('.ranges').hide(); + this.container.find('.drp-calendar.left').addClass('single'); + this.container.find('.drp-calendar.left').show(); + this.container.find('.drp-calendar.right').hide(); + if (!this.timePicker && this.autoApply) { + this.container.addClass('auto-apply'); } } if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) { this.container.addClass('show-calendar'); } this.container.addClass('opens' + this.opens); - //swap the position of the predefined ranges if opens right - if (typeof options.ranges !== 'undefined' && this.opens == 'right') { - this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() ); - } - //apply CSS classes and labels to buttons this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses); - if (this.applyClass.length) - this.container.find('.applyBtn').addClass(this.applyClass); - if (this.cancelClass.length) - this.container.find('.cancelBtn').addClass(this.cancelClass); + if (this.applyButtonClasses.length) + this.container.find('.applyBtn').addClass(this.applyButtonClasses); + if (this.cancelButtonClasses.length) + this.container.find('.cancelBtn').addClass(this.cancelButtonClasses); this.container.find('.applyBtn').html(this.locale.applyLabel); this.container.find('.cancelBtn').html(this.locale.cancelLabel); // // event listeners // - this.container.find('.calendar') + this.container.find('.drp-calendar') .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this)) .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this)) .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this)) .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this)) - .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this)) .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this)) .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this)) - .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this)) - .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this)) - .on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this)) - .on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this)) - .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this)) - .on('keydown.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsKeydown, this)); + .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this)); this.container.find('.ranges') + .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this)); + + this.container.find('.drp-buttons') .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this)) - .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this)) - .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this)) - .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this)) - .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this)); + .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this)); if (this.element.is('input') || this.element.is('button')) { this.element.on({ 'click.daterangepicker': $.proxy(this.show, this), 'focus.daterangepicker': $.proxy(this.show, this), @@ -448,17 +440,11 @@ // // if attached to a text input, set the initial value // - if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); - this.element.trigger('change'); - } else if (this.element.is('input') && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format)); - this.element.trigger('change'); - } + this.updateElement(); }; DateRangePicker.prototype = { @@ -501,26 +487,28 @@ if (typeof endDate === 'object') this.endDate = moment(endDate); if (!this.timePicker) - this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second'); + this.endDate = this.endDate.endOf('day'); if (this.timePicker && this.timePickerIncrement) this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); if (this.endDate.isBefore(this.startDate)) this.endDate = this.startDate.clone(); if (this.maxDate && this.endDate.isAfter(this.maxDate)) this.endDate = this.maxDate.clone(); - if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate)) - this.endDate = this.startDate.clone().add(this.dateLimit); + if (this.maxSpan && this.startDate.clone().add(this.maxSpan).isBefore(this.endDate)) + this.endDate = this.startDate.clone().add(this.maxSpan); this.previousRightTime = this.endDate.clone(); + this.container.find('.drp-selected').html(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); + if (!this.isShowing) this.updateElement(); this.updateMonthsInView(); }, @@ -536,22 +524,17 @@ updateView: function() { if (this.timePicker) { this.renderTimePicker('left'); this.renderTimePicker('right'); if (!this.endDate) { - this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled'); + this.container.find('.right .calendar-time select').prop('disabled', true).addClass('disabled'); } else { - this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled'); + this.container.find('.right .calendar-time select').prop('disabled', false).removeClass('disabled'); } } - if (this.endDate) { - this.container.find('input[name="daterangepicker_end"]').removeClass('active'); - this.container.find('input[name="daterangepicker_start"]').addClass('active'); - } else { - this.container.find('input[name="daterangepicker_end"]').addClass('active'); - this.container.find('input[name="daterangepicker_start"]').removeClass('active'); - } + if (this.endDate) + this.container.find('.drp-selected').html(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); this.updateMonthsInView(); this.updateCalendars(); this.updateFormInputs(); }, @@ -591,10 +574,13 @@ if (this.timePicker) { var hour, minute, second; if (this.endDate) { hour = parseInt(this.container.find('.left .hourselect').val(), 10); minute = parseInt(this.container.find('.left .minuteselect').val(), 10); + if (isNaN(minute)) { + minute = parseInt(this.container.find('.left .minuteselect option:last').val(), 10); + } second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; if (!this.timePicker24Hour) { var ampm = this.container.find('.left .ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; @@ -602,10 +588,13 @@ hour = 0; } } else { hour = parseInt(this.container.find('.right .hourselect').val(), 10); minute = parseInt(this.container.find('.right .minuteselect').val(), 10); + if (isNaN(minute)) { + minute = parseInt(this.container.find('.right .minuteselect option:last').val(), 10); + } second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; if (!this.timePicker24Hour) { var ampm = this.container.find('.right .ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; @@ -708,28 +697,28 @@ // add empty cell for week number if (this.showWeekNumbers || this.showISOWeekNumbers) html += '<th></th>'; if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) { - html += '<th class="prev available"><i class="fa fa-' + arrow.left + ' glyphicon glyphicon-' + arrow.left + '"></i></th>'; + html += '<th class="prev available"><span></span></th>'; } else { html += '<th></th>'; } var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY"); if (this.showDropdowns) { var currentMonth = calendar[1][1].month(); var currentYear = calendar[1][1].year(); - var maxYear = (maxDate && maxDate.year()) || (currentYear + 5); - var minYear = (minDate && minDate.year()) || (currentYear - 50); + var maxYear = (maxDate && maxDate.year()) || (this.maxYear); + var minYear = (minDate && minDate.year()) || (this.minYear); var inMinYear = currentYear == minYear; var inMaxYear = currentYear == maxYear; var monthHtml = '<select class="monthselect">'; for (var m = 0; m < 12; m++) { - if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) { + if ((!inMinYear || (minDate && m >= minDate.month())) && (!inMaxYear || (maxDate && m <= maxDate.month()))) { monthHtml += "<option value='" + m + "'" + (m === currentMonth ? " selected='selected'" : "") + ">" + this.locale.monthNames[m] + "</option>"; } else { monthHtml += "<option value='" + m + "'" + @@ -750,11 +739,11 @@ dateHtml = monthHtml + yearHtml; } html += '<th colspan="5" class="month">' + dateHtml + '</th>'; if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) { - html += '<th class="next available"><i class="fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '"></i></th>'; + html += '<th class="next available"><span></span></th>'; } else { html += '<th></th>'; } html += '</tr>'; @@ -770,14 +759,14 @@ html += '</tr>'; html += '</thead>'; html += '<tbody>'; - //adjust maxDate to reflect the dateLimit setting in order to - //grey out end dates beyond the dateLimit - if (this.endDate == null && this.dateLimit) { - var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day'); + //adjust maxDate to reflect the maxSpan setting in order to + //grey out end dates beyond the maxSpan + if (this.endDate == null && this.maxSpan) { + var maxLimit = this.startDate.clone().add(this.maxSpan).endOf('day'); if (!maxDate || maxLimit.isBefore(maxDate)) { maxDate = maxLimit; } } @@ -802,11 +791,11 @@ if (calendar[row][col].isoWeekday() > 5) classes.push('weekend'); //grey out the dates in other months displayed at beginning and end of this calendar if (calendar[row][col].month() != calendar[1][1].month()) - classes.push('off'); + classes.push('off', 'ends'); //don't allow selection of dates before the minimum date if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day')) classes.push('off', 'disabled'); @@ -855,11 +844,11 @@ } html += '</tbody>'; html += '</table>'; - this.container.find('.calendar.' + side + ' .calendar-table').html(html); + this.container.find('.drp-calendar.' + side + ' .calendar-table').html(html); }, renderTimePicker: function(side) { @@ -867,27 +856,27 @@ // because an end date hasn't been clicked yet if (side == 'right' && !this.endDate) return; var html, selected, minDate, maxDate = this.maxDate; - if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate))) - maxDate = this.startDate.clone().add(this.dateLimit); + if (this.maxSpan && (!this.maxDate || this.startDate.clone().add(this.maxSpan).isBefore(this.maxDate))) + maxDate = this.startDate.clone().add(this.maxSpan); if (side == 'left') { selected = this.startDate.clone(); minDate = this.minDate; } else if (side == 'right') { selected = this.endDate.clone(); minDate = this.startDate; //Preserve the time already selected - var timeSelector = this.container.find('.calendar.right .calendar-time div'); + var timeSelector = this.container.find('.drp-calendar.right .calendar-time'); if (timeSelector.html() != '') { - selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour()); - selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute()); - selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second()); + selected.hour(!isNaN(selected.hour()) ? selected.hour() : timeSelector.find('.hourselect option:selected').val()); + selected.minute(!isNaN(selected.minute()) ? selected.minute() : timeSelector.find('.minuteselect option:selected').val()); + selected.second(!isNaN(selected.second()) ? selected.second() : timeSelector.find('.secondselect option:selected').val()); if (!this.timePicker24Hour) { var ampm = timeSelector.find('.ampmselect option:selected').val(); if (ampm === 'PM' && selected.hour() < 12) selected.hour(selected.hour() + 12); @@ -1016,86 +1005,115 @@ } html += '</select>'; } - this.container.find('.calendar.' + side + ' .calendar-time div').html(html); + this.container.find('.drp-calendar.' + side + ' .calendar-time').html(html); }, updateFormInputs: function() { - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) - return; - - this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format)); - if (this.endDate) - this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format)); - if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) { - this.container.find('button.applyBtn').removeAttr('disabled'); + this.container.find('button.applyBtn').prop('disabled', false); } else { - this.container.find('button.applyBtn').attr('disabled', 'disabled'); + this.container.find('button.applyBtn').prop('disabled', true); } }, move: function() { var parentOffset = { top: 0, left: 0 }, - containerTop; + containerTop, + drops = this.drops; + var parentRightEdge = $(window).width(); if (!this.parentEl.is('body')) { parentOffset = { top: this.parentEl.offset().top - this.parentEl.scrollTop(), left: this.parentEl.offset().left - this.parentEl.scrollLeft() }; parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left; } - if (this.drops == 'up') + switch (drops) { + case 'auto': + containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; + if (containerTop + this.container.outerHeight() >= this.parentEl[0].scrollHeight) { + containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; + drops = 'up'; + } + break; + case 'up': containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; - else + break; + default: containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; - this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup'); + break; + } + // Force the container to it's actual width + this.container.css({ + top: 0, + left: 0, + right: 'auto' + }); + var containerWidth = this.container.outerWidth(); + + this.container.toggleClass('drop-up', drops == 'up'); + if (this.opens == 'left') { - this.container.css({ - top: containerTop, - right: parentRightEdge - this.element.offset().left - this.element.outerWidth(), - left: 'auto' - }); - if (this.container.offset().left < 0) { + var containerRight = parentRightEdge - this.element.offset().left - this.element.outerWidth(); + if (containerWidth + containerRight > $(window).width()) { this.container.css({ + top: containerTop, right: 'auto', left: 9 }); + } else { + this.container.css({ + top: containerTop, + right: containerRight, + left: 'auto' + }); } } else if (this.opens == 'center') { - this.container.css({ - top: containerTop, - left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 - - this.container.outerWidth() / 2, - right: 'auto' - }); - if (this.container.offset().left < 0) { + var containerLeft = this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 + - containerWidth / 2; + if (containerLeft < 0) { this.container.css({ + top: containerTop, right: 'auto', left: 9 }); + } else if (containerLeft + containerWidth > $(window).width()) { + this.container.css({ + top: containerTop, + left: 'auto', + right: 0 + }); + } else { + this.container.css({ + top: containerTop, + left: containerLeft, + right: 'auto' + }); } } else { - this.container.css({ - top: containerTop, - left: this.element.offset().left - parentOffset.left, - right: 'auto' - }); - if (this.container.offset().left + this.container.outerWidth() > $(window).width()) { + var containerLeft = this.element.offset().left - parentOffset.left; + if (containerLeft + containerWidth > $(window).width()) { this.container.css({ + top: containerTop, left: 'auto', right: 0 }); + } else { + this.container.css({ + top: containerTop, + left: containerLeft, + right: 'auto' + }); } } }, show: function(e) { @@ -1137,11 +1155,11 @@ this.endDate = this.oldEndDate.clone(); } //if a new date range was selected, invoke the user callback function if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate)) - this.callback(this.startDate, this.endDate, this.chosenLabel); + this.callback(this.startDate.clone(), this.endDate.clone(), this.chosenLabel); //if picker is attached to a text input, update it this.updateElement(); $(document).off('.daterangepicker'); @@ -1183,28 +1201,10 @@ hideCalendars: function() { this.container.removeClass('show-calendar'); this.element.trigger('hideCalendar.daterangepicker', this); }, - hoverRange: function(e) { - - //ignore mouse movements while an above-calendar text input has focus - if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) - return; - - var label = e.target.getAttribute('data-range-key'); - - if (label == this.locale.customRangeLabel) { - this.updateView(); - } else { - var dates = this.ranges[label]; - this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format)); - this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format)); - } - - }, - clickRange: function(e) { var label = e.target.getAttribute('data-range-key'); this.chosenLabel = label; if (label == this.locale.customRangeLabel) { this.showCalendars(); @@ -1223,11 +1223,11 @@ this.clickApply(); } }, clickPrev: function(e) { - var cal = $(e.target).parents('.calendar'); + var cal = $(e.target).parents('.drp-calendar'); if (cal.hasClass('left')) { this.leftCalendar.month.subtract(1, 'month'); if (this.linkedCalendars) this.rightCalendar.month.subtract(1, 'month'); } else { @@ -1235,11 +1235,11 @@ } this.updateCalendars(); }, clickNext: function(e) { - var cal = $(e.target).parents('.calendar'); + var cal = $(e.target).parents('.drp-calendar'); if (cal.hasClass('left')) { this.leftCalendar.month.add(1, 'month'); } else { this.rightCalendar.month.add(1, 'month'); if (this.linkedCalendars) @@ -1248,44 +1248,33 @@ this.updateCalendars(); }, hoverDate: function(e) { - //ignore mouse movements while an above-calendar text input has focus - //if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) - // return; - //ignore dates that can't be selected if (!$(e.target).hasClass('available')) return; - //have the text inputs above calendars reflect the date being hovered over var title = $(e.target).attr('data-title'); var row = title.substr(1, 1); var col = title.substr(3, 1); - var cal = $(e.target).parents('.calendar'); + var cal = $(e.target).parents('.drp-calendar'); var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; - if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) { - this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format)); - } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) { - this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format)); - } - //highlight the dates between the start date and the date being hovered as a potential end date var leftCalendar = this.leftCalendar; var rightCalendar = this.rightCalendar; var startDate = this.startDate; if (!this.endDate) { - this.container.find('.calendar tbody td').each(function(index, el) { + this.container.find('.drp-calendar tbody td').each(function(index, el) { //skip week numbers, only look at dates if ($(el).hasClass('week')) return; var title = $(el).attr('data-title'); var row = title.substr(1, 1); var col = title.substr(3, 1); - var cal = $(el).parents('.calendar'); + var cal = $(el).parents('.drp-calendar'); var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col]; if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) { $(el).addClass('in-range'); } else { @@ -1302,11 +1291,11 @@ if (!$(e.target).hasClass('available')) return; var title = $(e.target).attr('data-title'); var row = title.substr(1, 1); var col = title.substr(3, 1); - var cal = $(e.target).parents('.calendar'); + var cal = $(e.target).parents('.drp-calendar'); var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; // // this function needs to do a few things: // * alternate between selecting a start and end date for the range, @@ -1325,10 +1314,13 @@ hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } var minute = parseInt(this.container.find('.left .minuteselect').val(), 10); + if (isNaN(minute)) { + minute = parseInt(this.container.find('.left .minuteselect option:last').val(), 10); + } var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; date = date.clone().hour(hour).minute(minute).second(second); } this.endDate = null; this.setStartDate(date.clone()); @@ -1345,10 +1337,13 @@ hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } var minute = parseInt(this.container.find('.right .minuteselect').val(), 10); + if (isNaN(minute)) { + minute = parseInt(this.container.find('.right .minuteselect option:last').val(), 10); + } var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; date = date.clone().hour(hour).minute(minute).second(second); } this.setEndDate(date.clone()); if (this.autoApply) { @@ -1357,11 +1352,11 @@ } } if (this.singleDatePicker) { this.setEndDate(this.startDate); - if (!this.timePicker) + if (!this.timePicker && this.autoApply) this.clickApply(); } this.updateView(); @@ -1373,30 +1368,30 @@ calculateChosenLabel: function () { var customRange = true; var i = 0; for (var range in this.ranges) { if (this.timePicker) { - var format = this.timePickerSeconds ? "YYYY-MM-DD hh:mm:ss" : "YYYY-MM-DD hh:mm"; + var format = this.timePickerSeconds ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD HH:mm"; //ignore times when comparing dates if time picker seconds is not enabled if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) { customRange = false; - this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); + this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').attr('data-range-key'); break; } } else { //ignore times when comparing dates if time picker is not enabled 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')) { customRange = false; - this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); + this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').attr('data-range-key'); break; } } i++; } if (customRange) { if (this.showCustomRangeLabel) { - this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html(); + this.chosenLabel = this.container.find('.ranges li:last').addClass('active').attr('data-range-key'); } else { this.chosenLabel = null; } this.showCalendars(); } @@ -1413,13 +1408,13 @@ this.hide(); this.element.trigger('cancel.daterangepicker', this); }, monthOrYearChanged: function(e) { - var isLeft = $(e.target).closest('.calendar').hasClass('left'), + var isLeft = $(e.target).closest('.drp-calendar').hasClass('left'), leftOrRight = isLeft ? 'left' : 'right', - cal = this.container.find('.calendar.'+leftOrRight); + cal = this.container.find('.drp-calendar.'+leftOrRight); // Month must be Number for new moment versions var month = parseInt(cal.find('.monthselect').val(), 10); var year = cal.find('.yearselect').val(); @@ -1456,15 +1451,18 @@ this.updateCalendars(); }, timeChanged: function(e) { - var cal = $(e.target).closest('.calendar'), + var cal = $(e.target).closest('.drp-calendar'), isLeft = cal.hasClass('left'); var hour = parseInt(cal.find('.hourselect').val(), 10); var minute = parseInt(cal.find('.minuteselect').val(), 10); + if (isNaN(minute)) { + minute = parseInt(cal.find('.minuteselect option:last').val(), 10); + } var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0; if (!this.timePicker24Hour) { var ampm = cal.find('.ampmselect').val(); if (ampm === 'PM' && hour < 12) @@ -1502,85 +1500,10 @@ this.renderTimePicker('left'); this.renderTimePicker('right'); }, - formInputsChanged: function(e) { - var isRight = $(e.target).closest('.calendar').hasClass('right'); - var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format); - var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format); - - if (start.isValid() && end.isValid()) { - - if (isRight && end.isBefore(start)) - start = end.clone(); - - this.setStartDate(start); - this.setEndDate(end); - - if (isRight) { - this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format)); - } else { - this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format)); - } - - } - - this.updateView(); - }, - - formInputsFocused: function(e) { - - // Highlight the focused input - this.container.find('input[name="daterangepicker_start"], input[name="daterangepicker_end"]').removeClass('active'); - $(e.target).addClass('active'); - - // Set the state such that if the user goes back to using a mouse, - // the calendars are aware we're selecting the end of the range, not - // the start. This allows someone to edit the end of a date range without - // re-selecting the beginning, by clicking on the end date input then - // using the calendar. - var isRight = $(e.target).closest('.calendar').hasClass('right'); - if (isRight) { - this.endDate = null; - this.setStartDate(this.startDate.clone()); - this.updateView(); - } - - }, - - formInputsBlurred: function(e) { - - // this function has one purpose right now: if you tab from the first - // text input to the second in the UI, the endDate is nulled so that - // you can click another, but if you tab out without clicking anything - // or changing the input value, the old endDate should be retained - - if (!this.endDate) { - var val = this.container.find('input[name="daterangepicker_end"]').val(); - var end = moment(val, this.locale.format); - if (end.isValid()) { - this.setEndDate(end); - this.updateView(); - } - } - - }, - - formInputsKeydown: function(e) { - // This function ensures that if the 'enter' key was pressed in the input, then the calendars - // are updated with the startDate and endDate. - // This behaviour is automatic in Chrome/Firefox/Edge but not in IE 11 hence why this exists. - // Other browsers and versions of IE are untested and the behaviour is unknown. - if (e.keyCode === 13) { - // Prevent the calendar from being updated twice on Chrome/Firefox/Edge - e.preventDefault(); - this.formInputsChanged(e); - } - }, - - elementChanged: function() { if (!this.element.is('input')) return; if (!this.element.val().length) return; var dateString = this.element.val().split(this.locale.separator), @@ -1618,15 +1541,17 @@ this.hide(); } }, updateElement: function() { - if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); - this.element.trigger('change'); - } else if (this.element.is('input') && this.autoUpdateInput) { - this.element.val(this.startDate.format(this.locale.format)); - this.element.trigger('change'); + if (this.element.is('input') && this.autoUpdateInput) { + var newValue = this.startDate.format(this.locale.format); + if (!this.singleDatePicker) { + newValue += this.locale.separator + this.endDate.format(this.locale.format); + } + if (newValue !== this.element.val()) { + this.element.val(newValue).trigger('change'); + } } }, remove: function() { this.container.remove();