Файловый менеджер - Редактировать - /home/u418952908/domains/fitnesstricks.xyz/public_html/js/tempusdominus/js/tempus-dominus.js
�азад
/*! * Tempus Dominus v6.0.1 (https://getdatepicker.com/) * Copyright 2013-2022 Jonathan Peterson * Licensed under MIT (https://github.com/Eonasdan/tempus-dominus/blob/master/LICENSE) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) : typeof define === 'function' && define.amd ? define(['@popperjs/core'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.tempusDominus = factory(global.Popper)); })(this, (function (core) { 'use strict'; var Unit; (function (Unit) { Unit["seconds"] = "seconds"; Unit["minutes"] = "minutes"; Unit["hours"] = "hours"; Unit["date"] = "date"; Unit["month"] = "month"; Unit["year"] = "year"; })(Unit || (Unit = {})); const twoDigitTemplate = { month: '2-digit', day: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true, }; const twoDigitTwentyFourTemplate = { hour: '2-digit', hour12: false }; const getFormatByUnit = (unit) => { switch (unit) { case 'date': return { dateStyle: 'short' }; case 'month': return { month: 'numeric', year: 'numeric' }; case 'year': return { year: 'numeric' }; } }; /** * For the most part this object behaves exactly the same way * as the native Date object with a little extra spice. */ class DateTime extends Date { constructor() { super(...arguments); /** * Used with Intl.DateTimeFormat */ this.locale = 'default'; this.nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; this.leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; } /** * Chainable way to set the {@link locale} * @param value */ setLocale(value) { this.locale = value; return this; } /** * Converts a plain JS date object to a DateTime object. * Doing this allows access to format, etc. * @param date * @param locale */ static convert(date, locale = 'default') { if (!date) throw new Error(`A date is required`); return new DateTime(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()).setLocale(locale); } /** * Attempts to create a DateTime from a string. A customDateFormat is required for non US dates. * @param input * @param localization */ static fromString(input, localization) { return new DateTime(input); } /** * Native date manipulations are not pure functions. This function creates a duplicate of the DateTime object. */ get clone() { return new DateTime(this.year, this.month, this.date, this.hours, this.minutes, this.seconds, this.getMilliseconds()).setLocale(this.locale); } /** * Sets the current date to the start of the {@link unit} provided * Example: Consider a date of "April 30, 2021, 11:45:32.984 AM" => new DateTime(2021, 3, 30, 11, 45, 32, 984).startOf('month') * would return April 1, 2021, 12:00:00.000 AM (midnight) * @param unit * @param startOfTheWeek Allows for the changing the start of the week. */ startOf(unit, startOfTheWeek = 0) { if (this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); switch (unit) { case 'seconds': this.setMilliseconds(0); break; case 'minutes': this.setSeconds(0, 0); break; case 'hours': this.setMinutes(0, 0, 0); break; case 'date': this.setHours(0, 0, 0, 0); break; case 'weekDay': this.startOf(Unit.date); if (this.weekDay === startOfTheWeek) break; let goBack = this.weekDay; if (startOfTheWeek !== 0 && this.weekDay === 0) goBack = 8 - startOfTheWeek; this.manipulate(startOfTheWeek - goBack, Unit.date); break; case 'month': this.startOf(Unit.date); this.setDate(1); break; case 'year': this.startOf(Unit.date); this.setMonth(0, 1); break; } return this; } /** * Sets the current date to the end of the {@link unit} provided * Example: Consider a date of "April 30, 2021, 11:45:32.984 AM" => new DateTime(2021, 3, 30, 11, 45, 32, 984).endOf('month') * would return April 30, 2021, 11:59:59.999 PM * @param unit * @param startOfTheWeek */ endOf(unit, startOfTheWeek = 0) { if (this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); switch (unit) { case 'seconds': this.setMilliseconds(999); break; case 'minutes': this.setSeconds(59, 999); break; case 'hours': this.setMinutes(59, 59, 999); break; case 'date': this.setHours(23, 59, 59, 999); break; case 'weekDay': this.endOf(Unit.date); this.manipulate((6 + startOfTheWeek) - this.weekDay, Unit.date); break; case 'month': this.endOf(Unit.date); this.manipulate(1, Unit.month); this.setDate(0); break; case 'year': this.endOf(Unit.date); this.manipulate(1, Unit.year); this.setDate(0); break; } return this; } /** * Change a {@link unit} value. Value can be positive or negative * Example: Consider a date of "April 30, 2021, 11:45:32.984 AM" => new DateTime(2021, 3, 30, 11, 45, 32, 984).manipulate(1, 'month') * would return May 30, 2021, 11:45:32.984 AM * @param value A positive or negative number * @param unit */ manipulate(value, unit) { if (this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); this[unit] += value; return this; } /** * Returns a string format. * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat * for valid templates and locale objects * @param template An object. Uses browser defaults otherwise. * @param locale Can be a string or an array of strings. Uses browser defaults otherwise. */ format(template, locale = this.locale) { return new Intl.DateTimeFormat(locale, template).format(this); } /** * Return true if {@link compare} is before this date * @param compare The Date/DateTime to compare * @param unit If provided, uses {@link startOf} for * comparision. */ isBefore(compare, unit) { if (!unit) return this.valueOf() < compare.valueOf(); if (this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); return (this.clone.startOf(unit).valueOf() < compare.clone.startOf(unit).valueOf()); } /** * Return true if {@link compare} is after this date * @param compare The Date/DateTime to compare * @param unit If provided, uses {@link startOf} for * comparision. */ isAfter(compare, unit) { if (!unit) return this.valueOf() > compare.valueOf(); if (this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); return (this.clone.startOf(unit).valueOf() > compare.clone.startOf(unit).valueOf()); } /** * Return true if {@link compare} is same this date * @param compare The Date/DateTime to compare * @param unit If provided, uses {@link startOf} for * comparision. */ isSame(compare, unit) { if (!unit) return this.valueOf() === compare.valueOf(); if (this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); compare = DateTime.convert(compare); return (this.clone.startOf(unit).valueOf() === compare.startOf(unit).valueOf()); } /** * Check if this is between two other DateTimes, optionally looking at unit scale. The match is exclusive. * @param left * @param right * @param unit. * @param inclusivity. A [ indicates inclusion of a value. A ( indicates exclusion. * If the inclusivity parameter is used, both indicators must be passed. */ isBetween(left, right, unit, inclusivity = '()') { if (unit && this[unit] === undefined) throw new Error(`Unit '${unit}' is not valid`); const leftInclusivity = inclusivity[0] === '('; const rightInclusivity = inclusivity[1] === ')'; return (((leftInclusivity ? this.isAfter(left, unit) : !this.isBefore(left, unit)) && (rightInclusivity ? this.isBefore(right, unit) : !this.isAfter(right, unit))) || ((leftInclusivity ? this.isBefore(left, unit) : !this.isAfter(left, unit)) && (rightInclusivity ? this.isAfter(right, unit) : !this.isBefore(right, unit)))); } /** * Returns flattened object of the date. Does not include literals * @param locale * @param template */ parts(locale = this.locale, template = { dateStyle: 'full', timeStyle: 'long' }) { const parts = {}; new Intl.DateTimeFormat(locale, template) .formatToParts(this) .filter((x) => x.type !== 'literal') .forEach((x) => (parts[x.type] = x.value)); return parts; } /** * Shortcut to Date.getSeconds() */ get seconds() { return this.getSeconds(); } /** * Shortcut to Date.setSeconds() */ set seconds(value) { this.setSeconds(value); } /** * Returns two digit hours */ get secondsFormatted() { return this.parts(undefined, twoDigitTemplate).second; } /** * Shortcut to Date.getMinutes() */ get minutes() { return this.getMinutes(); } /** * Shortcut to Date.setMinutes() */ set minutes(value) { this.setMinutes(value); } /** * Returns two digit minutes */ get minutesFormatted() { return this.parts(undefined, twoDigitTemplate).minute; } /** * Shortcut to Date.getHours() */ get hours() { return this.getHours(); } /** * Shortcut to Date.setHours() */ set hours(value) { this.setHours(value); } /** * Returns two digit hours */ get hoursFormatted() { return this.parts(undefined, twoDigitTwentyFourTemplate).hour; } /** * Returns two digit hours but in twelve hour mode e.g. 13 -> 1 */ get twelveHoursFormatted() { return this.parts(undefined, twoDigitTemplate).hour; } /** * Get the meridiem of the date. E.g. AM or PM. * If the {@link locale} provides a "dayPeriod" then this will be returned, * otherwise it will return AM or PM. * @param locale */ meridiem(locale = this.locale) { var _a; return (_a = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true }) .formatToParts(this) .find((p) => p.type === 'dayPeriod')) === null || _a === void 0 ? void 0 : _a.value; } /** * Shortcut to Date.getDate() */ get date() { return this.getDate(); } /** * Shortcut to Date.setDate() */ set date(value) { this.setDate(value); } /** * Return two digit date */ get dateFormatted() { return this.parts(undefined, twoDigitTemplate).day; } /** * Shortcut to Date.getDay() */ get weekDay() { return this.getDay(); } /** * Shortcut to Date.getMonth() */ get month() { return this.getMonth(); } /** * Shortcut to Date.setMonth() */ set month(value) { const targetMonth = new Date(this.year, value + 1); targetMonth.setDate(0); const endOfMonth = targetMonth.getDate(); if (this.date > endOfMonth) { this.date = endOfMonth; } this.setMonth(value); } /** * Return two digit, human expected month. E.g. January = 1, December = 12 */ get monthFormatted() { return this.parts(undefined, twoDigitTemplate).month; } /** * Shortcut to Date.getFullYear() */ get year() { return this.getFullYear(); } /** * Shortcut to Date.setFullYear() */ set year(value) { this.setFullYear(value); } // borrowed a bunch of stuff from Luxon /** * Gets the week of the year */ get week() { const ordinal = this.computeOrdinal(), weekday = this.getUTCDay(); let weekNumber = Math.floor((ordinal - weekday + 10) / 7); if (weekNumber < 1) { weekNumber = this.weeksInWeekYear(this.year - 1); } else if (weekNumber > this.weeksInWeekYear(this.year)) { weekNumber = 1; } return weekNumber; } weeksInWeekYear(weekYear) { const p1 = (weekYear + Math.floor(weekYear / 4) - Math.floor(weekYear / 100) + Math.floor(weekYear / 400)) % 7, last = weekYear - 1, p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7; return p1 === 4 || p2 === 3 ? 53 : 52; } get isLeapYear() { return this.year % 4 === 0 && (this.year % 100 !== 0 || this.year % 400 === 0); } computeOrdinal() { return this.date + (this.isLeapYear ? this.leapLadder : this.nonLeapLadder)[this.month]; } } class TdError extends Error { } class ErrorMessages { constructor() { this.base = 'TD:'; //#endregion //#region used with notify.error /** * Used with an Error Event type if the user selects a date that * fails restriction validation. */ this.failedToSetInvalidDate = 'Failed to set invalid date'; /** * Used with an Error Event type when a user changes the value of the * input field directly, and does not provide a valid date. */ this.failedToParseInput = 'Failed parse input field'; //#endregion } //#region out to console /** * Throws an error indicating that a key in the options object is invalid. * @param optionName */ unexpectedOption(optionName) { const error = new TdError(`${this.base} Unexpected option: ${optionName} does not match a known option.`); error.code = 1; throw error; } /** * Throws an error indicating that one more keys in the options object is invalid. * @param optionName */ unexpectedOptions(optionName) { const error = new TdError(`${this.base}: ${optionName.join(', ')}`); error.code = 1; throw error; } /** * Throws an error when an option is provide an unsupported value. * For example a value of 'cheese' for toolbarPlacement which only supports * 'top', 'bottom', 'default'. * @param optionName * @param badValue * @param validOptions */ unexpectedOptionValue(optionName, badValue, validOptions) { const error = new TdError(`${this.base} Unexpected option value: ${optionName} does not accept a value of "${badValue}". Valid values are: ${validOptions.join(', ')}`); error.code = 2; throw error; } /** * Throws an error when an option value is the wrong type. * For example a string value was provided to multipleDates which only * supports true or false. * @param optionName * @param badType * @param expectedType */ typeMismatch(optionName, badType, expectedType) { const error = new TdError(`${this.base} Mismatch types: ${optionName} has a type of ${badType} instead of the required ${expectedType}`); error.code = 3; throw error; } /** * Throws an error when an option value is outside of the expected range. * For example restrictions.daysOfWeekDisabled excepts a value between 0 and 6. * @param optionName * @param lower * @param upper */ numbersOutOfRage(optionName, lower, upper) { const error = new TdError(`${this.base} ${optionName} expected an array of number between ${lower} and ${upper}.`); error.code = 4; throw error; } /** * Throws an error when a value for a date options couldn't be parsed. Either * the option was an invalid string or an invalid Date object. * @param optionName * @param date * @param soft If true, logs a warning instead of an error. */ failedToParseDate(optionName, date, soft = false) { const error = new TdError(`${this.base} Could not correctly parse "${date}" to a date for ${optionName}.`); error.code = 5; if (!soft) throw error; console.warn(error); } /** * Throws when an element to attach to was not provided in the constructor. */ mustProvideElement() { const error = new TdError(`${this.base} No element was provided.`); error.code = 6; throw error; } /** * Throws if providing an array for the events to subscribe method doesn't have * the same number of callbacks. E.g., subscribe([1,2], [1]) */ subscribeMismatch() { const error = new TdError(`${this.base} The subscribed events does not match the number of callbacks`); error.code = 7; throw error; } /** * Throws if the configuration has conflicting rules e.g. minDate is after maxDate */ conflictingConfiguration(message) { const error = new TdError(`${this.base} A configuration value conflicts with another rule. ${message}`); error.code = 8; throw error; } /** * customDateFormat errors */ customDateFormatError(message) { const error = new TdError(`${this.base} customDateFormat: ${message}`); error.code = 9; throw error; } /** * Logs a warning if a date option value is provided as a string, instead of * a date/datetime object. */ dateString() { console.warn(`${this.base} Using a string for date options is not recommended unless you specify an ISO string or use the customDateFormat plugin.`); } throwError(message) { const error = new TdError(`${this.base} ${message}`); error.code = 9; throw error; } } // this is not the way I want this to stay but nested classes seemed to blown up once its compiled. const NAME = 'tempus-dominus', dataKey = 'td'; /** * Events */ class Events { constructor() { this.key = `.${dataKey}`; /** * Change event. Fired when the user selects a date. * See also EventTypes.ChangeEvent */ this.change = `change${this.key}`; /** * Emit when the view changes for example from month view to the year view. * See also EventTypes.ViewUpdateEvent */ this.update = `update${this.key}`; /** * Emits when a selected date or value from the input field fails to meet the provided validation rules. * See also EventTypes.FailEvent */ this.error = `error${this.key}`; /** * Show event * @event Events#show */ this.show = `show${this.key}`; /** * Hide event * @event Events#hide */ this.hide = `hide${this.key}`; // blur and focus are used in the jQuery provider but are otherwise unused. // keyup/down will be used later for keybinding options this.blur = `blur${this.key}`; this.focus = `focus${this.key}`; this.keyup = `keyup${this.key}`; this.keydown = `keydown${this.key}`; } } class Css { constructor() { /** * The outer element for the widget. */ this.widget = `${NAME}-widget`; /** * Hold the previous, next and switcher divs */ this.calendarHeader = 'calendar-header'; /** * The element for the action to change the calendar view. E.g. month -> year. */ this.switch = 'picker-switch'; /** * The elements for all the toolbar options */ this.toolbar = 'toolbar'; /** * Disables the hover and rounding affect. */ this.noHighlight = 'no-highlight'; /** * Applied to the widget element when the side by side option is in use. */ this.sideBySide = 'timepicker-sbs'; /** * The element for the action to change the calendar view, e.g. August -> July */ this.previous = 'previous'; /** * The element for the action to change the calendar view, e.g. August -> September */ this.next = 'next'; /** * Applied to any action that would violate any restriction options. ALso applied * to an input field if the disabled function is called. */ this.disabled = 'disabled'; /** * Applied to any date that is less than requested view, * e.g. the last day of the previous month. */ this.old = 'old'; /** * Applied to any date that is greater than of requested view, * e.g. the last day of the previous month. */ this.new = 'new'; /** * Applied to any date that is currently selected. */ this.active = 'active'; //#region date element /** * The outer element for the calendar view. */ this.dateContainer = 'date-container'; /** * The outer element for the decades view. */ this.decadesContainer = `${this.dateContainer}-decades`; /** * Applied to elements within the decades container, e.g. 2020, 2030 */ this.decade = 'decade'; /** * The outer element for the years view. */ this.yearsContainer = `${this.dateContainer}-years`; /** * Applied to elements within the years container, e.g. 2021, 2021 */ this.year = 'year'; /** * The outer element for the month view. */ this.monthsContainer = `${this.dateContainer}-months`; /** * Applied to elements within the month container, e.g. January, February */ this.month = 'month'; /** * The outer element for the calendar view. */ this.daysContainer = `${this.dateContainer}-days`; /** * Applied to elements within the day container, e.g. 1, 2..31 */ this.day = 'day'; /** * If display.calendarWeeks is enabled, a column displaying the week of year * is shown. This class is applied to each cell in that column. */ this.calendarWeeks = 'cw'; /** * Applied to the first row of the calendar view, e.g. Sunday, Monday */ this.dayOfTheWeek = 'dow'; /** * Applied to the current date on the calendar view. */ this.today = 'today'; /** * Applied to the locale's weekend dates on the calendar view, e.g. Sunday, Saturday */ this.weekend = 'weekend'; //#endregion //#region time element /** * The outer element for all time related elements. */ this.timeContainer = 'time-container'; /** * Applied the separator columns between time elements, e.g. hour *:* minute *:* second */ this.separator = 'separator'; /** * The outer element for the clock view. */ this.clockContainer = `${this.timeContainer}-clock`; /** * The outer element for the hours selection view. */ this.hourContainer = `${this.timeContainer}-hour`; /** * The outer element for the minutes selection view. */ this.minuteContainer = `${this.timeContainer}-minute`; /** * The outer element for the seconds selection view. */ this.secondContainer = `${this.timeContainer}-second`; /** * Applied to each element in the hours selection view. */ this.hour = 'hour'; /** * Applied to each element in the minutes selection view. */ this.minute = 'minute'; /** * Applied to each element in the seconds selection view. */ this.second = 'second'; /** * Applied AM/PM toggle button. */ this.toggleMeridiem = 'toggleMeridiem'; //#endregion //#region collapse /** * Applied the element of the current view mode, e.g. calendar or clock. */ this.show = 'show'; /** * Applied to the currently showing view mode during a transition * between calendar and clock views */ this.collapsing = 'td-collapsing'; /** * Applied to the currently hidden view mode. */ this.collapse = 'td-collapse'; //#endregion /** * Applied to the widget when the option display.inline is enabled. */ this.inline = 'inline'; /** * Applied to the widget when the option display.theme is light. */ this.lightTheme = 'light'; /** * Applied to the widget when the option display.theme is dark. */ this.darkTheme = 'dark'; /** * Used for detecting if the system color preference is dark mode */ this.isDarkPreferredQuery = '(prefers-color-scheme: dark)'; } } class Namespace { } Namespace.NAME = NAME; // noinspection JSUnusedGlobalSymbols Namespace.dataKey = dataKey; Namespace.events = new Events(); Namespace.css = new Css(); Namespace.errorMessages = new ErrorMessages(); class ServiceLocator { constructor() { this.cache = new Map(); } locate(identifier) { const service = this.cache.get(identifier); if (service) return service; const value = new identifier(); this.cache.set(identifier, value); return value; } } const setupServiceLocator = () => { serviceLocator = new ServiceLocator(); }; let serviceLocator; const CalendarModes = [ { name: 'calendar', className: Namespace.css.daysContainer, unit: Unit.month, step: 1, }, { name: 'months', className: Namespace.css.monthsContainer, unit: Unit.year, step: 1, }, { name: 'years', className: Namespace.css.yearsContainer, unit: Unit.year, step: 10, }, { name: 'decades', className: Namespace.css.decadesContainer, unit: Unit.year, step: 100, }, ]; class OptionsStore { constructor() { this.viewDate = new DateTime(); this._currentCalendarViewMode = 0; this.minimumCalendarViewMode = 0; this.currentView = 'calendar'; } get currentCalendarViewMode() { return this._currentCalendarViewMode; } set currentCalendarViewMode(value) { this._currentCalendarViewMode = value; this.currentView = CalendarModes[value].name; } /** * When switching back to the calendar from the clock, * this sets currentView to the correct calendar view. */ refreshCurrentView() { this.currentView = CalendarModes[this.currentCalendarViewMode].name; } } /** * Main class for date validation rules based on the options provided. */ class Validation { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); } /** * Checks to see if the target date is valid based on the rules provided in the options. * Granularity can be provided to check portions of the date instead of the whole. * @param targetDate * @param granularity */ isValid(targetDate, granularity) { var _a; if (this.optionsStore.options.restrictions.disabledDates.length > 0 && this._isInDisabledDates(targetDate)) { return false; } if (this.optionsStore.options.restrictions.enabledDates.length > 0 && !this._isInEnabledDates(targetDate)) { return false; } if (granularity !== Unit.month && granularity !== Unit.year && ((_a = this.optionsStore.options.restrictions.daysOfWeekDisabled) === null || _a === void 0 ? void 0 : _a.length) > 0 && this.optionsStore.options.restrictions.daysOfWeekDisabled.indexOf(targetDate.weekDay) !== -1) { return false; } if (this.optionsStore.options.restrictions.minDate && targetDate.isBefore(this.optionsStore.options.restrictions.minDate, granularity)) { return false; } if (this.optionsStore.options.restrictions.maxDate && targetDate.isAfter(this.optionsStore.options.restrictions.maxDate, granularity)) { return false; } if (granularity === Unit.hours || granularity === Unit.minutes || granularity === Unit.seconds) { if (this.optionsStore.options.restrictions.disabledHours.length > 0 && this._isInDisabledHours(targetDate)) { return false; } if (this.optionsStore.options.restrictions.enabledHours.length > 0 && !this._isInEnabledHours(targetDate)) { return false; } if (this.optionsStore.options.restrictions.disabledTimeIntervals.length > 0) { for (let disabledTimeIntervals of this.optionsStore.options.restrictions.disabledTimeIntervals) { if (targetDate.isBetween(disabledTimeIntervals.from, disabledTimeIntervals.to)) return false; } } } return true; } /** * Checks to see if the disabledDates option is in use and returns true (meaning invalid) * if the `testDate` is with in the array. Granularity is by date. * @param testDate * @private */ _isInDisabledDates(testDate) { if (!this.optionsStore.options.restrictions.disabledDates || this.optionsStore.options.restrictions.disabledDates.length === 0) return false; const formattedDate = testDate.format(getFormatByUnit(Unit.date)); return this.optionsStore.options.restrictions.disabledDates .map((x) => x.format(getFormatByUnit(Unit.date))) .find((x) => x === formattedDate); } /** * Checks to see if the enabledDates option is in use and returns true (meaning valid) * if the `testDate` is with in the array. Granularity is by date. * @param testDate * @private */ _isInEnabledDates(testDate) { if (!this.optionsStore.options.restrictions.enabledDates || this.optionsStore.options.restrictions.enabledDates.length === 0) return true; const formattedDate = testDate.format(getFormatByUnit(Unit.date)); return this.optionsStore.options.restrictions.enabledDates .map((x) => x.format(getFormatByUnit(Unit.date))) .find((x) => x === formattedDate); } /** * Checks to see if the disabledHours option is in use and returns true (meaning invalid) * if the `testDate` is with in the array. Granularity is by hours. * @param testDate * @private */ _isInDisabledHours(testDate) { if (!this.optionsStore.options.restrictions.disabledHours || this.optionsStore.options.restrictions.disabledHours.length === 0) return false; const formattedDate = testDate.hours; return this.optionsStore.options.restrictions.disabledHours.find((x) => x === formattedDate); } /** * Checks to see if the enabledHours option is in use and returns true (meaning valid) * if the `testDate` is with in the array. Granularity is by hours. * @param testDate * @private */ _isInEnabledHours(testDate) { if (!this.optionsStore.options.restrictions.enabledHours || this.optionsStore.options.restrictions.enabledHours.length === 0) return true; const formattedDate = testDate.hours; return this.optionsStore.options.restrictions.enabledHours.find((x) => x === formattedDate); } } class EventEmitter { constructor() { this.subscribers = []; } subscribe(callback) { this.subscribers.push(callback); return this.unsubscribe.bind(this, this.subscribers.length - 1); } unsubscribe(index) { this.subscribers.splice(index, 1); } emit(value) { this.subscribers.forEach((callback) => { callback(value); }); } destroy() { this.subscribers = null; this.subscribers = []; } } class EventEmitters { constructor() { this.triggerEvent = new EventEmitter(); this.viewUpdate = new EventEmitter(); this.updateDisplay = new EventEmitter(); this.action = new EventEmitter(); } destroy() { this.triggerEvent.destroy(); this.viewUpdate.destroy(); this.updateDisplay.destroy(); this.action.destroy(); } } const DefaultOptions = { restrictions: { minDate: undefined, maxDate: undefined, disabledDates: [], enabledDates: [], daysOfWeekDisabled: [], disabledTimeIntervals: [], disabledHours: [], enabledHours: [] }, display: { icons: { type: 'icons', time: 'fa-solid fa-clock', date: 'fa-solid fa-calendar', up: 'fa-solid fa-arrow-up', down: 'fa-solid fa-arrow-down', previous: 'fa-solid fa-chevron-left', next: 'fa-solid fa-chevron-right', today: 'fa-solid fa-calendar-check', clear: 'fa-solid fa-trash', close: 'fa-solid fa-xmark' }, sideBySide: false, calendarWeeks: false, viewMode: 'calendar', toolbarPlacement: 'bottom', keepOpen: false, buttons: { today: false, clear: false, close: false }, components: { calendar: true, date: true, month: true, year: true, decades: true, clock: true, hours: true, minutes: true, seconds: false, useTwentyfourHour: undefined }, inline: false, theme: 'auto' }, stepping: 1, useCurrent: true, defaultDate: undefined, localization: { today: 'Go to today', clear: 'Clear selection', close: 'Close the picker', selectMonth: 'Select Month', previousMonth: 'Previous Month', nextMonth: 'Next Month', selectYear: 'Select Year', previousYear: 'Previous Year', nextYear: 'Next Year', selectDecade: 'Select Decade', previousDecade: 'Previous Decade', nextDecade: 'Next Decade', previousCentury: 'Previous Century', nextCentury: 'Next Century', pickHour: 'Pick Hour', incrementHour: 'Increment Hour', decrementHour: 'Decrement Hour', pickMinute: 'Pick Minute', incrementMinute: 'Increment Minute', decrementMinute: 'Decrement Minute', pickSecond: 'Pick Second', incrementSecond: 'Increment Second', decrementSecond: 'Decrement Second', toggleMeridiem: 'Toggle Meridiem', selectTime: 'Select Time', selectDate: 'Select Date', dayViewHeaderFormat: { month: 'long', year: '2-digit' }, locale: 'default', startOfTheWeek: 0, /** * This is only used with the customDateFormat plugin */ dateFormats: { LTS: 'h:mm:ss T', LT: 'h:mm T', L: 'MM/dd/yyyy', LL: 'MMMM d, yyyy', LLL: 'MMMM d, yyyy h:mm T', LLLL: 'dddd, MMMM d, yyyy h:mm T', }, /** * This is only used with the customDateFormat plugin */ ordinal: (n) => n, /** * This is only used with the customDateFormat plugin */ format: 'L' }, keepInvalid: false, debug: false, allowInputToggle: false, viewDate: new DateTime(), multipleDates: false, multipleDatesSeparator: '; ', promptTimeOnDateChange: false, promptTimeOnDateChangeTransitionDelay: 200, meta: {}, container: undefined }; class OptionConverter { static deepCopy(input) { const o = {}; Object.keys(input).forEach((key) => { const inputElement = input[key]; o[key] = inputElement; if (typeof inputElement !== 'object' || inputElement instanceof HTMLElement || inputElement instanceof Element || inputElement instanceof Date) return; if (!Array.isArray(inputElement)) { o[key] = OptionConverter.deepCopy(inputElement); } }); return o; } /** * Finds value out of an object based on a string, period delimited, path * @param paths * @param obj */ static objectPath(paths, obj) { if (paths.charAt(0) === '.') paths = paths.slice(1); if (!paths) return obj; return paths.split('.') .reduce((value, key) => (OptionConverter.isValue(value) || OptionConverter.isValue(value[key]) ? value[key] : undefined), obj); } /** * The spread operator caused sub keys to be missing after merging. * This is to fix that issue by using spread on the child objects first. * Also handles complex options like disabledDates * @param provided An option from new providedOptions * @param copyTo Destination object. This was added to prevent reference copies * @param path * @param localization */ static spread(provided, copyTo, path = '', localization) { const defaultOptions = OptionConverter.objectPath(path, DefaultOptions); const unsupportedOptions = Object.keys(provided).filter((x) => !Object.keys(defaultOptions).includes(x)); if (unsupportedOptions.length > 0) { const flattenedOptions = OptionConverter.getFlattenDefaultOptions(); const errors = unsupportedOptions.map((x) => { let error = `"${path}.${x}" in not a known option.`; let didYouMean = flattenedOptions.find((y) => y.includes(x)); if (didYouMean) error += ` Did you mean "${didYouMean}"?`; return error; }); Namespace.errorMessages.unexpectedOptions(errors); } Object.keys(provided).filter(key => key !== '__proto__' && key !== 'constructor').forEach((key) => { path += `.${key}`; if (path.charAt(0) === '.') path = path.slice(1); const defaultOptionValue = defaultOptions[key]; let providedType = typeof provided[key]; let defaultType = typeof defaultOptionValue; let value = provided[key]; if (value === undefined || value === null) { copyTo[key] = value; path = path.substring(0, path.lastIndexOf(`.${key}`)); return; } if (typeof defaultOptionValue === 'object' && !Array.isArray(provided[key]) && !(defaultOptionValue instanceof Date || OptionConverter.ignoreProperties.includes(key))) { OptionConverter.spread(provided[key], copyTo[key], path, localization); } else { copyTo[key] = OptionConverter.processKey(key, value, providedType, defaultType, path, localization); } path = path.substring(0, path.lastIndexOf(`.${key}`)); }); } static processKey(key, value, providedType, defaultType, path, localization) { switch (key) { case 'defaultDate': { const dateTime = this.dateConversion(value, 'defaultDate', localization); if (dateTime !== undefined) { dateTime.setLocale(localization.locale); return dateTime; } Namespace.errorMessages.typeMismatch('defaultDate', providedType, 'DateTime or Date'); break; } case 'viewDate': { const dateTime = this.dateConversion(value, 'viewDate', localization); if (dateTime !== undefined) { dateTime.setLocale(localization.locale); return dateTime; } Namespace.errorMessages.typeMismatch('viewDate', providedType, 'DateTime or Date'); break; } case 'minDate': { if (value === undefined) { return value; } const dateTime = this.dateConversion(value, 'restrictions.minDate', localization); if (dateTime !== undefined) { dateTime.setLocale(localization.locale); return dateTime; } Namespace.errorMessages.typeMismatch('restrictions.minDate', providedType, 'DateTime or Date'); break; } case 'maxDate': { if (value === undefined) { return value; } const dateTime = this.dateConversion(value, 'restrictions.maxDate', localization); if (dateTime !== undefined) { dateTime.setLocale(localization.locale); return dateTime; } Namespace.errorMessages.typeMismatch('restrictions.maxDate', providedType, 'DateTime or Date'); break; } case 'disabledHours': if (value === undefined) { return []; } this._typeCheckNumberArray('restrictions.disabledHours', value, providedType); if (value.filter((x) => x < 0 || x > 24).length > 0) Namespace.errorMessages.numbersOutOfRage('restrictions.disabledHours', 0, 23); return value; case 'enabledHours': if (value === undefined) { return []; } this._typeCheckNumberArray('restrictions.enabledHours', value, providedType); if (value.filter((x) => x < 0 || x > 24).length > 0) Namespace.errorMessages.numbersOutOfRage('restrictions.enabledHours', 0, 23); return value; case 'daysOfWeekDisabled': if (value === undefined) { return []; } this._typeCheckNumberArray('restrictions.daysOfWeekDisabled', value, providedType); if (value.filter((x) => x < 0 || x > 6).length > 0) Namespace.errorMessages.numbersOutOfRage('restrictions.daysOfWeekDisabled', 0, 6); return value; case 'enabledDates': if (value === undefined) { return []; } this._typeCheckDateArray('restrictions.enabledDates', value, providedType, localization); return value; case 'disabledDates': if (value === undefined) { return []; } this._typeCheckDateArray('restrictions.disabledDates', value, providedType, localization); return value; case 'disabledTimeIntervals': if (value === undefined) { return []; } if (!Array.isArray(value)) { Namespace.errorMessages.typeMismatch(key, providedType, 'array of { from: DateTime|Date, to: DateTime|Date }'); } const valueObject = value; for (let i = 0; i < valueObject.length; i++) { Object.keys(valueObject[i]).forEach((vk) => { const subOptionName = `${key}[${i}].${vk}`; let d = valueObject[i][vk]; const dateTime = this.dateConversion(d, subOptionName, localization); if (!dateTime) { Namespace.errorMessages.typeMismatch(subOptionName, typeof d, 'DateTime or Date'); } dateTime.setLocale(localization.locale); valueObject[i][vk] = dateTime; }); } return valueObject; case 'toolbarPlacement': case 'type': case 'viewMode': case 'theme': const optionValues = { toolbarPlacement: ['top', 'bottom', 'default'], type: ['icons', 'sprites'], viewMode: ['clock', 'calendar', 'months', 'years', 'decades'], theme: ['light', 'dark', 'auto'] }; const keyOptions = optionValues[key]; if (!keyOptions.includes(value)) Namespace.errorMessages.unexpectedOptionValue(path.substring(1), value, keyOptions); return value; case 'meta': case 'dayViewHeaderFormat': return value; case 'container': if (value && !(value instanceof HTMLElement || value instanceof Element || (value === null || value === void 0 ? void 0 : value.appendChild))) { Namespace.errorMessages.typeMismatch(path.substring(1), typeof value, 'HTMLElement'); } return value; case 'useTwentyfourHour': if (value === undefined || providedType === 'boolean') return value; Namespace.errorMessages.typeMismatch(path, providedType, defaultType); break; default: switch (defaultType) { case 'boolean': return value === 'true' || value === true; case 'number': return +value; case 'string': return value.toString(); case 'object': return {}; case 'function': return value; default: Namespace.errorMessages.typeMismatch(path, providedType, defaultType); } } } static _mergeOptions(providedOptions, mergeTo) { var _a; const newConfig = OptionConverter.deepCopy(mergeTo); //see if the options specify a locale const localization = ((_a = mergeTo.localization) === null || _a === void 0 ? void 0 : _a.locale) !== 'default' ? mergeTo.localization : (providedOptions === null || providedOptions === void 0 ? void 0 : providedOptions.localization) || DefaultOptions.localization; OptionConverter.spread(providedOptions, newConfig, '', localization); return newConfig; } static _dataToOptions(element, options) { const eData = JSON.parse(JSON.stringify(element.dataset)); if (eData === null || eData === void 0 ? void 0 : eData.tdTargetInput) delete eData.tdTargetInput; if (eData === null || eData === void 0 ? void 0 : eData.tdTargetToggle) delete eData.tdTargetToggle; if (!eData || Object.keys(eData).length === 0 || eData.constructor !== DOMStringMap) return options; let dataOptions = {}; // because dataset returns camelCase including the 'td' key the option // key won't align const objectToNormalized = (object) => { const lowered = {}; Object.keys(object).forEach((x) => { lowered[x.toLowerCase()] = x; }); return lowered; }; const rabbitHole = (split, index, optionSubgroup, value) => { // first round = display { ... } const normalizedOptions = objectToNormalized(optionSubgroup); const keyOption = normalizedOptions[split[index].toLowerCase()]; const internalObject = {}; if (keyOption === undefined) return internalObject; // if this is another object, continue down the rabbit hole if (optionSubgroup[keyOption].constructor === Object) { index++; internalObject[keyOption] = rabbitHole(split, index, optionSubgroup[keyOption], value); } else { internalObject[keyOption] = value; } return internalObject; }; const optionsLower = objectToNormalized(options); Object.keys(eData) .filter((x) => x.startsWith(Namespace.dataKey)) .map((x) => x.substring(2)) .forEach((key) => { let keyOption = optionsLower[key.toLowerCase()]; // dataset merges dashes to camelCase... yay // i.e. key = display_components_seconds if (key.includes('_')) { // [display, components, seconds] const split = key.split('_'); // display keyOption = optionsLower[split[0].toLowerCase()]; if (keyOption !== undefined && options[keyOption].constructor === Object) { dataOptions[keyOption] = rabbitHole(split, 1, options[keyOption], eData[`td${key}`]); } } // or key = multipleDate else if (keyOption !== undefined) { dataOptions[keyOption] = eData[`td${key}`]; } }); return this._mergeOptions(dataOptions, options); } /** * Attempts to prove `d` is a DateTime or Date or can be converted into one. * @param d If a string will attempt creating a date from it. * @param localization object containing locale and format settings. Only used with the custom formats * @private */ static _dateTypeCheck(d, localization) { if (d.constructor.name === DateTime.name) return d; if (d.constructor.name === Date.name) { return DateTime.convert(d); } if (typeof d === typeof '') { const dateTime = DateTime.fromString(d, localization); if (JSON.stringify(dateTime) === 'null') { return null; } return dateTime; } return null; } /** * Type checks that `value` is an array of Date or DateTime * @param optionName Provides text to error messages e.g. disabledDates * @param value Option value * @param providedType Used to provide text to error messages * @param localization */ static _typeCheckDateArray(optionName, value, providedType, localization) { var _a; if (!Array.isArray(value)) { Namespace.errorMessages.typeMismatch(optionName, providedType, 'array of DateTime or Date'); } for (let i = 0; i < value.length; i++) { let d = value[i]; const dateTime = this.dateConversion(d, optionName, localization); if (!dateTime) { Namespace.errorMessages.typeMismatch(optionName, typeof d, 'DateTime or Date'); } dateTime.setLocale((_a = localization === null || localization === void 0 ? void 0 : localization.locale) !== null && _a !== void 0 ? _a : 'default'); value[i] = dateTime; } } /** * Type checks that `value` is an array of numbers * @param optionName Provides text to error messages e.g. disabledDates * @param value Option value * @param providedType Used to provide text to error messages */ static _typeCheckNumberArray(optionName, value, providedType) { if (!Array.isArray(value) || value.find((x) => typeof x !== typeof 0)) { Namespace.errorMessages.typeMismatch(optionName, providedType, 'array of numbers'); } } /** * Attempts to convert `d` to a DateTime object * @param d value to convert * @param optionName Provides text to error messages e.g. disabledDates * @param localization object containing locale and format settings. Only used with the custom formats */ static dateConversion(d, optionName, localization) { if (typeof d === typeof '' && optionName !== 'input') { Namespace.errorMessages.dateString(); } const converted = this._dateTypeCheck(d, localization); if (!converted) { Namespace.errorMessages.failedToParseDate(optionName, d, optionName === 'input'); } return converted; } static getFlattenDefaultOptions() { if (this._flattenDefaults) return this._flattenDefaults; const deepKeys = (t, pre = []) => { if (Array.isArray(t)) return []; if (Object(t) === t) { return Object.entries(t).flatMap(([k, v]) => deepKeys(v, [...pre, k])); } else { return pre.join('.'); } }; this._flattenDefaults = deepKeys(DefaultOptions); return this._flattenDefaults; } /** * Some options conflict like min/max date. Verify that these kinds of options * are set correctly. * @param config */ static _validateConflicts(config) { if (config.display.sideBySide && (!config.display.components.clock || !(config.display.components.hours || config.display.components.minutes || config.display.components.seconds))) { Namespace.errorMessages.conflictingConfiguration('Cannot use side by side mode without the clock components'); } if (config.restrictions.minDate && config.restrictions.maxDate) { if (config.restrictions.minDate.isAfter(config.restrictions.maxDate)) { Namespace.errorMessages.conflictingConfiguration('minDate is after maxDate'); } if (config.restrictions.maxDate.isBefore(config.restrictions.minDate)) { Namespace.errorMessages.conflictingConfiguration('maxDate is before minDate'); } } } } OptionConverter.ignoreProperties = ['meta', 'dayViewHeaderFormat', 'container', 'dateForms', 'ordinal']; OptionConverter.isValue = a => a != null; // everything except undefined + null class Dates { constructor() { this._dates = []; this.optionsStore = serviceLocator.locate(OptionsStore); this.validation = serviceLocator.locate(Validation); this._eventEmitters = serviceLocator.locate(EventEmitters); } /** * Returns the array of selected dates */ get picked() { return this._dates; } /** * Returns the last picked value. */ get lastPicked() { return this._dates[this.lastPickedIndex]; } /** * Returns the length of picked dates -1 or 0 if none are selected. */ get lastPickedIndex() { if (this._dates.length === 0) return 0; return this._dates.length - 1; } /** * Formats a DateTime object to a string. Used when setting the input value. * @param date */ formatInput(date) { const components = this.optionsStore.options.display.components; if (!date) return ''; return date.format({ year: components.calendar && components.year ? 'numeric' : undefined, month: components.calendar && components.month ? '2-digit' : undefined, day: components.calendar && components.date ? '2-digit' : undefined, hour: components.clock && components.hours ? components.useTwentyfourHour ? '2-digit' : 'numeric' : undefined, minute: components.clock && components.minutes ? '2-digit' : undefined, second: components.clock && components.seconds ? '2-digit' : undefined, hour12: !components.useTwentyfourHour, }); } /** * parse the value into a DateTime object. * this can be overwritten to supply your own parsing. */ parseInput(value) { return OptionConverter.dateConversion(value, 'input', this.optionsStore.options.localization); } /** * Tries to convert the provided value to a DateTime object. * If value is null|undefined then clear the value of the provided index (or 0). * @param value Value to convert or null|undefined * @param index When using multidates this is the index in the array */ setFromInput(value, index) { if (!value) { this.setValue(undefined, index); return; } const converted = this.parseInput(value); if (converted) { converted.setLocale(this.optionsStore.options.localization.locale); this.setValue(converted, index); } } /** * Adds a new DateTime to selected dates array * @param date */ add(date) { this._dates.push(date); } /** * Returns true if the `targetDate` is part of the selected dates array. * If `unit` is provided then a granularity to that unit will be used. * @param targetDate * @param unit */ isPicked(targetDate, unit) { if (!unit) return this._dates.find((x) => x === targetDate) !== undefined; const format = getFormatByUnit(unit); let innerDateFormatted = targetDate.format(format); return (this._dates .map((x) => x.format(format)) .find((x) => x === innerDateFormatted) !== undefined); } /** * Returns the index at which `targetDate` is in the array. * This is used for updating or removing a date when multi-date is used * If `unit` is provided then a granularity to that unit will be used. * @param targetDate * @param unit */ pickedIndex(targetDate, unit) { if (!unit) return this._dates.indexOf(targetDate); const format = getFormatByUnit(unit); let innerDateFormatted = targetDate.format(format); return this._dates.map((x) => x.format(format)).indexOf(innerDateFormatted); } /** * Clears all selected dates. */ clear() { this.optionsStore.unset = true; this._eventEmitters.triggerEvent.emit({ type: Namespace.events.change, date: undefined, oldDate: this.lastPicked, isClear: true, isValid: true, }); this._dates = []; } /** * Find the "book end" years given a `year` and a `factor` * @param factor e.g. 100 for decades * @param year e.g. 2021 */ static getStartEndYear(factor, year) { const step = factor / 10, startYear = Math.floor(year / factor) * factor, endYear = startYear + step * 9, focusValue = Math.floor(year / step) * step; return [startYear, endYear, focusValue]; } /** * Attempts to either clear or set the `target` date at `index`. * If the `target` is null then the date will be cleared. * If multi-date is being used then it will be removed from the array. * If `target` is valid and multi-date is used then if `index` is * provided the date at that index will be replaced, otherwise it is appended. * @param target * @param index */ setValue(target, index) { const noIndex = typeof index === 'undefined', isClear = !target && noIndex; let oldDate = this.optionsStore.unset ? null : this._dates[index]; if (!oldDate && !this.optionsStore.unset && noIndex && isClear) { oldDate = this.lastPicked; } const updateInput = () => { if (!this.optionsStore.input) return; let newValue = this.formatInput(target); if (this.optionsStore.options.multipleDates) { newValue = this._dates .map((d) => this.formatInput(d)) .join(this.optionsStore.options.multipleDatesSeparator); } if (this.optionsStore.input.value != newValue) this.optionsStore.input.value = newValue; }; if (target && (oldDate === null || oldDate === void 0 ? void 0 : oldDate.isSame(target))) { updateInput(); return; } // case of calling setValue(null) if (!target) { if (!this.optionsStore.options.multipleDates || this._dates.length === 1 || isClear) { this.optionsStore.unset = true; this._dates = []; } else { this._dates.splice(index, 1); } updateInput(); this._eventEmitters.triggerEvent.emit({ type: Namespace.events.change, date: undefined, oldDate, isClear, isValid: true, }); this._eventEmitters.updateDisplay.emit('all'); return; } index = index || 0; target = target.clone; // minute stepping is being used, force the minute to the closest value if (this.optionsStore.options.stepping !== 1) { target.minutes = Math.round(target.minutes / this.optionsStore.options.stepping) * this.optionsStore.options.stepping; target.seconds = 0; } if (this.validation.isValid(target)) { this._dates[index] = target; this.optionsStore.viewDate = target.clone; updateInput(); this.optionsStore.unset = false; this._eventEmitters.updateDisplay.emit('all'); this._eventEmitters.triggerEvent.emit({ type: Namespace.events.change, date: target, oldDate, isClear, isValid: true, }); return; } if (this.optionsStore.options.keepInvalid) { this._dates[index] = target; this.optionsStore.viewDate = target.clone; updateInput(); this._eventEmitters.triggerEvent.emit({ type: Namespace.events.change, date: target, oldDate, isClear, isValid: false, }); } this._eventEmitters.triggerEvent.emit({ type: Namespace.events.error, reason: Namespace.errorMessages.failedToSetInvalidDate, date: target, oldDate, }); } } var ActionTypes; (function (ActionTypes) { ActionTypes["next"] = "next"; ActionTypes["previous"] = "previous"; ActionTypes["changeCalendarView"] = "changeCalendarView"; ActionTypes["selectMonth"] = "selectMonth"; ActionTypes["selectYear"] = "selectYear"; ActionTypes["selectDecade"] = "selectDecade"; ActionTypes["selectDay"] = "selectDay"; ActionTypes["selectHour"] = "selectHour"; ActionTypes["selectMinute"] = "selectMinute"; ActionTypes["selectSecond"] = "selectSecond"; ActionTypes["incrementHours"] = "incrementHours"; ActionTypes["incrementMinutes"] = "incrementMinutes"; ActionTypes["incrementSeconds"] = "incrementSeconds"; ActionTypes["decrementHours"] = "decrementHours"; ActionTypes["decrementMinutes"] = "decrementMinutes"; ActionTypes["decrementSeconds"] = "decrementSeconds"; ActionTypes["toggleMeridiem"] = "toggleMeridiem"; ActionTypes["togglePicker"] = "togglePicker"; ActionTypes["showClock"] = "showClock"; ActionTypes["showHours"] = "showHours"; ActionTypes["showMinutes"] = "showMinutes"; ActionTypes["showSeconds"] = "showSeconds"; ActionTypes["clear"] = "clear"; ActionTypes["close"] = "close"; ActionTypes["today"] = "today"; })(ActionTypes || (ActionTypes = {})); var ActionTypes$1 = ActionTypes; /** * Creates and updates the grid for `date` */ class DateDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.dates = serviceLocator.locate(Dates); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement("div"); container.classList.add(Namespace.css.daysContainer); container.append(...this._daysOfTheWeek()); if (this.optionsStore.options.display.calendarWeeks) { const div = document.createElement("div"); div.classList.add(Namespace.css.calendarWeeks, Namespace.css.noHighlight); container.appendChild(div); } for (let i = 0; i < 42; i++) { if (i !== 0 && i % 7 === 0) { if (this.optionsStore.options.display.calendarWeeks) { const div = document.createElement("div"); div.classList.add(Namespace.css.calendarWeeks, Namespace.css.noHighlight); container.appendChild(div); } } const div = document.createElement("div"); div.setAttribute("data-action", ActionTypes$1.selectDay); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { const container = widget.getElementsByClassName(Namespace.css.daysContainer)[0]; if (this.optionsStore.currentView === "calendar") { const [previous, switcher, next] = container.parentElement .getElementsByClassName(Namespace.css.calendarHeader)[0] .getElementsByTagName("div"); switcher.setAttribute(Namespace.css.daysContainer, this.optionsStore.viewDate.format(this.optionsStore.options.localization.dayViewHeaderFormat)); this.optionsStore.options.display.components.month ? switcher.classList.remove(Namespace.css.disabled) : switcher.classList.add(Namespace.css.disabled); this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, Unit.month), Unit.month) ? previous.classList.remove(Namespace.css.disabled) : previous.classList.add(Namespace.css.disabled); this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, Unit.month), Unit.month) ? next.classList.remove(Namespace.css.disabled) : next.classList.add(Namespace.css.disabled); } let innerDate = this.optionsStore.viewDate.clone .startOf(Unit.month) .startOf("weekDay", this.optionsStore.options.localization.startOfTheWeek) .manipulate(12, Unit.hours); container .querySelectorAll(`[data-action="${ActionTypes$1.selectDay}"], .${Namespace.css.calendarWeeks}`) .forEach((containerClone) => { if (this.optionsStore.options.display.calendarWeeks && containerClone.classList.contains(Namespace.css.calendarWeeks)) { if (containerClone.innerText === "#") return; containerClone.innerText = `${innerDate.week}`; return; } let classes = []; classes.push(Namespace.css.day); if (innerDate.isBefore(this.optionsStore.viewDate, Unit.month)) { classes.push(Namespace.css.old); } if (innerDate.isAfter(this.optionsStore.viewDate, Unit.month)) { classes.push(Namespace.css.new); } if (!this.optionsStore.unset && this.dates.isPicked(innerDate, Unit.date)) { classes.push(Namespace.css.active); } if (!this.validation.isValid(innerDate, Unit.date)) { classes.push(Namespace.css.disabled); } if (innerDate.isSame(new DateTime(), Unit.date)) { classes.push(Namespace.css.today); } if (innerDate.weekDay === 0 || innerDate.weekDay === 6) { classes.push(Namespace.css.weekend); } paint(Unit.date, innerDate, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute("data-value", `${innerDate.year}-${innerDate.monthFormatted}-${innerDate.dateFormatted}`); containerClone.setAttribute("data-day", `${innerDate.date}`); containerClone.innerText = innerDate.format({ day: "numeric" }); innerDate.manipulate(1, Unit.date); }); } /*** * Generates an html row that contains the days of the week. * @private */ _daysOfTheWeek() { let innerDate = this.optionsStore.viewDate.clone .startOf("weekDay", this.optionsStore.options.localization.startOfTheWeek) .startOf(Unit.date); const row = []; document.createElement("div"); if (this.optionsStore.options.display.calendarWeeks) { const htmlDivElement = document.createElement("div"); htmlDivElement.classList.add(Namespace.css.calendarWeeks, Namespace.css.noHighlight); htmlDivElement.innerText = "#"; row.push(htmlDivElement); } for (let i = 0; i < 7; i++) { const htmlDivElement = document.createElement("div"); htmlDivElement.classList.add(Namespace.css.dayOfTheWeek, Namespace.css.noHighlight); htmlDivElement.innerText = innerDate.format({ weekday: "short" }); innerDate.manipulate(1, Unit.date); row.push(htmlDivElement); } return row; } } /** * Creates and updates the grid for `month` */ class MonthDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.dates = serviceLocator.locate(Dates); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement('div'); container.classList.add(Namespace.css.monthsContainer); for (let i = 0; i < 12; i++) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.selectMonth); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { const container = widget.getElementsByClassName(Namespace.css.monthsContainer)[0]; if (this.optionsStore.currentView === 'months') { const [previous, switcher, next] = container.parentElement .getElementsByClassName(Namespace.css.calendarHeader)[0] .getElementsByTagName('div'); switcher.setAttribute(Namespace.css.monthsContainer, this.optionsStore.viewDate.format({ year: 'numeric' })); this.optionsStore.options.display.components.year ? switcher.classList.remove(Namespace.css.disabled) : switcher.classList.add(Namespace.css.disabled); this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, Unit.year), Unit.year) ? previous.classList.remove(Namespace.css.disabled) : previous.classList.add(Namespace.css.disabled); this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, Unit.year), Unit.year) ? next.classList.remove(Namespace.css.disabled) : next.classList.add(Namespace.css.disabled); } let innerDate = this.optionsStore.viewDate.clone.startOf(Unit.year); container .querySelectorAll(`[data-action="${ActionTypes$1.selectMonth}"]`) .forEach((containerClone, index) => { let classes = []; classes.push(Namespace.css.month); if (!this.optionsStore.unset && this.dates.isPicked(innerDate, Unit.month)) { classes.push(Namespace.css.active); } if (!this.validation.isValid(innerDate, Unit.month)) { classes.push(Namespace.css.disabled); } paint(Unit.month, innerDate, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute('data-value', `${index}`); containerClone.innerText = `${innerDate.format({ month: 'short' })}`; innerDate.manipulate(1, Unit.month); }); } } /** * Creates and updates the grid for `year` */ class YearDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.dates = serviceLocator.locate(Dates); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement("div"); container.classList.add(Namespace.css.yearsContainer); for (let i = 0; i < 12; i++) { const div = document.createElement("div"); div.setAttribute("data-action", ActionTypes$1.selectYear); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { this._startYear = this.optionsStore.viewDate.clone.manipulate(-1, Unit.year); this._endYear = this.optionsStore.viewDate.clone.manipulate(10, Unit.year); const container = widget.getElementsByClassName(Namespace.css.yearsContainer)[0]; if (this.optionsStore.currentView === "years") { const [previous, switcher, next] = container.parentElement .getElementsByClassName(Namespace.css.calendarHeader)[0] .getElementsByTagName("div"); switcher.setAttribute(Namespace.css.yearsContainer, `${this._startYear.format({ year: "numeric" })}-${this._endYear.format({ year: "numeric" })}`); this.optionsStore.options.display.components.decades ? switcher.classList.remove(Namespace.css.disabled) : switcher.classList.add(Namespace.css.disabled); this.validation.isValid(this._startYear, Unit.year) ? previous.classList.remove(Namespace.css.disabled) : previous.classList.add(Namespace.css.disabled); this.validation.isValid(this._endYear, Unit.year) ? next.classList.remove(Namespace.css.disabled) : next.classList.add(Namespace.css.disabled); } let innerDate = this.optionsStore.viewDate.clone .startOf(Unit.year) .manipulate(-1, Unit.year); container .querySelectorAll(`[data-action="${ActionTypes$1.selectYear}"]`) .forEach((containerClone) => { let classes = []; classes.push(Namespace.css.year); if (!this.optionsStore.unset && this.dates.isPicked(innerDate, Unit.year)) { classes.push(Namespace.css.active); } if (!this.validation.isValid(innerDate, Unit.year)) { classes.push(Namespace.css.disabled); } paint(Unit.year, innerDate, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute("data-value", `${innerDate.year}`); containerClone.innerText = innerDate.format({ year: "numeric" }); innerDate.manipulate(1, Unit.year); }); } } /** * Creates and updates the grid for `seconds` */ class DecadeDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.dates = serviceLocator.locate(Dates); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement("div"); container.classList.add(Namespace.css.decadesContainer); for (let i = 0; i < 12; i++) { const div = document.createElement("div"); div.setAttribute("data-action", ActionTypes$1.selectDecade); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { const [start, end] = Dates.getStartEndYear(100, this.optionsStore.viewDate.year); this._startDecade = this.optionsStore.viewDate.clone.startOf(Unit.year); this._startDecade.year = start; this._endDecade = this.optionsStore.viewDate.clone.startOf(Unit.year); this._endDecade.year = end; const container = widget.getElementsByClassName(Namespace.css.decadesContainer)[0]; const [previous, switcher, next] = container.parentElement .getElementsByClassName(Namespace.css.calendarHeader)[0] .getElementsByTagName("div"); if (this.optionsStore.currentView === 'decades') { switcher.setAttribute(Namespace.css.decadesContainer, `${this._startDecade.format({ year: "numeric" })}-${this._endDecade.format({ year: "numeric" })}`); this.validation.isValid(this._startDecade, Unit.year) ? previous.classList.remove(Namespace.css.disabled) : previous.classList.add(Namespace.css.disabled); this.validation.isValid(this._endDecade, Unit.year) ? next.classList.remove(Namespace.css.disabled) : next.classList.add(Namespace.css.disabled); } const pickedYears = this.dates.picked.map((x) => x.year); container .querySelectorAll(`[data-action="${ActionTypes$1.selectDecade}"]`) .forEach((containerClone, index) => { if (index === 0) { containerClone.classList.add(Namespace.css.old); if (this._startDecade.year - 10 < 0) { containerClone.textContent = " "; previous.classList.add(Namespace.css.disabled); containerClone.classList.add(Namespace.css.disabled); containerClone.setAttribute("data-value", ``); return; } else { containerClone.innerText = this._startDecade.clone.manipulate(-10, Unit.year).format({ year: "numeric" }); containerClone.setAttribute("data-value", `${this._startDecade.year}`); return; } } let classes = []; classes.push(Namespace.css.decade); const startDecadeYear = this._startDecade.year; const endDecadeYear = this._startDecade.year + 9; if (!this.optionsStore.unset && pickedYears.filter((x) => x >= startDecadeYear && x <= endDecadeYear) .length > 0) { classes.push(Namespace.css.active); } paint("decade", this._startDecade, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute("data-value", `${this._startDecade.year}`); containerClone.innerText = `${this._startDecade.format({ year: "numeric" })}`; this._startDecade.manipulate(10, Unit.year); }); } } /** * Creates the clock display */ class TimeDisplay { constructor() { this._gridColumns = ''; this.optionsStore = serviceLocator.locate(OptionsStore); this.dates = serviceLocator.locate(Dates); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the clock display * @private */ getPicker(iconTag) { const container = document.createElement('div'); container.classList.add(Namespace.css.clockContainer); container.append(...this._grid(iconTag)); return container; } /** * Populates the various elements with in the clock display * like the current hour and if the manipulation icons are enabled. * @private */ _update(widget) { const timesDiv = (widget.getElementsByClassName(Namespace.css.clockContainer)[0]); const lastPicked = (this.dates.lastPicked || this.optionsStore.viewDate).clone; timesDiv .querySelectorAll('.disabled') .forEach((element) => element.classList.remove(Namespace.css.disabled)); if (this.optionsStore.options.display.components.hours) { if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, Unit.hours), Unit.hours)) { timesDiv .querySelector(`[data-action=${ActionTypes$1.incrementHours}]`) .classList.add(Namespace.css.disabled); } if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, Unit.hours), Unit.hours)) { timesDiv .querySelector(`[data-action=${ActionTypes$1.decrementHours}]`) .classList.add(Namespace.css.disabled); } timesDiv.querySelector(`[data-time-component=${Unit.hours}]`).innerText = this.optionsStore.options.display.components.useTwentyfourHour ? lastPicked.hoursFormatted : lastPicked.twelveHoursFormatted; } if (this.optionsStore.options.display.components.minutes) { if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, Unit.minutes), Unit.minutes)) { timesDiv .querySelector(`[data-action=${ActionTypes$1.incrementMinutes}]`) .classList.add(Namespace.css.disabled); } if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, Unit.minutes), Unit.minutes)) { timesDiv .querySelector(`[data-action=${ActionTypes$1.decrementMinutes}]`) .classList.add(Namespace.css.disabled); } timesDiv.querySelector(`[data-time-component=${Unit.minutes}]`).innerText = lastPicked.minutesFormatted; } if (this.optionsStore.options.display.components.seconds) { if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(1, Unit.seconds), Unit.seconds)) { timesDiv .querySelector(`[data-action=${ActionTypes$1.incrementSeconds}]`) .classList.add(Namespace.css.disabled); } if (!this.validation.isValid(this.optionsStore.viewDate.clone.manipulate(-1, Unit.seconds), Unit.seconds)) { timesDiv .querySelector(`[data-action=${ActionTypes$1.decrementSeconds}]`) .classList.add(Namespace.css.disabled); } timesDiv.querySelector(`[data-time-component=${Unit.seconds}]`).innerText = lastPicked.secondsFormatted; } if (!this.optionsStore.options.display.components.useTwentyfourHour) { const toggle = timesDiv.querySelector(`[data-action=${ActionTypes$1.toggleMeridiem}]`); toggle.innerText = lastPicked.meridiem(); if (!this.validation.isValid(lastPicked.clone.manipulate(lastPicked.hours >= 12 ? -12 : 12, Unit.hours))) { toggle.classList.add(Namespace.css.disabled); } else { toggle.classList.remove(Namespace.css.disabled); } } timesDiv.style.gridTemplateAreas = `"${this._gridColumns}"`; } /** * Creates the table for the clock display depending on what options are selected. * @private */ _grid(iconTag) { this._gridColumns = ''; const top = [], middle = [], bottom = [], separator = document.createElement('div'), upIcon = iconTag(this.optionsStore.options.display.icons.up), downIcon = iconTag(this.optionsStore.options.display.icons.down); separator.classList.add(Namespace.css.separator, Namespace.css.noHighlight); const separatorColon = separator.cloneNode(true); separatorColon.innerHTML = ':'; const getSeparator = (colon = false) => { return colon ? separatorColon.cloneNode(true) : separator.cloneNode(true); }; if (this.optionsStore.options.display.components.hours) { let divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.incrementHour); divElement.setAttribute('data-action', ActionTypes$1.incrementHours); divElement.appendChild(upIcon.cloneNode(true)); top.push(divElement); divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.pickHour); divElement.setAttribute('data-action', ActionTypes$1.showHours); divElement.setAttribute('data-time-component', Unit.hours); middle.push(divElement); divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.decrementHour); divElement.setAttribute('data-action', ActionTypes$1.decrementHours); divElement.appendChild(downIcon.cloneNode(true)); bottom.push(divElement); this._gridColumns += 'a'; } if (this.optionsStore.options.display.components.minutes) { this._gridColumns += ' a'; if (this.optionsStore.options.display.components.hours) { top.push(getSeparator()); middle.push(getSeparator(true)); bottom.push(getSeparator()); this._gridColumns += ' a'; } let divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.incrementMinute); divElement.setAttribute('data-action', ActionTypes$1.incrementMinutes); divElement.appendChild(upIcon.cloneNode(true)); top.push(divElement); divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.pickMinute); divElement.setAttribute('data-action', ActionTypes$1.showMinutes); divElement.setAttribute('data-time-component', Unit.minutes); middle.push(divElement); divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.decrementMinute); divElement.setAttribute('data-action', ActionTypes$1.decrementMinutes); divElement.appendChild(downIcon.cloneNode(true)); bottom.push(divElement); } if (this.optionsStore.options.display.components.seconds) { this._gridColumns += ' a'; if (this.optionsStore.options.display.components.minutes) { top.push(getSeparator()); middle.push(getSeparator(true)); bottom.push(getSeparator()); this._gridColumns += ' a'; } let divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.incrementSecond); divElement.setAttribute('data-action', ActionTypes$1.incrementSeconds); divElement.appendChild(upIcon.cloneNode(true)); top.push(divElement); divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.pickSecond); divElement.setAttribute('data-action', ActionTypes$1.showSeconds); divElement.setAttribute('data-time-component', Unit.seconds); middle.push(divElement); divElement = document.createElement('div'); divElement.setAttribute('title', this.optionsStore.options.localization.decrementSecond); divElement.setAttribute('data-action', ActionTypes$1.decrementSeconds); divElement.appendChild(downIcon.cloneNode(true)); bottom.push(divElement); } if (!this.optionsStore.options.display.components.useTwentyfourHour) { this._gridColumns += ' a'; let divElement = getSeparator(); top.push(divElement); let button = document.createElement('button'); button.setAttribute('title', this.optionsStore.options.localization.toggleMeridiem); button.setAttribute('data-action', ActionTypes$1.toggleMeridiem); button.setAttribute('tabindex', '-1'); if (Namespace.css.toggleMeridiem.includes(',')) { //todo move this to paint function? button.classList.add(...Namespace.css.toggleMeridiem.split(',')); } else button.classList.add(Namespace.css.toggleMeridiem); divElement = document.createElement('div'); divElement.classList.add(Namespace.css.noHighlight); divElement.appendChild(button); middle.push(divElement); divElement = getSeparator(); bottom.push(divElement); } this._gridColumns = this._gridColumns.trim(); return [...top, ...middle, ...bottom]; } } /** * Creates and updates the grid for `hours` */ class HourDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement('div'); container.classList.add(Namespace.css.hourContainer); for (let i = 0; i < (this.optionsStore.options.display.components.useTwentyfourHour ? 24 : 12); i++) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.selectHour); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { const container = widget.getElementsByClassName(Namespace.css.hourContainer)[0]; let innerDate = this.optionsStore.viewDate.clone.startOf(Unit.date); container .querySelectorAll(`[data-action="${ActionTypes$1.selectHour}"]`) .forEach((containerClone) => { let classes = []; classes.push(Namespace.css.hour); if (!this.validation.isValid(innerDate, Unit.hours)) { classes.push(Namespace.css.disabled); } paint(Unit.hours, innerDate, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute('data-value', `${innerDate.hours}`); containerClone.innerText = this.optionsStore.options.display.components .useTwentyfourHour ? innerDate.hoursFormatted : innerDate.twelveHoursFormatted; innerDate.manipulate(1, Unit.hours); }); } } /** * Creates and updates the grid for `minutes` */ class MinuteDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement('div'); container.classList.add(Namespace.css.minuteContainer); let step = this.optionsStore.options.stepping === 1 ? 5 : this.optionsStore.options.stepping; for (let i = 0; i < 60 / step; i++) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.selectMinute); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { const container = widget.getElementsByClassName(Namespace.css.minuteContainer)[0]; let innerDate = this.optionsStore.viewDate.clone.startOf(Unit.hours); let step = this.optionsStore.options.stepping === 1 ? 5 : this.optionsStore.options.stepping; container .querySelectorAll(`[data-action="${ActionTypes$1.selectMinute}"]`) .forEach((containerClone) => { let classes = []; classes.push(Namespace.css.minute); if (!this.validation.isValid(innerDate, Unit.minutes)) { classes.push(Namespace.css.disabled); } paint(Unit.minutes, innerDate, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute('data-value', `${innerDate.minutes}`); containerClone.innerText = innerDate.minutesFormatted; innerDate.manipulate(step, Unit.minutes); }); } } /** * Creates and updates the grid for `seconds` */ class secondDisplay { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.validation = serviceLocator.locate(Validation); } /** * Build the container html for the display * @private */ getPicker() { const container = document.createElement('div'); container.classList.add(Namespace.css.secondContainer); for (let i = 0; i < 12; i++) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.selectSecond); container.appendChild(div); } return container; } /** * Populates the grid and updates enabled states * @private */ _update(widget, paint) { const container = widget.getElementsByClassName(Namespace.css.secondContainer)[0]; let innerDate = this.optionsStore.viewDate.clone.startOf(Unit.minutes); container .querySelectorAll(`[data-action="${ActionTypes$1.selectSecond}"]`) .forEach((containerClone) => { let classes = []; classes.push(Namespace.css.second); if (!this.validation.isValid(innerDate, Unit.seconds)) { classes.push(Namespace.css.disabled); } paint(Unit.seconds, innerDate, classes, containerClone); containerClone.classList.remove(...containerClone.classList); containerClone.classList.add(...classes); containerClone.setAttribute('data-value', `${innerDate.seconds}`); containerClone.innerText = innerDate.secondsFormatted; innerDate.manipulate(5, Unit.seconds); }); } } /** * Provides a collapse functionality to the view changes */ class Collapse { /** * Flips the show/hide state of `target` * @param target html element to affect. */ static toggle(target) { if (target.classList.contains(Namespace.css.show)) { this.hide(target); } else { this.show(target); } } /** * Skips any animation or timeouts and immediately set the element to show. * @param target */ static showImmediately(target) { target.classList.remove(Namespace.css.collapsing); target.classList.add(Namespace.css.collapse, Namespace.css.show); target.style.height = ''; } /** * If `target` is not already showing, then show after the animation. * @param target */ static show(target) { if (target.classList.contains(Namespace.css.collapsing) || target.classList.contains(Namespace.css.show)) return; const complete = () => { Collapse.showImmediately(target); }; target.style.height = '0'; target.classList.remove(Namespace.css.collapse); target.classList.add(Namespace.css.collapsing); setTimeout(complete, this.getTransitionDurationFromElement(target)); target.style.height = `${target.scrollHeight}px`; } /** * Skips any animation or timeouts and immediately set the element to hide. * @param target */ static hideImmediately(target) { if (!target) return; target.classList.remove(Namespace.css.collapsing, Namespace.css.show); target.classList.add(Namespace.css.collapse); } /** * If `target` is not already hidden, then hide after the animation. * @param target HTML Element */ static hide(target) { if (target.classList.contains(Namespace.css.collapsing) || !target.classList.contains(Namespace.css.show)) return; const complete = () => { Collapse.hideImmediately(target); }; target.style.height = `${target.getBoundingClientRect()['height']}px`; const reflow = (element) => element.offsetHeight; reflow(target); target.classList.remove(Namespace.css.collapse, Namespace.css.show); target.classList.add(Namespace.css.collapsing); target.style.height = ''; setTimeout(complete, this.getTransitionDurationFromElement(target)); } } /** * Gets the transition duration from the `element` by getting css properties * `transition-duration` and `transition-delay` * @param element HTML Element */ Collapse.getTransitionDurationFromElement = (element) => { if (!element) { return 0; } // Get transition-duration of the element let { transitionDuration, transitionDelay } = window.getComputedStyle(element); const floatTransitionDuration = Number.parseFloat(transitionDuration); const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found if (!floatTransitionDuration && !floatTransitionDelay) { return 0; } // If multiple durations are defined, take the first transitionDuration = transitionDuration.split(',')[0]; transitionDelay = transitionDelay.split(',')[0]; return ((Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * 1000); }; /** * Main class for all things display related. */ class Display { constructor() { this._isVisible = false; /** * A document click event to hide the widget if click is outside * @private * @param e MouseEvent */ this._documentClickEvent = (e) => { var _a; if (this.optionsStore.options.debug || window.debug) return; if (this._isVisible && !e.composedPath().includes(this.widget) && // click inside the widget !((_a = e.composedPath()) === null || _a === void 0 ? void 0 : _a.includes(this.optionsStore.element)) // click on the element ) { this.hide(); } }; /** * Click event for any action like selecting a date * @param e MouseEvent * @private */ this._actionsClickEvent = (e) => { this._eventEmitters.action.emit({ e: e }); }; this.optionsStore = serviceLocator.locate(OptionsStore); this.validation = serviceLocator.locate(Validation); this.dates = serviceLocator.locate(Dates); this.dateDisplay = serviceLocator.locate(DateDisplay); this.monthDisplay = serviceLocator.locate(MonthDisplay); this.yearDisplay = serviceLocator.locate(YearDisplay); this.decadeDisplay = serviceLocator.locate(DecadeDisplay); this.timeDisplay = serviceLocator.locate(TimeDisplay); this.hourDisplay = serviceLocator.locate(HourDisplay); this.minuteDisplay = serviceLocator.locate(MinuteDisplay); this.secondDisplay = serviceLocator.locate(secondDisplay); this._eventEmitters = serviceLocator.locate(EventEmitters); this._widget = undefined; this._eventEmitters.updateDisplay.subscribe((result) => { this._update(result); }); } /** * Returns the widget body or undefined * @private */ get widget() { return this._widget; } /** * Returns this visible state of the picker (shown) */ get isVisible() { return this._isVisible; } /** * Updates the table for a particular unit. Used when an option as changed or * whenever the class list might need to be refreshed. * @param unit * @private */ _update(unit) { if (!this.widget) return; //todo do I want some kind of error catching or other guards here? switch (unit) { case Unit.seconds: this.secondDisplay._update(this.widget, this.paint); break; case Unit.minutes: this.minuteDisplay._update(this.widget, this.paint); break; case Unit.hours: this.hourDisplay._update(this.widget, this.paint); break; case Unit.date: this.dateDisplay._update(this.widget, this.paint); break; case Unit.month: this.monthDisplay._update(this.widget, this.paint); break; case Unit.year: this.yearDisplay._update(this.widget, this.paint); break; case 'clock': if (!this._hasTime) break; this.timeDisplay._update(this.widget); this._update(Unit.hours); this._update(Unit.minutes); this._update(Unit.seconds); break; case 'calendar': this._update(Unit.date); this._update(Unit.year); this._update(Unit.month); this.decadeDisplay._update(this.widget, this.paint); this._updateCalendarHeader(); break; case 'all': if (this._hasTime) { this._update('clock'); } if (this._hasDate) { this._update('calendar'); } } } // noinspection JSUnusedLocalSymbols /** * Allows developers to add/remove classes from an element. * @param _unit * @param _date * @param _classes * @param _element */ paint(_unit, _date, _classes, _element) { // implemented in plugin } /** * Shows the picker and creates a Popper instance if needed. * Add document click event to hide when clicking outside the picker. * fires Events#show */ show() { var _a, _b; if (this.widget == undefined) { if (this.dates.picked.length == 0) { if (this.optionsStore.options.useCurrent && !this.optionsStore.options.defaultDate) { const date = new DateTime().setLocale(this.optionsStore.options.localization.locale); if (!this.optionsStore.options.keepInvalid) { let tries = 0; let direction = 1; if ((_a = this.optionsStore.options.restrictions.maxDate) === null || _a === void 0 ? void 0 : _a.isBefore(date)) { direction = -1; } while (!this.validation.isValid(date)) { date.manipulate(direction, Unit.date); if (tries > 31) break; tries++; } } this.dates.setValue(date); } if (this.optionsStore.options.defaultDate) { this.dates.setValue(this.optionsStore.options.defaultDate); } } this._buildWidget(); this._updateTheme(); // If modeView is only clock const onlyClock = this._hasTime && !this._hasDate; // reset the view to the clock if there's no date components if (onlyClock) { this.optionsStore.currentView = 'clock'; this._eventEmitters.action.emit({ e: null, action: ActionTypes$1.showClock, }); } // otherwise return to the calendar view if (!this.optionsStore.currentCalendarViewMode) { this.optionsStore.currentCalendarViewMode = this.optionsStore.minimumCalendarViewMode; } if (!onlyClock && this.optionsStore.options.display.viewMode !== 'clock') { if (this._hasTime) { if (!this.optionsStore.options.display.sideBySide) { Collapse.hideImmediately(this.widget.querySelector(`div.${Namespace.css.timeContainer}`)); } else { Collapse.show(this.widget.querySelector(`div.${Namespace.css.timeContainer}`)); } } Collapse.show(this.widget.querySelector(`div.${Namespace.css.dateContainer}`)); } if (this._hasDate) { this._showMode(); } if (!this.optionsStore.options.display.inline) { // If needed to change the parent container const container = ((_b = this.optionsStore.options) === null || _b === void 0 ? void 0 : _b.container) || document.body; container.appendChild(this.widget); this._popperInstance = core.createPopper(this.optionsStore.element, this.widget, { modifiers: [{ name: 'eventListeners', enabled: true }], //#2400 placement: document.documentElement.dir === 'rtl' ? 'bottom-end' : 'bottom-start', }); } else { this.optionsStore.element.appendChild(this.widget); } if (this.optionsStore.options.display.viewMode == 'clock') { this._eventEmitters.action.emit({ e: null, action: ActionTypes$1.showClock, }); } this.widget .querySelectorAll('[data-action]') .forEach((element) => element.addEventListener('click', this._actionsClickEvent)); // show the clock when using sideBySide if (this._hasTime && this.optionsStore.options.display.sideBySide) { this.timeDisplay._update(this.widget); this.widget.getElementsByClassName(Namespace.css.clockContainer)[0].style.display = 'grid'; } } this.widget.classList.add(Namespace.css.show); if (!this.optionsStore.options.display.inline) { this._popperInstance.update(); document.addEventListener('click', this._documentClickEvent); } this._eventEmitters.triggerEvent.emit({ type: Namespace.events.show }); this._isVisible = true; } /** * Changes the calendar view mode. E.g. month <-> year * @param direction -/+ number to move currentViewMode * @private */ _showMode(direction) { if (!this.widget) { return; } if (direction) { const max = Math.max(this.optionsStore.minimumCalendarViewMode, Math.min(3, this.optionsStore.currentCalendarViewMode + direction)); if (this.optionsStore.currentCalendarViewMode == max) return; this.optionsStore.currentCalendarViewMode = max; } this.widget .querySelectorAll(`.${Namespace.css.dateContainer} > div:not(.${Namespace.css.calendarHeader}), .${Namespace.css.timeContainer} > div:not(.${Namespace.css.clockContainer})`) .forEach((e) => (e.style.display = 'none')); const datePickerMode = CalendarModes[this.optionsStore.currentCalendarViewMode]; let picker = this.widget.querySelector(`.${datePickerMode.className}`); switch (datePickerMode.className) { case Namespace.css.decadesContainer: this.decadeDisplay._update(this.widget, this.paint); break; case Namespace.css.yearsContainer: this.yearDisplay._update(this.widget, this.paint); break; case Namespace.css.monthsContainer: this.monthDisplay._update(this.widget, this.paint); break; case Namespace.css.daysContainer: this.dateDisplay._update(this.widget, this.paint); break; } picker.style.display = 'grid'; this._updateCalendarHeader(); this._eventEmitters.viewUpdate.emit(); } /** * Changes the theme. E.g. light, dark or auto * @param theme the theme name * @private */ _updateTheme(theme) { if (!this.widget) { return; } if (theme) { if (this.optionsStore.options.display.theme === theme) return; this.optionsStore.options.display.theme = theme; } this.widget.classList.remove('light', 'dark'); this.widget.classList.add(this._getThemeClass()); if (this.optionsStore.options.display.theme === 'auto') { window .matchMedia(Namespace.css.isDarkPreferredQuery) .addEventListener('change', () => this._updateTheme()); } else { window .matchMedia(Namespace.css.isDarkPreferredQuery) .removeEventListener('change', () => this._updateTheme()); } } _getThemeClass() { const currentTheme = this.optionsStore.options.display.theme || 'auto'; const isDarkMode = window.matchMedia && window.matchMedia(Namespace.css.isDarkPreferredQuery).matches; switch (currentTheme) { case 'light': return Namespace.css.lightTheme; case 'dark': return Namespace.css.darkTheme; case 'auto': return isDarkMode ? Namespace.css.darkTheme : Namespace.css.lightTheme; } } _updateCalendarHeader() { const showing = [ ...this.widget.querySelector(`.${Namespace.css.dateContainer} div[style*="display: grid"]`).classList, ].find((x) => x.startsWith(Namespace.css.dateContainer)); const [previous, switcher, next] = this.widget .getElementsByClassName(Namespace.css.calendarHeader)[0] .getElementsByTagName('div'); switch (showing) { case Namespace.css.decadesContainer: previous.setAttribute('title', this.optionsStore.options.localization.previousCentury); switcher.setAttribute('title', ''); next.setAttribute('title', this.optionsStore.options.localization.nextCentury); break; case Namespace.css.yearsContainer: previous.setAttribute('title', this.optionsStore.options.localization.previousDecade); switcher.setAttribute('title', this.optionsStore.options.localization.selectDecade); next.setAttribute('title', this.optionsStore.options.localization.nextDecade); break; case Namespace.css.monthsContainer: previous.setAttribute('title', this.optionsStore.options.localization.previousYear); switcher.setAttribute('title', this.optionsStore.options.localization.selectYear); next.setAttribute('title', this.optionsStore.options.localization.nextYear); break; case Namespace.css.daysContainer: previous.setAttribute('title', this.optionsStore.options.localization.previousMonth); switcher.setAttribute('title', this.optionsStore.options.localization.selectMonth); next.setAttribute('title', this.optionsStore.options.localization.nextMonth); switcher.innerText = this.optionsStore.viewDate.format(this.optionsStore.options.localization.dayViewHeaderFormat); break; } switcher.innerText = switcher.getAttribute(showing); } /** * Hides the picker if needed. * Remove document click event to hide when clicking outside the picker. * fires Events#hide */ hide() { if (!this.widget || !this._isVisible) return; this.widget.classList.remove(Namespace.css.show); if (this._isVisible) { this._eventEmitters.triggerEvent.emit({ type: Namespace.events.hide, date: this.optionsStore.unset ? null : this.dates.lastPicked ? this.dates.lastPicked.clone : void 0, }); this._isVisible = false; } document.removeEventListener('click', this._documentClickEvent); } /** * Toggles the picker's open state. Fires a show/hide event depending. */ toggle() { return this._isVisible ? this.hide() : this.show(); } /** * Removes document and data-action click listener and reset the widget * @private */ _dispose() { document.removeEventListener('click', this._documentClickEvent); if (!this.widget) return; this.widget .querySelectorAll('[data-action]') .forEach((element) => element.removeEventListener('click', this._actionsClickEvent)); this.widget.parentNode.removeChild(this.widget); this._widget = undefined; } /** * Builds the widgets html template. * @private */ _buildWidget() { const template = document.createElement('div'); template.classList.add(Namespace.css.widget); const dateView = document.createElement('div'); dateView.classList.add(Namespace.css.dateContainer); dateView.append(this.getHeadTemplate(), this.decadeDisplay.getPicker(), this.yearDisplay.getPicker(), this.monthDisplay.getPicker(), this.dateDisplay.getPicker()); const timeView = document.createElement('div'); timeView.classList.add(Namespace.css.timeContainer); timeView.appendChild(this.timeDisplay.getPicker(this._iconTag.bind(this))); timeView.appendChild(this.hourDisplay.getPicker()); timeView.appendChild(this.minuteDisplay.getPicker()); timeView.appendChild(this.secondDisplay.getPicker()); const toolbar = document.createElement('div'); toolbar.classList.add(Namespace.css.toolbar); toolbar.append(...this.getToolbarElements()); if (this.optionsStore.options.display.inline) { template.classList.add(Namespace.css.inline); } if (this.optionsStore.options.display.calendarWeeks) { template.classList.add('calendarWeeks'); } if (this.optionsStore.options.display.sideBySide && this._hasDate && this._hasTime) { template.classList.add(Namespace.css.sideBySide); if (this.optionsStore.options.display.toolbarPlacement === 'top') { template.appendChild(toolbar); } const row = document.createElement('div'); row.classList.add('td-row'); dateView.classList.add('td-half'); timeView.classList.add('td-half'); row.appendChild(dateView); row.appendChild(timeView); template.appendChild(row); if (this.optionsStore.options.display.toolbarPlacement === 'bottom') { template.appendChild(toolbar); } this._widget = template; return; } if (this.optionsStore.options.display.toolbarPlacement === 'top') { template.appendChild(toolbar); } if (this._hasDate) { if (this._hasTime) { dateView.classList.add(Namespace.css.collapse); if (this.optionsStore.options.display.viewMode !== 'clock') dateView.classList.add(Namespace.css.show); } template.appendChild(dateView); } if (this._hasTime) { if (this._hasDate) { timeView.classList.add(Namespace.css.collapse); if (this.optionsStore.options.display.viewMode === 'clock') timeView.classList.add(Namespace.css.show); } template.appendChild(timeView); } if (this.optionsStore.options.display.toolbarPlacement === 'bottom') { template.appendChild(toolbar); } const arrow = document.createElement('div'); arrow.classList.add('arrow'); arrow.setAttribute('data-popper-arrow', ''); template.appendChild(arrow); this._widget = template; } /** * Returns true if the hours, minutes, or seconds component is turned on */ get _hasTime() { return (this.optionsStore.options.display.components.clock && (this.optionsStore.options.display.components.hours || this.optionsStore.options.display.components.minutes || this.optionsStore.options.display.components.seconds)); } /** * Returns true if the year, month, or date component is turned on */ get _hasDate() { return (this.optionsStore.options.display.components.calendar && (this.optionsStore.options.display.components.year || this.optionsStore.options.display.components.month || this.optionsStore.options.display.components.date)); } /** * Get the toolbar html based on options like buttons.today * @private */ getToolbarElements() { const toolbar = []; if (this.optionsStore.options.display.buttons.today) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.today); div.setAttribute('title', this.optionsStore.options.localization.today); div.appendChild(this._iconTag(this.optionsStore.options.display.icons.today)); toolbar.push(div); } if (!this.optionsStore.options.display.sideBySide && this._hasDate && this._hasTime) { let title, icon; if (this.optionsStore.options.display.viewMode === 'clock') { title = this.optionsStore.options.localization.selectDate; icon = this.optionsStore.options.display.icons.date; } else { title = this.optionsStore.options.localization.selectTime; icon = this.optionsStore.options.display.icons.time; } const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.togglePicker); div.setAttribute('title', title); div.appendChild(this._iconTag(icon)); toolbar.push(div); } if (this.optionsStore.options.display.buttons.clear) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.clear); div.setAttribute('title', this.optionsStore.options.localization.clear); div.appendChild(this._iconTag(this.optionsStore.options.display.icons.clear)); toolbar.push(div); } if (this.optionsStore.options.display.buttons.close) { const div = document.createElement('div'); div.setAttribute('data-action', ActionTypes$1.close); div.setAttribute('title', this.optionsStore.options.localization.close); div.appendChild(this._iconTag(this.optionsStore.options.display.icons.close)); toolbar.push(div); } return toolbar; } /*** * Builds the base header template with next and previous icons * @private */ getHeadTemplate() { const calendarHeader = document.createElement('div'); calendarHeader.classList.add(Namespace.css.calendarHeader); const previous = document.createElement('div'); previous.classList.add(Namespace.css.previous); previous.setAttribute('data-action', ActionTypes$1.previous); previous.appendChild(this._iconTag(this.optionsStore.options.display.icons.previous)); const switcher = document.createElement('div'); switcher.classList.add(Namespace.css.switch); switcher.setAttribute('data-action', ActionTypes$1.changeCalendarView); const next = document.createElement('div'); next.classList.add(Namespace.css.next); next.setAttribute('data-action', ActionTypes$1.next); next.appendChild(this._iconTag(this.optionsStore.options.display.icons.next)); calendarHeader.append(previous, switcher, next); return calendarHeader; } /** * Builds an icon tag as either an `<i>` * or with icons.type is `sprites` then a svg tag instead * @param iconClass * @private */ _iconTag(iconClass) { if (this.optionsStore.options.display.icons.type === 'sprites') { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const icon = document.createElementNS('http://www.w3.org/2000/svg', 'use'); icon.setAttribute('xlink:href', iconClass); // Deprecated. Included for backward compatibility icon.setAttribute('href', iconClass); svg.appendChild(icon); return svg; } const icon = document.createElement('i'); icon.classList.add(...iconClass.split(' ')); return icon; } /** * Causes the widget to get rebuilt on next show. If the picker is already open * then hide and reshow it. * @private */ _rebuild() { const wasVisible = this._isVisible; if (wasVisible) this.hide(); this._dispose(); if (wasVisible) { this.show(); } } } /** * */ class Actions { constructor() { this.optionsStore = serviceLocator.locate(OptionsStore); this.dates = serviceLocator.locate(Dates); this.validation = serviceLocator.locate(Validation); this.display = serviceLocator.locate(Display); this._eventEmitters = serviceLocator.locate(EventEmitters); this._eventEmitters.action.subscribe((result) => { this.do(result.e, result.action); }); } /** * Performs the selected `action`. See ActionTypes * @param e This is normally a click event * @param action If not provided, then look for a [data-action] */ do(e, action) { var _a, _b; const currentTarget = e === null || e === void 0 ? void 0 : e.currentTarget; if ((_a = currentTarget === null || currentTarget === void 0 ? void 0 : currentTarget.classList) === null || _a === void 0 ? void 0 : _a.contains(Namespace.css.disabled)) return false; action = action || ((_b = currentTarget === null || currentTarget === void 0 ? void 0 : currentTarget.dataset) === null || _b === void 0 ? void 0 : _b.action); const lastPicked = (this.dates.lastPicked || this.optionsStore.viewDate) .clone; switch (action) { case ActionTypes$1.next: case ActionTypes$1.previous: this.handleNextPrevious(action); break; case ActionTypes$1.changeCalendarView: this.display._showMode(1); this.display._updateCalendarHeader(); break; case ActionTypes$1.selectMonth: case ActionTypes$1.selectYear: case ActionTypes$1.selectDecade: const value = +currentTarget.dataset.value; switch (action) { case ActionTypes$1.selectMonth: this.optionsStore.viewDate.month = value; break; case ActionTypes$1.selectYear: case ActionTypes$1.selectDecade: this.optionsStore.viewDate.year = value; break; } if (this.optionsStore.currentCalendarViewMode === this.optionsStore.minimumCalendarViewMode) { this.dates.setValue(this.optionsStore.viewDate, this.dates.lastPickedIndex); if (!this.optionsStore.options.display.inline) { this.display.hide(); } } else { this.display._showMode(-1); } break; case ActionTypes$1.selectDay: const day = this.optionsStore.viewDate.clone; if (currentTarget.classList.contains(Namespace.css.old)) { day.manipulate(-1, Unit.month); } if (currentTarget.classList.contains(Namespace.css.new)) { day.manipulate(1, Unit.month); } day.date = +currentTarget.dataset.day; let index = 0; if (this.optionsStore.options.multipleDates) { index = this.dates.pickedIndex(day, Unit.date); if (index !== -1) { this.dates.setValue(null, index); //deselect multi-date } else { this.dates.setValue(day, this.dates.lastPickedIndex + 1); } } else { this.dates.setValue(day, this.dates.lastPickedIndex); } if (!this.display._hasTime && !this.optionsStore.options.display.keepOpen && !this.optionsStore.options.display.inline && !this.optionsStore.options.multipleDates) { this.display.hide(); } break; case ActionTypes$1.selectHour: let hour = +currentTarget.dataset.value; if (lastPicked.hours >= 12 && !this.optionsStore.options.display.components.useTwentyfourHour) hour += 12; lastPicked.hours = hour; this.dates.setValue(lastPicked, this.dates.lastPickedIndex); this.hideOrClock(e); break; case ActionTypes$1.selectMinute: lastPicked.minutes = +currentTarget.dataset.value; this.dates.setValue(lastPicked, this.dates.lastPickedIndex); this.hideOrClock(e); break; case ActionTypes$1.selectSecond: lastPicked.seconds = +currentTarget.dataset.value; this.dates.setValue(lastPicked, this.dates.lastPickedIndex); this.hideOrClock(e); break; case ActionTypes$1.incrementHours: this.manipulateAndSet(lastPicked, Unit.hours); break; case ActionTypes$1.incrementMinutes: this.manipulateAndSet(lastPicked, Unit.minutes, this.optionsStore.options.stepping); break; case ActionTypes$1.incrementSeconds: this.manipulateAndSet(lastPicked, Unit.seconds); break; case ActionTypes$1.decrementHours: this.manipulateAndSet(lastPicked, Unit.hours, -1); break; case ActionTypes$1.decrementMinutes: this.manipulateAndSet(lastPicked, Unit.minutes, this.optionsStore.options.stepping * -1); break; case ActionTypes$1.decrementSeconds: this.manipulateAndSet(lastPicked, Unit.seconds, -1); break; case ActionTypes$1.toggleMeridiem: this.manipulateAndSet(lastPicked, Unit.hours, this.dates.lastPicked.hours >= 12 ? -12 : 12); break; case ActionTypes$1.togglePicker: if (currentTarget.getAttribute('title') === this.optionsStore.options.localization.selectDate) { currentTarget.setAttribute('title', this.optionsStore.options.localization.selectTime); currentTarget.innerHTML = this.display._iconTag(this.optionsStore.options.display.icons.time).outerHTML; this.display._updateCalendarHeader(); this.optionsStore.refreshCurrentView(); } else { currentTarget.setAttribute('title', this.optionsStore.options.localization.selectDate); currentTarget.innerHTML = this.display._iconTag(this.optionsStore.options.display.icons.date).outerHTML; if (this.display._hasTime) { this.handleShowClockContainers(ActionTypes$1.showClock); this.display._update('clock'); } } this.display.widget .querySelectorAll(`.${Namespace.css.dateContainer}, .${Namespace.css.timeContainer}`) .forEach((htmlElement) => Collapse.toggle(htmlElement)); this._eventEmitters.viewUpdate.emit(); break; case ActionTypes$1.showClock: case ActionTypes$1.showHours: case ActionTypes$1.showMinutes: case ActionTypes$1.showSeconds: //make sure the clock is actually displaying if (!this.optionsStore.options.display.sideBySide && this.optionsStore.currentView !== 'clock') { //hide calendar Collapse.hideImmediately(this.display.widget.querySelector(`div.${Namespace.css.dateContainer}`)); //show clock Collapse.showImmediately(this.display.widget.querySelector(`div.${Namespace.css.timeContainer}`)); } this.handleShowClockContainers(action); break; case ActionTypes$1.clear: this.dates.setValue(null); this.display._updateCalendarHeader(); break; case ActionTypes$1.close: this.display.hide(); break; case ActionTypes$1.today: const today = new DateTime().setLocale(this.optionsStore.options.localization.locale); this.optionsStore.viewDate = today; if (this.validation.isValid(today, Unit.date)) this.dates.setValue(today, this.dates.lastPickedIndex); break; } } handleShowClockContainers(action) { if (!this.display._hasTime) { Namespace.errorMessages.throwError('Cannot show clock containers when time is disabled.'); return; } this.optionsStore.currentView = 'clock'; this.display.widget .querySelectorAll(`.${Namespace.css.timeContainer} > div`) .forEach((htmlElement) => (htmlElement.style.display = 'none')); let classToUse = ''; switch (action) { case ActionTypes$1.showClock: classToUse = Namespace.css.clockContainer; this.display._update('clock'); break; case ActionTypes$1.showHours: classToUse = Namespace.css.hourContainer; this.display._update(Unit.hours); break; case ActionTypes$1.showMinutes: classToUse = Namespace.css.minuteContainer; this.display._update(Unit.minutes); break; case ActionTypes$1.showSeconds: classToUse = Namespace.css.secondContainer; this.display._update(Unit.seconds); break; } (this.display.widget.getElementsByClassName(classToUse)[0]).style.display = 'grid'; } handleNextPrevious(action) { const { unit, step } = CalendarModes[this.optionsStore.currentCalendarViewMode]; if (action === ActionTypes$1.next) this.optionsStore.viewDate.manipulate(step, unit); else this.optionsStore.viewDate.manipulate(step * -1, unit); this._eventEmitters.viewUpdate.emit(); this.display._showMode(); } /** * After setting the value it will either show the clock or hide the widget. * @param e */ hideOrClock(e) { if (this.optionsStore.options.display.components.useTwentyfourHour && !this.optionsStore.options.display.components.minutes && !this.optionsStore.options.display.keepOpen && !this.optionsStore.options.display.inline) { this.display.hide(); } else { this.do(e, ActionTypes$1.showClock); } } /** * Common function to manipulate {@link lastPicked} by `unit`. * @param lastPicked * @param unit * @param value Value to change by */ manipulateAndSet(lastPicked, unit, value = 1) { const newDate = lastPicked.manipulate(value, unit); if (this.validation.isValid(newDate, unit)) { this.dates.setValue(newDate, this.dates.lastPickedIndex); } } } /** * A robust and powerful date/time picker component. */ class TempusDominus { constructor(element, options = {}) { this._subscribers = {}; this._isDisabled = false; /** * Event for when the input field changes. This is a class level method so there's * something for the remove listener function. * @private */ this._inputChangeEvent = (event) => { const internallyTriggered = event === null || event === void 0 ? void 0 : event.detail; if (internallyTriggered) return; const setViewDate = () => { if (this.dates.lastPicked) this.optionsStore.viewDate = this.dates.lastPicked.clone; }; const value = this.optionsStore.input.value; if (this.optionsStore.options.multipleDates) { try { const valueSplit = value.split(this.optionsStore.options.multipleDatesSeparator); for (let i = 0; i < valueSplit.length; i++) { this.dates.setFromInput(valueSplit[i], i); } setViewDate(); } catch (_a) { console.warn('TD: Something went wrong trying to set the multipleDates values from the input field.'); } } else { this.dates.setFromInput(value, 0); setViewDate(); } }; /** * Event for when the toggle is clicked. This is a class level method so there's * something for the remove listener function. * @private */ this._toggleClickEvent = () => { var _a, _b; if (((_a = this.optionsStore.element) === null || _a === void 0 ? void 0 : _a.disabled) || ((_b = this.optionsStore.input) === null || _b === void 0 ? void 0 : _b.disabled)) return; this.toggle(); }; setupServiceLocator(); this._eventEmitters = serviceLocator.locate(EventEmitters); this.optionsStore = serviceLocator.locate(OptionsStore); this.display = serviceLocator.locate(Display); this.dates = serviceLocator.locate(Dates); this.actions = serviceLocator.locate(Actions); if (!element) { Namespace.errorMessages.mustProvideElement(); } this.optionsStore.element = element; this._initializeOptions(options, DefaultOptions, true); this.optionsStore.viewDate.setLocale(this.optionsStore.options.localization.locale); this.optionsStore.unset = true; this._initializeInput(); this._initializeToggle(); if (this.optionsStore.options.display.inline) this.display.show(); this._eventEmitters.triggerEvent.subscribe((e) => { this._triggerEvent(e); }); this._eventEmitters.viewUpdate.subscribe(() => { this._viewUpdate(); }); } get viewDate() { return this.optionsStore.viewDate; } // noinspection JSUnusedGlobalSymbols /** * Update the picker options. If `reset` is provide `options` will be merged with DefaultOptions instead. * @param options * @param reset * @public */ updateOptions(options, reset = false) { if (reset) this._initializeOptions(options, DefaultOptions); else this._initializeOptions(options, this.optionsStore.options); this.display._rebuild(); } // noinspection JSUnusedGlobalSymbols /** * Toggles the picker open or closed. If the picker is disabled, nothing will happen. * @public */ toggle() { if (this._isDisabled) return; this.display.toggle(); } // noinspection JSUnusedGlobalSymbols /** * Shows the picker unless the picker is disabled. * @public */ show() { if (this._isDisabled) return; this.display.show(); } // noinspection JSUnusedGlobalSymbols /** * Hides the picker unless the picker is disabled. * @public */ hide() { this.display.hide(); } // noinspection JSUnusedGlobalSymbols /** * Disables the picker and the target input field. * @public */ disable() { var _a; this._isDisabled = true; // todo this might be undesired. If a dev disables the input field to // only allow using the picker, this will break that. (_a = this.optionsStore.input) === null || _a === void 0 ? void 0 : _a.setAttribute('disabled', 'disabled'); this.display.hide(); } // noinspection JSUnusedGlobalSymbols /** * Enables the picker and the target input field. * @public */ enable() { var _a; this._isDisabled = false; (_a = this.optionsStore.input) === null || _a === void 0 ? void 0 : _a.removeAttribute('disabled'); } // noinspection JSUnusedGlobalSymbols /** * Clears all the selected dates * @public */ clear() { this.optionsStore.input.value = ''; this.dates.clear(); } // noinspection JSUnusedGlobalSymbols /** * Allows for a direct subscription to picker events, without having to use addEventListener on the element. * @param eventTypes See Namespace.Events * @param callbacks Function to call when event is triggered * @public */ subscribe(eventTypes, callbacks) { if (typeof eventTypes === 'string') { eventTypes = [eventTypes]; } let callBackArray; if (!Array.isArray(callbacks)) { callBackArray = [callbacks]; } else { callBackArray = callbacks; } if (eventTypes.length !== callBackArray.length) { Namespace.errorMessages.subscribeMismatch(); } const returnArray = []; for (let i = 0; i < eventTypes.length; i++) { const eventType = eventTypes[i]; if (!Array.isArray(this._subscribers[eventType])) { this._subscribers[eventType] = []; } this._subscribers[eventType].push(callBackArray[i]); returnArray.push({ unsubscribe: this._unsubscribe.bind(this, eventType, this._subscribers[eventType].length - 1), }); if (eventTypes.length === 1) { return returnArray[0]; } } return returnArray; } // noinspection JSUnusedGlobalSymbols /** * Hides the picker and removes event listeners */ dispose() { var _a, _b, _c; this.display.hide(); // this will clear the document click event listener this.display._dispose(); (_a = this.optionsStore.input) === null || _a === void 0 ? void 0 : _a.removeEventListener('change', this._inputChangeEvent); if (this.optionsStore.options.allowInputToggle) { (_b = this.optionsStore.input) === null || _b === void 0 ? void 0 : _b.removeEventListener('click', this._toggleClickEvent); } (_c = this._toggle) === null || _c === void 0 ? void 0 : _c.removeEventListener('click', this._toggleClickEvent); this._subscribers = {}; } /** * Updates the options to use the provided language. * THe language file must be loaded first. * @param language */ locale(language) { let asked = loadedLocales[language]; if (!asked) return; this.updateOptions({ localization: asked, }); } /** * Triggers an event like ChangeEvent when the picker has updated the value * of a selected date. * @param event Accepts a BaseEvent object. * @private */ _triggerEvent(event) { var _a, _b; event.viewMode = this.optionsStore.currentView; const isChangeEvent = event.type === Namespace.events.change; if (isChangeEvent) { const { date, oldDate, isClear } = event; if ((date && oldDate && date.isSame(oldDate)) || (!isClear && !date && !oldDate)) { return; } this._handleAfterChangeEvent(event); (_a = this.optionsStore.input) === null || _a === void 0 ? void 0 : _a.dispatchEvent(new CustomEvent(event.type, { detail: event })); (_b = this.optionsStore.input) === null || _b === void 0 ? void 0 : _b.dispatchEvent(new CustomEvent('change', { detail: event })); } this.optionsStore.element.dispatchEvent(new CustomEvent(event.type, { detail: event })); if (window.jQuery) { const $ = window.jQuery; if (isChangeEvent && this.optionsStore.input) { $(this.optionsStore.input).trigger(event); } else { $(this.optionsStore.element).trigger(event); } } this._publish(event); } _publish(event) { // return if event is not subscribed if (!Array.isArray(this._subscribers[event.type])) { return; } // Trigger callback for each subscriber this._subscribers[event.type].forEach((callback) => { callback(event); }); } /** * Fires a ViewUpdate event when, for example, the month view is changed. * @private */ _viewUpdate() { this._triggerEvent({ type: Namespace.events.update, viewDate: this.optionsStore.viewDate.clone, }); } _unsubscribe(eventName, index) { this._subscribers[eventName].splice(index, 1); } /** * Merges two Option objects together and validates options type * @param config new Options * @param mergeTo Options to merge into * @param includeDataset When true, the elements data-td attributes will be included in the * @private */ _initializeOptions(config, mergeTo, includeDataset = false) { var _a, _b; let newConfig = OptionConverter.deepCopy(config); newConfig = OptionConverter._mergeOptions(newConfig, mergeTo); if (includeDataset) newConfig = OptionConverter._dataToOptions(this.optionsStore.element, newConfig); OptionConverter._validateConflicts(newConfig); newConfig.viewDate = newConfig.viewDate.setLocale(newConfig.localization.locale); if (!this.optionsStore.viewDate.isSame(newConfig.viewDate)) { this.optionsStore.viewDate = newConfig.viewDate; } /** * Sets the minimum view allowed by the picker. For example the case of only * allowing year and month to be selected but not date. */ if (newConfig.display.components.year) { this.optionsStore.minimumCalendarViewMode = 2; } if (newConfig.display.components.month) { this.optionsStore.minimumCalendarViewMode = 1; } if (newConfig.display.components.date) { this.optionsStore.minimumCalendarViewMode = 0; } this.optionsStore.currentCalendarViewMode = Math.max(this.optionsStore.minimumCalendarViewMode, this.optionsStore.currentCalendarViewMode); // Update view mode if needed if (CalendarModes[this.optionsStore.currentCalendarViewMode].name !== newConfig.display.viewMode) { this.optionsStore.currentCalendarViewMode = Math.max(CalendarModes.findIndex((x) => x.name === newConfig.display.viewMode), this.optionsStore.minimumCalendarViewMode); } if ((_a = this.display) === null || _a === void 0 ? void 0 : _a.isVisible) { this.display._update('all'); } if (newConfig.display.components.useTwentyfourHour === undefined) { newConfig.display.components.useTwentyfourHour = !!!((_b = newConfig.viewDate.parts()) === null || _b === void 0 ? void 0 : _b.dayPeriod); } this.optionsStore.options = newConfig; } /** * Checks if an input field is being used, attempts to locate one and sets an * event listener if found. * @private */ _initializeInput() { if (this.optionsStore.element.tagName == 'INPUT') { this.optionsStore.input = this.optionsStore.element; } else { let query = this.optionsStore.element.dataset.tdTargetInput; if (query == undefined || query == 'nearest') { this.optionsStore.input = this.optionsStore.element.querySelector('input'); } else { this.optionsStore.input = this.optionsStore.element.querySelector(query); } } if (!this.optionsStore.input) return; this.optionsStore.input.addEventListener('change', this._inputChangeEvent); if (this.optionsStore.options.allowInputToggle) { this.optionsStore.input.addEventListener('click', this._toggleClickEvent); } if (this.optionsStore.input.value) { this._inputChangeEvent(); } } /** * Attempts to locate a toggle for the picker and sets an event listener * @private */ _initializeToggle() { if (this.optionsStore.options.display.inline) return; let query = this.optionsStore.element.dataset.tdTargetToggle; if (query == 'nearest') { query = '[data-td-toggle="datetimepicker"]'; } this._toggle = query == undefined ? this.optionsStore.element : this.optionsStore.element.querySelector(query); this._toggle.addEventListener('click', this._toggleClickEvent); } /** * If the option is enabled this will render the clock view after a date pick. * @param e change event * @private */ _handleAfterChangeEvent(e) { var _a, _b; if ( // options is disabled !this.optionsStore.options.promptTimeOnDateChange || this.optionsStore.options.display.inline || this.optionsStore.options.display.sideBySide || // time is disabled !this.display._hasTime || ( // clock component is already showing (_a = this.display.widget) === null || _a === void 0 ? void 0 : _a.getElementsByClassName(Namespace.css.show)[0].classList.contains(Namespace.css.timeContainer))) return; // First time ever. If useCurrent option is set to true (default), do nothing // because the first date is selected automatically. // or date didn't change (time did) or date changed because time did. if ((!e.oldDate && this.optionsStore.options.useCurrent) || (e.oldDate && ((_b = e.date) === null || _b === void 0 ? void 0 : _b.isSame(e.oldDate)))) { return; } clearTimeout(this._currentPromptTimeTimeout); this._currentPromptTimeTimeout = setTimeout(() => { if (this.display.widget) { this._eventEmitters.action.emit({ e: { currentTarget: this.display.widget.querySelector(`.${Namespace.css.switch} div`), }, action: ActionTypes$1.togglePicker, }); } }, this.optionsStore.options.promptTimeOnDateChangeTransitionDelay); } } /** * Whenever a locale is loaded via a plugin then store it here based on the * locale name. E.g. loadedLocales['ru'] */ const loadedLocales = {}; // noinspection JSUnusedGlobalSymbols /** * Called from a locale plugin. * @param l locale object for localization options */ const loadLocale = (l) => { if (loadedLocales[l.name]) return; loadedLocales[l.name] = l.localization; }; /** * A sets the global localization options to the provided locale name. * `loadLocale` MUST be called first. * @param l */ const locale = (l) => { let asked = loadedLocales[l]; if (!asked) return; DefaultOptions.localization = asked; }; // noinspection JSUnusedGlobalSymbols /** * Called from a plugin to extend or override picker defaults. * @param plugin * @param option */ const extend = function (plugin, option) { if (!plugin) return tempusDominus; if (!plugin.installed) { // install plugin only once plugin(option, { TempusDominus, Dates, Display, DateTime, ErrorMessages }, tempusDominus); plugin.installed = true; } return tempusDominus; }; const version = '6.0.1'; const tempusDominus = { TempusDominus, extend, loadLocale, locale, Namespace, DefaultOptions, DateTime, Unit, version }; return tempusDominus; })); //# sourceMappingURL=tempus-dominus.js.map
| ver. 1.4 |
Github
|
.
| PHP 8.2.27 | Генераци� �траницы: 0 |
proxy
|
phpinfo
|
�а�тройка