UNPKG

@senx/discovery-widgets

Version:

Discovery Widgets Elements

1,077 lines (1,073 loc) 105 kB
import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/internal/client'; import { m as moment, U as Utils, b as momentTimezone, P as Param, G as GTSLib } from './utils.js'; /* * Copyright 2025 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class DTPickerOptions { } /* * Copyright 2025 SenX S.A.S. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // alternate jquery function (subset) class JQ { addClassSub(el, classes) { const classsList = classes.split(' '); for (const item of classsList) { el.classList.add(item.trim()); } } addClass(el, classes) { if (!el) { return; } if (typeof el.length === 'number') { for (const item of el) { this.addClassSub(item, classes); } } else { this.addClassSub(el, classes); } } findLast(el) { if (!el) { return null; } if (typeof el.length === 'number') { if (el.length > 0) { return el[el.length - 1]; } else { return null; } } else { return el; } } findSelectedOption(el) { if (!el || !el.options || !el.options.length) { return null; } for (const opt of el.options) { if (opt.selected) { return opt; } } return null; } getSelectorFromElement(el) { if (!el || !(el instanceof Element)) { return null; } let selector = el.nodeName.toLowerCase(); if (el.id) { return selector + '#' + el.id; } for (const clazz of el.classList) { selector += `.${clazz}`; } return selector; } html(el, html) { if (el) { el.innerHTML = html; } } offset(el) { if (!el) { return { top: 0, left: 0 }; } // Return zeros for disconnected and hidden (display: none) elements (gh-2310) // Support: IE <=11 only // Running getBoundingClientRect on a // disconnected node in IE throws an error if (!el.getClientRects().length) { return { top: 0, left: 0 }; } // Get document-relative position by adding viewport scroll to viewport-relative gBCR const rect = el.getBoundingClientRect(); const win = el.ownerDocument.defaultView; return { top: rect.top + win.pageYOffset, left: rect.left + win.pageXOffset, }; } offSub(el, event, listener) { if (typeof el.length === 'number') { for (const item of el) { item.removeEventListener(event, listener); } } else { el.removeEventListener(event, listener); } } off(el, event, param1, param2) { if (!el) { return; } if (typeof param1 === 'function') { // param is listener this.offSub(el, event, param1); } else { // param is selector if (typeof el.length === 'number') { for (const item of el) { this.offSub(item.querySelectorAll(param1), event, param2); } } else { this.offSub(el.querySelectorAll(param1), event, param2); } } } onSub(el, event, listener) { if (typeof el.length === 'number') { for (const item of el) { item.addEventListener(event, listener); } } else { el.addEventListener(event, listener); } } on(el, event, param1, param2) { if (!el) { return; } if (typeof param1 === 'function') { // param1 is listener this.onSub(el, event, param1); } else { // param1 is selector if (typeof el.length === 'number') { for (const item of el) { this.onSub(item.querySelectorAll(param1), event, param2); } } else { this.onSub(el.querySelectorAll(param1), event, param2); } } } } // IE browser doesn't support "class" class DateRangePicker { constructor(element, options, cb) { var _b, _c, _d, _f; this.jq = new JQ(); this.startDate = moment(); this.endDate = moment(); this.oldStartDate = moment(); this.oldEndDate = moment(); this.outsideClickProxy = (e) => this.outsideClick(e); this.moveProxy = (e) => this.move(e); this.clickRangeProxy = (e) => this.clickRange(e); this.clickApplyProxy = (e) => this.clickApply(e); this.clickCancelProxy = (e) => this.clickCancel(e); this.showProxy = (e) => this.show(e); this.elementChangedProxy = (e) => this.elementChanged(e); this.keydownProxy = (e) => this.keydown(e); this.toggleProxy = (e) => this.toggle(e); this.clickPrevProxy = (e) => this.clickPrev(e); this.clickNextProxy = (e) => this.clickNext(e); this.clickDateProxy = (e) => this.clickDate(e); this.hoverDateProxy = (e) => this.hoverDate(e); this.monthOrYearChangedProxy = (e) => this.monthOrYearChanged(e); this.timeChangedProxy = (e) => this.timeChanged(e); let rangeHtml; let elem; // default settings for options this.parentEl = document.body; if (typeof element === 'string') { this.element = document.getElementById(element); } else { this.element = element; } this.options = { startDate: moment().startOf('day'), endDate: moment().endOf('day'), minDate: undefined, maxDate: undefined, maxSpan: undefined, autoApply: false, singleDatePicker: false, showDropdowns: false, minYear: moment().subtract(100, 'year').format('YYYY'), maxYear: moment().add(100, 'year').format('YYYY'), showWeekNumbers: false, showISOWeekNumbers: false, showCustomRangeLabel: true, timePicker: false, timePicker24Hour: false, timePickerIncrement: 1, timePickerSeconds: false, linkedCalendars: true, autoUpdateInput: true, alwaysShowCalendars: false, ranges: {}, opens: 'right', drops: 'down', buttonClasses: 'btn btn-sm', applyButtonClasses: 'btn-primary', cancelButtonClasses: 'btn-default', wrapperClasses: '', locale: { locale: 'en', 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(), }, }; if (this.element.classList.contains('pull-right')) { this.options.opens = 'left'; } if (this.element.classList.contains('dropup')) { this.options.drops = 'up'; } this.callback = () => { // empty }; // some state information this.isShowing = false; this.leftCalendar = {}; this.rightCalendar = {}; // custom options from user if (typeof options !== 'object' || options === null) { options = new DTPickerOptions(); } // 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 = `<div class="daterangepicker"> <div class="dp-wrapper"> <div class="ranges"></div> <div class="drp-calendar left"> <div class="calendar-table"></div> <div class="calendar-time"></div> </div> <div class="drp-calendar right"> <div class="calendar-table"></div> <div class="calendar-time"></div> </div> </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 : this.parentEl; const 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') { this.options.locale.locale = (_b = options.locale.locale) !== null && _b !== void 0 ? _b : 'en'; const loc = Utils.clone(moment.localeData((_c = options.locale.locale) !== null && _c !== void 0 ? _c : 'en')); this.options.locale.timeZone = (_d = options.locale.timeZone) !== null && _d !== void 0 ? _d : 'UTC'; if (typeof options.locale.direction === 'string') { this.options.locale.direction = options.locale.direction; } if (typeof options.locale.format === 'string') { this.options.locale.format = options.locale.format; } if (typeof options.locale.separator === 'string') { this.options.locale.separator = options.locale.separator; } this.options.locale.monthNames = loc.monthsShort(); this.options.locale.daysOfWeek = loc.weekdaysShort(); this.options.locale.firstDay = loc.firstDayOfWeek(); if (typeof options.locale.applyLabel === 'string') { this.options.locale.applyLabel = options.locale.applyLabel; } if (typeof options.locale.cancelLabel === 'string') { this.options.locale.cancelLabel = options.locale.cancelLabel; } if (typeof options.locale.weekLabel === 'string') { this.options.locale.weekLabel = options.locale.weekLabel; } if (typeof options.locale.customRangeLabel === 'string') { // Support Unicode chars in the custom range name. elem = document.createElement('textarea'); elem.innerHTML = options.locale.customRangeLabel; rangeHtml = elem.value; this.options.locale.customRangeLabel = rangeHtml; } } this.container.classList.add(this.options.locale.direction); if (typeof options.startDate === 'object') { this.options.startDate = moment(options.startDate); } if (typeof options.endDate === 'object') { this.options.endDate = moment(options.endDate); } if (typeof options.minDate === 'object') { this.options.minDate = moment(options.minDate); } if (typeof options.maxDate === 'object') { this.options.maxDate = moment(options.maxDate); } // sanity check for bad options if (this.options.minDate && this.options.startDate.isBefore(this.options.minDate)) { this.options.startDate = this.options.minDate.clone(); } // sanity check for bad options if (this.options.maxDate && this.options.endDate.isAfter(this.options.maxDate)) { this.options.endDate = this.options.maxDate.clone(); } if (typeof options.applyButtonClasses === 'string') { this.options.applyButtonClasses = options.applyButtonClasses; } if (typeof options.cancelButtonClasses === 'string') { this.options.cancelButtonClasses = options.cancelButtonClasses; } if (typeof options.maxSpan === 'object') { this.options.maxSpan = options.maxSpan; } if (typeof options.opens === 'string') { this.options.opens = options.opens; } if (typeof options.drops === 'string') { this.options.drops = options.drops; } if (typeof options.showWeekNumbers === 'boolean') { this.options.showWeekNumbers = options.showWeekNumbers; } if (typeof options.showISOWeekNumbers === 'boolean') { this.options.showISOWeekNumbers = options.showISOWeekNumbers; } if (typeof options.buttonClasses === 'string') { this.options.buttonClasses = options.buttonClasses; } if (typeof options.buttonClasses === 'object') { this.options.buttonClasses = ((_f = options.buttonClasses) !== null && _f !== void 0 ? _f : []).join(' '); } if (typeof options.showDropdowns === 'boolean') { this.options.showDropdowns = options.showDropdowns; } if (typeof options.minYear === 'number') { this.options.minYear = options.minYear; } if (typeof options.maxYear === 'number') { this.options.maxYear = options.maxYear; } if (typeof options.showCustomRangeLabel === 'boolean') { this.options.showCustomRangeLabel = options.showCustomRangeLabel; } if (typeof options.singleDatePicker === 'boolean') { this.options.singleDatePicker = options.singleDatePicker; if (this.options.singleDatePicker) { this.options.endDate = this.options.startDate.clone(); } } if (typeof options.timePicker === 'boolean') { this.options.timePicker = options.timePicker; } if (typeof options.timePickerSeconds === 'boolean') { this.options.timePickerSeconds = options.timePickerSeconds; } if (typeof options.timePickerIncrement === 'number') { this.options.timePickerIncrement = options.timePickerIncrement; } if (typeof options.timePicker24Hour === 'boolean') { this.options.timePicker24Hour = options.timePicker24Hour; } if (typeof options.autoApply === 'boolean') { this.options.autoApply = options.autoApply; } if (typeof options.autoUpdateInput === 'boolean') { this.options.autoUpdateInput = options.autoUpdateInput; } if (typeof options.linkedCalendars === 'boolean') { this.options.linkedCalendars = options.linkedCalendars; } if (typeof options.isInvalidDate === 'function') { this.options.isInvalidDate = options.isInvalidDate; } if (typeof options.isCustomDate === 'function') { this.options.isCustomDate = options.isCustomDate; } if (typeof options.alwaysShowCalendars === 'boolean') { this.options.alwaysShowCalendars = options.alwaysShowCalendars; } // update day names order to firstDay if (this.options.locale.firstDay !== 0) { let iterator = this.options.locale.firstDay; while (iterator > 0) { this.options.locale.daysOfWeek.push(this.options.locale.daysOfWeek.shift()); iterator--; } } let start; let end; let 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') { const val = this.element.value; const split = val.split(this.options.locale.separator); start = end = null; if (split.length === 2) { start = moment(split[0], this.options.locale.format); end = moment(split[1], this.options.locale.format); } else if (this.options.singleDatePicker && val !== '') { start = moment(val, this.options.locale.format); end = moment(val, this.options.locale.format); } if (start !== null && end !== null) { this.setStartDate(start); this.setEndDate(end); } } } else { this.setStartDate(options.startDate); this.setEndDate(options.endDate); } if (typeof options.ranges === 'object') { const rangesKeys = Object.keys(options.ranges); for (const range of rangesKeys) { start = moment(options.ranges[range][0]); if (typeof options.ranges[range][1] === 'string') { end = moment(options.ranges[range][1], this.options.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.options.minDate && start.isBefore(this.options.minDate)) { start = this.options.minDate.clone(); } let maxDate = this.options.maxDate; if (this.options.maxSpan && maxDate && start.clone().add(this.options.maxSpan).isAfter(maxDate)) { maxDate = start.clone().add(this.options.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.options.minDate && end.isBefore(this.options.minDate, this.timepicker ? 'minute' : 'day')) || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day'))) { continue; } // Support Unicode chars in the range names. elem = document.createElement('textarea'); elem.innerHTML = range; rangeHtml = elem.value; this.options.ranges[rangeHtml] = [start, end]; } let list = '<ul>'; for (range in this.options.ranges) { if (range) { list += `<li data-range-key="${range}">${range}</li>`; } } if (this.options.showCustomRangeLabel) { list += `<li data-range-key="${this.options.locale.customRangeLabel}">${this.options.locale.customRangeLabel}</li>`; } list += '</ul>'; this.container.querySelector('.ranges').insertAdjacentHTML('afterbegin', list); } if (typeof cb === 'function') { this.callback = cb; } if (!this.options.timePicker) { this.options.startDate = this.options.startDate.startOf('day'); this.options.endDate = this.options.endDate.endOf('day'); this.container.style.display = 'none'; } // can't be used together for now if (this.options.timePicker && this.options.autoApply) { this.options.autoApply = false; } if (this.options.autoApply) { this.container.classList.add('auto-apply'); } if (typeof options.ranges === 'object') { this.container.classList.add('show-ranges'); } if (typeof options.wrapperClasses === 'string') { options.wrapperClasses.split(' ').forEach((className) => { if (className.trim()) { this.container.classList.add(className); } }); } if (this.options.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.options.timePicker && this.options.autoApply) { this.container.classList.add('auto-apply'); } } if ((typeof options.ranges === 'undefined' && !this.options.singleDatePicker) || this.options.alwaysShowCalendars) { this.container.classList.add('show-calendar'); } this.container.classList.add('opens' + this.options.opens); // apply CSS classes and labels to buttons const applyBtnEl = this.container.querySelector('.applyBtn'); const cancelBtnEl = this.container.querySelector('.cancelBtn'); this.jq.addClass(applyBtnEl, this.options.buttonClasses); this.jq.addClass(cancelBtnEl, this.options.buttonClasses); if (this.options.applyButtonClasses.length) { this.jq.addClass(applyBtnEl, this.options.applyButtonClasses); } if (this.options.cancelButtonClasses.length) { this.jq.addClass(cancelBtnEl, this.options.cancelButtonClasses); } this.jq.html(applyBtnEl, this.options.locale.applyLabel); this.jq.html(cancelBtnEl, this.options.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.jq.on(this.container.querySelector('.ranges'), 'click', 'li', this.clickRangeProxy); const drpButtonsEl = this.container.querySelector('.drp-buttons'); this.jq.on(drpButtonsEl, 'click', 'button.applyBtn', this.clickApplyProxy); this.jq.on(drpButtonsEl, 'click', 'button.cancelBtn', this.clickCancelProxy); if (this.element.tagName === 'INPUT' || this.element.tagName === 'BUTTON') { this.jq.on(this.element, 'click', this.showProxy); this.jq.on(this.element, 'focus', this.showProxy); this.jq.on(this.element, 'keyup', this.elementChangedProxy); this.jq.on(this.element, 'keydown', this.keydownProxy); } else { this.jq.on(this.element, 'click', this.toggleProxy); this.jq.on(this.element, 'keydown', this.toggleProxy); } // // if attached to a text input, set the initial value // this.updateElement(); } setStartDate(startDate) { this.startDate = moment(startDate); if (!this.options.timePicker) { this.startDate = this.startDate.startOf('day'); } if (this.options.timePicker && this.options.timePickerIncrement) { this.startDate.minute(Math.round(this.startDate.minute() / this.options.timePickerIncrement) * this.options.timePickerIncrement); } if (this.options.minDate && this.startDate.isBefore(this.options.minDate)) { this.startDate = this.options.minDate.clone(); if (this.options.timePicker && this.options.timePickerIncrement) { this.startDate.minute(Math.round(this.startDate.minute() / this.options.timePickerIncrement) * this.options.timePickerIncrement); } } if (this.options.maxDate && this.startDate.isAfter(this.options.maxDate)) { this.startDate = this.options.maxDate.clone(); if (this.options.timePicker && this.options.timePickerIncrement) { this.startDate.minute(Math.floor(this.startDate.minute() / this.options.timePickerIncrement) * this.options.timePickerIncrement); } } if (!this.isShowing) { this.updateElement(); } this.updateMonthsInView(); } setEndDate(endDate) { this.endDate = moment(endDate); if (!this.options.timePicker) { this.endDate = this.endDate.endOf('day'); } if (this.options.timePicker && this.options.timePickerIncrement) { this.endDate.minute(Math.round(this.endDate.minute() / this.options.timePickerIncrement) * this.options.timePickerIncrement); } if (this.endDate.isBefore(this.startDate)) { this.endDate = this.startDate.clone(); } if (this.options.maxDate && this.endDate.isAfter(this.options.maxDate)) { this.endDate = this.options.maxDate.clone(); } if (this.options.maxSpan && this.startDate.clone().add(this.options.maxSpan).isBefore(this.endDate)) { this.endDate = this.startDate.clone().add(this.options.maxSpan); } if (!this.isShowing) { this.updateElement(); } this.updateMonthsInView(); } isInvalidDate(_a) { return false; } isCustomDate(_a) { return false; } updateView() { if (this.options.timePicker) { this.renderTimePicker('left'); this.renderTimePicker('right'); const selectElList = this.container.querySelectorAll('.right .calendar-time select'); if (!this.endDate) { for (const item of selectElList) { item.disabled = true; item.classList.add('disabled'); } } else { for (const item of selectElList) { item.disabled = false; item.classList.remove('disabled'); } } } this.updateMonthsInView(); this.updateCalendars(); this.updateFormInputs(); } updateMonthsInView() { if (this.endDate) { // if both dates are visible already, do nothing if (!this.options.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.options.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.options.maxDate && this.options.linkedCalendars && !this.options.singleDatePicker && this.rightCalendar.month > this.options.maxDate) { this.rightCalendar.month = this.options.maxDate.clone().date(2); this.leftCalendar.month = this.options.maxDate.clone().date(2).subtract(1, 'month'); } } updateCalendars() { let ampm; /* -- 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)); -------------------------------------------------------------------------------------- */ const drpCalendarElList = this.container.querySelectorAll('.drp-calendar'); this.jq.off(drpCalendarElList, 'click', '.prev', this.clickPrevProxy); this.jq.off(drpCalendarElList, 'click', '.next', this.clickNextProxy); this.jq.off(drpCalendarElList, 'mousedown', 'td.available', this.clickDateProxy); this.jq.off(drpCalendarElList, 'mouseenter', 'td.available', this.hoverDateProxy); this.jq.off(drpCalendarElList, 'change', 'select.yearselect', this.monthOrYearChangedProxy); this.jq.off(drpCalendarElList, 'change', 'select.monthselect', this.monthOrYearChangedProxy); this.jq.off(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy); if (this.options.timePicker) { let hour; let minute; let 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(this.jq.findLast(this.container.querySelector('.left .minuteselect')).value, 10); } second = this.options.timePickerSeconds ? parseInt(this.container.querySelector('.left .secondselect').value, 10) : 0; if (!this.options.timePicker24Hour) { 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(this.jq.findLast(this.container.querySelector('.right .minuteselect')).value, 10); } second = this.options.timePickerSeconds ? parseInt(this.container.querySelector('.right .secondselect').value, 10) : 0; if (!this.options.timePicker24Hour) { 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 this.rangesLiElList = this.container.querySelectorAll('.ranges li'); for (const item of this.rangesLiElList) { item.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)); -------------------------------------------------------------------------------------- */ this.jq.on(drpCalendarElList, 'click', '.prev', this.clickPrevProxy); this.jq.on(drpCalendarElList, 'click', '.next', this.clickNextProxy); this.jq.on(drpCalendarElList, 'mousedown', 'td.available', this.clickDateProxy); this.jq.on(drpCalendarElList, 'mouseenter', 'td.available', this.hoverDateProxy); this.jq.on(drpCalendarElList, 'change', 'select.yearselect', this.monthOrYearChangedProxy); this.jq.on(drpCalendarElList, 'change', 'select.monthselect', this.monthOrYearChangedProxy); this.jq.on(drpCalendarElList, 'change', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', this.timeChangedProxy); } renderCalendar(side) { // Build the matrix of dates that will populate the calendar let i; let calendar = side === 'left' ? this.leftCalendar : this.rightCalendar; const month = calendar.month.month(); const year = calendar.month.year(); const hour = calendar.month.hour(); const minute = calendar.month.minute(); const second = calendar.month.second(); const daysInMonth = moment([year, month]).daysInMonth(); const firstDay = moment([year, month, 1]); const lastDay = moment([year, month, daysInMonth]); const lastMonth = moment(firstDay).subtract(1, 'month').month(); const lastYear = moment(firstDay).subtract(1, 'month').year(); const daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth(); const dayOfWeek = firstDay.day(); // initialize 6 rows x 7 columns array for the calendar calendar = []; calendar.firstDay = firstDay; calendar.lastDay = lastDay; for (i = 0; i < 6; i++) { calendar[i] = []; } // populate the calendar with date objects let startDay = daysInLastMonth - dayOfWeek + this.options.locale.firstDay + 1; if (startDay > daysInLastMonth) { startDay -= 7; } if (dayOfWeek === this.options.locale.firstDay) { startDay = daysInLastMonth - 6; } let curDate = momentTimezone.exports.tz([lastYear, lastMonth, startDay, 12, minute, second], this.options.locale.timeZone); i = 0; let col = 0; let row = 0; for (; i < 42; i++, col++, curDate = 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.options.minDate && calendar[row][col].format('YYYY-MM-DD') === this.options.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.options.minDate) && side === 'left') { calendar[row][col] = this.options.minDate.clone(); } if (this.options.maxDate && calendar[row][col].format('YYYY-MM-DD') === this.options.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.options.maxDate) && side === 'right') { calendar[row][col] = this.options.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 // const minDate = side === 'left' ? this.options.minDate : this.startDate; let maxDate = this.options.maxDate; /* const selected = side === 'left' ? this.startDate : this.endDate; const arrow = this.options.locale.direction === 'ltr' ? { left: 'chevron-left', right: 'chevron-right', } : { left: 'chevron-right', right: 'chevron-left' };*/ let html = '<table class="table-condensed">'; html += '<thead>'; html += '<tr>'; // add empty cell for week number if (this.options.showWeekNumbers || this.options.showISOWeekNumbers) { html += '<th></th>'; } if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.options.linkedCalendars || side === 'left')) { html += '<th class="prev available"><span></span></th>'; } else { html += '<th></th>'; } let dateHtml = this.options.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(' YYYY'); if (this.options.showDropdowns) { const currentMonth = calendar[1][1].month(); const currentYear = calendar[1][1].year(); const maxYear = (maxDate && maxDate.year()) || parseInt(this.options.maxYear, 10); const minYear = (minDate && minDate.year()) || parseInt(this.options.minYear, 10); const inMinYear = currentYear === minYear; const inMaxYear = currentYear === maxYear; let monthHtml = '<select class="monthselect">'; for (let m = 0; m < 12; m++) { if ((!inMinYear || (minDate && m >= minDate.month())) && (!inMaxYear || (maxDate && m <= maxDate.month()))) { monthHtml += `<option value='${m}'${m === currentMonth ? ' selected=\'selected\'' : ''}>${this.options.locale.monthNames[m]}</option>`; } else { monthHtml += `<option value='${m}'${m === currentMonth ? ' selected=\'selected\'' : ''} disabled='disabled'>${this.options.locale.monthNames[m]}</option>`; } } monthHtml += '</select>'; let yearHtml = '<select class="yearselect">'; for (let y = minYear; y <= maxYear; y++) { yearHtml += `<option value="${y}"${y === currentYear ? ' selected="selected"' : ''}>${y}</option>`; } yearHtml += '</select>'; dateHtml = monthHtml + yearHtml; } html += '<th colspan="5" class="month">' + dateHtml + '</th>'; if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.options.linkedCalendars || side === 'right' || this.options.singleDatePicker)) { html += '<th class="next available"><span></span></th>'; } else { html += '<th></th>'; } html += '</tr>'; html += '<tr>'; // add week number label if (this.options.showWeekNumbers || this.options.showISOWeekNumbers) { html += `<th class="week">${this.options.locale.weekLabel}</th>`; } for (const item of this.options.locale.daysOfWeek) { html += `<th>${item}</th>`; } html += '</tr>'; html += '</thead>'; html += '<tbody>'; // adjust maxDate to reflect the maxSpan setting in order to // grey out end dates beyond the maxSpan if (this.endDate === null && this.options.maxSpan) { const maxLimit = this.startDate.clone().add(this.options.maxSpan).endOf('day'); if (!maxDate || maxLimit.isBefore(maxDate)) { maxDate = maxLimit; } } for (row = 0; row < 6; row++) { html += '<tr>'; // add week number if (this.options.showWeekNumbers) { html += `<td class="week">${calendar[row][0].week()}</td>`; } else if (this.options.showISOWeekNumbers) { html += `<td class="week">${calendar[row][0].isoWeek()}</td>`; } for (col = 0; col < 7; col++) { const 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.options.minDate && calendar[row][col].isBefore(this.options.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 const isCustom = this.isCustomDate(calendar[row][col]); if (isCustom !== false) { if (typeof isCustom === 'string') { classes.push(isCustom); } else { Array.prototype.push.apply(classes, isCustom); } } let cname = ''; let disabled = false; for (i = 0; i < classes.length; i++) { cname += classes[i] + ' '; if (classes[i] === 'disabled') { disabled = true; } } if (!disabled) { cname += 'available'; } html += `<td class="${cname.replace(/^\s+|\s+$/g, '')}" data-title="r${row}c${col}">${calendar[row][col].date()}</td>`; } html += '</tr>'; } html += '</tbody>'; html += '</table>'; this.jq.html(this.container.querySelector(`.drp-calendar.${side} .calendar-table`), html); } renderTimePicker(side) { let padded; let disabled; let time; // Don't bother updating the time picker if it's currently disabled let i; // because an end date hasn't been clicked yet if (side === 'right' && !this.endDate) return; let html; let selected; let minDate; let maxDate = this.options.maxDate; if (this.options.maxSpan && (!this.options.maxDate || this.startDate.clone().add(this.options.maxSpan).isBefore(this.options.maxDate))) { maxDate = this.startDate.clone().add(this.options.maxSpan); } if (side === 'left') { selected = this.startDate.clone(); minDate = this.options.minDate; } else if (side === 'right') { selected = this.endDate.clone(); minDate = this.startDate; // Preserve the time already selected const timeSelector = this.container.querySelector('.drp-calendar.right .calendar-time'); if (timeSelector.innerHTML !== '') { selected.hour(!isNaN(selected.hour()) ? selected.hour() : this.jq.findSelectedOption(timeSelector.querySelector('.hourselect')).value); selected.minute(!isNaN(selected.minute()) ? selected.minute() : this.jq.findSelectedOption(timeSelector.querySelector('.minuteselect')).value); selected.second(!isNaN(selected.second()) ? selected.second() : this.jq.findSelectedOption(timeSelector.querySelector('.secondselect')).value); if (!this.options.timePicker24Hour) { const ampm = this.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 = '<select class="hourselect">'; const start = this.options.timePicker24Hour ? 0 : 1; const end = this.options.timePicker24Hour ? 23 : 12; for (i = start; i <= end; i++) { let i_in_24 = i; if (!this.options.timePicker24Hour) { i_in_24 = selected.hour() >= 12 ? (i === 12 ? 12 : i + 12) : (i === 12 ? 0 : i); } time = selected.clone().hour(i_in_24); disabled = !!(minDate && time.minute(59).isBefore(minDate)); if (maxDate && time.minute(0).isAfter(maxDate)) { disabled = true; } padded = i < 10 ? `0${i}` : `${i}`; if (i_in_24 === selected.hour() && !disabled) { html += `<option value="${i}" selected="selected">${padded}</option>`; } else if (disabled) { html += `<option value="${i}" disabled="disabled" class="disabled">${padded}</option>`; } else { html += `<option value="${i}">${padded}</option>`; } } html += '</select> '; // // minutes // html += ': <select class="minuteselect">'; for (i = 0; i < 60; i += this.options.timePickerIncrement) { padded = i < 10 ? `0${i}` : `${i}`; time = selected.clone().minute(i); disabled = !!(minDate && time.second(59).isBefore(minDate));