UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

363 lines (360 loc) • 15.4 kB
/** * DevExtreme (cjs/__internal/scheduler/m_recurrence.js) * Version: 23.2.6 * Build date: Wed May 01 2024 * * Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getRecurrenceProcessor = getRecurrenceProcessor; var _errors = _interopRequireDefault(require("../../core/errors")); var _date = _interopRequireDefault(require("../../core/utils/date")); var _iterator = require("../../core/utils/iterator"); var _rrule = require("rrule"); var _m_utils_time_zone = _interopRequireDefault(require("./m_utils_time_zone")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj } } const toMs = _date.default.dateToMilliseconds; const ruleNames = ["freq", "interval", "byday", "byweekno", "byyearday", "bymonth", "bymonthday", "count", "until", "byhour", "byminute", "bysecond", "bysetpos", "wkst"]; const freqNames = ["DAILY", "WEEKLY", "MONTHLY", "YEARLY", "SECONDLY", "MINUTELY", "HOURLY"]; const days = { SU: 0, MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6 }; const loggedWarnings = []; const MS_IN_HOUR = 36e5; const MS_IN_DAY = 864e5; const RRULE_BROKEN_TIMEZONES = ["Etc/GMT-13", "MIT", "Pacific/Apia", "Pacific/Enderbury", "Pacific/Tongatapu", "Etc/GMT-14", "Pacific/Kiritimati"]; let recurrence = null; function getRecurrenceProcessor() { if (!recurrence) { recurrence = new RecurrenceProcessor } return recurrence } let RecurrenceProcessor = function() { function RecurrenceProcessor() { this.rRule = null; this.rRuleSet = null; this.validator = new RecurrenceValidator } var _proto = RecurrenceProcessor.prototype; _proto.generateDates = function(options) { const recurrenceRule = this.evalRecurrenceRule(options.rule); const { rule: rule } = recurrenceRule; if (!recurrenceRule.isValid || !rule.freq) { return [] } const rruleIntervalParams = this._createRruleIntervalParams(options); this._initializeRRule(options, rruleIntervalParams.startIntervalDate, rule.until); return this.rRuleSet.between(rruleIntervalParams.minViewDate, rruleIntervalParams.maxViewDate, true).filter(date => date.getTime() + rruleIntervalParams.appointmentDuration >= rruleIntervalParams.minViewTime).map(date => this._convertRruleResult(rruleIntervalParams, options, date)) }; _proto._createRruleIntervalParams = function(options) { const { start: start, min: min, max: max, appointmentTimezoneOffset: appointmentTimezoneOffset } = options; const clientOffsets_startDate = _m_utils_time_zone.default.getClientTimezoneOffset(start), clientOffsets_minViewDate = _m_utils_time_zone.default.getClientTimezoneOffset(min), clientOffsets_maxViewDate = _m_utils_time_zone.default.getClientTimezoneOffset(max); const duration = options.end ? options.end.getTime() - options.start.getTime() : 0; const startIntervalDate = _m_utils_time_zone.default.setOffsetsToDate(options.start, [-clientOffsets_startDate, appointmentTimezoneOffset]); const minViewTime = options.min.getTime() - clientOffsets_minViewDate + appointmentTimezoneOffset; const minViewDate = new Date(minViewTime - duration); const maxViewDate = _m_utils_time_zone.default.setOffsetsToDate(options.max, [-clientOffsets_maxViewDate, appointmentTimezoneOffset]); const startDateDSTDifferenceMs = _m_utils_time_zone.default.getDiffBetweenClientTimezoneOffsets(options.start, startIntervalDate); const switchToSummerTime = startDateDSTDifferenceMs < 0; return { startIntervalDate: startIntervalDate, minViewTime: minViewTime, minViewDate: minViewDate, maxViewDate: maxViewDate, startIntervalDateDSTShift: switchToSummerTime ? 0 : startDateDSTDifferenceMs, appointmentDuration: duration } }; _proto._convertRruleResult = function(rruleIntervalParams, options, rruleDate) { const convertedBackDate = _m_utils_time_zone.default.setOffsetsToDate(rruleDate, [...this._getLocalMachineOffset(rruleDate), -options.appointmentTimezoneOffset, rruleIntervalParams.startIntervalDateDSTShift]); const convertedDateDSTShift = _m_utils_time_zone.default.getDiffBetweenClientTimezoneOffsets(convertedBackDate, rruleDate); const switchToSummerTime = convertedDateDSTShift < 0; const resultDate = _m_utils_time_zone.default.setOffsetsToDate(convertedBackDate, [convertedDateDSTShift]); const resultDateDSTShift = _m_utils_time_zone.default.getDiffBetweenClientTimezoneOffsets(resultDate, convertedBackDate); if (resultDateDSTShift && switchToSummerTime) { return new Date(resultDate.getTime() + resultDateDSTShift) } return resultDate }; _proto._getLocalMachineOffset = function(rruleDate) { const machineTimezoneOffset = _m_utils_time_zone.default.getClientTimezoneOffset(rruleDate); const machineTimezoneName = _date.default.getMachineTimezoneName(); const result = [machineTimezoneOffset]; const isTimezoneOffsetInBrokenRange = machineTimezoneOffset / 36e5 <= -13; const isTimezoneNameInBrokenNames = !machineTimezoneName || RRULE_BROKEN_TIMEZONES.some(timezone => machineTimezoneName.includes(timezone)); if (isTimezoneOffsetInBrokenRange && isTimezoneNameInBrokenNames) { result.push(-864e5) } return result }; _proto.hasRecurrence = function(options) { return !!this.generateDates(options).length }; _proto.evalRecurrenceRule = function(rule) { const result = { rule: {}, isValid: false }; if (rule) { result.rule = this._parseRecurrenceRule(rule); result.isValid = this.validator.validateRRule(result.rule, rule) } return result }; _proto.isValidRecurrenceRule = function(rule) { return this.evalRecurrenceRule(rule).isValid }; _proto.daysFromByDayRule = function(rule) { let result = []; if (rule.byday) { if (Array.isArray(rule.byday)) { result = rule.byday } else { result = rule.byday.split(",") } } return result.map(item => { const match = item.match(/[A-Za-z]+/); return !!match && match[0] }).filter(item => !!item) }; _proto.getAsciiStringByDate = function(date) { const currentOffset = date.getTimezoneOffset() * toMs("minute"); const offsetDate = new Date(date.getTime() + currentOffset); return "".concat(offsetDate.getFullYear() + "0".concat(offsetDate.getMonth() + 1).slice(-2) + "0".concat(offsetDate.getDate()).slice(-2), "T").concat("0".concat(offsetDate.getHours()).slice(-2)).concat("0".concat(offsetDate.getMinutes()).slice(-2)).concat("0".concat(offsetDate.getSeconds()).slice(-2), "Z") }; _proto.getRecurrenceString = function(object) { if (!object || !object.freq) { return } let result = ""; for (const field in object) { let value = object[field]; if ("interval" === field && value < 2) { continue } if ("until" === field) { value = this.getAsciiStringByDate(value) } result += "".concat(field, "=").concat(value, ";") } result = result.substring(0, result.length - 1); return result.toUpperCase() }; _proto._parseExceptionToRawArray = function(value) { return value.match(/(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2}))?(Z)?/) }; _proto.getDateByAsciiString = function(exceptionText) { if ("string" !== typeof exceptionText) { return exceptionText } const result = this._parseExceptionToRawArray(exceptionText); if (!result) { return null } const [year, month, date, hours, minutes, seconds, isUtc] = this._createDateTuple(result); if (isUtc) { return new Date(Date.UTC(year, month, date, hours, minutes, seconds)) } return new Date(year, month, date, hours, minutes, seconds) }; _proto._dispose = function() { if (this.rRuleSet) { delete this.rRuleSet; this.rRuleSet = null } if (this.rRule) { delete this.rRule; this.rRule = null } }; _proto._getTimeZoneOffset = function() { return (new Date).getTimezoneOffset() }; _proto._initializeRRule = function(options, startDateUtc, until) { const ruleOptions = _rrule.RRule.parseString(options.rule); const { firstDayOfWeek: firstDayOfWeek } = options; ruleOptions.dtstart = startDateUtc; if (!ruleOptions.wkst && firstDayOfWeek) { const weekDayNumbers = [6, 0, 1, 2, 3, 4, 5]; ruleOptions.wkst = weekDayNumbers[firstDayOfWeek] } if (until) { ruleOptions.until = _m_utils_time_zone.default.setOffsetsToDate(until, [-_m_utils_time_zone.default.getClientTimezoneOffset(until), options.appointmentTimezoneOffset]) } this._createRRule(ruleOptions); if (options.exception) { const exceptionStrings = options.exception; const exceptionDates = exceptionStrings.split(",").map(rule => this.getDateByAsciiString(rule)); exceptionDates.forEach(date => { if (options.getPostProcessedException) { date = options.getPostProcessedException(date) } const utcDate = _m_utils_time_zone.default.setOffsetsToDate(date, [-_m_utils_time_zone.default.getClientTimezoneOffset(date), options.appointmentTimezoneOffset]); this.rRuleSet.exdate(utcDate) }) } }; _proto._createRRule = function(ruleOptions) { this._dispose(); this.rRuleSet = new _rrule.RRuleSet; this.rRule = new _rrule.RRule(ruleOptions); this.rRuleSet.rrule(this.rRule) }; _proto._parseRecurrenceRule = function(recurrence) { const ruleObject = {}; const ruleParts = recurrence.split(";"); for (let i = 0, len = ruleParts.length; i < len; i++) { const rule = ruleParts[i].split("="); const ruleName = rule[0].toLowerCase(); const ruleValue = rule[1]; ruleObject[ruleName] = ruleValue } const count = parseInt(ruleObject.count); if (!isNaN(count)) { ruleObject.count = count } if (ruleObject.interval) { const interval = parseInt(ruleObject.interval); if (!isNaN(interval)) { ruleObject.interval = interval } } else { ruleObject.interval = 1 } if (ruleObject.freq && ruleObject.until) { ruleObject.until = this.getDateByAsciiString(ruleObject.until) } return ruleObject }; _proto._createDateTuple = function(parseResult) { const isUtc = void 0 !== parseResult[8]; parseResult.shift(); if (void 0 === parseResult[3]) { parseResult.splice(3) } else { parseResult.splice(3, 1); parseResult.splice(6) } parseResult[1]--; parseResult.unshift(null); return [parseInt(parseResult[1]), parseInt(parseResult[2]), parseInt(parseResult[3]), parseInt(parseResult[4]) || 0, parseInt(parseResult[5]) || 0, parseInt(parseResult[6]) || 0, isUtc] }; return RecurrenceProcessor }(); let RecurrenceValidator = function() { function RecurrenceValidator() {} var _proto2 = RecurrenceValidator.prototype; _proto2.validateRRule = function(rule, recurrence) { if (this._brokenRuleNameExists(rule) || !freqNames.includes(rule.freq) || this._wrongCountRule(rule) || this._wrongIntervalRule(rule) || this._wrongDayOfWeek(rule) || this._wrongByMonthDayRule(rule) || this._wrongByMonth(rule) || this._wrongUntilRule(rule)) { this._logBrokenRule(recurrence); return false } return true }; _proto2._wrongUntilRule = function(rule) { let wrongUntil = false; const { until: until } = rule; if (void 0 !== until && !(until instanceof Date)) { wrongUntil = true } return wrongUntil }; _proto2._wrongCountRule = function(rule) { let wrongCount = false; const { count: count } = rule; if (count && "string" === typeof count) { wrongCount = true } return wrongCount }; _proto2._wrongByMonthDayRule = function(rule) { let wrongByMonthDay = false; const byMonthDay = rule.bymonthday; if (byMonthDay && isNaN(parseInt(byMonthDay))) { wrongByMonthDay = true } return wrongByMonthDay }; _proto2._wrongByMonth = function(rule) { let wrongByMonth = false; const byMonth = rule.bymonth; if (byMonth && isNaN(parseInt(byMonth))) { wrongByMonth = true } return wrongByMonth }; _proto2._wrongIntervalRule = function(rule) { let wrongInterval = false; const { interval: interval } = rule; if (interval && "string" === typeof interval) { wrongInterval = true } return wrongInterval }; _proto2._wrongDayOfWeek = function(rule) { const byDay = rule.byday; const daysByRule = getRecurrenceProcessor().daysFromByDayRule(rule); let brokenDaysExist = false; if ("" === byDay) { brokenDaysExist = true }(0, _iterator.each)(daysByRule, (_, day) => { if (!Object.prototype.hasOwnProperty.call(days, day)) { brokenDaysExist = true; return false } return }); return brokenDaysExist }; _proto2._brokenRuleNameExists = function(rule) { let brokenRuleExists = false; (0, _iterator.each)(rule, ruleName => { if (!ruleNames.includes(ruleName)) { brokenRuleExists = true; return false } return }); return brokenRuleExists }; _proto2._logBrokenRule = function(recurrence) { if (!loggedWarnings.includes(recurrence)) { _errors.default.log("W0006", recurrence); loggedWarnings.push(recurrence) } }; return RecurrenceValidator }();