Repository: alumuko/vanilla-datetimerange-picker
Branch: main
Commit: 32608b3e2f17
Files: 10
Total size: 119.4 KB
Directory structure:
gitextract_xgd1tudv/
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── dist/
│ ├── vanilla-datetimerange-picker.css
│ └── vanilla-datetimerange-picker.js
├── examples/
│ ├── datetime-example-dark.html
│ ├── datetime-example-simple.html
│ └── datetime-example.html
└── extra/
└── vanilla-datetimerange-picker-dark.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
dist/* linguist-documentation=false
dist/* linguist-vendored=false
================================================
FILE: .gitignore
================================================
.vscode/*
.git_credentials
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 alumuko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# vanilla-datetimerange-picker
## Overview.
A JavaScript component that is a date & time range picker, no need to build, no dependencies except Moment.js, that is inspired by [Dan Grossman's bootstrap-daterangepicker](https://github.com/dangrossman/daterangepicker).
Actually, this program is base on Dan Grossman's bootstrap-daterangepicker (version 3.1).
I just changed the code a bit to not need jquery.
## Requirements
- [Moment.js](https://momentjs.com/) (version 2.29.1 is recommended.)
### IE11
If you want to use on Internet Explorer 11, include [Polyfill](https://polyfill.io/v3/polyfill.js?ua=ie/11) to use CustomEvent, Object.assign, Element.prototype.closest and Element.prototype.matches features.
## Quick start using CDN.
1. Include Moment.js and Date Range Picker's css and js files in your webpage:
```
```
2. Put "INPUT" element into "BODY", and bind DateRangePicker instance.
```
```
See [simple example page](examples/datetime-example-simple.html).
## Usage
```
new DateRangePicker(bindElement, options, callback);
```
| parameter | type | description |
----|----|----
| bindElement | string or object | bind element id or bind HTMLElement object |
| options | object | option set (see Options) |
| callback | function(momemt, moment) | change datetime callback, 2 parameters: start and end datetime |
## Options
Almost same as Dan Grossman's bootstrap-daterangepicker Version 3.1
| name | type | description |
----|----|----
| startDate | Date or string | The beginning date of the initially selected date range. If you provide a string, it must match the date format string set in your locale setting.|
| endDate | Date or string | The end date of the initially selected date range.|
| minDate | Date or string | The earliest date a user may select.|
| maxDate | Date or string | The latest date a user may select. |
| maxSpan | object | The maximum span between the selected start and end dates. You can provide any object the moment library would let you add to a date. |
|showDropdowns | true/**false** | Show year and month select boxes above calendars to jump to a specific month and year. |
minYear | number | The minimum year shown in the dropdowns when **showDropdowns** is set to true.|
| maxYear | number | The maximum year shown in the dropdowns when **showDropdowns** is set to true.|
| showWeekNumbers | true/**false** | Show localized week numbers at the start of each week on the calendars.|
| showISOWeekNumbers | true/**false** | Show ISO week numbers at the start of each week on the calendars.|
| timePicker | true/**false** | Adds select boxes to choose times in addition to dates.|
| timePickerIncrement | number | Increment of the minutes selection list for times (i.e. 30 to allow only selection of times ending in 0 or 30).|
timePicker24Hour | true/**false** | Use 24-hour instead of 12-hour times, removing the AM/PM selection.|
| timePickerSeconds | true/**false** | Show seconds in the timePicker. |
| ranges | object |Set predefined date ranges the user can select from. Each key is the label for the range, and its value an array with two dates representing the bounds of the range. See example code.|
| showCustomRangeLabel | **true**/false | Displays "Custom Range" at the end of the list of predefined ranges, when the ranges option is used. This option will be highlighted whenever the current date range selection does not match one of the predefined ranges. Clicking it will display the calendars to select a new range. |
| alwaysShowCalendars | true/**false** | Normally, if you use the ranges option to specify pre-defined date ranges, calendars for choosing a custom date range are not shown until the user clicks "Custom Range". When this option is set to true, the calendars for choosing a custom date range are always shown instead. |
| opens | 'left'/**'right'**/'center' | Whether the picker appears aligned to the left, to the right, or centered under the HTML element it's attached to. |
| drops | **'down'**/'up'/'auto' | Whether the picker appears below (default) or above the HTML element it's attached to. |
| buttonClasses | string | CSS class names that will be added to both the apply and cancel buttons.|
| applyButtonClasses | string | CSS class names that will be added only to the apply button.|
| cancelButtonClasses | string | CSS class names that will be added only to the cancel button. |
| singleDatePicker | true/**false** | Show only a single calendar to choose one date, instead of a range picker with two calendars. The start and end dates provided to your callback will be the same single date chosen.|
| autoApply | true/**false** | Hide the apply and cancel buttons, and automatically apply a new date range as soon as two dates are clicked.|
| linkedCalendars | **true**/false | When enabled, the two calendars displayed will always be for two sequential months (i.e. January and February), and both will be advanced when clicking the left or right arrows above the calendars. When disabled, the two calendars can be individually advanced and display any month/year.|
| isInvalidDate | function(moment) | A function that is passed each date in the two calendars before they are displayed, and may return true or false to indicate whether that date should be available for selection or not.|
| isCustomDate | function(moment) | A function that is passed each date in the two calendars before they are displayed, and may return a string or array of CSS class names to apply to that date's calendar cell.|
| autoUpdateInput | **true**/false | Indicates whether the date range picker should automatically update the value of the <input> element it's attached to at initialization and when the selected dates change.|
| parentEl | string | the parent element that the date range picker will be added to, if not provided this will be 'body'|
| locale | object ||
| locale.format | string | date time text locale format like "YYYY年MM月DD日 HH時mm分ss秒".|
| locale.separator | string | separator between 2 date times. default separator is "**-**"|
| locale.applyLabel | string | label text of the apply button. default is "**Apply**"|
| locale.cancelLabel | string | label text of the cancel button. default is "**Cancel**"|
| locale.weekLabel | string | label text of week number column like "**W**"|
| locale.daysOfWeek | array of 7 strings | 7 label texts of week column like **['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']** |
| locale.monthNames | array of 12 strings | 12 label texts of month nameweek column. like **['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']** |
| locale.firstDay | number | 0 = from Sunday, 1 = from Monday, ..., 6 = from Saturday |
> **strong text value** means default value.
## Methods
You can programmatically update the startDate and endDate in the picker using the setStartDate and setEndDate methods. You can access the Date Range Picker object and its functions and properties through data properties of the element you attached it to.
| name | type | description |
----|----|----
| setStartDate | Date or string | Sets the date range picker's currently selected start date to the provided date |
| setEndDate | Date or string | Sets the date range picker's currently selected end date to the provided date |
### Usage
```
let drp = new DateRangePicker('datetimerange-input1', { alwaysShowCalendars: true });,
drp.setStartDate('2014/03/01');
drp.setEndDate('2014/03/03');
```
## Events
Several events are triggered on the element you attach the picker to, which you can listen for.
| name | description |
----|----
| show.daterangepicker | Triggered when the picker is shown |
| hide.daterangepicker | Triggered when the picker is hidden |
| showCalendar.daterangepicker | Triggered when the calendar(s) are shown |
| hideCalendar.daterangepicker | Triggered when the calendar(s) are hidden |
| apply.daterangepicker |Triggered when the apply button is clicked, or when a predefined range is clicked |
| cancel.daterangepicker |Triggered when the cancel button is clicked |
### Usage
```
window.addEventListener('apply.daterangepicker', function (ev) {
console.log(ev.detail.startDate.format('YYYY-MM-DD'));
console.log(ev.detail.endDate.format('YYYY-MM-DD'));
});
```
## Examples
### Data Time Range Picker with a Callback.

