ical-generator
Version:
ical-generator is a small piece of code which generates ical calendar files
1 lines • 180 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/tools.ts","../src/event.ts","../src/attendee.ts","../src/alarm.ts","../src/category.ts","../src/calendar.ts"],"sourcesContent":["/**\n * ical-generator entrypoint\n */\n\n'use strict';\n\nimport ICalCalendar, { type ICalCalendarData } from './calendar.ts';\n\n\n/**\n * Create a new, empty calendar and returns it.\n *\n * ```javascript\n * import ical from 'ical-generator';\n *\n * // or use require:\n * // const { default: ical } = require('ical-generator');\n *\n * const cal = ical();\n * ```\n *\n * You can pass options to setup your calendar or use setters to do this.\n *\n * ```javascript\n * import ical from 'ical-generator';\n *\n * // or use require:\n * // const { default: ical } = require('ical-generator');\n * const cal = ical({domain: 'sebbo.net'});\n *\n * // is the same as\n *\n * const cal = ical().domain('sebbo.net');\n *\n * // is the same as\n *\n * const cal = ical();\n * cal.domain('sebbo.net');\n * ```\n *\n * @param data Calendar data\n */\nfunction ical(data?: ICalCalendarData): ICalCalendar {\n return new ICalCalendar(data);\n}\n\nexport default ical;\n\nexport {\n ICalAlarmRelatesTo,\n ICalAlarmType,\n default as ICalAlarm,\n type ICalAlarmBaseData,\n type ICalAlarmData,\n type ICalAlarmJSONData,\n type ICalAlarmRepeatData,\n type ICalAlarmTriggerAfterData,\n type ICalAlarmTriggerBeforeData,\n type ICalAlarmTriggerData,\n type ICalAlarmTypeValue,\n type ICalAttachment\n} from './alarm.ts';\n\nexport {\n ICalAttendeeRole,\n ICalAttendeeStatus,\n ICalAttendeeType,\n default as ICalAttendee,\n type ICalAttendeeData,\n type ICalAttendeeJSONData\n} from './attendee.ts';\n\nexport {\n ICalCalendarMethod,\n default as ICalCalendar,\n type ICalCalendarData,\n type ICalCalendarJSONData,\n type ICalCalendarProdIdData\n} from './calendar.ts';\n\nexport {\n default as ICalCategory,\n type ICalCategoryData,\n type ICalCategoryJSONData\n} from './category.ts';\n\nexport {\n ICalEventBusyStatus,\n ICalEventClass,\n ICalEventStatus,\n ICalEventTransparency,\n default as ICalEvent,\n type ICalEventData,\n type ICalEventJSONData,\n type ICalEventJSONRepeatingData\n} from './event.ts';\n\nexport {\n ICalEventRepeatingFreq,\n ICalWeekday,\n type ICalDateTimeValue,\n type ICalDayJsStub,\n type ICalDescription,\n type ICalGeo,\n type ICalLocation,\n type ICalLocationWithTitle,\n type ICalLocationWithoutTitle,\n type ICalLuxonDateTimeStub,\n type ICalMomentDurationStub,\n type ICalMomentStub,\n type ICalMomentTimezoneStub,\n type ICalOrganizer,\n type ICalRRuleStub,\n type ICalRepeatingOptions,\n type ICalTimezone,\n} from './types.ts';\n\nexport {\n escape,\n foldLines,\n formatDate,\n formatDateTZ\n} from './tools.ts';\n","/**\n * ical-generator supports [native Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date),\n * [moment.js](https://momentjs.com/) (and [moment-timezone](https://momentjs.com/timezone/), [Day.js](https://day.js.org/en/) and\n * [Luxon](https://moment.github.io/luxon/)'s [DateTime](https://moment.github.io/luxon/docs/class/src/datetime.js~DateTime.html)\n * objects. You can also pass a string which is then passed to javascript's Date internally.\n */\nexport type ICalDateTimeValue = Date | ICalMomentStub | ICalMomentTimezoneStub | ICalLuxonDateTimeStub | ICalDayJsStub | string;\n\nexport interface ICalRepeatingOptions {\n freq: ICalEventRepeatingFreq;\n count?: number;\n interval?: number;\n until?: ICalDateTimeValue;\n byDay?: ICalWeekday[] | ICalWeekday;\n byMonth?: number[] | number;\n byMonthDay?: number[] | number;\n bySetPos?: number[] | number;\n exclude?: ICalDateTimeValue[] | ICalDateTimeValue;\n startOfWeek?: ICalWeekday;\n}\n\nexport type ICalLocation = ICalLocationWithTitle | ICalLocationWithoutTitle;\n\nexport interface ICalLocationWithTitle {\n title: string;\n address?: string;\n radius?: number;\n geo?: ICalGeo;\n}\n\nexport interface ICalLocationWithoutTitle {\n geo: ICalGeo;\n}\n\nexport interface ICalGeo {\n lat: number;\n lon: number;\n}\n\nexport interface ICalOrganizer {\n name: string;\n email?: string;\n mailto?: string;\n sentBy?: string;\n}\n\nexport interface ICalDescription {\n plain: string;\n html?: string;\n}\n\nexport interface ICalTimezone {\n name: string | null;\n generator?: (timezone: string) => string|null;\n}\n\nexport interface ICalMomentStub {\n format(format?: string): string;\n clone(): ICalMomentStub;\n utc(): ICalMomentStub;\n toDate(): Date;\n isValid(): boolean;\n toJSON(): string;\n}\n\nexport interface ICalMomentTimezoneStub extends ICalMomentStub {\n clone(): ICalMomentTimezoneStub;\n utc(): ICalMomentTimezoneStub;\n tz(): string | undefined;\n tz(timezone: string): ICalMomentTimezoneStub;\n}\n\nexport interface ICalMomentDurationStub {\n asSeconds(): number;\n}\n\nexport interface ICalLuxonDateTimeStub {\n setZone(zone?: string): ICalLuxonDateTimeStub;\n zone: { type: string; };\n toFormat(fmt: string): string;\n toJSDate(): Date;\n get isValid(): boolean;\n toJSON(): string | null;\n}\n\nexport interface ICalDayJsStub {\n tz(zone?: string): ICalDayJsStub;\n utc(): ICalDayJsStub;\n format(format?: string): string;\n toDate(): Date;\n isValid(): boolean;\n toJSON(): string;\n}\n\nexport interface ICalRRuleStub {\n between(after: Date, before: Date, inc?: boolean, iterator?: (d: Date, len: number) => boolean): Date[];\n toString(): string;\n}\n\nexport enum ICalEventRepeatingFreq {\n SECONDLY = 'SECONDLY',\n MINUTELY = 'MINUTELY',\n HOURLY = 'HOURLY',\n DAILY = 'DAILY',\n WEEKLY = 'WEEKLY',\n MONTHLY = 'MONTHLY',\n YEARLY = 'YEARLY'\n}\n\nexport enum ICalWeekday {\n SU = 'SU',\n MO = 'MO',\n TU = 'TU',\n WE = 'WE',\n TH = 'TH',\n FR = 'FR',\n SA = 'SA'\n}\n","'use strict';\n\nimport {\n type ICalDateTimeValue,\n type ICalDayJsStub,\n type ICalLuxonDateTimeStub,\n type ICalMomentDurationStub,\n type ICalMomentStub,\n type ICalMomentTimezoneStub,\n type ICalOrganizer,\n type ICalRRuleStub\n} from './types.ts';\n\n/**\n * Converts a valid date/time object supported by this library to a string.\n */\nexport function formatDate (timezone: string | null, d: ICalDateTimeValue, dateonly?: boolean, floating?: boolean): string {\n if(timezone?.startsWith('/')) {\n timezone = timezone.substr(1);\n }\n\n if(typeof d === 'string' || d instanceof Date) {\n const m = new Date(d);\n\n // (!dateonly && !floating) || !timezone => utc\n let s = m.getUTCFullYear() +\n String(m.getUTCMonth() + 1).padStart(2, '0') +\n m.getUTCDate().toString().padStart(2, '0');\n\n // (dateonly || floating) && timezone => tz\n if(timezone) {\n s = m.getFullYear() +\n String(m.getMonth() + 1).padStart(2, '0') +\n m.getDate().toString().padStart(2, '0');\n }\n\n if(dateonly) {\n return s;\n }\n\n if(timezone) {\n s += 'T' + m.getHours().toString().padStart(2, '0') +\n m.getMinutes().toString().padStart(2, '0') +\n m.getSeconds().toString().padStart(2, '0');\n\n return s;\n }\n\n s += 'T' + m.getUTCHours().toString().padStart(2, '0') +\n m.getUTCMinutes().toString().padStart(2, '0') +\n m.getUTCSeconds().toString().padStart(2, '0') +\n (floating ? '' : 'Z');\n\n return s;\n }\n else if(isMoment(d)) {\n // @see https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/\n const m = timezone\n ? (isMomentTZ(d) && !d.tz() ? d.clone().tz(timezone) : d)\n : (floating || (dateonly && isMomentTZ(d) && d.tz()) ? d : d.utc());\n\n return m.format('YYYYMMDD') + (!dateonly ? (\n 'T' + m.format('HHmmss') + (floating || timezone ? '' : 'Z')\n ) : '');\n }\n else if(isLuxonDate(d)) {\n const m = timezone\n ? d.setZone(timezone)\n : (floating || (dateonly && d.zone.type !== 'system') ? d : d.setZone('utc'));\n\n return m.toFormat('yyyyLLdd') + (!dateonly ? (\n 'T' + m.toFormat('HHmmss') + (floating || timezone ? '' : 'Z')\n ) : '');\n }\n else {\n // @see https://day.js.org/docs/en/plugin/utc\n\n let m = d;\n if(timezone) {\n m = typeof d.tz === 'function' ? d.tz(timezone) : d;\n }\n else if(floating) {\n // m = d;\n }\n\n else if (typeof d.utc === 'function') {\n m = d.utc();\n }\n else {\n throw new Error('Unable to convert dayjs object to UTC value: UTC plugin is not available!');\n }\n\n return m.format('YYYYMMDD') + (!dateonly ? (\n 'T' + m.format('HHmmss') + (floating || timezone ? '' : 'Z')\n ) : '');\n }\n}\n\n/**\n * Converts a valid date/time object supported by this library to a string.\n * For information about this format, see RFC 5545, section 3.3.5\n * https://tools.ietf.org/html/rfc5545#section-3.3.5\n */\nexport function formatDateTZ (timezone: string | null, property: string, date: ICalDateTimeValue | Date | string, eventData?: {floating?: boolean | null, timezone?: string | null}): string {\n let tzParam = '';\n let floating = eventData?.floating || false;\n\n if (eventData?.timezone) {\n tzParam = ';TZID=' + eventData.timezone;\n\n // This isn't a 'floating' event because it has a timezone;\n // but we use it to omit the 'Z' UTC specifier in formatDate()\n floating = true;\n }\n\n return property + tzParam + ':' + formatDate(timezone, date, false, floating);\n}\n\n/**\n * Escapes special characters in the given string\n */\nexport function escape (str: string | unknown, inQuotes: boolean): string {\n return String(str).replace(inQuotes ? /[\\\\\"]/g : /[\\\\;,]/g, function (match) {\n return '\\\\' + match;\n }).replace(/(?:\\r\\n|\\r|\\n)/g, '\\\\n');\n}\n\n/**\n * Trim line length of given string\n */\nexport function foldLines (input: string): string {\n return input.split('\\r\\n').map(function (line) {\n let result = '';\n let c = 0;\n for (let i = 0; i < line.length; i++) {\n let ch = line.charAt(i);\n\n // surrogate pair, see https://mathiasbynens.be/notes/javascript-encoding#surrogate-pairs\n if (ch >= '\\ud800' && ch <= '\\udbff') {\n ch += line.charAt(++i);\n }\n\n // TextEncoder is available in browsers and node.js >= 11.0.0\n const charsize = new TextEncoder().encode(ch).length;\n c += charsize;\n if (c > 74) {\n result += '\\r\\n ';\n c = charsize;\n }\n\n result += ch;\n }\n return result;\n }).join('\\r\\n');\n}\n\nexport function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrArray: ({key: string, value: string})[] | [string, string][] | Record<string, string>): void;\nexport function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrArray: string, value: string): void;\nexport function addOrGetCustomAttributes (data: {x: [string, string][]}): ({key: string, value: string})[];\nexport function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrArray?: ({key: string, value: string})[] | [string, string][] | Record<string, string> | string | undefined, value?: string | undefined): void | ({key: string, value: string})[] {\n if (Array.isArray(keyOrArray)) {\n data.x = keyOrArray.map((o: {key: string, value: string} | [string, string]) => {\n if(Array.isArray(o)) {\n return o;\n }\n if (typeof o.key !== 'string' || typeof o.value !== 'string') {\n throw new Error('Either key or value is not a string!');\n }\n if (o.key.substr(0, 2) !== 'X-') {\n throw new Error('Key has to start with `X-`!');\n }\n\n return [o.key, o.value] as [string, string];\n });\n }\n else if (typeof keyOrArray === 'object') {\n data.x = Object.entries(keyOrArray).map(([key, value]) => {\n if (typeof key !== 'string' || typeof value !== 'string') {\n throw new Error('Either key or value is not a string!');\n }\n if (key.substr(0, 2) !== 'X-') {\n throw new Error('Key has to start with `X-`!');\n }\n\n return [key, value];\n });\n }\n else if (typeof keyOrArray === 'string' && typeof value === 'string') {\n if (keyOrArray.substr(0, 2) !== 'X-') {\n throw new Error('Key has to start with `X-`!');\n }\n\n data.x.push([keyOrArray, value]);\n }\n else {\n return data.x.map(a => ({\n key: a[0],\n value: a[1]\n }));\n }\n}\n\nexport function generateCustomAttributes (data: {x: [string, string][]}): string {\n const str = data.x\n .map(([key, value]) => key.toUpperCase() + ':' + escape(value, false))\n .join('\\r\\n');\n return str.length ? str + '\\r\\n' : '';\n}\n\n/**\n * Check the given string or ICalOrganizer. Parses\n * the string for name and email address if possible.\n *\n * @param attribute Attribute name for error messages\n * @param value Value to parse name/email from\n */\nexport function checkNameAndMail (attribute: string, value: string | ICalOrganizer): ICalOrganizer {\n let result: ICalOrganizer | null = null;\n\n if (typeof value === 'string') {\n const match = value.match(/^(.+) ?<([^>]+)>$/);\n if (match) {\n result = {\n name: match[1].trim(),\n email: match[2].trim()\n };\n }\n else if(value.includes('@')) {\n result = {\n name: value.trim(),\n email: value.trim()\n };\n }\n }\n else if (typeof value === 'object') {\n result = {\n name: value.name,\n email: value.email,\n mailto: value.mailto,\n sentBy: value.sentBy\n };\n }\n\n if (!result && typeof value === 'string') {\n throw new Error(\n '`' + attribute + '` isn\\'t formated correctly. See https://sebbo2002.github.io/ical-generator/develop/'+\n 'reference/interfaces/ICalOrganizer.html'\n );\n }\n else if (!result) {\n throw new Error(\n '`' + attribute + '` needs to be a valid formed string or an object. See https://sebbo2002.github.io/'+\n 'ical-generator/develop/reference/interfaces/ICalOrganizer.html'\n );\n }\n\n if (!result.name) {\n throw new Error('`' + attribute + '.name` is empty!');\n }\n\n return result;\n}\n\n/**\n * Checks if the given string `value` is a\n * valid one for the type `type`\n */\nexport function checkEnum(type: Record<string, string>, value: unknown): unknown {\n const allowedValues = Object.values(type);\n const valueStr = String(value).toUpperCase();\n\n if (!valueStr || !allowedValues.includes(valueStr)) {\n throw new Error(`Input must be one of the following: ${allowedValues.join(', ')}`);\n }\n\n return valueStr;\n}\n\n/**\n * Checks if the given input is a valid date and\n * returns the internal representation (= moment object)\n */\nexport function checkDate(value: ICalDateTimeValue, attribute: string): ICalDateTimeValue {\n\n // Date & String\n if(\n (value instanceof Date && isNaN(value.getTime())) ||\n (typeof value === 'string' && isNaN(new Date(value).getTime()))\n ) {\n throw new Error(`\\`${attribute}\\` has to be a valid date!`);\n }\n if(value instanceof Date || typeof value === 'string') {\n return value;\n }\n\n // Luxon\n if(isLuxonDate(value) && value.isValid === true) {\n return value;\n }\n\n // Moment / Moment Timezone\n if((isMoment(value) || isDayjs(value)) && value.isValid()) {\n return value;\n }\n\n throw new Error(`\\`${attribute}\\` has to be a valid date!`);\n}\n\nexport function toDate(value: ICalDateTimeValue): Date {\n if(typeof value === 'string' || value instanceof Date) {\n return new Date(value);\n }\n\n if(isLuxonDate(value)) {\n return value.toJSDate();\n }\n\n return value.toDate();\n}\n\nexport function isMoment(value: ICalDateTimeValue): value is ICalMomentStub {\n\n // @ts-expect-error _isAMomentObject is a private property\n return value != null && value._isAMomentObject != null;\n}\nexport function isMomentTZ(value: ICalDateTimeValue): value is ICalMomentTimezoneStub {\n return isMoment(value) && 'tz' in value && typeof value.tz === 'function';\n}\nexport function isDayjs(value: ICalDateTimeValue): value is ICalDayJsStub {\n return typeof value === 'object' &&\n value !== null &&\n !(value instanceof Date) &&\n !isMoment(value) &&\n !isLuxonDate(value);\n}\nexport function isLuxonDate(value: ICalDateTimeValue): value is ICalLuxonDateTimeStub {\n return typeof value === 'object' && value !== null && 'toJSDate' in value && typeof value.toJSDate === 'function';\n}\n\nexport function isMomentDuration(value: unknown): value is ICalMomentDurationStub {\n return value !== null && typeof value === 'object' && 'asSeconds' in value && typeof value.asSeconds === 'function';\n}\n\nexport function isRRule(value: unknown): value is ICalRRuleStub {\n return value !== null && typeof value === 'object' && 'between' in value && typeof value.between === 'function' && typeof value.toString === 'function';\n}\n\nexport function toJSON(value: ICalDateTimeValue | null | undefined): string | null | undefined {\n if(!value) {\n return null;\n }\n if(typeof value === 'string') {\n return value;\n }\n\n return value.toJSON();\n}\n\nexport function toDurationString(seconds: number): string {\n let string = '';\n\n // < 0\n if(seconds < 0) {\n string = '-';\n seconds *= -1;\n }\n\n string += 'P';\n\n // DAYS\n if(seconds >= 86400) {\n string += Math.floor(seconds / 86400) + 'D';\n seconds %= 86400;\n }\n if(!seconds && string.length > 1) {\n return string;\n }\n\n string += 'T';\n\n // HOURS\n if(seconds >= 3600) {\n string += Math.floor(seconds / 3600) + 'H';\n seconds %= 3600;\n }\n\n // MINUTES\n if(seconds >= 60) {\n string += Math.floor(seconds / 60) + 'M';\n seconds %= 60;\n }\n\n // SECONDS\n if(seconds > 0) {\n string += seconds + 'S';\n }\n else if(string.length <= 2) {\n string += '0S';\n }\n\n return string;\n}\n","'use strict';\n\nimport uuid from 'uuid-random';\nimport {\n addOrGetCustomAttributes,\n checkDate,\n checkEnum,\n checkNameAndMail,\n escape,\n formatDate,\n formatDateTZ,\n generateCustomAttributes,\n isRRule,\n toDate,\n toJSON\n} from './tools.ts';\nimport ICalAttendee, { type ICalAttendeeData } from './attendee.ts';\nimport ICalAlarm, { type ICalAlarmData } from './alarm.ts';\nimport ICalCategory, { type ICalCategoryData } from './category.ts';\nimport ICalCalendar from './calendar.ts';\nimport {\n ICalEventRepeatingFreq,\n ICalWeekday,\n type ICalDateTimeValue,\n type ICalDescription,\n type ICalLocation,\n type ICalOrganizer,\n type ICalRRuleStub,\n type ICalRepeatingOptions\n} from './types.ts';\n\n\nexport enum ICalEventStatus {\n CONFIRMED = 'CONFIRMED',\n TENTATIVE = 'TENTATIVE',\n CANCELLED = 'CANCELLED'\n}\n\nexport enum ICalEventBusyStatus {\n FREE = 'FREE',\n TENTATIVE = 'TENTATIVE',\n BUSY = 'BUSY',\n OOF = 'OOF'\n}\n\nexport enum ICalEventTransparency {\n TRANSPARENT = 'TRANSPARENT',\n OPAQUE = 'OPAQUE'\n}\n\nexport enum ICalEventClass {\n PUBLIC = 'PUBLIC',\n PRIVATE = 'PRIVATE',\n CONFIDENTIAL = 'CONFIDENTIAL'\n}\n\nexport interface ICalEventData {\n id?: string | number | null,\n sequence?: number,\n start: ICalDateTimeValue,\n end?: ICalDateTimeValue | null,\n recurrenceId?: ICalDateTimeValue | null,\n timezone?: string | null,\n stamp?: ICalDateTimeValue,\n allDay?: boolean,\n floating?: boolean,\n repeating?: ICalRepeatingOptions | ICalRRuleStub | string | null,\n summary?: string,\n location?: ICalLocation | string | null,\n description?: ICalDescription | string | null,\n organizer?: ICalOrganizer | string | null,\n attendees?: ICalAttendee[] | ICalAttendeeData[],\n alarms?: ICalAlarm[] | ICalAlarmData[],\n categories?: ICalCategory[] | ICalCategoryData[],\n status?: ICalEventStatus | null,\n busystatus?: ICalEventBusyStatus | null,\n priority?: number | null,\n url?: string | null,\n attachments?: string[],\n transparency?: ICalEventTransparency | null,\n created?: ICalDateTimeValue | null,\n lastModified?: ICalDateTimeValue | null,\n class?: ICalEventClass | null;\n x?: {key: string, value: string}[] | [string, string][] | Record<string, string>;\n}\n\ninterface ICalEventInternalData {\n id: string,\n sequence: number,\n start: ICalDateTimeValue,\n end: ICalDateTimeValue | null,\n recurrenceId: ICalDateTimeValue | null,\n timezone: string | null,\n stamp: ICalDateTimeValue,\n allDay: boolean,\n floating: boolean,\n repeating: ICalEventJSONRepeatingData | ICalRRuleStub | string | null,\n summary: string,\n location: ICalLocation | null,\n description: ICalDescription | null,\n organizer: ICalOrganizer | null,\n attendees: ICalAttendee[],\n alarms: ICalAlarm[],\n categories: ICalCategory[],\n status: ICalEventStatus | null,\n busystatus: ICalEventBusyStatus | null,\n priority: number | null,\n url: string | null,\n attachments: string[],\n transparency: ICalEventTransparency | null,\n created: ICalDateTimeValue | null,\n lastModified: ICalDateTimeValue | null,\n class: ICalEventClass | null,\n x: [string, string][];\n}\n\nexport interface ICalEventJSONData {\n id: string,\n sequence: number,\n start: string,\n end: string | null,\n recurrenceId: string | null,\n timezone: string | null,\n stamp: string,\n allDay: boolean,\n floating: boolean,\n repeating: ICalEventJSONRepeatingData | string | null,\n summary: string,\n location: ICalLocation | null,\n description: ICalDescription | null,\n organizer: ICalOrganizer | null,\n attendees: ICalAttendee[],\n alarms: ICalAlarm[],\n categories: ICalCategory[],\n status: ICalEventStatus | null,\n busystatus: ICalEventBusyStatus | null,\n priority?: number | null,\n url: string | null,\n attachments: string[],\n transparency: ICalEventTransparency | null,\n created: string | null,\n lastModified: string | null,\n x: {key: string, value: string}[];\n}\n\nexport interface ICalEventJSONRepeatingData {\n freq: ICalEventRepeatingFreq;\n count?: number;\n interval?: number;\n until?: ICalDateTimeValue;\n byDay?: ICalWeekday[];\n byMonth?: number[];\n byMonthDay?: number[];\n bySetPos?: number[];\n exclude?: ICalDateTimeValue[];\n startOfWeek?: ICalWeekday;\n}\n\n\n/**\n * Usually you get an {@link ICalEvent} object like this:\n * ```javascript\n * import ical from 'ical-generator';\n * const calendar = ical();\n * const event = calendar.createEvent();\n * ```\n */\nexport default class ICalEvent {\n private readonly data: ICalEventInternalData;\n private readonly calendar: ICalCalendar;\n\n /**\n * Constructor of [[`ICalEvent`]. The calendar reference is\n * required to query the calendar's timezone when required.\n *\n * @param data Calendar Event Data\n * @param calendar Reference to ICalCalendar object\n */\n constructor(data: ICalEventData, calendar: ICalCalendar) {\n this.data = {\n id: uuid(),\n sequence: 0,\n start: new Date(),\n end: null,\n recurrenceId: null,\n timezone: null,\n stamp: new Date(),\n allDay: false,\n floating: false,\n repeating: null,\n summary: '',\n location: null,\n description: null,\n organizer: null,\n attendees: [],\n alarms: [],\n categories: [],\n status: null,\n busystatus: null,\n priority: null,\n url: null,\n attachments: [],\n transparency: null,\n created: null,\n lastModified: null,\n class: null,\n x: []\n };\n\n this.calendar = calendar;\n if (!calendar) {\n throw new Error('`calendar` option required!');\n }\n\n if (data.id) this.id(data.id);\n if (data.sequence !== undefined) this.sequence(data.sequence);\n if (data.start) this.start(data.start);\n if (data.end !== undefined) this.end(data.end);\n if (data.recurrenceId !== undefined) this.recurrenceId(data.recurrenceId);\n if (data.timezone !== undefined) this.timezone(data.timezone);\n if (data.stamp !== undefined) this.stamp(data.stamp);\n if (data.allDay !== undefined) this.allDay(data.allDay);\n if (data.floating !== undefined) this.floating(data.floating);\n if (data.repeating !== undefined) this.repeating(data.repeating);\n if (data.summary !== undefined) this.summary(data.summary);\n if (data.location !== undefined) this.location(data.location);\n if (data.description !== undefined) this.description(data.description);\n if (data.organizer !== undefined) this.organizer(data.organizer);\n if (data.attendees !== undefined) this.attendees(data.attendees);\n if (data.alarms !== undefined) this.alarms(data.alarms);\n if (data.categories !== undefined) this.categories(data.categories);\n if (data.status !== undefined) this.status(data.status);\n if (data.busystatus !== undefined) this.busystatus(data.busystatus);\n if (data.priority !== undefined) this.priority(data.priority);\n if (data.url !== undefined) this.url(data.url);\n if (data.attachments !== undefined) this.attachments(data.attachments);\n if (data.transparency !== undefined) this.transparency(data.transparency);\n if (data.created !== undefined) this.created(data.created);\n if (data.lastModified !== undefined) this.lastModified(data.lastModified);\n if (data.class !== undefined) this.class(data.class);\n if (data.x !== undefined) this.x(data.x);\n }\n\n /**\n * Get the event's ID\n * @since 0.2.0\n */\n id(): string;\n\n /**\n * Use this method to set the event's ID.\n * If not set, a UUID will be generated randomly.\n *\n * @param id Event ID you want to set\n */\n id(id: string | number): this;\n id(id?: string | number): this | string {\n if (id === undefined) {\n return this.data.id;\n }\n\n this.data.id = String(id);\n return this;\n }\n\n /**\n * Get the event's ID\n * @since 0.2.0\n * @see {@link id}\n */\n uid(): string;\n\n /**\n * Use this method to set the event's ID.\n * If not set, a UUID will be generated randomly.\n *\n * @param id Event ID you want to set\n */\n uid(id: string | number): this;\n uid(id?: string | number): this | string {\n return id === undefined ? this.id() : this.id(id);\n }\n\n /**\n * Get the event's SEQUENCE number. Use this method to get the event's\n * revision sequence number of the calendar component within a sequence of revisions.\n *\n * @since 0.2.6\n */\n sequence(): number;\n\n /**\n * Set the event's SEQUENCE number. For a new event, this should be zero.\n * Each time the organizer makes a significant revision, the sequence\n * number should be incremented.\n *\n * @param sequence Sequence number or null to unset it\n */\n sequence(sequence: number): this;\n sequence(sequence?: number): this | number {\n if (sequence === undefined) {\n return this.data.sequence;\n }\n\n const s = parseInt(String(sequence), 10);\n if (isNaN(s)) {\n throw new Error('`sequence` must be a number!');\n }\n\n this.data.sequence = sequence;\n return this;\n }\n\n /**\n * Get the event start time which is currently\n * set. Can be any supported date object.\n *\n * @since 0.2.0\n */\n start(): ICalDateTimeValue;\n\n /**\n * Set the appointment date of beginning, which is required for all events.\n * You can use any supported date object, see\n * [Readme](https://github.com/sebbo2002/ical-generator#-date-time--timezones)\n * for details about supported values and timezone handling.\n *\n * ```typescript\n * import ical from 'ical-generator';\n *\n * const cal = ical();\n *\n * const event = cal.createEvent({\n * start: new Date('2020-01-01')\n * });\n *\n * // overwrites old start date\n * event.start(new Date('2024-02-01'));\n *\n * cal.toString();\n * ```\n *\n * ```text\n * BEGIN:VCALENDAR\n * VERSION:2.0\n * PRODID:-//sebbo.net//ical-generator//EN\n * BEGIN:VEVENT\n * UID:7e2aee64-b07a-4256-9b3e-e9eaa452bac8\n * SEQUENCE:0\n * DTSTAMP:20240212T190915Z\n * DTSTART:20240201T000000Z\n * SUMMARY:\n * END:VEVENT\n * END:VCALENDAR\n * ```\n *\n * @since 0.2.0\n */\n start(start: ICalDateTimeValue): this;\n start(start?: ICalDateTimeValue): this | ICalDateTimeValue {\n if (start === undefined) {\n this.swapStartAndEndIfRequired();\n return this.data.start;\n }\n\n this.data.start = checkDate(start, 'start');\n return this;\n }\n\n /**\n * Get the event end time which is currently\n * set. Can be any supported date object.\n *\n * @since 0.2.0\n */\n end(): ICalDateTimeValue | null;\n\n /**\n * Set the appointment date of end. You can use any supported date object, see\n * [readme](https://github.com/sebbo2002/ical-generator#-date-time--timezones)\n * for details about supported values and timezone handling.\n *\n * @since 0.2.0\n */\n end(end: ICalDateTimeValue | null): this;\n end(end?: ICalDateTimeValue | null): this | ICalDateTimeValue | null {\n if (end === undefined) {\n this.swapStartAndEndIfRequired();\n return this.data.end;\n }\n if (end === null) {\n this.data.end = null;\n return this;\n }\n\n this.data.end = checkDate(end, 'end');\n return this;\n }\n\n /**\n * Checks if the start date is after the end date and swaps them if necessary.\n * @private\n */\n private swapStartAndEndIfRequired(): void {\n if (this.data.start && this.data.end && toDate(this.data.start).getTime() > toDate(this.data.end).getTime()) {\n const t = this.data.start;\n this.data.start = this.data.end;\n this.data.end = t;\n }\n }\n\n /**\n * Get the event's recurrence id\n * @since 0.2.0\n */\n recurrenceId(): ICalDateTimeValue | null;\n\n /**\n * Set the event's recurrence id. You can use any supported date object, see\n * [readme](https://github.com/sebbo2002/ical-generator#-date-time--timezones)\n * for details about supported values and timezone handling.\n *\n * @since 0.2.0\n */\n recurrenceId(recurrenceId: ICalDateTimeValue | null): this;\n recurrenceId(recurrenceId?: ICalDateTimeValue | null): this | ICalDateTimeValue | null {\n if (recurrenceId === undefined) {\n return this.data.recurrenceId;\n }\n if (recurrenceId === null) {\n this.data.recurrenceId = null;\n return this;\n }\n\n this.data.recurrenceId = checkDate(recurrenceId, 'recurrenceId');\n return this;\n }\n\n /**\n * Get the event's timezone.\n * @since 0.2.6\n */\n timezone(): string | null;\n\n /**\n * Sets the time zone to be used for this event. If a time zone has been\n * defined in both the event and the calendar, the time zone of the event\n * is used.\n *\n * Please note that if the time zone is set, ical-generator assumes\n * that all times are already in the correct time zone. Alternatively,\n * a `moment-timezone` or a Luxon object can be passed with `setZone`,\n * ical-generator will then set the time zone itself.\n *\n * This and the 'floating' flag (see below) are mutually exclusive, and setting a timezone will unset the\n * 'floating' flag. If neither 'timezone' nor 'floating' are set, the date will be output with in UTC format\n * (see [date-time form #2 in section 3.3.5 of RFC 554](https://tools.ietf.org/html/rfc5545#section-3.3.5)).\n *\n * See [Readme](https://github.com/sebbo2002/ical-generator#-date-time--timezones) for details about\n * supported values and timezone handling.\n *\n * ```javascript\n * event.timezone('America/New_York');\n * ```\n *\n * @see https://github.com/sebbo2002/ical-generator#-date-time--timezones\n * @since 0.2.6\n */\n timezone(timezone: string | null): this;\n timezone(timezone?: string | null): this | string | null {\n if (timezone === undefined && this.data.timezone !== null) {\n return this.data.timezone;\n }\n if (timezone === undefined) {\n return this.calendar.timezone();\n }\n\n this.data.timezone = timezone && timezone !== 'UTC' ? timezone.toString() : null;\n if (this.data.timezone) {\n this.data.floating = false;\n }\n\n return this;\n }\n\n /**\n * Get the event's timestamp\n * @since 0.2.0\n * @see {@link timestamp}\n */\n stamp(): ICalDateTimeValue;\n\n /**\n * Set the appointment date of creation. Defaults to the current time and date (`new Date()`). You can use\n * any supported date object, see [readme](https://github.com/sebbo2002/ical-generator#-date-time--timezones)\n * for details about supported values and timezone handling.\n *\n * @since 0.2.0\n * @see {@link timestamp}\n */\n stamp(stamp: ICalDateTimeValue): this;\n stamp(stamp?: ICalDateTimeValue): this | ICalDateTimeValue {\n if (stamp === undefined) {\n return this.data.stamp;\n }\n\n this.data.stamp = checkDate(stamp, 'stamp');\n return this;\n }\n\n /**\n * Get the event's timestamp\n * @since 0.2.0\n * @see {@link stamp}\n */\n timestamp(): ICalDateTimeValue;\n\n /**\n * Set the appointment date of creation. Defaults to the current time and date (`new Date()`). You can use\n * any supported date object, see [readme](https://github.com/sebbo2002/ical-generator#-date-time--timezones)\n * for details about supported values and timezone handling.\n *\n * @since 0.2.0\n * @see {@link stamp}\n */\n timestamp(stamp: ICalDateTimeValue): this;\n timestamp(stamp?: ICalDateTimeValue): this | ICalDateTimeValue {\n if (stamp === undefined) {\n return this.stamp();\n }\n\n return this.stamp(stamp);\n }\n\n /**\n * Get the event's allDay flag\n * @since 0.2.0\n */\n allDay(): boolean;\n\n /**\n * Set the event's allDay flag.\n *\n * ```javascript\n * event.allDay(true); // → appointment is for the whole day\n * ```\n *\n * ```typescript\n * import ical from 'ical-generator';\n *\n * const cal = ical();\n *\n * cal.createEvent({\n * start: new Date('2020-01-01'),\n * summary: 'Very Important Day',\n * allDay: true\n * });\n *\n * cal.toString();\n * ```\n *\n * ```text\n * BEGIN:VCALENDAR\n * VERSION:2.0\n * PRODID:-//sebbo.net//ical-generator//EN\n * BEGIN:VEVENT\n * UID:1964fe8d-32c5-4f2a-bd62-7d9d7de5992b\n * SEQUENCE:0\n * DTSTAMP:20240212T191956Z\n * DTSTART;VALUE=DATE:20200101\n * X-MICROSOFT-CDO-ALLDAYEVENT:TRUE\n * X-MICROSOFT-MSNCALENDAR-ALLDAYEVENT:TRUE\n * SUMMARY:Very Important Day\n * END:VEVENT\n * END:VCALENDAR\n * ```\n *\n * @since 0.2.0\n */\n allDay(allDay: boolean): this;\n allDay(allDay?: boolean): this | boolean {\n if (allDay === undefined) {\n return this.data.allDay;\n }\n\n this.data.allDay = Boolean(allDay);\n return this;\n }\n\n /**\n * Get the event's floating flag.\n * @since 0.2.0\n */\n floating(): boolean;\n floating(floating: boolean): this;\n\n /**\n * Set the event's floating flag. This unsets the event's timezone.\n * Events whose floating flag is set to true always take place at the\n * same time, regardless of the time zone.\n *\n * ```typescript\n * import ical from 'ical-generator';\n *\n * const cal = ical();\n *\n * cal.createEvent({\n * start: new Date('2020-01-01T20:00:00Z'),\n * summary: 'Always at 20:00 in every <Timezone',\n * floating: true\n * });\n *\n * cal.toString();\n * ```\n *\n * ```text\n * BEGIN:VCALENDAR\n * VERSION:2.0\n * PRODID:-//sebbo.net//ical-generator//EN\n * BEGIN:VEVENT\n * UID:5d7278f9-ada3-40ef-83d1-23c29ce0a763\n * SEQUENCE:0\n * DTSTAMP:20240212T192214Z\n * DTSTART:20200101T200000\n * SUMMARY:Always at 20:00 in every <Timezone\n * END:VEVENT\n * END:VCALENDAR\n * ```\n *\n * @since 0.2.0\n */\n floating(floating?: boolean): this | boolean {\n if (floating === undefined) {\n return this.data.floating;\n }\n\n this.data.floating = Boolean(floating);\n if (this.data.floating) {\n this.data.timezone = null;\n }\n\n return this;\n }\n\n /**\n * Get the event's repeating options\n * @since 0.2.0\n */\n repeating(): ICalEventJSONRepeatingData | ICalRRuleStub | string | null;\n\n /**\n * Set the event's repeating options by passing an {@link ICalRepeatingOptions} object.\n *\n * ```javascript\n * event.repeating({\n * freq: 'MONTHLY', // required\n * count: 5,\n * interval: 2,\n * until: new Date('Jan 01 2014 00:00:00 UTC'),\n * byDay: ['su', 'mo'], // repeat only sunday and monday\n * byMonth: [1, 2], // repeat only in january and february,\n * byMonthDay: [1, 15], // repeat only on the 1st and 15th\n * bySetPos: 3, // repeat every 3rd sunday (will take the first element of the byDay array)\n * exclude: [new Date('Dec 25 2013 00:00:00 UTC')], // exclude these dates\n * excludeTimezone: 'Europe/Berlin', // timezone of exclude\n * wkst: 'SU' // Start the week on Sunday, default is Monday\n * });\n * ```\n *\n * **Example:**\n *\n *```typescript\n * import ical, { ICalEventRepeatingFreq } from 'ical-generator';\n *\n * const cal = ical();\n *\n * const event = cal.createEvent({\n * start: new Date('2020-01-01T20:00:00Z'),\n * summary: 'Repeating Event'\n * });\n * event.repeating({\n * freq: ICalEventRepeatingFreq.WEEKLY,\n * count: 4\n * });\n *\n * cal.toString();\n * ```\n *\n * ```text\n * BEGIN:VCALENDAR\n * VERSION:2.0\n * PRODID:-//sebbo.net//ical-generator//EN\n * BEGIN:VEVENT\n * UID:b80e6a68-c2cd-48f5-b94d-cecc7ce83871\n * SEQUENCE:0\n * DTSTAMP:20240212T193646Z\n * DTSTART:20200101T200000Z\n * RRULE:FREQ=WEEKLY;COUNT=4\n * SUMMARY:Repeating Event\n * END:VEVENT\n * END:VCALENDAR\n * ```\n *\n * @since 0.2.0\n */\n repeating(repeating: ICalRepeatingOptions | null): this;\n\n /**\n * Set the event's repeating options by passing an [RRule object](https://github.com/jakubroztocil/rrule).\n * @since 2.0.0-develop.5\n *\n * ```typescript\n * import ical from 'ical-generator';\n * import { datetime, RRule } from 'rrule';\n *\n * const cal = ical();\n *\n * const event = cal.createEvent({\n * start: new Date('2020-01-01T20:00:00Z'),\n * summary: 'Repeating Event'\n * });\n *\n * const rule = new RRule({\n * freq: RRule.WEEKLY,\n * interval: 5,\n * byweekday: [RRule.MO, RRule.FR],\n * dtstart: datetime(2012, 2, 1, 10, 30),\n * until: datetime(2012, 12, 31)\n * })\n * event.repeating(rule);\n *\n * cal.toString();\n * ```\n *\n * ```text\n * BEGIN:VCALENDAR\n * VERSION:2.0\n * PRODID:-//sebbo.net//ical-generator//EN\n * BEGIN:VEVENT\n * UID:36585e40-8fa8-460d-af0c-88b6f434030b\n * SEQUENCE:0\n * DTSTAMP:20240212T193827Z\n * DTSTART:20200101T200000Z\n * RRULE:FREQ=WEEKLY;INTERVAL=5;BYDAY=MO,FR;UNTIL=20121231T000000Z\n * SUMMARY:Repeating Event\n * END:VEVENT\n * END:VCALENDAR\n * ```\n */\n repeating(repeating: ICalRRuleStub | null): this;\n\n /**\n * Set the events repeating options by passing a string which is inserted in the ical file.\n * @since 2.0.0-develop.5\n */\n repeating(repeating: string | null): this;\n\n /**\n * @internal\n */\n repeating(repeating: ICalRepeatingOptions | ICalRRuleStub | string | null): this;\n repeating(repeating?: ICalRepeatingOptions | ICalRRuleStub | string | null): this | ICalEventJSONRepeatingData | ICalRRuleStub | string | null {\n if (repeating === undefined) {\n return this.data.repeating;\n }\n if (!repeating) {\n this.data.repeating = null;\n return this;\n }\n if(isRRule(repeating) || typeof repeating === 'string') {\n this.data.repeating = repeating;\n return this;\n }\n\n this.data.repeating = {\n freq: checkEnum(ICalEventRepeatingFreq, repeating.freq) as ICalEventRepeatingFreq\n };\n\n if (repeating.count) {\n if (!isFinite(repeating.count)) {\n throw new Error('`repeating.count` must be a finite number!');\n }\n\n this.data.repeating.count = repeating.count;\n }\n\n if (repeating.interval) {\n if (!isFinite(repeating.interval)) {\n throw new Error('`repeating.interval` must be a finite number!');\n }\n\n this.data.repeating.interval = repeating.interval;\n }\n\n if (repeating.until !== undefined) {\n this.data.repeating.until = checkDate(repeating.until, 'repeating.until');\n }\n\n if (repeating.byDay) {\n const byDayArray = Array.isArray(repeating.byDay) ? repeating.byDay : [repeating.byDay];\n this.data.repeating.byDay = byDayArray.map(day => checkEnum(ICalWeekday, day) as ICalWeekday);\n }\n\n if (repeating.byMonth) {\n const byMonthArray = Array.isArray(repeating.byMonth) ? repeating.byMonth : [repeating.byMonth];\n this.data.repeating.byMonth = byMonthArray.map(month => {\n if (typeof month !== 'number' || month < 1 || month > 12) {\n throw new Error('`repeating.byMonth` contains invalid value `' + month + '`!');\n }\n\n return month;\n });\n }\n\n if (repeating.byMonthDay) {\n const byMonthDayArray = Array.isArray(repeating.byMonthDay) ? repeating.byMonthDay : [repeating.byMonthDay];\n\n\n this.data.repeating.byMonthDay = byMonthDayArray.map(monthDay => {\n if (typeof monthDay !== 'number' || monthDay < -31 || monthDay > 31 || monthDay === 0) {\n throw new Error('`repeating.byMonthDay` contains invalid value `' + monthDay + '`!');\n }\n\n return monthDay;\n });\n }\n\n if (repeating.bySetPos) {\n if (!this.data.repeating.byDay) {\n throw '`repeating.bySetPos` must be used along with `repeating.byDay`!';\n }\n const bySetPosArray = Array.isArray(repeating.bySetPos) ? repeating.bySetPos : [repeating.bySetPos];\n this.data.repeating.bySetPos = bySetPosArray.map(bySetPos => {\n if (typeof bySetPos !== 'number' || bySetPos < -366 || bySetPos > 366 || bySetPos === 0) {\n throw '`repeating.bySetPos` contains invalid value `' + bySetPos + '`!';\n }\n return bySetPos;\n });\n }\n\n if (repeating.exclude) {\n const excludeArray = Array.isArray(repeating.exclude) ? repeating.exclude : [repeating.exclude];\n this.data.repeating.exclude = excludeArray.map((exclude, i) => {\n return checkDate(exclude, `repeating.exclude[${i}]`);\n });\n }\n\n if (repeating.startOfWeek) {\n this.data.repeating.startOfWeek = checkEnum(ICalWeekday, repeating.startOfWeek) as ICalWeekday;\n }\n\n return this;\n }\n\n /**\n * Get the event's summary\n * @since 0.2.0\n */\n summary(): string;\n\n /**\n * Set the event's summary.\n * Defaults to an empty string if nothing is set.\n *\n * @since 0.2.0\n */\n summary(summary: string): this;\n summary(summary?: string): this | string {\n if (summary === undefined) {\n return this.data.summary;\n }\n\n this.data.summary = summary ? String(summary) : '';\n return this;\n }\n\n\n /**\n * Get the event's location\n * @since 0.2.0\n */\n location(): ICalLocation | null;\n\n /**\n * Set the event's location by passing a string (minimum) or\n * an {@link ICalLocationWithTitle} object which will also fill the iCal\n * `GEO` attribute and Apple's `X-APPLE-STRUCTURED-LOCATION`.\n *\n * ```javascript\n * event.location({\n * title: 'Apple Store Kurfürstendamm',\n * address: 'Kurfürstendamm 26, 10719 Berlin, Deutschland',\n * radius: 141.1751386318387,\n * geo: {\n * lat: 52.503630,\n * lon: 13.328650\n * }\n * });\n * ```\n *\n * ```text\n * LOCATION:Apple Store Kurfürstendamm\\nKurfürstendamm 26\\, 10719 Berlin\\,\n * Deutschland\n * X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=Kurfürstendamm 26\\, 10719\n * Berlin\\, Deutschland;X-APPLE-RADIUS=141.1751386318387;X-TITLE=Apple Store\n * Kurfürstendamm:geo:52.50363,13.32865\n * GEO:52.50363;13.32865\n * ```\n *\n * Since v6.1.0 you can also pass a {@link ICalLocationWithoutTitle} object to pass\n * the geolocation only. This will only fill the iCal `GEO` attribute.\n *\n * ```javascript\n * event.location({\n * geo: {\n * lat: 52.503630,\n * lon: 13.328650\n * }\n * });\n * ```\n *\n * ```text\n * GEO:52.50363;13.32865\n * ```\n *\n * @since 0.2.0\n */\n location(location: ICalLocation | string | null): this;\n location(location?: ICalLocation | string | null): this | ICalLocation | null {\n if (location === undefined) {\n return this.data.location;\n }\n if (typeof location === 'string') {\n this.data.location = {\n title: location\n };\n return this;\n }\n if (location && (\n ('title' in location && !location.title) ||\n (location?.geo && (typeof location.geo.lat !== 'number' || !isFinite(location.geo.lat) || typeof location.geo.lon !== 'number' || !isFinite(location.geo.lon))) ||\n (!('title' in location) && !location?.geo)\n )) {\n throw new Error(\n '`location` isn\\'t formatted correctly. See https://sebbo2002.github.io/ical-generator/'+\n 'develop/reference/classes/ICalEvent.html#location'\n );\n }\n\n this.data.location = location || null;\n return this;\n }\n\n\n /**\n * Get the event's description as an {@link ICalDescription} object.\n * @since 0.2.0\n */\n description(): ICalDescription | null;\n\n /**\n * Set the events description by passing a plaintext string or\n * an object containing both a plaintext and a html description.\n * Only a few calendar apps support html descriptions and like in\n * emails, supported HTML tags and styling is limited.\n *\n * ```javascript\n * event.description({\n * plain: 'Hello World!',\n * html: '<p>Hello World!</p>'\n * });\n * ```\n *\n * ```text\n * DESCRIPTION:Hello World!\n * X-ALT-DESC;FMTTYPE=text/html:<p>Hello World!</p>\n * ```\n *\n * @since 0.2.0