UNPKG

@openhab-ui/setup-and-maintenance

Version:

Configuration and maintenance interface for openHAB

1,266 lines (1,258 loc) 93 kB
/* flatpickr v4.5.2, @license MIT */ const pad = (number) => `0${number}`.slice(-2); const int = (bool) => (bool === true ? 1 : 0); function debounce(func, wait, immediate = false) { let timeout; return function () { let context = this, args = arguments; timeout !== null && clearTimeout(timeout); timeout = window.setTimeout(function () { timeout = null; if (!immediate) func.apply(context, args); }, wait); if (immediate && !timeout) func.apply(context, args); }; } const arrayify = (obj) => obj instanceof Array ? obj : [obj]; const do_nothing = () => undefined; const monthToStr = (monthNumber, shorthand, locale) => locale.months[shorthand ? "shorthand" : "longhand"][monthNumber]; const revFormat = { D: do_nothing, F: function (dateObj, monthName, locale) { dateObj.setMonth(locale.months.longhand.indexOf(monthName)); }, G: (dateObj, hour) => { dateObj.setHours(parseFloat(hour)); }, H: (dateObj, hour) => { dateObj.setHours(parseFloat(hour)); }, J: (dateObj, day) => { dateObj.setDate(parseFloat(day)); }, K: (dateObj, amPM, locale) => { dateObj.setHours((dateObj.getHours() % 12) + 12 * int(new RegExp(locale.amPM[1], "i").test(amPM))); }, M: function (dateObj, shortMonth, locale) { dateObj.setMonth(locale.months.shorthand.indexOf(shortMonth)); }, S: (dateObj, seconds) => { dateObj.setSeconds(parseFloat(seconds)); }, U: (_, unixSeconds) => new Date(parseFloat(unixSeconds) * 1000), W: function (dateObj, weekNum) { const weekNumber = parseInt(weekNum); return new Date(dateObj.getFullYear(), 0, 2 + (weekNumber - 1) * 7, 0, 0, 0, 0); }, Y: (dateObj, year) => { dateObj.setFullYear(parseFloat(year)); }, Z: (_, ISODate) => new Date(ISODate), d: (dateObj, day) => { dateObj.setDate(parseFloat(day)); }, h: (dateObj, hour) => { dateObj.setHours(parseFloat(hour)); }, i: (dateObj, minutes) => { dateObj.setMinutes(parseFloat(minutes)); }, j: (dateObj, day) => { dateObj.setDate(parseFloat(day)); }, l: do_nothing, m: (dateObj, month) => { dateObj.setMonth(parseFloat(month) - 1); }, n: (dateObj, month) => { dateObj.setMonth(parseFloat(month) - 1); }, s: (dateObj, seconds) => { dateObj.setSeconds(parseFloat(seconds)); }, u: (_, unixMillSeconds) => new Date(parseFloat(unixMillSeconds)), w: do_nothing, y: (dateObj, year) => { dateObj.setFullYear(2000 + parseFloat(year)); }, }; const tokenRegex = { D: "(\\w+)", F: "(\\w+)", G: "(\\d\\d|\\d)", H: "(\\d\\d|\\d)", J: "(\\d\\d|\\d)\\w+", K: "", M: "(\\w+)", S: "(\\d\\d|\\d)", U: "(.+)", W: "(\\d\\d|\\d)", Y: "(\\d{4})", Z: "(.+)", d: "(\\d\\d|\\d)", h: "(\\d\\d|\\d)", i: "(\\d\\d|\\d)", j: "(\\d\\d|\\d)", l: "(\\w+)", m: "(\\d\\d|\\d)", n: "(\\d\\d|\\d)", s: "(\\d\\d|\\d)", u: "(.+)", w: "(\\d\\d|\\d)", y: "(\\d{2})", }; const formats = { Z: (date) => date.toISOString(), D: function (date, locale, options) { return locale.weekdays.shorthand[formats.w(date, locale, options)]; }, F: function (date, locale, options) { return monthToStr(formats.n(date, locale, options) - 1, false, locale); }, G: function (date, locale, options) { return pad(formats.h(date, locale, options)); }, H: (date) => pad(date.getHours()), J: function (date, locale) { return locale.ordinal !== undefined ? date.getDate() + locale.ordinal(date.getDate()) : date.getDate(); }, K: (date, locale) => locale.amPM[int(date.getHours() > 11)], M: function (date, locale) { return monthToStr(date.getMonth(), true, locale); }, S: (date) => pad(date.getSeconds()), U: (date) => date.getTime() / 1000, W: function (date, _, options) { return options.getWeek(date); }, Y: (date) => date.getFullYear(), d: (date) => pad(date.getDate()), h: (date) => (date.getHours() % 12 ? date.getHours() % 12 : 12), i: (date) => pad(date.getMinutes()), j: (date) => date.getDate(), l: function (date, locale) { return locale.weekdays.longhand[date.getDay()]; }, m: (date) => pad(date.getMonth() + 1), n: (date) => date.getMonth() + 1, s: (date) => date.getSeconds(), u: (date) => date.getTime(), w: (date) => date.getDay(), y: (date) => String(date.getFullYear()).substring(2), }; const english = { weekdays: { shorthand: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], longhand: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", ], }, months: { shorthand: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ], longhand: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ], }, daysInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], firstDayOfWeek: 0, ordinal: (nth) => { const s = nth % 100; if (s > 3 && s < 21) return "th"; switch (s % 10) { case 1: return "st"; case 2: return "nd"; case 3: return "rd"; default: return "th"; } }, rangeSeparator: " to ", weekAbbreviation: "Wk", scrollTitle: "Scroll to increment", toggleTitle: "Click to toggle", amPM: ["AM", "PM"], yearAriaLabel: "Year", }; const createDateFormatter = ({ config = defaults, l10n = english, }) => (dateObj, frmt, overrideLocale) => { const locale = overrideLocale || l10n; if (config.formatDate !== undefined) { return config.formatDate(dateObj, frmt, locale); } return frmt .split("") .map((c, i, arr) => formats[c] && arr[i - 1] !== "\\" ? formats[c](dateObj, locale, config) : c !== "\\" ? c : "") .join(""); }; const createDateParser = ({ config = defaults, l10n = english }) => (date, givenFormat, timeless, customLocale) => { if (date !== 0 && !date) return undefined; const locale = customLocale || l10n; let parsedDate; const date_orig = date; if (date instanceof Date) parsedDate = new Date(date.getTime()); else if (typeof date !== "string" && date.toFixed !== undefined) parsedDate = new Date(date); else if (typeof date === "string") { const format = givenFormat || (config || defaults).dateFormat; const datestr = String(date).trim(); if (datestr === "today") { parsedDate = new Date(); timeless = true; } else if (/Z$/.test(datestr) || /GMT$/.test(datestr)) parsedDate = new Date(date); else if (config && config.parseDate) parsedDate = config.parseDate(date, format); else { parsedDate = !config || !config.noCalendar ? new Date(new Date().getFullYear(), 0, 1, 0, 0, 0, 0) : new Date(new Date().setHours(0, 0, 0, 0)); let matched, ops = []; for (let i = 0, matchIndex = 0, regexStr = ""; i < format.length; i++) { const token = format[i]; const isBackSlash = token === "\\"; const escaped = format[i - 1] === "\\" || isBackSlash; if (tokenRegex[token] && !escaped) { regexStr += tokenRegex[token]; const match = new RegExp(regexStr).exec(date); if (match && (matched = true)) { ops[token !== "Y" ? "push" : "unshift"]({ fn: revFormat[token], val: match[++matchIndex], }); } } else if (!isBackSlash) regexStr += "."; ops.forEach(({ fn, val }) => (parsedDate = fn(parsedDate, val, locale) || parsedDate)); } parsedDate = matched ? parsedDate : undefined; } } if (!(parsedDate instanceof Date && !isNaN(parsedDate.getTime()))) { config.errorHandler(new Error(`Invalid date provided: ${date_orig}`)); return undefined; } if (timeless === true) parsedDate.setHours(0, 0, 0, 0); return parsedDate; }; function compareDates(date1, date2, timeless = true) { if (timeless !== false) { return (new Date(date1.getTime()).setHours(0, 0, 0, 0) - new Date(date2.getTime()).setHours(0, 0, 0, 0)); } return date1.getTime() - date2.getTime(); } const getWeek = (givenDate) => { const date = new Date(givenDate.getTime()); date.setHours(0, 0, 0, 0); date.setDate(date.getDate() + 3 - ((date.getDay() + 6) % 7)); var week1 = new Date(date.getFullYear(), 0, 4); return (1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + ((week1.getDay() + 6) % 7)) / 7)); }; const isBetween = (ts, ts1, ts2) => { return ts > Math.min(ts1, ts2) && ts < Math.max(ts1, ts2); }; const duration = { DAY: 86400000, }; const HOOKS = [ "onChange", "onClose", "onDayCreate", "onDestroy", "onKeyDown", "onMonthChange", "onOpen", "onParseConfig", "onReady", "onValueUpdate", "onYearChange", "onPreCalendarPosition", ]; const defaults = { _disable: [], _enable: [], allowInput: false, altFormat: "F j, Y", altInput: false, altInputClass: "form-control input", animate: typeof window === "object" && window.navigator.userAgent.indexOf("MSIE") === -1, ariaDateFormat: "F j, Y", clickOpens: true, closeOnSelect: true, conjunction: ", ", dateFormat: "Y-m-d", defaultHour: 12, defaultMinute: 0, defaultSeconds: 0, disable: [], disableMobile: false, enable: [], enableSeconds: false, enableTime: false, errorHandler: (err) => typeof console !== "undefined" && console.warn(err), getWeek, hourIncrement: 1, ignoredFocusElements: [], inline: false, locale: "default", minuteIncrement: 5, mode: "single", nextArrow: "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M13.207 8.472l-7.854 7.854-0.707-0.707 7.146-7.146-7.146-7.148 0.707-0.707 7.854 7.854z' /></svg>", noCalendar: false, now: new Date(), onChange: [], onClose: [], onDayCreate: [], onDestroy: [], onKeyDown: [], onMonthChange: [], onOpen: [], onParseConfig: [], onReady: [], onValueUpdate: [], onYearChange: [], onPreCalendarPosition: [], plugins: [], position: "auto", positionElement: undefined, prevArrow: "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M5.207 8.471l7.146 7.147-0.707 0.707-7.853-7.854 7.854-7.853 0.707 0.707-7.147 7.146z' /></svg>", shorthandCurrentMonth: false, showMonths: 1, static: false, time_24hr: false, weekNumbers: false, wrap: false, }; function toggleClass(elem, className, bool) { if (bool === true) return elem.classList.add(className); elem.classList.remove(className); } function createElement(tag, className, content) { const e = window.document.createElement(tag); className = className || ""; content = content || ""; e.className = className; if (content !== undefined) e.textContent = content; return e; } function clearNode(node) { while (node.firstChild) node.removeChild(node.firstChild); } function findParent(node, condition) { if (condition(node)) return node; else if (node.parentNode) return findParent(node.parentNode, condition); return undefined; } function createNumberInput(inputClassName, opts) { const wrapper = createElement("div", "numInputWrapper"), numInput = createElement("input", "numInput " + inputClassName), arrowUp = createElement("span", "arrowUp"), arrowDown = createElement("span", "arrowDown"); numInput.type = "text"; numInput.pattern = "\\d*"; if (opts !== undefined) for (const key in opts) numInput.setAttribute(key, opts[key]); wrapper.appendChild(numInput); wrapper.appendChild(arrowUp); wrapper.appendChild(arrowDown); return wrapper; } function getEventTarget(event) { if (typeof event.composedPath === "function") { const path = event.composedPath(); return path[0]; } return event.target; } if (typeof Object.assign !== "function") { Object.assign = function (target, ...args) { if (!target) { throw TypeError("Cannot convert undefined or null to object"); } for (const source of args) { if (source) { Object.keys(source).forEach(key => (target[key] = source[key])); } } return target; }; } const DEBOUNCED_CHANGE_MS = 300; function FlatpickrInstance(element, instanceConfig) { const self = { config: Object.assign({}, defaults), l10n: english, }; self.parseDate = createDateParser({ config: self.config, l10n: self.l10n }); self._handlers = []; self._bind = bind; self._setHoursFromDate = setHoursFromDate; self._positionCalendar = positionCalendar; self.changeMonth = changeMonth; self.changeYear = changeYear; self.clear = clear; self.close = close; self._createElement = createElement; self.destroy = destroy; self.isEnabled = isEnabled; self.jumpToDate = jumpToDate; self.open = open; self.redraw = redraw; self.set = set; self.setDate = setDate; self.toggle = toggle; function setupHelperFunctions() { self.utils = { getDaysInMonth(month = self.currentMonth, yr = self.currentYear) { if (month === 1 && ((yr % 4 === 0 && yr % 100 !== 0) || yr % 400 === 0)) return 29; return self.l10n.daysInMonth[month]; }, }; } function init() { self.element = self.input = element; self.isOpen = false; parseConfig(); setupLocale(); setupInputs(); setupDates(); setupHelperFunctions(); if (!self.isMobile) build(); bindEvents(); if (self.selectedDates.length || self.config.noCalendar) { if (self.config.enableTime) { setHoursFromDate(self.config.noCalendar ? self.latestSelectedDateObj || self.config.minDate : undefined); } updateValue(false); } setCalendarWidth(); self.showTimeInput = self.selectedDates.length > 0 || self.config.noCalendar; const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); if (!self.isMobile && isSafari) { positionCalendar(); } triggerEvent("onReady"); } function bindToInstance(fn) { return fn.bind(self); } function setCalendarWidth() { const config = self.config; if (config.weekNumbers === false && config.showMonths === 1) return; else if (config.noCalendar !== true) { window.requestAnimationFrame(function () { if (self.calendarContainer !== undefined) { self.calendarContainer.style.visibility = "hidden"; self.calendarContainer.style.display = "block"; } if (self.daysContainer !== undefined) { const daysWidth = (self.days.offsetWidth + 1) * config.showMonths; self.daysContainer.style.width = daysWidth + "px"; self.calendarContainer.style.width = daysWidth + (self.weekWrapper !== undefined ? self.weekWrapper.offsetWidth : 0) + "px"; self.calendarContainer.style.removeProperty("visibility"); self.calendarContainer.style.removeProperty("display"); } }); } } function updateTime(e) { if (self.selectedDates.length === 0) return; if (e !== undefined && e.type !== "blur") { timeWrapper(e); } const prevValue = self._input.value; setHoursFromInputs(); updateValue(); if (self._input.value !== prevValue) { self._debouncedChange(); } } function ampm2military(hour, amPM) { return (hour % 12) + 12 * int(amPM === self.l10n.amPM[1]); } function military2ampm(hour) { switch (hour % 24) { case 0: case 12: return 12; default: return hour % 12; } } function setHoursFromInputs() { if (self.hourElement === undefined || self.minuteElement === undefined) return; let hours = (parseInt(self.hourElement.value.slice(-2), 10) || 0) % 24, minutes = (parseInt(self.minuteElement.value, 10) || 0) % 60, seconds = self.secondElement !== undefined ? (parseInt(self.secondElement.value, 10) || 0) % 60 : 0; if (self.amPM !== undefined) { hours = ampm2military(hours, self.amPM.textContent); } const limitMinHours = self.config.minTime !== undefined || (self.config.minDate && self.minDateHasTime && self.latestSelectedDateObj && compareDates(self.latestSelectedDateObj, self.config.minDate, true) === 0); const limitMaxHours = self.config.maxTime !== undefined || (self.config.maxDate && self.maxDateHasTime && self.latestSelectedDateObj && compareDates(self.latestSelectedDateObj, self.config.maxDate, true) === 0); if (limitMaxHours) { const maxTime = self.config.maxTime !== undefined ? self.config.maxTime : self.config.maxDate; hours = Math.min(hours, maxTime.getHours()); if (hours === maxTime.getHours()) minutes = Math.min(minutes, maxTime.getMinutes()); if (minutes === maxTime.getMinutes()) seconds = Math.min(seconds, maxTime.getSeconds()); } if (limitMinHours) { const minTime = self.config.minTime !== undefined ? self.config.minTime : self.config.minDate; hours = Math.max(hours, minTime.getHours()); if (hours === minTime.getHours()) minutes = Math.max(minutes, minTime.getMinutes()); if (minutes === minTime.getMinutes()) seconds = Math.max(seconds, minTime.getSeconds()); } setHours(hours, minutes, seconds); } function setHoursFromDate(dateObj) { const date = dateObj || self.latestSelectedDateObj; if (date) setHours(date.getHours(), date.getMinutes(), date.getSeconds()); } function setDefaultHours() { let hours = self.config.defaultHour; let minutes = self.config.defaultMinute; let seconds = self.config.defaultSeconds; if (self.config.minDate !== undefined) { const min_hr = self.config.minDate.getHours(); const min_minutes = self.config.minDate.getMinutes(); hours = Math.max(hours, min_hr); if (hours === min_hr) minutes = Math.max(min_minutes, minutes); if (hours === min_hr && minutes === min_minutes) seconds = self.config.minDate.getSeconds(); } if (self.config.maxDate !== undefined) { const max_hr = self.config.maxDate.getHours(); const max_minutes = self.config.maxDate.getMinutes(); hours = Math.min(hours, max_hr); if (hours === max_hr) minutes = Math.min(max_minutes, minutes); if (hours === max_hr && minutes === max_minutes) seconds = self.config.maxDate.getSeconds(); } setHours(hours, minutes, seconds); } function setHours(hours, minutes, seconds) { if (self.latestSelectedDateObj !== undefined) { self.latestSelectedDateObj.setHours(hours % 24, minutes, seconds || 0, 0); } if (!self.hourElement || !self.minuteElement || self.isMobile) return; self.hourElement.value = pad(!self.config.time_24hr ? ((12 + hours) % 12) + 12 * int(hours % 12 === 0) : hours); self.minuteElement.value = pad(minutes); if (self.amPM !== undefined) self.amPM.textContent = self.l10n.amPM[int(hours >= 12)]; if (self.secondElement !== undefined) self.secondElement.value = pad(seconds); } function onYearInput(event) { const year = parseInt(event.target.value) + (event.delta || 0); if (year / 1000 > 1 || (event.key === "Enter" && !/[^\d]/.test(year.toString()))) { changeYear(year); } } function bind(element, event, handler, options) { if (event instanceof Array) return event.forEach(ev => bind(element, ev, handler, options)); if (element instanceof Array) return element.forEach(el => bind(el, event, handler, options)); element.addEventListener(event, handler, options); self._handlers.push({ element: element, event, handler, options, }); } function onClick(handler) { return evt => { evt.which === 1 && handler(evt); }; } function triggerChange() { triggerEvent("onChange"); } function bindEvents() { if (self.config.wrap) { ["open", "close", "toggle", "clear"].forEach(evt => { Array.prototype.forEach.call(self.element.querySelectorAll(`[data-${evt}]`), (el) => bind(el, "click", self[evt])); }); } if (self.isMobile) { setupMobile(); return; } const debouncedResize = debounce(onResize, 50); self._debouncedChange = debounce(triggerChange, DEBOUNCED_CHANGE_MS); if (self.daysContainer && !/iPhone|iPad|iPod/i.test(navigator.userAgent)) bind(self.daysContainer, "mouseover", (e) => { if (self.config.mode === "range") onMouseOver(e.target); }); bind(window.document.body, "keydown", onKeyDown); if (!self.config.static) bind(self._input, "keydown", onKeyDown); if (!self.config.inline && !self.config.static) bind(window, "resize", debouncedResize); if (window.ontouchstart !== undefined) bind(window.document, "click", documentClick); else bind(window.document, "mousedown", onClick(documentClick)); bind(window.document, "focus", documentClick, { capture: true }); if (self.config.clickOpens === true) { bind(self._input, "focus", self.open); bind(self._input, "mousedown", onClick(self.open)); } if (self.daysContainer !== undefined) { bind(self.monthNav, "mousedown", onClick(onMonthNavClick)); bind(self.monthNav, ["keyup", "increment"], onYearInput); bind(self.daysContainer, "mousedown", onClick(selectDate)); } if (self.timeContainer !== undefined && self.minuteElement !== undefined && self.hourElement !== undefined) { const selText = (e) => e.target.select(); bind(self.timeContainer, ["increment"], updateTime); bind(self.timeContainer, "blur", updateTime, { capture: true }); bind(self.timeContainer, "mousedown", onClick(timeIncrement)); bind([self.hourElement, self.minuteElement], ["focus", "click"], selText); if (self.secondElement !== undefined) bind(self.secondElement, "focus", () => self.secondElement && self.secondElement.select()); if (self.amPM !== undefined) { bind(self.amPM, "mousedown", onClick(e => { updateTime(e); triggerChange(); })); } } } function jumpToDate(jumpDate) { const jumpTo = jumpDate !== undefined ? self.parseDate(jumpDate) : self.latestSelectedDateObj || (self.config.minDate && self.config.minDate > self.now ? self.config.minDate : self.config.maxDate && self.config.maxDate < self.now ? self.config.maxDate : self.now); try { if (jumpTo !== undefined) { self.currentYear = jumpTo.getFullYear(); self.currentMonth = jumpTo.getMonth(); } } catch (e) { e.message = "Invalid date supplied: " + jumpTo; self.config.errorHandler(e); } self.redraw(); } function timeIncrement(e) { if (~e.target.className.indexOf("arrow")) incrementNumInput(e, e.target.classList.contains("arrowUp") ? 1 : -1); } function incrementNumInput(e, delta, inputElem) { const target = e && e.target; const input = inputElem || (target && target.parentNode && target.parentNode.firstChild); const event = createEvent("increment"); event.delta = delta; input && input.dispatchEvent(event); } function build() { const fragment = window.document.createDocumentFragment(); self.calendarContainer = createElement("div", "flatpickr-calendar"); self.calendarContainer.tabIndex = -1; if (!self.config.noCalendar) { fragment.appendChild(buildMonthNav()); self.innerContainer = createElement("div", "flatpickr-innerContainer"); if (self.config.weekNumbers) { const { weekWrapper, weekNumbers } = buildWeeks(); self.innerContainer.appendChild(weekWrapper); self.weekNumbers = weekNumbers; self.weekWrapper = weekWrapper; } self.rContainer = createElement("div", "flatpickr-rContainer"); self.rContainer.appendChild(buildWeekdays()); if (!self.daysContainer) { self.daysContainer = createElement("div", "flatpickr-days"); self.daysContainer.tabIndex = -1; } buildDays(); self.rContainer.appendChild(self.daysContainer); self.innerContainer.appendChild(self.rContainer); fragment.appendChild(self.innerContainer); } if (self.config.enableTime) { fragment.appendChild(buildTime()); } toggleClass(self.calendarContainer, "rangeMode", self.config.mode === "range"); toggleClass(self.calendarContainer, "animate", self.config.animate === true); toggleClass(self.calendarContainer, "multiMonth", self.config.showMonths > 1); self.calendarContainer.appendChild(fragment); const customAppend = self.config.appendTo !== undefined && self.config.appendTo.nodeType !== undefined; if (self.config.inline || self.config.static) { self.calendarContainer.classList.add(self.config.inline ? "inline" : "static"); if (self.config.inline) { if (!customAppend && self.element.parentNode) self.element.parentNode.insertBefore(self.calendarContainer, self._input.nextSibling); else if (self.config.appendTo !== undefined) self.config.appendTo.appendChild(self.calendarContainer); } if (self.config.static) { const wrapper = createElement("div", "flatpickr-wrapper"); if (self.element.parentNode) self.element.parentNode.insertBefore(wrapper, self.element); wrapper.appendChild(self.element); if (self.altInput) wrapper.appendChild(self.altInput); wrapper.appendChild(self.calendarContainer); } } if (!self.config.static && !self.config.inline) (self.config.appendTo !== undefined ? self.config.appendTo : window.document.body).appendChild(self.calendarContainer); } function createDay(className, date, dayNumber, i) { const dateIsEnabled = isEnabled(date, true), dayElement = createElement("span", "flatpickr-day " + className, date.getDate().toString()); dayElement.dateObj = date; dayElement.$i = i; dayElement.setAttribute("aria-label", self.formatDate(date, self.config.ariaDateFormat)); if (className.indexOf("hidden") === -1 && compareDates(date, self.now) === 0) { self.todayDateElem = dayElement; dayElement.classList.add("today"); dayElement.setAttribute("aria-current", "date"); } if (dateIsEnabled) { dayElement.tabIndex = -1; if (isDateSelected(date)) { dayElement.classList.add("selected"); self.selectedDateElem = dayElement; if (self.config.mode === "range") { toggleClass(dayElement, "startRange", self.selectedDates[0] && compareDates(date, self.selectedDates[0], true) === 0); toggleClass(dayElement, "endRange", self.selectedDates[1] && compareDates(date, self.selectedDates[1], true) === 0); if (className === "nextMonthDay") dayElement.classList.add("inRange"); } } } else { dayElement.classList.add("disabled"); } if (self.config.mode === "range") { if (isDateInRange(date) && !isDateSelected(date)) dayElement.classList.add("inRange"); } if (self.weekNumbers && self.config.showMonths === 1 && className !== "prevMonthDay" && dayNumber % 7 === 1) { self.weekNumbers.insertAdjacentHTML("beforeend", "<span class='flatpickr-day'>" + self.config.getWeek(date) + "</span>"); } triggerEvent("onDayCreate", dayElement); return dayElement; } function focusOnDayElem(targetNode) { targetNode.focus(); if (self.config.mode === "range") onMouseOver(targetNode); } function getFirstAvailableDay(delta) { const startMonth = delta > 0 ? 0 : self.config.showMonths - 1; const endMonth = delta > 0 ? self.config.showMonths : -1; for (let m = startMonth; m != endMonth; m += delta) { const month = self.daysContainer.children[m]; const startIndex = delta > 0 ? 0 : month.children.length - 1; const endIndex = delta > 0 ? month.children.length : -1; for (let i = startIndex; i != endIndex; i += delta) { const c = month.children[i]; if (c.className.indexOf("hidden") === -1 && isEnabled(c.dateObj)) return c; } } return undefined; } function getNextAvailableDay(current, delta) { const givenMonth = current.className.indexOf("Month") === -1 ? current.dateObj.getMonth() : self.currentMonth; const endMonth = delta > 0 ? self.config.showMonths : -1; const loopDelta = delta > 0 ? 1 : -1; for (let m = givenMonth - self.currentMonth; m != endMonth; m += loopDelta) { const month = self.daysContainer.children[m]; const startIndex = givenMonth - self.currentMonth === m ? current.$i + delta : delta < 0 ? month.children.length - 1 : 0; const numMonthDays = month.children.length; for (let i = startIndex; i >= 0 && i < numMonthDays && i != (delta > 0 ? numMonthDays : -1); i += loopDelta) { const c = month.children[i]; if (c.className.indexOf("hidden") === -1 && isEnabled(c.dateObj) && Math.abs(current.$i - i) >= Math.abs(delta)) return focusOnDayElem(c); } } self.changeMonth(loopDelta); focusOnDay(getFirstAvailableDay(loopDelta), 0); return undefined; } function focusOnDay(current, offset) { const dayFocused = isInView(document.activeElement || document.body); const startElem = current !== undefined ? current : dayFocused ? document.activeElement : self.selectedDateElem !== undefined && isInView(self.selectedDateElem) ? self.selectedDateElem : self.todayDateElem !== undefined && isInView(self.todayDateElem) ? self.todayDateElem : getFirstAvailableDay(offset > 0 ? 1 : -1); if (startElem === undefined) return self._input.focus(); if (!dayFocused) return focusOnDayElem(startElem); getNextAvailableDay(startElem, offset); } function buildMonthDays(year, month) { const firstOfMonth = (new Date(year, month, 1).getDay() - self.l10n.firstDayOfWeek + 7) % 7; const prevMonthDays = self.utils.getDaysInMonth((month - 1 + 12) % 12); const daysInMonth = self.utils.getDaysInMonth(month), days = window.document.createDocumentFragment(), isMultiMonth = self.config.showMonths > 1, prevMonthDayClass = isMultiMonth ? "prevMonthDay hidden" : "prevMonthDay", nextMonthDayClass = isMultiMonth ? "nextMonthDay hidden" : "nextMonthDay"; let dayNumber = prevMonthDays + 1 - firstOfMonth, dayIndex = 0; for (; dayNumber <= prevMonthDays; dayNumber++ , dayIndex++) { days.appendChild(createDay(prevMonthDayClass, new Date(year, month - 1, dayNumber), dayNumber, dayIndex)); } for (dayNumber = 1; dayNumber <= daysInMonth; dayNumber++ , dayIndex++) { days.appendChild(createDay("", new Date(year, month, dayNumber), dayNumber, dayIndex)); } for (let dayNum = daysInMonth + 1; dayNum <= 42 - firstOfMonth && (self.config.showMonths === 1 || dayIndex % 7 !== 0); dayNum++ , dayIndex++) { days.appendChild(createDay(nextMonthDayClass, new Date(year, month + 1, dayNum % daysInMonth), dayNum, dayIndex)); } const dayContainer = createElement("div", "dayContainer"); dayContainer.appendChild(days); return dayContainer; } function buildDays() { if (self.daysContainer === undefined) { return; } clearNode(self.daysContainer); if (self.weekNumbers) clearNode(self.weekNumbers); const frag = document.createDocumentFragment(); for (let i = 0; i < self.config.showMonths; i++) { const d = new Date(self.currentYear, self.currentMonth, 1); d.setMonth(self.currentMonth + i); frag.appendChild(buildMonthDays(d.getFullYear(), d.getMonth())); } self.daysContainer.appendChild(frag); self.days = self.daysContainer.firstChild; if (self.config.mode === "range" && self.selectedDates.length === 1) { onMouseOver(); } } function buildMonth() { const container = createElement("div", "flatpickr-month"); const monthNavFragment = window.document.createDocumentFragment(); const monthElement = createElement("span", "cur-month"); const yearInput = createNumberInput("cur-year", { tabindex: "-1" }); const yearElement = yearInput.getElementsByTagName("input")[0]; yearElement.setAttribute("aria-label", self.l10n.yearAriaLabel); if (self.config.minDate) yearElement.setAttribute("data-min", self.config.minDate.getFullYear().toString()); if (self.config.maxDate) { yearElement.setAttribute("data-max", self.config.maxDate.getFullYear().toString()); yearElement.disabled = !!self.config.minDate && self.config.minDate.getFullYear() === self.config.maxDate.getFullYear(); } const currentMonth = createElement("div", "flatpickr-current-month"); currentMonth.appendChild(monthElement); currentMonth.appendChild(yearInput); monthNavFragment.appendChild(currentMonth); container.appendChild(monthNavFragment); return { container, yearElement, monthElement, }; } function buildMonths() { clearNode(self.monthNav); self.monthNav.appendChild(self.prevMonthNav); if (self.config.showMonths) { self.yearElements = []; self.monthElements = []; } for (let m = self.config.showMonths; m--;) { const month = buildMonth(); self.yearElements.push(month.yearElement); self.monthElements.push(month.monthElement); self.monthNav.appendChild(month.container); } self.monthNav.appendChild(self.nextMonthNav); } function buildMonthNav() { self.monthNav = createElement("div", "flatpickr-months"); self.yearElements = []; self.monthElements = []; self.prevMonthNav = createElement("span", "flatpickr-prev-month"); self.prevMonthNav.innerHTML = self.config.prevArrow; self.nextMonthNav = createElement("span", "flatpickr-next-month"); self.nextMonthNav.innerHTML = self.config.nextArrow; buildMonths(); Object.defineProperty(self, "_hidePrevMonthArrow", { get: () => self.__hidePrevMonthArrow, set(bool) { if (self.__hidePrevMonthArrow !== bool) { toggleClass(self.prevMonthNav, "disabled", bool); self.__hidePrevMonthArrow = bool; } }, }); Object.defineProperty(self, "_hideNextMonthArrow", { get: () => self.__hideNextMonthArrow, set(bool) { if (self.__hideNextMonthArrow !== bool) { toggleClass(self.nextMonthNav, "disabled", bool); self.__hideNextMonthArrow = bool; } }, }); self.currentYearElement = self.yearElements[0]; updateNavigationCurrentMonth(); return self.monthNav; } function buildTime() { self.calendarContainer.classList.add("hasTime"); if (self.config.noCalendar) self.calendarContainer.classList.add("noCalendar"); self.timeContainer = createElement("div", "flatpickr-time"); self.timeContainer.tabIndex = -1; const separator = createElement("span", "flatpickr-time-separator", ":"); const hourInput = createNumberInput("flatpickr-hour"); self.hourElement = hourInput.getElementsByTagName("input")[0]; const minuteInput = createNumberInput("flatpickr-minute"); self.minuteElement = minuteInput.getElementsByTagName("input")[0]; self.hourElement.tabIndex = self.minuteElement.tabIndex = -1; self.hourElement.value = pad(self.latestSelectedDateObj ? self.latestSelectedDateObj.getHours() : self.config.time_24hr ? self.config.defaultHour : military2ampm(self.config.defaultHour)); self.minuteElement.value = pad(self.latestSelectedDateObj ? self.latestSelectedDateObj.getMinutes() : self.config.defaultMinute); self.hourElement.setAttribute("data-step", self.config.hourIncrement.toString()); self.minuteElement.setAttribute("data-step", self.config.minuteIncrement.toString()); self.hourElement.setAttribute("data-min", self.config.time_24hr ? "0" : "1"); self.hourElement.setAttribute("data-max", self.config.time_24hr ? "23" : "12"); self.minuteElement.setAttribute("data-min", "0"); self.minuteElement.setAttribute("data-max", "59"); self.timeContainer.appendChild(hourInput); self.timeContainer.appendChild(separator); self.timeContainer.appendChild(minuteInput); if (self.config.time_24hr) self.timeContainer.classList.add("time24hr"); if (self.config.enableSeconds) { self.timeContainer.classList.add("hasSeconds"); const secondInput = createNumberInput("flatpickr-second"); self.secondElement = secondInput.getElementsByTagName("input")[0]; self.secondElement.value = pad(self.latestSelectedDateObj ? self.latestSelectedDateObj.getSeconds() : self.config.defaultSeconds); self.secondElement.setAttribute("data-step", self.minuteElement.getAttribute("data-step")); self.secondElement.setAttribute("data-min", self.minuteElement.getAttribute("data-min")); self.secondElement.setAttribute("data-max", self.minuteElement.getAttribute("data-max")); self.timeContainer.appendChild(createElement("span", "flatpickr-time-separator", ":")); self.timeContainer.appendChild(secondInput); } if (!self.config.time_24hr) { self.amPM = createElement("span", "flatpickr-am-pm", self.l10n.amPM[int((self.latestSelectedDateObj ? self.hourElement.value : self.config.defaultHour) > 11)]); self.amPM.title = self.l10n.toggleTitle; self.amPM.tabIndex = -1; self.timeContainer.appendChild(self.amPM); } return self.timeContainer; } function buildWeekdays() { if (!self.weekdayContainer) self.weekdayContainer = createElement("div", "flatpickr-weekdays"); else clearNode(self.weekdayContainer); for (let i = self.config.showMonths; i--;) { const container = createElement("div", "flatpickr-weekdaycontainer"); self.weekdayContainer.appendChild(container); } updateWeekdays(); return self.weekdayContainer; } function updateWeekdays() { const firstDayOfWeek = self.l10n.firstDayOfWeek; let weekdays = [...self.l10n.weekdays.shorthand]; if (firstDayOfWeek > 0 && firstDayOfWeek < weekdays.length) { weekdays = [ ...weekdays.splice(firstDayOfWeek, weekdays.length), ...weekdays.splice(0, firstDayOfWeek), ]; } for (let i = self.config.showMonths; i--;) { self.weekdayContainer.children[i].innerHTML = ` <span class='flatpickr-weekday'> ${weekdays.join("</span><span class='flatpickr-weekday'>")} </span> `; } } function buildWeeks() { self.calendarContainer.classList.add("hasWeeks"); const weekWrapper = createElement("div", "flatpickr-weekwrapper"); weekWrapper.appendChild(createElement("span", "flatpickr-weekday", self.l10n.weekAbbreviation)); const weekNumbers = createElement("div", "flatpickr-weeks"); weekWrapper.appendChild(weekNumbers); return { weekWrapper, weekNumbers, }; } function changeMonth(value, is_offset = true) { const delta = is_offset ? value : value - self.currentMonth; if ((delta < 0 && self._hidePrevMonthArrow === true) || (delta > 0 && self._hideNextMonthArrow === true)) return; self.currentMonth += delta; if (self.currentMonth < 0 || self.currentMonth > 11) { self.currentYear += self.currentMonth > 11 ? 1 : -1; self.currentMonth = (self.currentMonth + 12) % 12; triggerEvent("onYearChange"); } buildDays(); triggerEvent("onMonthChange"); updateNavigationCurrentMonth(); } function clear(triggerChangeEvent = true) { self.input.value = ""; if (self.altInput !== undefined) self.altInput.value = ""; if (self.mobileInput !== undefined) self.mobileInput.value = ""; self.selectedDates = []; self.latestSelectedDateObj = undefined; self.showTimeInput = false; if (self.config.enableTime === true) { setDefaultHours(); } self.redraw(); if (triggerChangeEvent) triggerEvent("onChange"); } function close() { self.isOpen = false; if (!self.isMobile) { if (self.calendarContainer !== undefined) { self.calendarContainer.classList.remove("open"); } if (self._input !== undefined) { self._input.classList.remove("active"); } } triggerEvent("onClose"); } function destroy() { if (self.config !== undefined) triggerEvent("onDestroy"); for (let i = self._handlers.length; i--;) { const h = self._handlers[i]; h.element.removeEventListener(h.event, h.handler, h.options); } self._handlers = []; if (self.mobileInput) { if (self.mobileInput.parentNode) self.mobileInput.parentNode.removeChild(self.mobileInput); self.mobileInput = undefined; } else if (self.calendarContainer && self.calendarContainer.parentNode) { if (self.config.static && self.calendarContainer.parentNode) { const wrapper = self.calendarContainer.parentNode; wrapper.lastChild && wrapper.removeChild(wrapper.lastChild); if (wrapper.parentNode) { while (wrapper.firstChild) wrapper.parentNode.insertBefore(wrapper.firstChild, wrapper); wrapper.parentNode.removeChild(wrapper); } } else self.calendarContainer.parentNode.removeChild(self.calendarContainer); } if (self.altInput) { self.input.type = "text"; if (self.altInput.parentNode) self.altInput.parentNode.removeChild(self.altInput); delete self.altInput; } if (self.input) { self.input.type = self.input._type; self.input.classList.remove("flatpickr-input"); self.input.removeAttribute("readonly"); self.input.value = ""; } [ "_showTimeInput", "latestSelectedDateObj", "_hideNextMonthArrow", "_hidePrevMonthArrow", "__hideNextMonthArrow", "__hidePrevMonthArrow", "isMobile", "isOpen", "selectedDateElem", "minDateHasTime", "maxDateHasTime", "days", "daysContainer", "_input", "_positionElement", "innerContainer", "rContainer", "monthNav", "todayDateElem", "calendarContainer", "weekdayContainer", "prevMonthNav", "nextMonthNav", "currentMonthElement", "currentYearElement", "navigationCurrentMonth", "selectedDateElem", "config", ].forEach(k => { try { delete self[k]; } catch (_) { } }); } function isCalendarElem(elem) { if (self.config.appendTo && self.config.appendTo.contains(elem)) return true; return self.calendarContainer.contains(elem); } function documentClick(e) { if (self.isOpen && !self.config.inline) { const eventTarget = getEventTarget(e); const isCalendarElement = isCalendarElem(eventTarget); const isInput = eventTarget === self.input || eventTarget === self.altInput || self.element.contains(eventTarget) || (e.path && e.path.indexOf && (~e.path.indexOf(self.input) || ~e.path.indexOf(self.altInput))); const lostFocus = e.type === "blur" ? isInput && e.relatedTarget && !isCalendarElem(e.relatedTarget) : !isInput && !isCalendarElement && !isCalendarElem(e.relatedTarget);