```
```
See [datetime example page](/examples/datetime-example.html)
## Extra
### A Dark Version CSS

change
```
```
to
```
```
See [datetime dark example page](/examples/datetime-example-dark.html).
### Dynamic DataRange Change (since 0.2.0)
you can change dateranges dynamically.
with extra function ```updateRanges(Object)```
See [datetime example page](/examples/datetime-example.html).
## Special Thanks
Special thanks to [Dan Grossman](http://www.dangrossman.info/)
## License
[MIT License](LICENSE)
================================================
FILE: dist/vanilla-datetimerange-picker.css
================================================
/**
* @author: Alumuko https://github.com/alumuko/alumuko
* @copyright: Copyright (c) 2021 Alumuko. All rights reserved.
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
* @website: https://github.com/alumuko/vanilla-datetimerange-picker
*
* Special thanks to Dan Grossman.
* This program is base on Dan Grossman's bootstrap-daterangepicker (version 3.1)
* I just changed the code a bit to not need jquery.
*/
.daterangepicker {
position: absolute;
color: inherit;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ddd;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker:before, .daterangepicker:after {
position: absolute;
display: inline-block;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker:before {
top: -7px;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
border-bottom: 7px solid #ccc;
}
.daterangepicker:after {
top: -6px;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
}
.daterangepicker.opensleft:before {
right: 9px;
}
.daterangepicker.opensleft:after {
right: 10px;
}
.daterangepicker.openscenter:before {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.openscenter:after {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.opensright:before {
left: 9px;
}
.daterangepicker.opensright:after {
left: 10px;
}
.daterangepicker.drop-up {
margin-top: -7px;
}
.daterangepicker.drop-up:before {
top: initial;
bottom: -7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.drop-up:after {
top: initial;
bottom: -6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
float: none;
}
.daterangepicker.single .drp-selected {
display: none;
}
.daterangepicker.show-calendar .drp-calendar {
display: block;
}
.daterangepicker.show-calendar .drp-buttons {
display: block;
}
.daterangepicker.auto-apply .drp-buttons {
display: none;
}
.daterangepicker .drp-calendar {
display: none;
max-width: 270px;
}
.daterangepicker .drp-calendar.left {
padding: 8px 0 8px 8px;
}
.daterangepicker .drp-calendar.right {
padding: 8px;
}
.daterangepicker .drp-calendar.single .calendar-table {
border: none;
}
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
color: #fff;
border: solid black;
border-width: 0 2px 2px 0;
border-radius: 0;
display: inline-block;
padding: 3px;
}
.daterangepicker .calendar-table .next span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
white-space: nowrap;
text-align: center;
vertical-align: middle;
min-width: 32px;
width: 32px;
height: 24px;
line-height: 24px;
font-size: 12px;
border-radius: 4px;
border: 1px solid transparent;
white-space: nowrap;
cursor: pointer;
}
.daterangepicker .calendar-table {
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
}
.daterangepicker .calendar-table table {
width: 100%;
margin: 0;
border-spacing: 0;
border-collapse: collapse;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #eee;
border-color: transparent;
color: inherit;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #fff;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #ebf4f8;
border-color: transparent;
color: #000;
border-radius: 0;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: transparent;
color: #fff;
}
.daterangepicker th.month {
width: auto;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
cursor: not-allowed;
text-decoration: line-through;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin: 0 auto;
background: #eee;
border: 1px solid #eee;
padding: 2px;
outline: 0;
font-size: 12px;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 4px auto 0 auto;
line-height: 30px;
position: relative;
}
.daterangepicker .calendar-time select.disabled {
color: #ccc;
cursor: not-allowed;
}
.daterangepicker .drp-buttons {
clear: both;
text-align: right;
padding: 8px;
border-top: 1px solid #ddd;
display: none;
line-height: 12px;
vertical-align: middle;
}
.daterangepicker .drp-selected {
display: inline-block;
font-size: 12px;
padding-right: 8px;
}
.daterangepicker .drp-buttons .btn {
margin-left: 8px;
font-size: 12px;
font-weight: bold;
padding: 4px 8px;
}
.daterangepicker.show-ranges.single.rtl .drp-calendar.left {
border-right: 1px solid #ddd;
}
.daterangepicker.show-ranges.single.ltr .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker.show-ranges.rtl .drp-calendar.right {
border-right: 1px solid #ddd;
}
.daterangepicker.show-ranges.ltr .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker .ranges {
float: none;
text-align: left;
margin: 0;
}
.daterangepicker.show-calendar .ranges {
margin-top: 8px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
}
.daterangepicker .ranges li {
font-size: 12px;
padding: 8px 12px;
cursor: pointer;
}
.daterangepicker .ranges li:hover {
background-color: #eee;
}
.daterangepicker .ranges li.active {
background-color: #08c;
color: #fff;
}
/* Larger Screen Styling */
@media (min-width: 564px) {
.daterangepicker {
width: auto;
}
.daterangepicker .ranges ul {
width: 140px;
}
.daterangepicker.single .ranges ul {
width: 100%;
}
.daterangepicker.single .drp-calendar.left {
clear: none;
}
.daterangepicker.single .ranges, .daterangepicker.single .drp-calendar {
float: left;
}
.daterangepicker {
direction: ltr;
text-align: left;
}
.daterangepicker .drp-calendar.left {
clear: left;
margin-right: 0;
}
.daterangepicker .drp-calendar.left .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.daterangepicker .drp-calendar.right {
margin-left: 0;
}
.daterangepicker .drp-calendar.right .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.daterangepicker .drp-calendar.left .calendar-table {
padding-right: 8px;
}
.daterangepicker .ranges, .daterangepicker .drp-calendar {
float: left;
}
}
@media (min-width: 730px) {
.daterangepicker .ranges {
width: auto;
}
.daterangepicker .ranges {
float: left;
}
.daterangepicker.rtl .ranges {
float: right;
}
.daterangepicker .drp-calendar.left {
clear: none !important;
}
}
================================================
FILE: dist/vanilla-datetimerange-picker.js
================================================
/**
* @author: Alumuko https://github.com/alumuko/alumuko
* @copyright: Copyright (c) 2021 Alumuko. All rights reserved.
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
* @website: https://github.com/alumuko/vanilla-datetimerange-picker
*
* Special thanks to Dan Grossman.
* This program is base on Dan Grossman's bootstrap-daterangepicker (version 3.1)
* I just changed the code a bit to not need jquery.
*/
// IE browser doesn't support "class"
var DateRangePicker;
(function () {
DateRangePicker = function (element, options, cb) {
//default settings for options
this.parentEl = document.body;
if (typeof element === 'string')
this.element = document.getElementById(element);
else
this.element = element;
this.startDate = moment().startOf('day');
this.endDate = moment().endOf('day');
this.minDate = false;
this.maxDate = 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;
this.timePickerIncrement = 1;
this.timePickerSeconds = false;
this.linkedCalendars = true;
this.autoUpdateInput = true;
this.alwaysShowCalendars = false;
this.ranges = {};
this.opens = 'right';
if (this.element.classList.contains('pull-right'))
this.opens = 'left';
this.drops = 'down';
if (this.element.classList.contains('dropup'))
this.drops = 'up';
this.buttonClasses = 'btn btn-sm';
this.applyButtonClasses = 'btn-primary';
this.cancelButtonClasses = 'btn-default';
this.locale = {
direction: 'ltr',
format: moment.localeData().longDateFormat('L'),
separator: ' - ',
applyLabel: 'Apply',
cancelLabel: 'Cancel',
weekLabel: 'W',
customRangeLabel: 'Custom Range',
daysOfWeek: moment.weekdaysMin(),
monthNames: moment.monthsShort(),
firstDay: moment.localeData().firstDayOfWeek()
};
this.callback = function() { };
//some state information
this.isShowing = false;
this.leftCalendar = {};
this.rightCalendar = {};
//custom options from user
if (typeof options !== 'object' || options === null)
options = {};
//allow setting options with data attributes
//data-api options will be overwritten with custom javascript options
options = Object.assign(Object.assign({}, this.element.dataset), options);
//html template for the picker UI
if (typeof options.template !== 'string')
options.template =
'
' +
'' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
' ' +
'
' +
'
';
this.parentEl = options.parentEl ? options.parentEl : this.parentEl;
var templateWrapEl = document.createElement('div');
templateWrapEl.innerHTML = options.template.trim();
this.container = templateWrapEl.firstElementChild;
this.parentEl.insertAdjacentElement('beforeEnd', this.container);
//
// handle all the possible options overriding defaults
//
if (typeof options.locale === 'object') {
if (typeof options.locale.direction === 'string')
this.locale.direction = options.locale.direction;
if (typeof options.locale.format === 'string')
this.locale.format = options.locale.format;
if (typeof options.locale.separator === 'string')
this.locale.separator = options.locale.separator;
if (typeof options.locale.daysOfWeek === 'object')
this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
if (typeof options.locale.monthNames === 'object')
this.locale.monthNames = options.locale.monthNames.slice();
if (typeof options.locale.firstDay === 'number')
this.locale.firstDay = options.locale.firstDay;
if (typeof options.locale.applyLabel === 'string')
this.locale.applyLabel = options.locale.applyLabel;
if (typeof options.locale.cancelLabel === 'string')
this.locale.cancelLabel = options.locale.cancelLabel;
if (typeof options.locale.weekLabel === 'string')
this.locale.weekLabel = options.locale.weekLabel;
if (typeof options.locale.customRangeLabel === 'string'){
//Support unicode chars in the custom range name.
var elem = document.createElement('textarea');
elem.innerHTML = options.locale.customRangeLabel;
var rangeHtml = elem.value;
this.locale.customRangeLabel = rangeHtml;
}
}
this.container.classList.add(this.locale.direction);
if (typeof options.startDate === 'string')
this.startDate = moment(options.startDate, this.locale.format);
if (typeof options.endDate === 'string')
this.endDate = moment(options.endDate, this.locale.format);
if (typeof options.minDate === 'string')
this.minDate = moment(options.minDate, this.locale.format);
if (typeof options.maxDate === 'string')
this.maxDate = moment(options.maxDate, this.locale.format);
if (typeof options.startDate === 'object')
this.startDate = moment(options.startDate);
if (typeof options.endDate === 'object')
this.endDate = moment(options.endDate);
if (typeof options.minDate === 'object')
this.minDate = moment(options.minDate);
if (typeof options.maxDate === 'object')
this.maxDate = moment(options.maxDate);
// sanity check for bad options
if (this.minDate && this.startDate.isBefore(this.minDate))
this.startDate = this.minDate.clone();
// sanity check for bad options
if (this.maxDate && this.endDate.isAfter(this.maxDate))
this.endDate = this.maxDate.clone();
if (typeof options.applyButtonClasses === 'string')
this.applyButtonClasses = options.applyButtonClasses;
if (typeof options.applyClass === 'string') //backwards compat
this.applyButtonClasses = options.applyClass;
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;
if (typeof options.showWeekNumbers === 'boolean')
this.showWeekNumbers = options.showWeekNumbers;
if (typeof options.showISOWeekNumbers === 'boolean')
this.showISOWeekNumbers = options.showISOWeekNumbers;
if (typeof options.buttonClasses === 'string')
this.buttonClasses = options.buttonClasses;
if (typeof options.buttonClasses === 'object')
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;
if (this.singleDatePicker)
this.endDate = this.startDate.clone();
}
if (typeof options.timePicker === 'boolean')
this.timePicker = options.timePicker;
if (typeof options.timePickerSeconds === 'boolean')
this.timePickerSeconds = options.timePickerSeconds;
if (typeof options.timePickerIncrement === 'number')
this.timePickerIncrement = options.timePickerIncrement;
if (typeof options.timePicker24Hour === 'boolean')
this.timePicker24Hour = options.timePicker24Hour;
if (typeof options.autoApply === 'boolean')
this.autoApply = options.autoApply;
if (typeof options.autoUpdateInput === 'boolean')
this.autoUpdateInput = options.autoUpdateInput;
if (typeof options.linkedCalendars === 'boolean')
this.linkedCalendars = options.linkedCalendars;
if (typeof options.isInvalidDate === 'function')
this.isInvalidDate = options.isInvalidDate;
if (typeof options.isCustomDate === 'function')
this.isCustomDate = options.isCustomDate;
if (typeof options.alwaysShowCalendars === 'boolean')
this.alwaysShowCalendars = options.alwaysShowCalendars;
// update day names order to firstDay
if (this.locale.firstDay != 0) {
var iterator = this.locale.firstDay;
while (iterator > 0) {
this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
iterator--;
}
}
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.tagName === 'INPUT' && this.element.type === 'text'){
var val = this.element.value,
split = val.split(this.locale.separator);
start = end = null;
if (split.length == 2) {
start = moment(split[0], this.locale.format);
end = moment(split[1], this.locale.format);
} else if (this.singleDatePicker && val !== "") {
start = moment(val, this.locale.format);
end = moment(val, this.locale.format);
}
if (start !== null && end !== null) {
this.setStartDate(start);
this.setEndDate(end);
}
}
}
if (typeof options.ranges === 'object') {
let rangesKeys = Object.keys(options.ranges);
for(let i = 0; i < rangesKeys.length; ++i){
let range = rangesKeys[i];
if (typeof options.ranges[range][0] === 'string')
start = moment(options.ranges[range][0], this.locale.format);
else
start = moment(options.ranges[range][0]);
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 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.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'))
|| (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))
continue;
//Support unicode chars in the range names.
var elem = document.createElement('textarea');
elem.innerHTML = range;
var rangeHtml = elem.value;
this.ranges[rangeHtml] = [start, end];
}
var list = '
';
for (range in this.ranges) {
list += '
' + range + '
';
}
if (this.showCustomRangeLabel) {
list += '
' + this.locale.customRangeLabel + '
';
}
list += '
';
this.container.querySelector('.ranges').insertAdjacentHTML('afterbegin', list);
}
if (typeof cb === 'function') {
this.callback = cb;
}
if (!this.timePicker) {
this.startDate = this.startDate.startOf('day');
this.endDate = this.endDate.endOf('day');
this.container.style.display = 'none';
this.container.querySelector('.calendar-time').display;
}
//can't be used together for now
if (this.timePicker && this.autoApply)
this.autoApply = false;
if (this.autoApply) {
this.container.classList.add('auto-apply');
}
if (typeof options.ranges === 'object')
this.container.classList.add('show-ranges');
if (this.singleDatePicker) {
this.container.classList.add('single');
this.container.querySelector('.drp-calendar.left').classList.add('single');
this.container.querySelector('.drp-calendar.left').style.display = 'block';
this.container.querySelector('.drp-calendar.right').style.display = 'none';
if (!this.timePicker && this.autoApply) {
this.container.classList.add('auto-apply');
}
}
if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) {
this.container.classList.add('show-calendar');
}
this.container.classList.add('opens' + this.opens);
//apply CSS classes and labels to buttons
let applyBtnEl = this.container.querySelector('.applyBtn');
let cancelBtnEl = this.container.querySelector('.cancelBtn');
jq.addClass(applyBtnEl, this.buttonClasses);
jq.addClass(cancelBtnEl, this.buttonClasses);
if (this.applyButtonClasses.length)
jq.addClass(applyBtnEl, this.applyButtonClasses);
if (this.cancelButtonClasses.length)
jq.addClass(cancelBtnEl, this.cancelButtonClasses);
jq.html(applyBtnEl, this.locale.applyLabel);
jq.html(cancelBtnEl, this.locale.cancelLabel);
//
// event listeners
//
/*
-- Note: jquery can set event listner before the target element has not been build. Vanilla-JS set event listner LATER.--
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('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));
--------------------------------------------------------------------------------------
*/
this.clickRangeProxy = function (e) { this.clickRange(e); }.bind(this);
jq.on(this.container.querySelector('.ranges'), 'click', 'li', this.clickRangeProxy);
let drpButtonsEl = this.container.querySelector('.drp-buttons');
this.clickApplyProxy = function (e) { this.clickApply(e); }.bind(this);
jq.on(drpButtonsEl, 'click', 'button.applyBtn', this.clickApplyProxy);
this.clickCancelProxy = function (e) { this.clickCancel(e); }.bind(this);
jq.on(drpButtonsEl, 'click', 'button.cancelBtn', this.clickCancelProxy);
if (this.element.tagName === 'INPUT' || this.element.tagName === 'BUTTON') {
this.showProxy = function (e) { this.show(e); }.bind(this);
jq.on(this.element, 'click', this.showProxy);
jq.on(this.element, 'focus', this.showProxy);
this.elementChangedProxy = function (e) { this.elementChanged(e); }.bind(this);
jq.on(this.element, 'keyup', this.elementChangedProxy);
this.keydownProxy = function (e) { this.keydown(e); }.bind(this); //IE 11 compatibility
jq.on(this.element, 'keydown', this.keydownProxy);
} else {
this.toggleProxy = function (e) { this.toggle(e); }.bind(this);
jq.on(this.element, 'click', this.toggleProxy);
jq.on(this.element, 'keydown', this.toggleProxy);
}
//
// if attached to a text input, set the initial value
//
this.updateElement();
};
DateRangePicker.prototype = {
constructor: DateRangePicker,
setStartDate: function(startDate) {
if (typeof startDate === 'string')
this.startDate = moment(startDate, this.locale.format);
if (typeof startDate === 'object')
this.startDate = moment(startDate);
if (!this.timePicker)
this.startDate = this.startDate.startOf('day');
if (this.timePicker && this.timePickerIncrement)
this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
if (this.minDate && this.startDate.isBefore(this.minDate)) {
this.startDate = this.minDate.clone();
if (this.timePicker && this.timePickerIncrement)
this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
}
if (this.maxDate && this.startDate.isAfter(this.maxDate)) {
this.startDate = this.maxDate.clone();
if (this.timePicker && this.timePickerIncrement)
this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
}
if (!this.isShowing)
this.updateElement();
this.updateMonthsInView();
},
setEndDate: function(endDate) {
if (typeof endDate === 'string')
this.endDate = moment(endDate, this.locale.format);
if (typeof endDate === 'object')
this.endDate = moment(endDate);
if (!this.timePicker)
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.maxSpan && this.startDate.clone().add(this.maxSpan).isBefore(this.endDate))
this.endDate = this.startDate.clone().add(this.maxSpan);
this.previousRightTime = this.endDate.clone();
jq.html(this.container.querySelector('.drp-selected'), this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
if (!this.isShowing)
this.updateElement();
this.updateMonthsInView();
},
isInvalidDate: function() {
return false;
},
isCustomDate: function() {
return false;
},
updateView: function() {
if (this.timePicker) {
this.renderTimePicker('left');
this.renderTimePicker('right');
let selectElList = this.container.querySelectorAll('.right .calendar-time select');
if (!this.endDate) {
for (let i = 0; i < selectElList.length; ++i){
selectElList[i].disabled = true;
selectElList[i].classList.add('disabled');
}
} else {
for (let i = 0; i < selectElList.length; ++i){
selectElList[i].disabled = false;
selectElList[i].classList.remove('disabled');
}
}
}
if (this.endDate)
jq.html(this.container.querySelector('.drp-selected'), this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
this.updateMonthsInView();
this.updateCalendars();
this.updateFormInputs();
},
updateMonthsInView: function() {
if (this.endDate) {
//if both dates are visible already, do nothing
if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month &&
(this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
&&
(this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
) {
return;
}
this.leftCalendar.month = this.startDate.clone().date(2);
if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) {
this.rightCalendar.month = this.endDate.clone().date(2);
} else {
this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
}
} else {
if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) {
this.leftCalendar.month = this.startDate.clone().date(2);
this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
}
}
if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) {
this.rightCalendar.month = this.maxDate.clone().date(2);
this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month');
}
},
updateCalendars: function() {
if(!this.clickPrevProxy)
this.clickPrevProxy = function (e) { this.clickPrev(e); }.bind(this);
if(!this.clickNextProxy)
this.clickNextProxy = function (e) { this.clickNext(e); }.bind(this);
if(!this.clickDateProxy)
this.clickDateProxy = function (e) { this.clickDate(e); }.bind(this);
if(!this.hoverDateProxy)
this.hoverDateProxy = function (e) { this.hoverDate(e); }.bind(this);
if(!this.monthOrYearChangedProxy)
this.monthOrYearChangedProxy = function (e) { this.monthOrYearChanged(e); }.bind(this);
if(!this.timeChangedProxy)
this.timeChangedProxy = function (e) { this.timeChanged(e); }.bind(this);
/*
-- Note: by jquery, we can set event listener before the target element has not been build. but we must remove event listener HERE by Vanilla-JS. --
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('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));
--------------------------------------------------------------------------------------
*/
let drpCalendarElList = this.container.querySelectorAll('.drp-calendar');
jq.off(drpCalendarElList, 'click', '.prev', this.clickPrevProxy);
jq.off(drpCalendarElList, 'click', '.next', this.clickNextProxy);
jq.off(drpCalendarElList, 'mousedown', 'td.available', this.clickDateProxy);
jq.off(drpCalendarElList, 'mouseenter', 'td.available', this.hoverDateProxy);
jq.off(drpCalendarElList, 'change', 'select.yearselect', this.monthOrYearChangedProxy);
jq.off(drpCalendarElList, 'change', 'select.monthselect', this.monthOrYearChangedProxy);
jq.off(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy);
if (this.timePicker) {
var hour, minute, second;
if (this.endDate) {
hour = parseInt(this.container.querySelector('.left .hourselect').value, 10);
minute = parseInt(this.container.querySelector('.left .minuteselect').value, 10);
if (isNaN(minute)) {
minute = parseInt(jq.findLast(this.container.querySelector('.left .minuteselect')).value, 10);
}
second = this.timePickerSeconds ? parseInt(this.container.querySelector('.left .secondselect').value, 10) : 0;
if (!this.timePicker24Hour) {
var ampm = this.container.querySelector('.left .ampmselect').value;
if (ampm === 'PM' && hour < 12)
hour += 12;
if (ampm === 'AM' && hour === 12)
hour = 0;
}
} else {
hour = parseInt(this.container.querySelector('.right .hourselect').value, 10);
minute = parseInt(this.container.querySelector('.right .minuteselect').value, 10);
if (isNaN(minute)) {
minute = parseInt(jq.findLast(this.container.querySelector('.right .minuteselect')).value, 10);
}
second = this.timePickerSeconds ? parseInt(this.container.querySelector('.right .secondselect').value, 10) : 0;
if (!this.timePicker24Hour) {
var ampm = this.container.querySelector('.right .ampmselect').value;
if (ampm === 'PM' && hour < 12)
hour += 12;
if (ampm === 'AM' && hour === 12)
hour = 0;
}
}
this.leftCalendar.month.hour(hour).minute(minute).second(second);
this.rightCalendar.month.hour(hour).minute(minute).second(second);
}
this.renderCalendar('left');
this.renderCalendar('right');
//highlight any predefined range matching the current start and end dates
rangesLiElList = this.container.querySelectorAll('.ranges li');
for(let i = 0; i < rangesLiElList.length; ++i)
rangesLiElList[i].classList.remove('active');
if (this.endDate != null)
this.calculateChosenLabel();
/*
-- Note: by jquery, we can set event listener before the target element has not been build. but we must set event listener HERE by Vanilla-JS. --
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('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));
--------------------------------------------------------------------------------------
*/
jq.on(drpCalendarElList, 'click', '.prev', this.clickPrevProxy);
jq.on(drpCalendarElList, 'click', '.next', this.clickNextProxy);
jq.on(drpCalendarElList, 'mousedown', 'td.available', this.clickDateProxy);
jq.on(drpCalendarElList, 'mouseenter', 'td.available', this.hoverDateProxy);
jq.on(drpCalendarElList, 'change', 'select.yearselect', this.monthOrYearChangedProxy);
jq.on(drpCalendarElList, 'change', 'select.monthselect', this.monthOrYearChangedProxy);
jq.on(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy);
},
renderCalendar: function(side) {
//
// Build the matrix of dates that will populate the calendar
//
var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar;
var month = calendar.month.month();
var year = calendar.month.year();
var hour = calendar.month.hour();
var minute = calendar.month.minute();
var second = calendar.month.second();
var daysInMonth = moment([year, month]).daysInMonth();
var firstDay = moment([year, month, 1]);
var lastDay = moment([year, month, daysInMonth]);
var lastMonth = moment(firstDay).subtract(1, 'month').month();
var lastYear = moment(firstDay).subtract(1, 'month').year();
var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
var dayOfWeek = firstDay.day();
//initialize a 6 rows x 7 columns array for the calendar
var calendar = [];
calendar.firstDay = firstDay;
calendar.lastDay = lastDay;
for (var i = 0; i < 6; i++) {
calendar[i] = [];
}
//populate the calendar with date objects
var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
if (startDay > daysInLastMonth)
startDay -= 7;
if (dayOfWeek == this.locale.firstDay)
startDay = daysInLastMonth - 6;
var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]);
var col, row;
for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
if (i > 0 && col % 7 === 0) {
col = 0;
row++;
}
calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second);
curDate.hour(12);
if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
calendar[row][col] = this.minDate.clone();
}
if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
calendar[row][col] = this.maxDate.clone();
}
}
//make the calendar object available to hoverDate/clickDate
if (side == 'left') {
this.leftCalendar.calendar = calendar;
} else {
this.rightCalendar.calendar = calendar;
}
//
// Display the calendar
//
var minDate = side == 'left' ? this.minDate : this.startDate;
var maxDate = this.maxDate;
var selected = side == 'left' ? this.startDate : this.endDate;
var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'};
var html = '
';
html += '';
html += '
';
// add empty cell for week number
if (this.showWeekNumbers || this.showISOWeekNumbers)
html += '
';
if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {
html += '
';
} else {
html += '
';
}
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()) || (this.maxYear);
var minYear = (minDate && minDate.year()) || (this.minYear);
var inMinYear = currentYear == minYear;
var inMaxYear = currentYear == maxYear;
var monthHtml = '";
var yearHtml = '';
dateHtml = monthHtml + yearHtml;
}
html += '
' + dateHtml + '
';
if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {
html += '
';
} else {
html += '
';
}
html += '
';
html += '
';
// add week number label
if (this.showWeekNumbers || this.showISOWeekNumbers)
html += '
' + this.locale.weekLabel + '
';
for(let i = 0; i < this.locale.daysOfWeek.length; ++i){
html += '
' + this.locale.daysOfWeek[i] + '
';
}
html += '
';
html += '';
html += '';
//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;
}
}
for (var row = 0; row < 6; row++) {
html += '
';
// add week number
if (this.showWeekNumbers)
html += '
' + calendar[row][0].week() + '
';
else if (this.showISOWeekNumbers)
html += '
' + calendar[row][0].isoWeek() + '
';
for (var col = 0; col < 7; col++) {
var classes = [];
//highlight today's date
if (calendar[row][col].isSame(new Date(), "day"))
classes.push('today');
//highlight weekends
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', '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');
//don't allow selection of dates after the maximum date
if (maxDate && calendar[row][col].isAfter(maxDate, 'day'))
classes.push('off', 'disabled');
//don't allow selection of date if a custom function decides it's invalid
if (this.isInvalidDate(calendar[row][col]))
classes.push('off', 'disabled');
//highlight the currently selected start date
if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD'))
classes.push('active', 'start-date');
//highlight the currently selected end date
if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD'))
classes.push('active', 'end-date');
//highlight dates in-between the selected dates
if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate)
classes.push('in-range');
//apply custom classes for this date
var isCustom = this.isCustomDate(calendar[row][col]);
if (isCustom !== false) {
if (typeof isCustom === 'string')
classes.push(isCustom);
else
Array.prototype.push.apply(classes, isCustom);
}
var cname = '', disabled = false;
for (var i = 0; i < classes.length; i++) {
cname += classes[i] + ' ';
if (classes[i] == 'disabled')
disabled = true;
}
if (!disabled)
cname += 'available';
html += '
' + calendar[row][col].date() + '
';
}
html += '
';
}
html += '';
html += '
';
jq.html(this.container.querySelector('.drp-calendar.' + side + ' .calendar-table'), html);
},
renderTimePicker: function(side) {
// Don't bother updating the time picker if it's currently disabled
// because an end date hasn't been clicked yet
if (side == 'right' && !this.endDate) return;
var html, selected, minDate, maxDate = this.maxDate;
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.querySelector('.drp-calendar.right .calendar-time');
if (timeSelector.innerHTML != '') {
selected.hour(!isNaN(selected.hour()) ? selected.hour() : jq.findSelectedOption(timeSelector.querySelector('.hourselect')).value);
selected.minute(!isNaN(selected.minute()) ? selected.minute() : jq.findSelectedOption(timeSelector.querySelector('.minuteselect')).value);
selected.second(!isNaN(selected.second()) ? selected.second() : jq.findSelectedOption(timeSelector.querySelector('.secondselect')).value);
if (!this.timePicker24Hour) {
var ampm = jq.findSelectedOption(timeSelector.querySelector('.ampmselect')).value;
if (ampm === 'PM' && selected.hour() < 12)
selected.hour(selected.hour() + 12);
if (ampm === 'AM' && selected.hour() === 12)
selected.hour(0);
}
}
if (selected.isBefore(this.startDate))
selected = this.startDate.clone();
if (maxDate && selected.isAfter(maxDate))
selected = maxDate.clone();
}
//
// hours
//
html = ' ';
//
// minutes
//
html += ': ';
//
// seconds
//
if (this.timePickerSeconds) {
html += ': ';
}
//
// AM/PM
//
if (!this.timePicker24Hour) {
html += '';
}
jq.html(this.container.querySelector('.drp-calendar.' + side + ' .calendar-time'), html);
},
updateFormInputs: function() {
if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {
this.container.querySelector('button.applyBtn').disabled = false;
} else {
this.container.querySelector('button.applyBtn').disabled = true;
}
},
move: function() {
var parentOffset = { top: 0, left: 0 },
containerTop,
drops = this.drops;
var parentRightEdge = window.innerWidth;
if (!(this.parentEl.tagName === 'BODY')) {
let parentElOffset = jq.offset(this.parentEl);
parentOffset = {
top: parentElOffset.top - this.parentEl.scrollTop,
left: parentElOffset.left - this.parentEl.scrollLeft
};
parentRightEdge = this.parentEl.clientWidth + parentElOffset.left;
}
/* Note: jquery this.container.outerHeight() returns non 0 even if not showing, but Vanilla-JS this.container.offsetHeight() returns 0 */
let elementOffset = jq.offset(this.element);
switch (drops) {
case 'auto':
containerTop = elementOffset.top + this.element.offsetHeight - parentOffset.top;
if (containerTop + this.container.offsetHeight >= this.parentEl.scrollHeight) {
containerTop = elementOffset.top - this.container.offsetHeight - parentOffset.top;
drops = 'up';
}
break;
case 'up':
containerTop = elementOffset.top - this.container.offsetHeight - parentOffset.top;
break;
default:
containerTop = elementOffset.top + this.element.offsetHeight - parentOffset.top;
break;
}
// Force the container to it's actual width
this.container.style.top = '0';
this.container.style.left = '0';
this.container.style.right = 'auto';
var containerWidth = this.container.offsetWidth;
if (drops == 'up')
this.container.classList.add('drop-up');
else
this.container.classList.remove('drop-up');
if (this.opens == 'left') {
var containerRight = parentRightEdge - elementOffset.left - this.element.offsetWidth;
if (containerWidth + containerRight > window.innerWidth) {
this.container.style.top = containerTop + 'px';
this.container.style.right = 'auto';
this.container.style.left = '9px';
} else {
this.container.style.top = containerTop + 'px';
this.container.style.right = containerRight + 'px';
this.container.style.left = 'auto';
}
} else if (this.opens == 'center') {
var containerLeft = elementOffset.left - parentOffset.left + this.element.offsetWidth / 2
- containerWidth / 2;
if (containerLeft < 0) {
this.container.style.top = containerTop + 'px';
this.container.style.right = 'auto';
this.container.style.left = '9px';
} else if (containerLeft + containerWidth > window.innerWidth) {
this.container.style.top = containerTop + 'px';
this.container.style.left = 'auto';
this.container.style.right = '0';
} else {
this.container.style.top = containerTop + 'px';
this.container.style.left = containerLeft + 'px';
this.container.style.right = 'auto';
}
} else {
var containerLeft = elementOffset.left - parentOffset.left;
if (containerLeft + containerWidth > window.innerWidth) {
this.container.style.top = containerTop + 'px';
this.container.style.left = 'auto';
this.container.style.right = '0';
} else {
this.container.style.top = containerTop + 'px';
this.container.style.left = containerLeft + 'px';
this.container.style.right = 'auto';
}
}
},
show: function(e) {
if (this.isShowing) return;
// Create a click proxy that is private to this instance of datepicker, for unbinding
if(!this._outsideClickProxy)
this._outsideClickProxy = function (e) { this.outsideClick(e); }.bind(this);
// Bind global datepicker mousedown for hiding and
document.addEventListener('mousedown', this._outsideClickProxy);
// also support mobile devices
document.addEventListener('touchend', this._outsideClickProxy);
jq.on(document, 'click', '[data-toggle=dropdown]', this._outsideClickProxy);
// and also close when focus changes to outside the picker (eg. tabbing between controls)
document.addEventListener('focusin', this._outsideClickProxy);
// Reposition the picker if the window is resized while it's open
if(!this.moveProxy)
this.moveProxy = function (e) { this.move(e); }.bind(this);
window.addEventListener('resize', this.moveProxy);
this.oldStartDate = this.startDate.clone();
this.oldEndDate = this.endDate.clone();
this.previousRightTime = this.endDate.clone();
this.updateView();
this.container.style.display = 'block';
this.move();
this.element.dispatchEvent(new CustomEvent('show.daterangepicker', {bubbles: true, detail: this}));
this.isShowing = true;
},
hide: function(e) {
if (!this.isShowing) return;
//incomplete date selection, revert to last values
if (!this.endDate) {
this.startDate = this.oldStartDate.clone();
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.clone(), this.endDate.clone(), this.chosenLabel);
//if picker is attached to a text input, update it
this.updateElement();
if(this._outsideClickProxy){
document.removeEventListener('mousedown', this._outsideClickProxy);
document.removeEventListener('touchend', this._outsideClickProxy);
jq.off(document, 'click', '[data-toggle=dropdown]', this._outsideClickProxy);
document.removeEventListener('focusin', this._outsideClickProxy);
}
if(this.moveProxy)
window.removeEventListener('resize', this.moveProxy);
this.container.style.display = 'none';
this.element.dispatchEvent(new CustomEvent('hide.daterangepicker', {bubbles: true, detail: this}));
this.isShowing = false;
},
toggle: function(e) {
if (this.isShowing) {
this.hide();
} else {
this.show();
}
},
outsideClick: function(e) {
var target = e.target;
// if the page is clicked anywhere except within the daterangerpicker/button
// itself then call this.hide()
if (
// ie modal dialog fix
e.type == "focusin" ||
target.closest(jq.getSelectorFromElement(this.element)) ||
target.closest(jq.getSelectorFromElement(this.container)) ||
target.closest('.calendar-table')
) return;
this.hide();
this.element.dispatchEvent(new CustomEvent('outsideClick.daterangepicker', {bubbles: true, detail: this}));
},
showCalendars: function() {
this.container.classList.add('show-calendar');
this.move();
this.element.dispatchEvent(new CustomEvent('showCalendar.daterangepicker', {bubbles: true, detail: this}));
},
hideCalendars: function() {
this.container.classList.remove('show-calendar');
this.element.dispatchEvent(new CustomEvent('hideCalendar.daterangepicker', {bubbles: true, detail: this}));
},
clickRange: function(e) {
var label = e.target.dataset.rangeKey;
this.chosenLabel = label;
if (label == this.locale.customRangeLabel) {
this.showCalendars();
} else {
var dates = this.ranges[label];
this.startDate = dates[0];
this.endDate = dates[1];
if (!this.timePicker) {
this.startDate.startOf('day');
this.endDate.endOf('day');
}
if (!this.alwaysShowCalendars)
this.hideCalendars();
this.clickApply(e);
}
},
clickPrev: function(e) {
var cal = e.target.closest('.drp-calendar'); // Note: original use parents not closest.
if (cal.classList.contains('left')) {
this.leftCalendar.month.subtract(1, 'month');
if (this.linkedCalendars)
this.rightCalendar.month.subtract(1, 'month');
} else {
this.rightCalendar.month.subtract(1, 'month');
}
this.updateCalendars();
},
clickNext: function(e) {
var cal = e.target.closest('.drp-calendar'); // Note: original use parents not closest.
if (cal.classList.contains('left')) {
this.leftCalendar.month.add(1, 'month');
} else {
this.rightCalendar.month.add(1, 'month');
if (this.linkedCalendars)
this.leftCalendar.month.add(1, 'month');
}
this.updateCalendars();
},
hoverDate: function(e) {
//ignore dates that can't be selected
if(!(e.target.classList.contains('available'))) return;
var title = e.target.dataset.title;
var row = title.substr(1, 1);
var col = title.substr(3, 1);
var cal = e.target.closest('.drp-calendar'); // Note: original use parents not closest.
var date = cal.classList.contains('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
//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) {
let tdElList = this.container.querySelectorAll('.drp-calendar tbody td');
for (let i = 0; i < tdElList.length; ++i) {
//skip week numbers, only look at dates
if(tdElList[i].classList.contains('week')) return;
var title = tdElList[i].dataset.title;
var row = title.substr(1, 1);
var col = title.substr(3, 1);
var cal = tdElList[i].closest('.drp-calendar'); // Note: original use parents not closest.
var dt = cal.classList.contains('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) {
tdElList[i].classList.add('in-range');
} else {
tdElList[i].classList.remove('in-range');
}
}
}
},
clickDate: function(e) {
if (!e.target.classList.contains('available')) return;
var title = e.target.dataset.title;
var row = title.substr(1, 1);
var col = title.substr(3, 1);
var cal = e.target.closest('.drp-calendar'); // Note: original use parents not closest.
var date = cal.classList.contains('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,
// * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date
// * if autoapply is enabled, and an end date was chosen, apply the selection
// * if single date picker mode, and time picker isn't enabled, apply the selection immediately
// * if one of the inputs above the calendars was focused, cancel that manual input
//
if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start
if (this.timePicker) {
var hour = parseInt(this.container.querySelector('.left .hourselect').value, 10);
if (!this.timePicker24Hour) {
var ampm = this.container.querySelector('.left .ampmselect').value;
if (ampm === 'PM' && hour < 12)
hour += 12;
if (ampm === 'AM' && hour === 12)
hour = 0;
}
var minute = parseInt(this.container.querySelector('.left .minuteselect').value, 10);
if (isNaN(minute)) {
minute = parseInt(this.container.querySelector('.left .minuteselect option:last').value, 10);
}
var second = this.timePickerSeconds ? parseInt(this.container.querySelector('.left .secondselect').value, 10) : 0;
date = date.clone().hour(hour).minute(minute).second(second);
}
this.endDate = null;
this.setStartDate(date.clone());
} else if (!this.endDate && date.isBefore(this.startDate)) {
//special case: clicking the same date for start/end,
//but the time of the end date is before the start date
this.setEndDate(this.startDate.clone());
} else { // picking end
if (this.timePicker) {
var hour = parseInt(this.container.querySelector('.right .hourselect').value, 10);
if (!this.timePicker24Hour) {
var ampm = this.container.querySelector('.right .ampmselect').value;
if (ampm === 'PM' && hour < 12)
hour += 12;
if (ampm === 'AM' && hour === 12)
hour = 0;
}
var minute = parseInt(this.container.querySelector('.right .minuteselect').value, 10);
if (isNaN(minute)) {
minute = parseInt(this.container.querySelector('.right .minuteselect option:last').value, 10);
}
var second = this.timePickerSeconds ? parseInt(this.container.querySelector('.right .secondselect').value, 10) : 0;
date = date.clone().hour(hour).minute(minute).second(second);
}
this.setEndDate(date.clone());
if (this.autoApply) {
this.calculateChosenLabel();
this.clickApply(e);
}
}
if (this.singleDatePicker) {
this.setEndDate(this.startDate);
if (!this.timePicker && this.autoApply)
this.clickApply(e);
}
this.updateView();
//This is to cancel the blur event handler if the mouse was in one of the inputs
e.stopPropagation();
},
calculateChosenLabel: function () {
var customRange = true;
let rangesKey = Object.keys(this.ranges);
for (let i = 0; i < rangesKey.length; ++i) {
let range = this.ranges[rangesKey[i]];
if (this.timePicker) {
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) == range[0].format(format) && this.endDate.format(format) == range[1].format(format)) {
customRange = false;
let rangesLiList = this.container.querySelectorAll('.ranges li');
rangesLiList[i].classList.add('active');
this.chosenLabel = rangesLiList[i].dataset.rangeKey;
break;
}
} else {
//ignore times when comparing dates if time picker is not enabled
if (this.startDate.format('YYYY-MM-DD') == range[0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == range[1].format('YYYY-MM-DD')) {
customRange = false;
let rangesLiList = this.container.querySelectorAll('.ranges li');
rangesLiList[i].classList.add('active');
this.chosenLabel = rangesLiList[i].dataset.rangeKey;
break;
}
}
}
if (customRange) {
if (this.showCustomRangeLabel) {
let rangesLiLastEl = jq.findLast(this.container.querySelectorAll('.ranges li'));
if (rangesLiLastEl) {
rangesLiLastEl.classList.add('active');
this.chosenLabel = rangesLiLastEl.dataset.rangeKey;
} else {
this.chosenLabel = null;
}
} else {
this.chosenLabel = null;
}
this.showCalendars();
}
},
clickApply: function(e) {
this.hide();
e.target.dispatchEvent(new CustomEvent('apply.daterangepicker', {bubbles: true, detail: this}));
},
clickCancel: function(e) {
this.startDate = this.oldStartDate;
this.endDate = this.oldEndDate;
this.hide();
e.target.dispatchEvent(new CustomEvent('cancel.daterangepicker', {bubbles: true, detail: this}));
},
monthOrYearChanged: function(e) {
var isLeft = e.target.closest('.drp-calendar').classList.contains('left'),
leftOrRight = isLeft ? 'left' : 'right',
cal = this.container.querySelector('.drp-calendar.'+leftOrRight);
// Month must be Number for new moment versions
var month = parseInt(cal.querySelector('.monthselect').value, 10);
var year = cal.querySelector('.yearselect').value;
if (!isLeft) {
if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {
month = this.startDate.month();
year = this.startDate.year();
}
}
if (this.minDate) {
if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {
month = this.minDate.month();
year = this.minDate.year();
}
}
if (this.maxDate) {
if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {
month = this.maxDate.month();
year = this.maxDate.year();
}
}
if (isLeft) {
this.leftCalendar.month.month(month).year(year);
if (this.linkedCalendars)
this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month');
} else {
this.rightCalendar.month.month(month).year(year);
if (this.linkedCalendars)
this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month');
}
this.updateCalendars();
},
timeChanged: function(e) {
var cal = e.target.closest('.drp-calendar'),
isLeft = cal.classList.contains('left');
var hour = parseInt(cal.querySelector('.hourselect').value, 10);
var minute = parseInt(cal.querySelector('.minuteselect').value, 10);
if (isNaN(minute)) {
minute = parseInt(jq.findLast(cal.querySelectorAll('.minuteselect option')).value, 10);
}
var second = this.timePickerSeconds ? parseInt(cal.querySelector('.secondselect').value, 10) : 0;
if (!this.timePicker24Hour) {
var ampm = cal.querySelector('.ampmselect').value;
if (ampm === 'PM' && hour < 12)
hour += 12;
if (ampm === 'AM' && hour === 12)
hour = 0;
}
if (isLeft) {
var start = this.startDate.clone();
start.hour(hour);
start.minute(minute);
start.second(second);
this.setStartDate(start);
if (this.singleDatePicker) {
this.endDate = this.startDate.clone();
} else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) {
this.setEndDate(start.clone());
}
} else if (this.endDate) {
var end = this.endDate.clone();
end.hour(hour);
end.minute(minute);
end.second(second);
this.setEndDate(end);
}
//update the calendars so all clickable dates reflect the new time component
this.updateCalendars();
//update the form inputs above the calendars with the new time
this.updateFormInputs();
//re-render the time pickers because changing one selection can affect what's enabled in another
let drpCalendarElList = this.container.querySelectorAll('.drp-calendar');
jq.off(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy);
this.renderTimePicker('left');
this.renderTimePicker('right');
jq.on(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy);
},
elementChanged: function () {
if(!(this.element.tagName === 'INPUT')) return;
if(!this.element.value || !this.element.value.length) return;
var dateString = this.element.value.split(this.locale.separator),
start = null,
end = null;
if (dateString.length === 2) {
start = moment(dateString[0], this.locale.format);
end = moment(dateString[1], this.locale.format);
}
if (this.singleDatePicker || start === null || end === null) {
start = moment(this.element.value, this.locale.format);
end = start;
}
if (!start.isValid() || !end.isValid()) return;
this.setStartDate(start);
this.setEndDate(end);
this.updateView();
},
keydown: function(e) {
//hide on tab or enter
if ((e.keyCode === 9) || (e.keyCode === 13)) {
this.hide();
}
//hide on esc and prevent propagation
if (e.keyCode === 27) {
e.preventDefault();
e.stopPropagation();
this.hide();
}
},
updateElement: function () {
if (this.element.tagName === 'INPUT' && this.autoUpdateInput) {
let newValue = this.startDate.format(this.locale.format);
if (!this.singleDatePicker) {
newValue += this.locale.separator + this.endDate.format(this.locale.format);
}
if (newValue !== this.element.value) {
this.element.value = newValue;
/* this.element.dispatchEvent(new Event('change')); Note: イベント? */
}
}
},
remove: function() {
if (this._outsideClickProxy) {
// Bind global datepicker mousedown for hiding and
document.removeEventListener('mousedown', this._outsideClickProxy);
// also support mobile devices
document.removeEventListener('touchend', this._outsideClickProxy);
jq.off(document, 'click', '[data-toggle=dropdown]', this._outsideClickProxy);
// and also close when focus changes to outside the picker (eg. tabbing between controls)
document.removeEventListener('focusin', this._outsideClickProxy);
delete this._outsideClickProxy;
}
if(this.moveProxy){
window.addEventListener('resize', this.moveProxy);
delete this.moveProxy;
}
if(this.clickRangeProxy){
jq.off(this.container.querySelector('.ranges'), 'click', 'li', this.clickRangeProxy);
delete this.clickRangeProxy;
}
let drpButtonsEl = this.container.querySelector('.drp-buttons');
if(this.clickApplyProxy){
jq.off(drpButtonsEl, 'click', 'button.applyBtn', this.clickApplyProxy);
delete this.clickApplyProxy;
}
if(this.clickCancelProxy){
jq.off(drpButtonsEl, 'click', 'button.cancelBtn', this.clickCancelProxy);
delete this.clickCancelProxy;
}
if (this.element.tagName === 'INPUT' || this.element.tagName === 'BUTTON') {
if(this.showProxy){
jq.off(this.element, 'click', this.showProxy);
jq.off(this.element, 'focus', this.showProxy);
delete this.showProxy;
}
if(this.elementChangedProxy){
jq.off(this.element, 'keyup', this.elementChangedProxy);
delete this.elementChangedProxy;
};
if(this.keydownProxy){
jq.off(this.element, 'keydown', this.keydownProxy);
delete this.keydownProxy;
}
} else {
if(this.toggleProxy){
jq.off(this.element, 'click', this.toggleProxy);
jq.off(this.element, 'keydown', this.toggleProxy);
delete this.toggleProxy;
};
}
let drpCalendarElList = this.container.querySelectorAll('.drp-calendar');
if(this.clickPrevProxy){
jq.off(drpCalendarElList, 'click', '.prev', this.clickPrevProxy);
delete this.clickPrevProxy;
}
if(this.clickNextProxy){
jq.off(drpCalendarElList, 'click', '.next', this.clickNextProxy);
delete this.clickNextProxy;
}
if(this.clickDateProxy){
jq.off(drpCalendarElList, 'mousedown', 'td.available', this.clickDateProxy);
delete this.clickDateProxy;
}
if(this.hoverDateProxy){
jq.off(drpCalendarElList, 'mouseenter', 'td.available', this.hoverDateProxy);
delete this.hoverDateProxy;
}
if(this.monthOrYearChangedProxy){
jq.off(drpCalendarElList, 'change', 'select.yearselect', this.monthOrYearChangedProxy);
jq.off(drpCalendarElList, 'change', 'select.monthselect', this.monthOrYearChangedProxy);
delete this.monthOrYearChangedProxy;
}
if(this.timeChangedProxy){
jq.off(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy);
delete this.timeChangedProxy;
}
delete this.container;
delete this.element.dataset;
},
updateRanges: function(newRanges){
if (typeof newRanges === 'object') {
jq.off(this.container.querySelector('.ranges'), 'click', 'li', this.clickRangeProxy);
this.ranges = [];
let rangesKeys = Object.keys(newRanges);
for(let i = 0; i < rangesKeys.length; ++i){
let range = rangesKeys[i];
if (typeof newRanges[range][0] === 'string')
start = moment(newRanges[range][0], this.locale.format);
else
start = moment(newRanges[range][0]);
if (typeof newRanges[range][1] === 'string')
end = moment(newRanges[range][1], this.locale.format);
else
end = moment(newRanges[range][1]);
// 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.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'))
|| (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))
continue;
//Support unicode chars in the range names.
var elem = document.createElement('textarea');
elem.innerHTML = range;
var rangeHtml = elem.value;
this.ranges[rangeHtml] = [start, end];
}
var list = '