678 lines
21 KiB
JavaScript
678 lines
21 KiB
JavaScript
/**
|
|
* @copyright Copyright (c) 2019 Georg Ehrke
|
|
*
|
|
* @author Georg Ehrke <oc.list@georgehrke.com>
|
|
*
|
|
* @license GNU AGPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
import { getDateFromDateTimeValue } from '../utils/date.js'
|
|
import DurationValue from 'calendar-js/src/values/durationValue.js'
|
|
import { getWeekDayFromDate } from '../utils/recurrence.js'
|
|
import { getAmountAndUnitForTimedEvents, getAmountHoursMinutesAndUnitForAllDayEvents } from '../utils/alarms.js'
|
|
import { getHexForColorName } from '../utils/color.js'
|
|
|
|
/**
|
|
* Creates a complete calendar-object-instance-object based on given props
|
|
*
|
|
* @param {Object} props The props already provided
|
|
* @returns {Object}
|
|
*/
|
|
export const getDefaultCalendarObjectInstanceObject = (props = {}) => Object.assign({}, {
|
|
// Title of the event
|
|
title: null,
|
|
// Start date of the event
|
|
startDate: null,
|
|
// Timezone of the start date
|
|
startTimezoneId: null,
|
|
// End date of the event
|
|
endDate: null,
|
|
// Timezone of the end date
|
|
endTimezoneId: null,
|
|
// Indicator whether or not event is all-day
|
|
isAllDay: false,
|
|
// Location that the event takes places in
|
|
location: null,
|
|
// description of the event
|
|
description: null,
|
|
// Access class of the event (PUBLIC, PRIVATE, CONFIDENTIAL)
|
|
accessClass: null,
|
|
// Status of the event (CONFIRMED, TENTATIVE, CANCELLED)
|
|
status: null,
|
|
// Whether or not to block this event in Free-Busy reports (TRANSPARENT, OPAQUE)
|
|
timeTransparency: null,
|
|
// The recurrence rule of this event. We only support one recurrence-rule
|
|
recurrenceRule: {
|
|
frequency: 'NONE',
|
|
interval: 1,
|
|
count: null,
|
|
until: null,
|
|
byDay: [],
|
|
byMonth: [],
|
|
byMonthDay: [],
|
|
bySetPosition: null,
|
|
isUnsupported: false,
|
|
recurrenceRuleValue: null,
|
|
},
|
|
// Attendees of this event
|
|
attendees: [],
|
|
// Organizer of the event
|
|
organizer: {
|
|
// name of the organizer
|
|
name: null,
|
|
// email of the organizer
|
|
uri: null,
|
|
},
|
|
// Alarm of the event
|
|
alarms: [],
|
|
// Custom color of the event
|
|
customColor: null,
|
|
// Categories
|
|
categories: [],
|
|
// Whether or not the user is allowed to toggle the all-day checkbox
|
|
canModifyAllDay: true,
|
|
// The real event-component coming from calendar-js
|
|
eventComponent: null,
|
|
}, props)
|
|
|
|
/**
|
|
* Map an EventComponent from calendar-js to our calendar object instance object
|
|
*
|
|
* @param {EventComponent} eventComponent The EventComponent object to map to an object
|
|
* @returns {{color: *, canModifyAllDay: *, timeTransparency: *, description: *, location: *, eventComponent: *, title: *, accessClass: *, status: *}}
|
|
*/
|
|
export const mapEventComponentToCalendarObjectInstanceObject = (eventComponent) => {
|
|
const calendarObjectInstanceObject = {
|
|
title: eventComponent.title,
|
|
location: eventComponent.location,
|
|
description: eventComponent.description,
|
|
accessClass: eventComponent.accessClass,
|
|
status: eventComponent.status,
|
|
timeTransparency: eventComponent.timeTransparency,
|
|
color: eventComponent.color,
|
|
canModifyAllDay: eventComponent.canModifyAllDay(),
|
|
eventComponent,
|
|
}
|
|
|
|
// The end date of an event is non-inclusive. This is rather intuitive for timed-events, but very unintuitive for all-day events.
|
|
// That's why, when an events is from 2019-10-03 to 2019-10-04, we will show 2019-10-03 to 2019-10-03 in the editor.
|
|
calendarObjectInstanceObject.isAllDay = eventComponent.isAllDay()
|
|
calendarObjectInstanceObject.startDate = getDateFromDateTimeValue(eventComponent.startDate)
|
|
calendarObjectInstanceObject.startTimezoneId = eventComponent.startDate.timezoneId
|
|
|
|
if (eventComponent.isAllDay()) {
|
|
const endDate = eventComponent.endDate.clone()
|
|
endDate.addDuration(DurationValue.fromSeconds(-1 * 60 * 60 * 24))
|
|
calendarObjectInstanceObject.endDate = getDateFromDateTimeValue(endDate)
|
|
} else {
|
|
calendarObjectInstanceObject.endDate = getDateFromDateTimeValue(eventComponent.endDate)
|
|
}
|
|
calendarObjectInstanceObject.endTimezoneId = eventComponent.endDate.timezoneId
|
|
|
|
calendarObjectInstanceObject.categories = getCategoriesFromEventComponent(eventComponent)
|
|
calendarObjectInstanceObject.organizer = getOrganizerFromEventComponent(eventComponent)
|
|
calendarObjectInstanceObject.recurrenceRule = getRecurrenceRuleFromEventComponent(eventComponent)
|
|
calendarObjectInstanceObject.hasMultipleRecurrenceRules
|
|
= Array.from(eventComponent.getPropertyIterator('RRULE')).length > 1
|
|
calendarObjectInstanceObject.attendees = getAttendeesFromEventComponent(eventComponent)
|
|
calendarObjectInstanceObject.alarms = getAlarmsFromEventComponent(eventComponent)
|
|
|
|
if (eventComponent.hasProperty('COLOR')) {
|
|
const hexColor = getHexForColorName(eventComponent.getFirstPropertyFirstValue('COLOR'))
|
|
if (hexColor !== null) {
|
|
calendarObjectInstanceObject.customColor = hexColor
|
|
}
|
|
}
|
|
|
|
return calendarObjectInstanceObject
|
|
}
|
|
|
|
/**
|
|
* Gets the organizer from the event component
|
|
*
|
|
* @param {EventComponent} eventComponent The event-component representing the instance
|
|
* @returns {null|{commonName: *, uri: *}}
|
|
*/
|
|
function getOrganizerFromEventComponent(eventComponent) {
|
|
if (eventComponent.organizer) {
|
|
const organizerProperty = eventComponent.getFirstProperty('ORGANIZER')
|
|
return {
|
|
commonName: organizerProperty.commonName,
|
|
uri: organizerProperty.email,
|
|
attendeeProperty: organizerProperty,
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Gets all categories (without a language tag) from the event component
|
|
*
|
|
* @param {EventComponent} eventComponent The event-component representing the instance
|
|
* @returns {String[]}
|
|
*/
|
|
function getCategoriesFromEventComponent(eventComponent) {
|
|
return Array.from(eventComponent.getCategoryIterator())
|
|
}
|
|
|
|
/**
|
|
* Gets the first recurrence rule from the event component
|
|
*
|
|
* @param {EventComponent} eventComponent The event-component representing the instance
|
|
* @returns {{byMonth: [], frequency: null, count: null, byDay: [], interval: number, until: null, bySetPosition: null, byMonthDay: []}|{byMonth: *, frequency: *, count: *, byDay: *, interval: *, until: *, bySetPosition: *, byMonthDay: *}}
|
|
*/
|
|
function getRecurrenceRuleFromEventComponent(eventComponent) {
|
|
/** @type {RecurValue} */
|
|
const recurrenceRule = eventComponent.getFirstPropertyFirstValue('RRULE')
|
|
if (recurrenceRule) {
|
|
const component = {
|
|
frequency: recurrenceRule.frequency,
|
|
interval: parseInt(recurrenceRule.interval, 10) || 1,
|
|
count: recurrenceRule.count,
|
|
until: null,
|
|
byDay: [],
|
|
byMonth: [],
|
|
byMonthDay: [],
|
|
bySetPosition: null,
|
|
isUnsupported: false,
|
|
recurrenceRuleValue: recurrenceRule,
|
|
}
|
|
|
|
if (recurrenceRule.until) {
|
|
component.until = recurrenceRule.until.jsDate
|
|
}
|
|
|
|
switch (component.frequency) {
|
|
case 'DAILY':
|
|
getRecurrenceComponentFromDailyRule(recurrenceRule, component)
|
|
break
|
|
|
|
case 'WEEKLY':
|
|
getRecurrenceComponentFromWeeklyRule(recurrenceRule, component, eventComponent)
|
|
break
|
|
|
|
case 'MONTHLY':
|
|
getRecurrenceComponentFromMonthlyRule(recurrenceRule, component, eventComponent)
|
|
break
|
|
|
|
case 'YEARLY':
|
|
getRecurrenceComponentFromYearlyRule(recurrenceRule, component, eventComponent)
|
|
break
|
|
|
|
default:
|
|
component.isUnsupported = true
|
|
break
|
|
}
|
|
|
|
return component
|
|
}
|
|
|
|
return {
|
|
frequency: 'NONE',
|
|
interval: 1,
|
|
count: null,
|
|
until: null,
|
|
byDay: [],
|
|
byMonth: [],
|
|
byMonthDay: [],
|
|
bySetPosition: null,
|
|
isUnsupported: false,
|
|
recurrenceRuleValue: null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the recurrence-rule contains any of the given components
|
|
*
|
|
* @param {RecurValue} recurrenceRule The recurrence-rule value to check for the given components
|
|
* @param {String[]} components List of components to check for
|
|
* @returns {Boolean}
|
|
*/
|
|
function containsRecurrenceComponent(recurrenceRule, components) {
|
|
for (const component of components) {
|
|
const componentValue = recurrenceRule.getComponent(component)
|
|
if (componentValue.length > 0) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Gets all attendees from the event component
|
|
*
|
|
* @param {EventComponent} eventComponent The event-component representing the instance
|
|
* @returns {[]}
|
|
*/
|
|
function getAttendeesFromEventComponent(eventComponent) {
|
|
const attendees = []
|
|
|
|
for (const attendee of eventComponent.getAttendeeIterator()) {
|
|
attendees.push({
|
|
commonName: attendee.commonName,
|
|
participationStatus: attendee.participationStatus,
|
|
role: attendee.role,
|
|
rsvp: attendee.rsvp,
|
|
uri: attendee.email,
|
|
attendeeProperty: attendee,
|
|
})
|
|
}
|
|
|
|
return attendees
|
|
}
|
|
|
|
/**
|
|
* Get all alarms from the event Component
|
|
*
|
|
* @param {EventComponent} eventComponent The event-component representing the instance
|
|
* @returns {[]}
|
|
*/
|
|
function getAlarmsFromEventComponent(eventComponent) {
|
|
const alarms = []
|
|
|
|
for (const alarm of eventComponent.getAlarmIterator()) {
|
|
alarms.push(getAlarmFromAlarmComponent(alarm))
|
|
}
|
|
|
|
return alarms
|
|
}
|
|
|
|
/**
|
|
* Get all numbers between start and end as strings
|
|
*
|
|
* @param {Number} start Lower end of range
|
|
* @param {Number} end Upper end of range
|
|
* @returns {string[]}
|
|
*/
|
|
function getRangeAsStrings(start, end) {
|
|
return Array
|
|
.apply(null, Array((end - start) + 1))
|
|
.map((_, n) => n + start)
|
|
.map((s) => s.toString())
|
|
}
|
|
|
|
/**
|
|
* Extracts the recurrence component from a daily recurrence rule
|
|
*
|
|
* @param {RecurValue} recurrenceRule The RecurValue to extract data from
|
|
* @param {Object} recurrenceComponent The recurrence component to write data into
|
|
*/
|
|
function getRecurrenceComponentFromDailyRule(recurrenceRule, recurrenceComponent) {
|
|
/**
|
|
* # Daily
|
|
*
|
|
* The Nextcloud-editor does not support any BY-parts for the daily rule, hence
|
|
* we will mark any DAILY rule with BY-parts as unsupported.
|
|
*/
|
|
const forbiddenComponents = [
|
|
'BYSECOND',
|
|
'BYMINUTE',
|
|
'BYHOUR',
|
|
'BYDAY',
|
|
'BYMONTHDAY',
|
|
'BYYEARDAY',
|
|
'BYWEEKNO',
|
|
'BYMONTH',
|
|
'BYSETPOS',
|
|
]
|
|
|
|
if (containsRecurrenceComponent(recurrenceRule, forbiddenComponents)) {
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts the recurrence component from a weekly recurrence rule
|
|
*
|
|
* @param {RecurValue} recurrenceRule The RecurValue to extract data from
|
|
* @param {Object} recurrenceComponent The recurrence component to write data into
|
|
* @param {EventComponent} eventComponent The event component needed for default values
|
|
*/
|
|
function getRecurrenceComponentFromWeeklyRule(recurrenceRule, recurrenceComponent, eventComponent) {
|
|
/**
|
|
* # Weekly
|
|
*
|
|
* The Nextcloud-editor only supports BYDAY in order to expand the weekly rule.
|
|
* It does not support other BY-parts like BYSETPOS or BYMONTH
|
|
*
|
|
* As defined by RFC 5545, the individual BYDAY components may not be preceded
|
|
* by a positive or negative integer.
|
|
*/
|
|
const forbiddenComponents = [
|
|
'BYSECOND',
|
|
'BYMINUTE',
|
|
'BYHOUR',
|
|
'BYMONTHDAY',
|
|
'BYYEARDAY',
|
|
'BYWEEKNO',
|
|
'BYMONTH',
|
|
'BYSETPOS',
|
|
]
|
|
|
|
if (containsRecurrenceComponent(recurrenceRule, forbiddenComponents)) {
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
|
|
recurrenceComponent.byDay = recurrenceRule.getComponent('BYDAY')
|
|
.filter((weekDay) => ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'].includes(weekDay))
|
|
|
|
// If the BYDAY is empty, add the day that the event occurs in
|
|
// E.g. if the event is on a Wednesday, automatically set BYDAY:WE
|
|
if (recurrenceComponent.byDay.length === 0) {
|
|
recurrenceComponent.byDay.push(getWeekDayFromDate(eventComponent.startDate.jsDate))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts the recurrence component from a monthly recurrence rule
|
|
*
|
|
* @param {RecurValue} recurrenceRule The RecurValue to extract data from
|
|
* @param {Object} recurrenceComponent The recurrence component to write data into
|
|
* @param {EventComponent} eventComponent The event component needed for default values
|
|
*/
|
|
function getRecurrenceComponentFromMonthlyRule(recurrenceRule, recurrenceComponent, eventComponent) {
|
|
/**
|
|
* # Monthly
|
|
*
|
|
* The Nextcloud-editor only supports BYMONTHDAY, BYDAY, BYSETPOS in order to expand the monthly rule.
|
|
* It supports either BYMONTHDAY or the combination of BYDAY and BYSETPOS. They have to be used exclusively
|
|
* and cannot be combined.
|
|
*
|
|
* It does not support other BY-parts like BYMONTH
|
|
*
|
|
* For monthly recurrence-rules, BYDAY components are allowed to be preceded by positive or negative integers.
|
|
* The Nextcloud-editor supports at most one BYDAY component with an integer.
|
|
* If it's presented with such a BYDAY component, it will internally be converted to BYDAY without integer and BYSETPOS.
|
|
* e.g.
|
|
* BYDAY=3WE => BYDAY=WE,BYSETPOS=3
|
|
*
|
|
* BYSETPOS is limited to -2, -1, 1, 2, 3, 4, 5
|
|
* Other values are not supported
|
|
*
|
|
* BYDAY is limited to "MO", "TU", "WE", "TH", "FR", "SA", "SU",
|
|
* "MO,TU,WE,TH,FR,SA,SU", "MO,TU,WE,TH,FR", "SA,SU"
|
|
*
|
|
* BYMONYHDAY is limited to "1", "2", ..., "31"
|
|
*/
|
|
const forbiddenComponents = [
|
|
'BYSECOND',
|
|
'BYMINUTE',
|
|
'BYHOUR',
|
|
'BYYEARDAY',
|
|
'BYWEEKNO',
|
|
'BYMONTH',
|
|
]
|
|
|
|
if (containsRecurrenceComponent(recurrenceRule, forbiddenComponents)) {
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
|
|
if (containsRecurrenceComponent(recurrenceRule, ['BYMONYHDAY'])) {
|
|
if (containsRecurrenceComponent(recurrenceRule, ['BYDAY', 'BYSETPOS'])) {
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
|
|
const allowedValues = getRangeAsStrings(1, 31)
|
|
const byMonthDayComponent = recurrenceRule.getComponent('BYMONYHDAY')
|
|
recurrenceComponent.byMonthDay = byMonthDayComponent.filter((day) =>
|
|
allowedValues.includes(day))
|
|
|
|
if (byMonthDayComponent.length !== recurrenceComponent.byMonthDay.length) {
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
// TODO: the following is duplicate code, the same as in the yearly function.
|
|
} else if (containsRecurrenceComponent(recurrenceRule, ['BYDAY']) && containsRecurrenceComponent(recurrenceRule, ['BYSETPOS'])) {
|
|
if (isAllowedByDay(recurrenceRule.getComponent('BYDAY'))) {
|
|
recurrenceComponent.byDay = recurrenceRule.getComponent('BYDAY')
|
|
} else {
|
|
recurrenceComponent.byDay = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
|
|
const setPositionArray = recurrenceRule.getComponent('BYSETPOS')
|
|
if (setPositionArray.length === 1 && isAllowedBySetPos(setPositionArray[0])) {
|
|
recurrenceComponent.bySetPosition = setPositionArray[0]
|
|
} else {
|
|
recurrenceComponent.bySetPosition = 1
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
} else if (containsRecurrenceComponent(recurrenceRule, ['BYDAY'])) {
|
|
const byDayArray = recurrenceRule.getComponent('BYDAY')
|
|
|
|
if (byDayArray.length > 1) {
|
|
recurrenceComponent.byMonthDay.push(eventComponent.startDate.day.toString())
|
|
recurrenceComponent.isUnsupported = true
|
|
} else {
|
|
const firstElement = byDayArray[0]
|
|
|
|
const match = /^(-?\d)([A-Z]){2}$/.exec(firstElement)
|
|
if (match) {
|
|
const bySetPosition = match[1]
|
|
const byDay = match[2]
|
|
|
|
if (isAllowedBySetPos(bySetPosition)) {
|
|
recurrenceComponent.byDay = [byDay]
|
|
recurrenceComponent.bySetPosition = bySetPosition
|
|
} else {
|
|
recurrenceComponent.byDay = [byDay]
|
|
recurrenceComponent.bySetPosition = 1
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
} else {
|
|
recurrenceComponent.byMonthDay.push(eventComponent.startDate.day.toString())
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
}
|
|
} else {
|
|
// If none of the previous rules are present, automatically set a BYMONTHDAY
|
|
recurrenceComponent.byMonthDay.push(eventComponent.startDate.day.toString())
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts the recurrence component from a yearly recurrence rule
|
|
*
|
|
* @param {RecurValue} recurrenceRule The RecurValue to extract data from
|
|
* @param {Object} recurrenceComponent The recurrence component to write data into
|
|
* @param {EventComponent} eventComponent The event component needed for default values
|
|
*/
|
|
function getRecurrenceComponentFromYearlyRule(recurrenceRule, recurrenceComponent, eventComponent) {
|
|
/**
|
|
* # YEARLY
|
|
*
|
|
* The Nextcloud-editor only supports BYMONTH, BYDAY, BYSETPOS in order to expand the monthly rule.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* BYSETPOS is limited to -2, -1, 1, 2, 3, 4, 5
|
|
* Other values are not supported
|
|
*
|
|
* BYDAY is limited to "MO", "TU", "WE", "TH", "FR", "SA", "SU",
|
|
* "MO,TU,WE,TH,FR,SA,SU", "MO,TU,WE,TH,FR", "SA,SU"
|
|
*/
|
|
const forbiddenComponents = [
|
|
'BYSECOND',
|
|
'BYMINUTE',
|
|
'BYHOUR',
|
|
'BYMONTHDAY',
|
|
'BYYEARDAY',
|
|
'BYWEEKNO',
|
|
]
|
|
|
|
if (containsRecurrenceComponent(recurrenceRule, forbiddenComponents)) {
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
|
|
if (containsRecurrenceComponent(recurrenceRule, ['BYMONTH'])) {
|
|
recurrenceComponent.byMonth = recurrenceRule.getComponent('BYMONTH')
|
|
} else {
|
|
recurrenceComponent.byMonth.push(eventComponent.startDate.month.toString())
|
|
}
|
|
|
|
// TODO: the following is duplicate code, the same as in the month function.
|
|
if (containsRecurrenceComponent(recurrenceRule, ['BYDAY']) && containsRecurrenceComponent(recurrenceRule, ['BYSETPOS'])) {
|
|
if (isAllowedByDay(recurrenceRule.getComponent('BYDAY'))) {
|
|
recurrenceComponent.byDay = recurrenceRule.getComponent('BYDAY')
|
|
} else {
|
|
recurrenceComponent.byDay = ['MO', 'TU', 'W E', 'TH', 'FR', 'SA', 'SU']
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
|
|
const setPositionArray = recurrenceRule.getComponent('BYSETPOS')
|
|
if (setPositionArray.length === 1 && isAllowedBySetPos(setPositionArray[0])) {
|
|
recurrenceComponent.bySetPosition = setPositionArray[0]
|
|
} else {
|
|
recurrenceComponent.bySetPosition = 1
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
} else if (containsRecurrenceComponent(recurrenceRule, ['BYDAY'])) {
|
|
const byDayArray = recurrenceRule.getComponent('BYDAY')
|
|
|
|
if (byDayArray.length > 1) {
|
|
recurrenceComponent.byMonthDay.push(eventComponent.startDate.day.toString())
|
|
recurrenceComponent.isUnsupported = true
|
|
} else {
|
|
const firstElement = byDayArray[0]
|
|
|
|
const match = /^(-?\d)([A-Z]){2}$/.exec(firstElement)
|
|
if (match) {
|
|
const bySetPosition = match[1]
|
|
const byDay = match[2]
|
|
|
|
if (isAllowedBySetPos(bySetPosition)) {
|
|
recurrenceComponent.byDay = [byDay]
|
|
recurrenceComponent.bySetPosition = bySetPosition
|
|
} else {
|
|
recurrenceComponent.byDay = [byDay]
|
|
recurrenceComponent.bySetPosition = 1
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
} else {
|
|
recurrenceComponent.byMonthDay.push(eventComponent.startDate.day.toString())
|
|
recurrenceComponent.isUnsupported = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the given parameter is a supported BYDAY value
|
|
*
|
|
* @param {String[]} byDay The byDay component to check
|
|
* @returns {Boolean}
|
|
*/
|
|
function isAllowedByDay(byDay) {
|
|
const allowedByDay = [
|
|
'MO',
|
|
'TU',
|
|
'WE',
|
|
'TH',
|
|
'FR',
|
|
'SA',
|
|
'SU',
|
|
'FR,MO,SA,SU,TH,TU,WE',
|
|
'FR,MO,TH,TU,WE',
|
|
'SA,SU',
|
|
]
|
|
|
|
return allowedByDay.includes(byDay.slice().sort().join(','))
|
|
}
|
|
|
|
/**
|
|
* Checks if the given parameter is a supported BYSETPOS value
|
|
*
|
|
* @param {String} bySetPos The bySetPos component to check
|
|
* @returns {Boolean}
|
|
*/
|
|
function isAllowedBySetPos(bySetPos) {
|
|
const allowedBySetPos = [
|
|
'-2',
|
|
'-1',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
]
|
|
|
|
return allowedBySetPos.includes(bySetPos.toString())
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {Object} alarm The alarm to set / update
|
|
* @param {AlarmComponent} alarmComponent The alarm component to read from
|
|
*/
|
|
export function updateAlarmFromAlarmComponent(alarm, alarmComponent) {
|
|
alarm.type = alarmComponent.action
|
|
alarm.isRelative = alarmComponent.trigger.isRelative()
|
|
|
|
alarm.absoluteDate = null
|
|
alarm.absoluteTimezoneId = null
|
|
|
|
alarm.relativeIsBefore = null
|
|
alarm.relativeIsRelatedToStart = null
|
|
|
|
alarm.relativeUnitTimed = null
|
|
alarm.relativeAmountTimed = null
|
|
|
|
alarm.relativeUnitAllDay = null
|
|
alarm.relativeAmountAllDay = null
|
|
alarm.relativeHoursAllDay = null
|
|
alarm.relativeMinutesAllDay = null
|
|
|
|
alarm.relativeTrigger = null
|
|
|
|
alarm.alarmComponent = alarmComponent
|
|
|
|
if (alarm.isRelative) {
|
|
alarm.relativeIsBefore = alarmComponent.trigger.value.isNegative
|
|
alarm.relativeIsRelatedToStart = alarmComponent.trigger.related === 'START'
|
|
|
|
const timedData = getAmountAndUnitForTimedEvents(alarmComponent.trigger.value.totalSeconds)
|
|
alarm.relativeAmountTimed = timedData.amount
|
|
alarm.relativeUnitTimed = timedData.unit
|
|
|
|
const allDayData = getAmountHoursMinutesAndUnitForAllDayEvents(alarmComponent.trigger.value.totalSeconds)
|
|
alarm.relativeUnitAllDay = allDayData.unit
|
|
alarm.relativeAmountAllDay = allDayData.amount
|
|
alarm.relativeHoursAllDay = allDayData.hours
|
|
alarm.relativeMinutesAllDay = allDayData.minutes
|
|
|
|
alarm.relativeTrigger = alarmComponent.trigger.value.totalSeconds
|
|
} else {
|
|
alarm.absoluteDate = getDateFromDateTimeValue(alarmComponent.trigger.value)
|
|
alarm.absoluteTimezoneId = alarmComponent.trigger.value.timezoneId
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {AlarmComponent} alarmComponent The alarm component to read from
|
|
* @returns {Object}
|
|
*/
|
|
export function getAlarmFromAlarmComponent(alarmComponent) {
|
|
const alarmObject = {}
|
|
updateAlarmFromAlarmComponent(alarmObject, alarmComponent)
|
|
|
|
return alarmObject
|
|
}
|