UNPKG

novo-elements

Version:

1,419 lines (1,411 loc) 545 kB
import { isDate, format, addDays, addWeeks, addMonths, startOfMinute, startOfDay, startOfWeek, startOfMonth, endOfDay, endOfWeek, endOfMonth, isSameDay, isSameMonth, isSameSecond, differenceInSeconds, differenceInCalendarDays, differenceInDays, isWithinInterval, getMonth, getYear, setMinutes, setHours, isBefore, isAfter, addMinutes, getDay, differenceInMinutes, addHours } from 'date-fns'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import * as i0 from '@angular/core'; import { TemplateRef, isDevMode, EventEmitter, Injectable } from '@angular/core'; var AppBridgeHandler; (function (AppBridgeHandler) { AppBridgeHandler[AppBridgeHandler["HTTP"] = 0] = "HTTP"; AppBridgeHandler[AppBridgeHandler["OPEN"] = 1] = "OPEN"; AppBridgeHandler[AppBridgeHandler["OPEN_LIST"] = 2] = "OPEN_LIST"; AppBridgeHandler[AppBridgeHandler["CLOSE"] = 3] = "CLOSE"; AppBridgeHandler[AppBridgeHandler["REFRESH"] = 4] = "REFRESH"; AppBridgeHandler[AppBridgeHandler["PIN"] = 5] = "PIN"; AppBridgeHandler[AppBridgeHandler["REGISTER"] = 6] = "REGISTER"; AppBridgeHandler[AppBridgeHandler["UPDATE"] = 7] = "UPDATE"; AppBridgeHandler[AppBridgeHandler["REQUEST_DATA"] = 8] = "REQUEST_DATA"; AppBridgeHandler[AppBridgeHandler["CALLBACK"] = 9] = "CALLBACK"; AppBridgeHandler[AppBridgeHandler["PING"] = 10] = "PING"; })(AppBridgeHandler || (AppBridgeHandler = {})); const HTTP_VERBS = { GET: 'get', POST: 'post', PUT: 'put', DELETE: 'delete', }; const MESSAGE_TYPES = { REGISTER: 'register', OPEN: 'open', OPEN_LIST: 'openList', CLOSE: 'close', REFRESH: 'refresh', PIN: 'pin', PING: 'ping', UPDATE: 'update', HTTP_GET: 'httpGET', HTTP_POST: 'httpPOST', HTTP_PUT: 'httpPUT', HTTP_DELETE: 'httpDELETE', CUSTOM_EVENT: 'customEvent', REQUEST_DATA: 'requestData', CALLBACK: 'callback', }; class AppBridgeService { create(name) { return new AppBridge(name); } } class DevAppBridgeService { constructor(http) { this.http = http; } create(name, postRobotRef) { return new DevAppBridge(name, this.http, postRobotRef); } } // remove attributes unsafe for postrobot.send() function cleanPacket(packet) { if (packet && typeof packet === 'object' && 'source' in packet) { delete packet.source; } } class AppBridge { // Type? constructor(traceName = 'AppBridge', postRobotRef) { this.id = `${Date.now()}`; this._registeredFrames = []; this._handlers = {}; this._tracing = false; this._eventListeners = {}; this.traceName = traceName; this.postRobot = postRobotRef || /* global */ postRobot; if (this.postRobot) { this.postRobot.CONFIG.LOG_LEVEL = 'error'; try { this._setupHandlers(); } catch (error) { // No op } } } set tracing(tracing) { this._tracing = tracing; } handle(type, handler) { this._handlers[type] = handler; } _trace(eventType, event) { if (this._tracing) { console.info(`[${this.traceName || this.id}] "${eventType}"`, event); } } _setupHandlers() { // map an object for all handlers, so that we can run some other actions before each of them const defaultMsgHandlers = { // Register [MESSAGE_TYPES.REGISTER]: async (event) => { this._registeredFrames.push(event); const windowName = await this.register(event.data); return { windowName }; }, // Update [MESSAGE_TYPES.UPDATE]: (event) => { return this.update(event.data).then((success) => { return { success }; }); }, // Open [MESSAGE_TYPES.OPEN]: (event) => { return this.open(event.data).then((success) => { return { success }; }); }, [MESSAGE_TYPES.OPEN_LIST]: (event) => { return this.openList(event.data).then((success) => { return { success }; }); }, // Close [MESSAGE_TYPES.CLOSE]: (event) => { const index = this._registeredFrames.findIndex((frame) => frame.data.id === event.data.id); if (index !== -1) { this._registeredFrames.splice(index, 1); } return this.close(event.data).then(success => ({ success })); }, // Refresh [MESSAGE_TYPES.REFRESH]: (event) => { return this.refresh(event.data).then((success) => { return { success }; }); }, // PIN [MESSAGE_TYPES.PIN]: (event) => { return this.pin(event.data).then((success) => { return { success }; }); }, // PING [MESSAGE_TYPES.PING]: (event) => { return this.httpGET('ping', undefined, event.data.origin).then((result) => { return { data: result.data, error: result.error }; }); }, // REQUEST_DATA [MESSAGE_TYPES.REQUEST_DATA]: (event) => { return this.requestData(event.data).then((result) => { return { data: result.data, error: result.error }; }); }, // CALLBACKS [MESSAGE_TYPES.CALLBACK]: (event) => { return this.callback(event.data).then((success) => { return { success }; }); }, // HTTP-GET [MESSAGE_TYPES.HTTP_GET]: (event) => { return this.httpGET(event.data.relativeURL, undefined, event.data.origin).then((result) => { return { data: result.data, error: result.error }; }); }, // HTTP-POST [MESSAGE_TYPES.HTTP_POST]: (event) => { return this.httpPOST(event.data.relativeURL, event.data.data, undefined, event.data.origin).then((result) => { return { data: result.data, error: result.error }; }); }, // HTTP-PUT [MESSAGE_TYPES.HTTP_PUT]: (event) => { return this.httpPUT(event.data.relativeURL, event.data.data, undefined, event.data.origin).then((result) => { return { data: result.data, error: result.error }; }); }, // HTTP-DELETE [MESSAGE_TYPES.HTTP_DELETE]: (event) => { return this.httpDELETE(event.data.relativeURL, undefined, event.data.origin).then((result) => { return { data: result.data, error: result.error }; }); }, // Custom Events [MESSAGE_TYPES.CUSTOM_EVENT]: async (event) => { if (this._eventListeners[event.data.event]) { this._eventListeners[event.data.event].forEach((listener) => { listener(event.data.data); }); } if (this._registeredFrames.length > 0) { cleanPacket(event.data); this._registeredFrames.forEach((frame) => { // TODO: Should this make sure it doesn't echo the custom event back to the author? this.postRobot.send(frame.source, MESSAGE_TYPES.CUSTOM_EVENT, event.data); }); } }, }; Object.keys(defaultMsgHandlers).forEach(msgType => { this.postRobot.on(msgType, event => { this._trace(msgType, event); const origin = Array.isArray(event.data.origin) ? event.data.origin : []; if (event.origin !== this.windowOrigin()) { origin.unshift(event.origin); } else if (origin.indexOf(event.data.originTraceName) === -1) { origin.unshift(event.data.originTraceName); } if (event.data) { // known gap - simple commands will not forward origin/source from subchild frames to parent event.data.origin = origin; event.data.source = event.source; } return defaultMsgHandlers[msgType](event); }); }); } windowOrigin() { return window.location.origin; } handleMessage({ msgType, handler, packet, echoPacket, resolveEventData }) { let returnPromise; if (this._handlers[handler]) { // Should be directly returning a promise. However, as a fallback, provide callback arguments let callbackSuccess; let callbackFail; returnPromise = new Promise((s, f) => { callbackSuccess = s; callbackFail = f; }); const handlerResult = this._handlers[handler](packet, callbackArg => { if (callbackArg) { callbackSuccess(true); } else { callbackFail(false); } }); if (handlerResult && 'then' in handlerResult) { returnPromise = handlerResult; } return returnPromise.then(result => true, () => false); } else { return this.postRobot.sendToParent(msgType, echoPacket || packet); } } /** * Fires or responds to an open event * @param packet any - packet of data to send with the open event */ open(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.OPEN]) { this._handlers[AppBridgeHandler.OPEN](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { cleanPacket(packet); Object.assign(packet, { id: this.id, windowName: this.windowName }); this.postRobot .sendToParent(MESSAGE_TYPES.OPEN, packet) .then((event) => { this._trace(`${MESSAGE_TYPES.OPEN} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires or responds to an openList event * @param packet any - packet of data to send with the open event */ openList(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.OPEN_LIST]) { this._handlers[AppBridgeHandler.OPEN_LIST](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { const openListPacket = {}; Object.assign(openListPacket, { type: 'List', entityType: packet.type, keywords: packet.keywords, criteria: packet.criteria }); this.postRobot .sendToParent(MESSAGE_TYPES.OPEN_LIST, packet) .then((event) => { this._trace(`${MESSAGE_TYPES.OPEN_LIST} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires or responds to an close event * @param packet any - packet of data to send with the close event */ update(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.UPDATE]) { this._handlers[AppBridgeHandler.UPDATE](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { cleanPacket(packet); Object.assign(packet, { id: this.id, windowName: this.windowName }); this.postRobot .sendToParent(MESSAGE_TYPES.UPDATE, packet) .then((event) => { this._trace(`${MESSAGE_TYPES.UPDATE} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires or responds to an close event */ close(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.CLOSE]) { this._handlers[AppBridgeHandler.CLOSE](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { if (packet) { console.info('[AppBridge] - close(packet) is deprecated! Please just use close()!'); } const realPacket = { id: this.id, windowName: this.windowName }; this.postRobot .sendToParent(MESSAGE_TYPES.CLOSE, realPacket) .then((event) => { this._trace(`${MESSAGE_TYPES.CLOSE} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires or responds to an close event */ refresh(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.REFRESH]) { this._handlers[AppBridgeHandler.REFRESH](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { if (packet) { console.info('[AppBridge] - refresh(packet) is deprecated! Please just use refresh()!'); } const realPacket = { id: this.id, windowName: this.windowName }; this.postRobot .sendToParent(MESSAGE_TYPES.REFRESH, realPacket) .then((event) => { this._trace(`${MESSAGE_TYPES.REFRESH} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } ping() { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.PING]) { this._handlers[AppBridgeHandler.PING]({}, (data, error) => { resolve({ data, error }); }); } else { this.postRobot .sendToParent(MESSAGE_TYPES.PING, {}) .then((event) => { resolve({ data: event.data.data, error: event.data.error }); }) .catch((err) => { reject(null); }); } }); } /** * Fires or responds to a pin event */ pin(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.PIN]) { this._handlers[AppBridgeHandler.PIN](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { if (packet) { console.info('[AppBridge] - pin(packet) is deprecated! Please just use pin()!'); } const realPacket = { id: this.id, windowName: this.windowName }; this.postRobot .sendToParent(MESSAGE_TYPES.PIN, realPacket) .then((event) => { this._trace(`${MESSAGE_TYPES.PIN} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires or responds to a requestData event * @param packet any - packet of data to send with the requestData event */ requestData(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.REQUEST_DATA]) { this._handlers[AppBridgeHandler.REQUEST_DATA](packet, (data) => { if (data) { resolve({ data }); } else { reject(false); } }); } else { cleanPacket(packet); Object.assign(packet, { id: this.id, windowName: this.windowName }); this.postRobot .sendToParent(MESSAGE_TYPES.REQUEST_DATA, packet) .then((event) => { this._trace(`${MESSAGE_TYPES.REQUEST_DATA} (callback)`, event); if (event.data) { resolve({ data: event.data.data }); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires a generic callback command * @param packet string - key: string, generic: boolean */ callback(packet) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.CALLBACK]) { this._handlers[AppBridgeHandler.CALLBACK](packet, (success) => { if (success) { resolve(true); } else { reject(false); } }); } else { cleanPacket(packet); Object.assign(packet, { id: this.id, windowName: this.windowName }); this.postRobot .sendToParent(MESSAGE_TYPES.CALLBACK, packet) .then((event) => { this._trace(`${MESSAGE_TYPES.CALLBACK} (callback)`, event); if (event.data) { resolve(true); } else { reject(false); } }) .catch((err) => { reject(false); }); } }); } /** * Fires or responds to an register event * @param packet any - packet of data to send with the event */ register(packet = {}) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.REGISTER]) { this._handlers[AppBridgeHandler.REGISTER](packet, (windowName) => { if (windowName) { resolve(windowName); } else { resolve(null); } }); } else { cleanPacket(packet); Object.assign(packet, { id: this.id }); this.postRobot .sendToParent(MESSAGE_TYPES.REGISTER, packet) .then((event) => { this._trace(`${MESSAGE_TYPES.REGISTER} (callback)`, event); if (event.data) { this.windowName = event.data.windowName; resolve(event.data.windowName); } else { resolve(null); } }) .catch((err) => { this._trace(`${MESSAGE_TYPES.REGISTER} - FAILED - (no parent)`, err); reject(err); }); } }); } /** * Fires or responds to an HTTP_GET event * @param relativeURL string - URL to fetch, relative to the mainframe URL * @param timeout - how long to attempt the request before reporting an error * @param originStack - the domain of the previous frame(s) the request originated from */ httpGET(relativeURL, timeout = 10000, originStack) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.HTTP]) { this._handlers[AppBridgeHandler.HTTP]({ verb: HTTP_VERBS.GET, relativeURL, origin: originStack || [this.traceName] }, (data, error) => { resolve({ data, error }); }); } else { this.postRobot .sendToParent(MESSAGE_TYPES.HTTP_GET, { relativeURL, origin: originStack, originTraceName: this.traceName }, { timeout }) .then((event) => { resolve({ data: event.data.data, error: event.data.error }); }) .catch((err) => { reject(null); }); } }); } /** * Fires or responds to an HTTP_POST event * @param relativeURL string - URL to fetch, relative to the mainframe URL * @param postData any - packet of data to send with the event * @param timeout - how long to attempt the request before reporting an error * @param originStack - the domain of the previous frame(s) the request originated from */ httpPOST(relativeURL, postData, timeout = 10000, originStack) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.HTTP]) { this._handlers[AppBridgeHandler.HTTP]({ verb: HTTP_VERBS.POST, relativeURL, data: postData, origin: originStack || [this.traceName] }, (data, error) => { resolve({ data, error }); }); } else { this.postRobot .sendToParent(MESSAGE_TYPES.HTTP_POST, { relativeURL, data: postData, origin: originStack, originTraceName: this.traceName }, { timeout }) .then((event) => { resolve({ data: event.data.data, error: event.data.error }); }) .catch((err) => { reject(null); }); } }); } /** * Fires or responds to an HTTP_PUT event * @param relativeURL string - URL to fetch, relative to the mainframe URL * @param packet any - packet of data to send with the event * @param timeout - how long to attempt the request before reporting an error * @param originStack - the domain of the previous frame(s) the request originated from */ httpPUT(relativeURL, putData, timeout = 10000, originStack) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.HTTP]) { this._handlers[AppBridgeHandler.HTTP]({ verb: HTTP_VERBS.PUT, relativeURL, data: putData, origin: originStack || [this.traceName] }, (data, error) => { resolve({ data, error }); }); } else { this.postRobot .sendToParent(MESSAGE_TYPES.HTTP_PUT, { relativeURL, data: putData, origin: originStack, originTraceName: this.traceName }, { timeout }) .then((event) => { resolve({ data: event.data.data, error: event.data.error }); }) .catch((err) => { reject(null); }); } }); } /** * Fires or responds to an HTTP_DELETE event * @param relativeURL string - URL to fetch, relative to the mainframe URL * @param timeout - how long to attempt the request before reporting an error * @param originStack - the domain of the previous frame(s) the request originated from */ httpDELETE(relativeURL, timeout = 10000, originStack) { return new Promise((resolve, reject) => { if (this._handlers[AppBridgeHandler.HTTP]) { this._handlers[AppBridgeHandler.HTTP]({ verb: HTTP_VERBS.DELETE, relativeURL, origin: originStack || [this.traceName] }, (data, error) => { resolve({ data, error }); }); } else { this.postRobot .sendToParent(MESSAGE_TYPES.HTTP_DELETE, { relativeURL, origin: originStack, originTraceName: this.traceName }, { timeout }) .then((event) => { resolve({ data: event.data.data, error: event.data.error }); }) .catch((err) => { reject(null); }); } }); } /** * Fires a custom event to anywhere in the application * @param event string - event name to fire * @param data any - data to be sent along with the event */ fireEvent(event, data) { return new Promise((resolve, reject) => { this.postRobot .sendToParent(MESSAGE_TYPES.CUSTOM_EVENT, { event, data }) .then((e) => { resolve(e); }) .catch((err) => { reject(null); }); }); } /** * Fires a custom event to all registered frames * @param event string - event name to fire * @param data any - data to be sent along with the event */ fireEventToChildren(event, data) { if (this._registeredFrames.length > 0) { this._registeredFrames.forEach((frame) => { this.postRobot.send(frame.source, MESSAGE_TYPES.CUSTOM_EVENT, { event, eventType: event, data, }); }); } } /** * Fires a custom event to specified frames * @param source Window - specific iframe contentWindow * @param event string - event name to fire * @param data any - data to be sent along with the event */ fireEventToChild(source, event, data) { if (source instanceof HTMLIFrameElement) { source = source.contentWindow; } this.postRobot.send(source, MESSAGE_TYPES.CUSTOM_EVENT, { event, data }); } /** * Adds an event listener to a custom event * @param event string - event name to listen to * @param callback function - callback to be fired when an event is caught */ addEventListener(event, callback) { if (!this._eventListeners[event]) { this._eventListeners[event] = []; } this._eventListeners[event].push(callback); } } class DevAppBridge extends AppBridge { constructor(traceName = 'DevAppBridge', http, postRobotRef) { super(traceName, postRobotRef); this.http = http; const cookie = this.getCookie('UlEncodedIdentity'); if (cookie && cookie.length) { const identity = JSON.parse(decodeURIComponent(cookie)); const endpoints = identity.sessions.reduce((obj, session) => { obj[session.name] = session.value.endpoint; return obj; }, {}); this.baseURL = endpoints.rest; } } _setupHandlers() { } /** * Fires or responds to an HTTP_GET event */ httpGET(relativeURL) { return this.http.get(`${this.baseURL}/${relativeURL}`, { withCredentials: true }).toPromise(); } /** * Fires or responds to an HTTP_POST event */ httpPOST(relativeURL, postData) { return this.http.post(`${this.baseURL}/${relativeURL}`, postData, { withCredentials: true }).toPromise(); } /** * Fires or responds to an HTTP_PUT event */ httpPUT(relativeURL, putData) { return this.http.put(`${this.baseURL}/${relativeURL}`, putData, { withCredentials: true }).toPromise(); } /** * Fires or responds to an HTTP_DELETE event */ httpDELETE(relativeURL) { return this.http.delete(`${this.baseURL}/${relativeURL}`, { withCredentials: true }).toPromise(); } getCookie(cname) { if (document) { const name = `${cname}=`; const ca = document.cookie.split(';'); for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === ' ') { c = c.substring(1); } if (c.indexOf(name) === 0) { return c.substring(name.length, c.length); } } } return false; } } class BaseRenderer { constructor() { this._data = {}; this._value = ''; this.meta = {}; } get data() { return this._data; } set data(d) { this._data = d; } get value() { return this._value; } set value(v) { this._value = v; } } /** * Copyright © 2022 Sasha Koss * https://www.npmjs.com/package/@date-fns/upgrade **/ const tokensMap = { // 'D MMMM': '', // 'Do MMMM': '', // 'DD MMMM': '', M: 'L', Mo: 'Mo', MM: 'LL', MMM: 'LLL', MMMM: 'LLLL', Q: 'q', Qo: 'qo', D: 'd', Do: 'do', DD: 'dd', DDD: 'D', DDDo: 'Do', DDDD: 'DDD', d: 'i', do: 'io', dd: 'iiiiii', ddd: 'iii', dddd: 'iiii', A: 'a', a: 'a', aa: 'aaaa', E: 'i', W: 'I', Wo: 'Io', WW: 'II', YY: 'yy', YYYY: 'yyyy', GG: 'RR', GGGG: 'RRRR', H: 'H', HH: 'HH', h: 'h', hh: 'hh', m: 'm', mm: 'mm', s: 's', ss: 'ss', S: 'S', SS: 'SS', SSS: 'SSS', Z: 'xxx', ZZ: 'xx', X: 't', x: 'T', }; const v1tokens = Object.keys(tokensMap) .sort() .reverse(); const tokensRegExp = new RegExp('(\\[[^\\[]*\\])|(\\\\)?' + '(' + v1tokens.join('|') + '|.)', 'g'); function convertTokens(format) { const tokensCaptures = format.match(tokensRegExp); if (tokensCaptures) { return tokensCaptures .reduce((acc, tokenString, index) => { const v2token = tokensMap[tokenString]; if (!v2token) { const escapedCaptures = tokenString.match(/^\[(.+)\]$/); if (escapedCaptures) { acc.textBuffer.push(escapedCaptures[1]); } else { acc.textBuffer.push(tokenString); } } const endOfString = index === tokensCaptures.length - 1; if (acc.textBuffer.length && (v2token || endOfString)) { acc.formatBuffer.push(`'${acc.textBuffer.join('')}'`); acc.textBuffer = []; } if (v2token) acc.formatBuffer.push(v2token); // eslint-disable-line return acc; }, { formatBuffer: [], textBuffer: [] }) .formatBuffer.join(''); } else { return format; } } /** * Copyright © 2022 Sasha Koss * https://www.npmjs.com/package/@date-fns/upgrade **/ const MILLISECONDS_IN_HOUR = 3600000; const MILLISECONDS_IN_MINUTE = 60000; const DEFAULT_ADDITIONAL_DIGITS = 2; const parseTokenDateTimeDelimeter = /[T ]/; const parseTokenPlainTime = /:/; // year tokens const parseTokenYY = /^(\d{2})$/; const parseTokensYYY = [ /^([+-]\d{2})$/, // 0 additional digits /^([+-]\d{3})$/, // 1 additional digit /^([+-]\d{4})$/, // 2 additional digits ]; const parseTokenYYYY = /^(\d{4})/; const parseTokensYYYYY = [ /^([+-]\d{4})/, // 0 additional digits /^([+-]\d{5})/, // 1 additional digit /^([+-]\d{6})/, // 2 additional digits ]; // date tokens const parseTokenMM = /^-(\d{2})$/; const parseTokenDDD = /^-?(\d{3})$/; const parseTokenMMDD = /^-?(\d{2})-?(\d{2})$/; const parseTokenWww = /^-?W(\d{2})$/; const parseTokenWwwD = /^-?W(\d{2})-?(\d{1})$/; // time tokens const parseTokenHH = /^(\d{2}([.,]\d*)?)$/; const parseTokenHHMM = /^(\d{2}):?(\d{2}([.,]\d*)?)$/; const parseTokenHHMMSS = /^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/; // timezone tokens const parseTokenTimezone = /([Z+-].*)$/; const parseTokenTimezoneZ = /^(Z)$/; const parseTokenTimezoneHH = /^([+-])(\d{2})$/; const parseTokenTimezoneHHMM = /^([+-])(\d{2}):?(\d{2})$/; function legacyParse(argument, options = {}) { if (isDate(argument)) { // Prevent the date to lose the milliseconds when passed to new Date() in IE10 return new Date(argument.getTime()); } else if (typeof argument !== 'string') { return new Date(argument); } const additionalDigits = options.additionalDigits == null ? DEFAULT_ADDITIONAL_DIGITS : Number(options.additionalDigits); const dateStrings = splitDateString(argument); const parseYearResult = parseYear(dateStrings.date || '', additionalDigits); const year = parseYearResult.year; const restDateString = parseYearResult.restDateString; const date = parseDate(restDateString || '', year); if (date) { const timestamp = date.getTime(); let time = 0; let offset; if (dateStrings.time) { time = parseTime(dateStrings.time) || 0; } if (dateStrings.timezone) { offset = parseTimezone(dateStrings.timezone) * MILLISECONDS_IN_MINUTE; } else { const fullTime = timestamp + time; const fullTimeDate = new Date(fullTime); offset = getTimezoneOffsetInMilliseconds(fullTimeDate); // Adjust time when it's coming from DST const fullTimeDateNextDay = new Date(fullTime); fullTimeDateNextDay.setDate(fullTimeDate.getDate() + 1); const offsetDiff = getTimezoneOffsetInMilliseconds(fullTimeDateNextDay) - getTimezoneOffsetInMilliseconds(fullTimeDate); if (offsetDiff > 0) { offset += offsetDiff; } } return new Date(timestamp + time + offset); } else { return new Date(argument); } } function splitDateString(dateString) { const array = dateString.split(parseTokenDateTimeDelimeter); let timeString; let date; let time; let timezone; if (parseTokenPlainTime.test(array[0])) { date = undefined; timeString = array[0]; } else { date = array[0]; timeString = array[1]; } if (timeString) { const token = parseTokenTimezone.exec(timeString); if (token) { time = timeString.replace(token[1], ''); timezone = token[1]; } else { time = timeString; } } return { date, time, timezone, }; } function parseYear(dateString, additionalDigits) { const parseTokenYYY = parseTokensYYY[additionalDigits]; const parseTokenYYYYY = parseTokensYYYYY[additionalDigits]; let token; // YYYY or ±YYYYY token = parseTokenYYYY.exec(dateString) || parseTokenYYYYY.exec(dateString); if (token) { const yearString = token[1]; return { year: parseInt(yearString, 10), restDateString: dateString.slice(yearString.length), }; } // YY or ±YYY token = parseTokenYY.exec(dateString) || parseTokenYYY.exec(dateString); if (token) { const centuryString = token[1]; return { year: parseInt(centuryString, 10) * 100, restDateString: dateString.slice(centuryString.length), }; } // Invalid ISO-formatted year return { year: null, }; } function parseDate(dateString, year) { // Invalid ISO-formatted year if (year === null) { return null; } let token; let date; let month; let week; // YYYY if (dateString.length === 0) { date = new Date(0); date.setUTCFullYear(year); return date; } // YYYY-MM token = parseTokenMM.exec(dateString); if (token) { date = new Date(0); month = parseInt(token[1], 10) - 1; date.setUTCFullYear(year, month); return date; } // YYYY-DDD or YYYYDDD token = parseTokenDDD.exec(dateString); if (token) { date = new Date(0); const dayOfYear = parseInt(token[1], 10); date.setUTCFullYear(year, 0, dayOfYear); return date; } // YYYY-MM-DD or YYYYMMDD token = parseTokenMMDD.exec(dateString); if (token) { date = new Date(0); month = parseInt(token[1], 10) - 1; const day = parseInt(token[2], 10); date.setUTCFullYear(year, month, day); return date; } // YYYY-Www or YYYYWww token = parseTokenWww.exec(dateString); if (token) { week = parseInt(token[1], 10) - 1; return dayOfISOYear(year, week); } // YYYY-Www-D or YYYYWwwD token = parseTokenWwwD.exec(dateString); if (token) { week = parseInt(token[1], 10) - 1; const dayOfWeek = parseInt(token[2], 10) - 1; return dayOfISOYear(year, week, dayOfWeek); } // Invalid ISO-formatted date return null; } function parseTime(timeString) { let token; let hours; let minutes; // hh token = parseTokenHH.exec(timeString); if (token) { hours = parseFloat(token[1].replace(',', '.')); return (hours % 24) * MILLISECONDS_IN_HOUR; } // hh:mm or hhmm token = parseTokenHHMM.exec(timeString); if (token) { hours = parseInt(token[1], 10); minutes = parseFloat(token[2].replace(',', '.')); return ((hours % 24) * MILLISECONDS_IN_HOUR + minutes * MILLISECONDS_IN_MINUTE); } // hh:mm:ss or hhmmss token = parseTokenHHMMSS.exec(timeString); if (token) { hours = parseInt(token[1], 10); minutes = parseInt(token[2], 10); const seconds = parseFloat(token[3].replace(',', '.')); return ((hours % 24) * MILLISECONDS_IN_HOUR + minutes * MILLISECONDS_IN_MINUTE + seconds * 1000); } // Invalid ISO-formatted time return null; } function parseTimezone(timezoneString) { let token; let absoluteOffset; // Z token = parseTokenTimezoneZ.exec(timezoneString); if (token) { return 0; } // ±hh token = parseTokenTimezoneHH.exec(timezoneString); if (token) { absoluteOffset = parseInt(token[2], 10) * 60; return token[1] === '+' ? -absoluteOffset : absoluteOffset; } // ±hh:mm or ±hhmm token = parseTokenTimezoneHHMM.exec(timezoneString); if (token) { absoluteOffset = parseInt(token[2], 10) * 60 + parseInt(token[3], 10); return token[1] === '+' ? -absoluteOffset : absoluteOffset; } return 0; } function dayOfISOYear(isoYear, week = 0, day = 0) { const date = new Date(0); date.setUTCFullYear(isoYear, 0, 4); const fourthOfJanuaryDay = date.getUTCDay() || 7; const diff = week * 7 + day + 1 - fourthOfJanuaryDay; date.setUTCDate(date.getUTCDate() + diff); return date; } function getTimezoneOffsetInMilliseconds(dirtyDate) { const date = new Date(dirtyDate.getTime()); const baseTimezoneOffset = date.getTimezoneOffset(); date.setSeconds(0, 0); const millisecondsPartOfTimezoneOffset = date.getTime() % MILLISECONDS_IN_MINUTE; return (baseTimezoneOffset * MILLISECONDS_IN_MINUTE + millisecondsPartOfTimezoneOffset); } /** * This DateUtil is a wrapper for calling new date-fns v2 functions with existing legacy * v1 function calls without having to refactor too much code and potentially introduce * breaking changes. * * The old calls generally called date-fns functions with loosely-typed date values, often * of type DateLike (Date | string | number). This was a problem when upgrading to date-fns * v2 since functions are now typed more strongly and no longer accept strings. * * If you are adding a new component/feature and looking here to add a new date-fns wrapper * function, strongly consider not doing that and instead refactoring your code to not use * DateLike, and calling the date-fns function(s) directly. **/ class DateUtil { static getDateFromAnyType(date) { return legacyParse(date); } static getWeekDayFromNumber(weekDay) { if (0 <= weekDay && weekDay <= 6) { return weekDay; } else { console.warn('Invalid weekDay value:', weekDay); return 0; } } static parse(date, options) { if (options?.userDateFormat && typeof date === 'string' && date.trim()) { date = this.rewireDatePositionsToMDY(date, options.userDateFormat); delete options.userDateFormat; } return legacyParse(date, options); } static format(date, formatString) { if (!date) { return ''; } date = this.getDateFromAnyType(date); formatString = convertTokens(formatString); return format(date, formatString); } static addDays(date, days) { date = this.getDateFromAnyType(date); return addDays(date, days); } static addWeeks(date, weeks) { date = this.getDateFromAnyType(date); return addWeeks(date, weeks); } static addMonths(date, months) { date = this.getDateFromAnyType(date); return addMonths(date, months); } static startOfMinute(date) { date = this.getDateFromAnyType(date); return startOfMinute(date); } static startOfDay(date) { date = this.getDateFromAnyType(date); return startOfDay(date); } static startOfWeek(date, options) { date = this.getDateFromAnyType(date); if (options?.weekStartsOn) { options.weekStartsOn = this.getWeekDayFromNumber(options.weekStartsOn); } return startOfWeek(date, options); } static startOfMonth(date) { date = this.getDateFromAnyType(date); return startOfMonth(date); } static endOfDay(date) { date = this.getDateFromAnyType(date); return endOfDay(date); } static endOfWeek(date, options) { date = this.getDateFromAnyType(date); if (options?.weekStartsOn) { options.weekStartsOn = this.getWeekDayFromNumber(options.weekStartsOn); } return endOfWeek(date, options); } static endOfMonth(date) { date = this.getDateFromAnyType(date); return endOfMonth(date); } static isSameDay(dateLeft, dateRight) { dateLeft = this.getDateFromAnyType(dateLeft); dateRight = this.getDateFromAnyType(dateRight); return isSameDay(dateLeft, dateRight); } static isSameMonth(dateLeft, dateRight) { dateLeft = this.getDateFromAnyType(dateLeft); dateRight = this.getDateFromAnyType(dateRight); return isSameMonth(dateLeft, dateRight); } static isSameSecond(dateLeft, dateRight) { dateLeft = this.getDateFromAnyType(dateLeft); dateRight = this.getDateFromAnyType(dateRight); return isSameSecond(dateLeft, dateRight); } static differenceInSeconds(date, start) { date = this.getDateFromAnyType(date); start = this.getDateFromAnyType(start); return differenceInSeconds(date, start); } static differenceInCalendarDays(date, start) { date = this.getDateFromAnyType(date); start = this.getDateFromAnyType(start); return differenceInCalendarDays(date, start); } static differenceInDays(date, start) { date = this.getDateFromAnyType(date); start = this.getDateFromAnyType(start); return differenceInDays(date, start); } static isWithinRange(date, start, end) { date = this.getDateFromAnyType(date); const interval = { start: this.getDateFromAnyType(start), end: this.getDateFromAnyType(end), }; /** * Need extra error handling here to retain backwards compatibility because the new * isWithinInterval replacement function throws an error for Invalid Dates and Invalid * Intervals instead of returning true or false. **/ try { return isWithinInterval(date, interval); } catch (e) { console.warn(e.toString()); return false; } } static getMonth(date) { date = this.getDateFromAnyType(date); return getMonth(date); } static getYear(date) { date = this.getDateFromAnyType(date); return getYear(date); } static setMinutes(date, minutes) { date = this.getDateFromAnyType(date); return setMinutes(date, minutes); } static setHours(date, hours) { date = this.getDateFromAnyType(date); return setHours(date, hours); } static isBefore(date, minDate) { date = this.getDateFromAnyType(date); return isBefore(date, minDate); } static isAfter(date, maxDate) { date = this.getDateFromAnyType(date); return isAfter(date, maxDate); } static rewireDatePositionsToMDY(dateStr, userDateFormat) { userDateFormat = userDateFormat.toUpperCase(); if (userDateFormat === 'MM/DD/YYYY') { return dateStr; } const separatorPattern = /[.\\/-]/; // Creates an order, eg ['D', 'M', 'Y'] const separatorChar = separatorPattern.exec(userDateFormat)[0]; const tokenOrder = userDateFormat.split(separatorPattern).map(chars => chars[0]); if (tokenOrder.length < 3) { // Could not determine date token order from localized date format return dateStr; } const tokenSourceOrder = ['M', 'D', 'Y'].map(token => tokenOrder.indexOf(token)); const dateTimeSplit = dateStr.split(', '); const datePortions = dateTimeSplit[0].split(separatorPattern); if (datePortions.length < 3) { // Could not retrieve M/D/Y from localized date format return dateStr; } let convertedDateStr = tokenSourceOrder.map(index => datePortions[index]).join(separatorChar); if (dateTimeSplit.length > 1) { convertedDateStr += ', ' + dateTimeSplit.slice(1).join(', '); } return convertedDateStr; } } class DateRange { constructor( /** The start date of the range. */ start, /** The end date of the range. */ end) { this.start = start; this.end = end; } } /* eslint-disable no-invalid-this */ /** * Copyright © 2018-2022 Ferdinand Prantl * https://www.npmjs.com/package/timezone-support **/ const formattingToken = /(\[[^[]*\])|([-:/.()\s]+)|(A|a|YYYY|YY?|MM?|DD?|d|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g; const formatTokenFunctions = {}; const formatters = {}; function formatZonedTime(time, format) { let formatter = formatters[format]; if (!formatter) { formatter = formatters[format] = makeFormatter(format); } return formatter(time); } function makeFormatter(format) { const array = format.match(formattingToken); const { length } = array; for (let i = 0; i < length; ++i) { const token = array[i]; const formatter = formatTokenFunctions[token]; if (formatter) { array[i] = formatter; } else { array[i] = token.replace(/^\[|\]$/g, ''); } } return function (time) { let output = ''; for (const token of array) { output += typeof token === 'function' ? token.call(time) : token; } return output; }; } const addFormatToken = function (token, padded, property) { const callback = typeof pr