iamcal 1.1.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -11
- package/lib/component.d.ts +62 -0
- package/lib/component.d.ts.map +1 -0
- package/lib/component.js +208 -0
- package/lib/components/Calendar.d.ts +66 -0
- package/lib/components/Calendar.d.ts.map +1 -0
- package/lib/components/Calendar.js +121 -0
- package/lib/components/CalendarEvent.d.ts +109 -0
- package/lib/components/CalendarEvent.d.ts.map +1 -0
- package/lib/components/CalendarEvent.js +223 -0
- package/lib/components/TimeZone.d.ts +74 -0
- package/lib/components/TimeZone.d.ts.map +1 -0
- package/lib/components/TimeZone.js +127 -0
- package/lib/components/TimeZoneOffset.d.ts +103 -0
- package/lib/components/TimeZoneOffset.d.ts.map +1 -0
- package/lib/components/TimeZoneOffset.js +148 -0
- package/lib/components/index.d.ts +5 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +21 -0
- package/lib/date.d.ts +165 -0
- package/lib/date.d.ts.map +1 -0
- package/lib/date.js +373 -0
- package/lib/index.d.ts +6 -153
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +21 -469
- package/lib/io.d.ts +10 -8
- package/lib/io.d.ts.map +1 -1
- package/lib/io.js +18 -13
- package/lib/parse.d.ts +33 -16
- package/lib/parse.d.ts.map +1 -1
- package/lib/parse.js +59 -56
- package/lib/patterns.d.ts +36 -0
- package/lib/patterns.d.ts.map +1 -0
- package/lib/patterns.js +50 -0
- package/lib/property.d.ts +149 -0
- package/lib/property.d.ts.map +1 -0
- package/lib/property.js +450 -0
- package/package.json +48 -43
- package/src/component.ts +248 -0
- package/src/components/Calendar.ts +162 -0
- package/src/components/CalendarEvent.ts +270 -0
- package/src/components/TimeZone.ts +152 -0
- package/src/components/TimeZoneOffset.ts +187 -0
- package/src/components/index.ts +4 -0
- package/src/date.ts +395 -0
- package/src/index.ts +6 -576
- package/src/io.ts +17 -13
- package/src/parse.ts +72 -52
- package/src/patterns.ts +69 -0
- package/src/property.ts +492 -0
- package/lib/types.d.ts +0 -6
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -3
- package/src/types.ts +0 -5
package/src/component.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { CalendarDateOrTime } from './date'
|
|
2
|
+
import {
|
|
3
|
+
AllowedPropertyName,
|
|
4
|
+
KnownPropertyName,
|
|
5
|
+
MissingPropertyError,
|
|
6
|
+
Property,
|
|
7
|
+
PropertyValidationError,
|
|
8
|
+
validateProperty,
|
|
9
|
+
} from './property'
|
|
10
|
+
|
|
11
|
+
/** Represents an error which occurs while validating a calendar component. */
|
|
12
|
+
export class ComponentValidationError extends Error {
|
|
13
|
+
constructor(message: string) {
|
|
14
|
+
super(message)
|
|
15
|
+
this.name = 'ComponentValidationError'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Max line length as defined by RFC 5545 3.1.
|
|
20
|
+
const MAX_LINE_LENGTH = 75
|
|
21
|
+
|
|
22
|
+
export class Component {
|
|
23
|
+
name: string
|
|
24
|
+
properties: Property[]
|
|
25
|
+
components: Component[]
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
name: string,
|
|
29
|
+
properties?: Property[],
|
|
30
|
+
components?: Component[]
|
|
31
|
+
) {
|
|
32
|
+
this.name = name
|
|
33
|
+
if (properties) {
|
|
34
|
+
this.properties = properties
|
|
35
|
+
} else {
|
|
36
|
+
this.properties = []
|
|
37
|
+
}
|
|
38
|
+
if (components) {
|
|
39
|
+
this.components = components
|
|
40
|
+
} else {
|
|
41
|
+
this.components = []
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
serialize(): string {
|
|
46
|
+
// Create lines
|
|
47
|
+
const lines = [`BEGIN:${this.name}`]
|
|
48
|
+
|
|
49
|
+
for (const property of this.properties) {
|
|
50
|
+
let line =
|
|
51
|
+
property['name'] + //
|
|
52
|
+
property.params.map(p => ';' + p).join('') +
|
|
53
|
+
':' +
|
|
54
|
+
property['value']
|
|
55
|
+
|
|
56
|
+
// Wrap lines
|
|
57
|
+
while (line.length > MAX_LINE_LENGTH) {
|
|
58
|
+
lines.push(line.substring(0, MAX_LINE_LENGTH))
|
|
59
|
+
line = ' ' + line.substring(MAX_LINE_LENGTH)
|
|
60
|
+
}
|
|
61
|
+
lines.push(line)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const component of this.components) {
|
|
65
|
+
lines.push(component.serialize())
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
lines.push(`END:${this.name}`)
|
|
69
|
+
|
|
70
|
+
// Join lines
|
|
71
|
+
const serialized = lines.join('\n')
|
|
72
|
+
|
|
73
|
+
return serialized
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getProperty(name: string): Property | null {
|
|
77
|
+
for (const property of this.properties) {
|
|
78
|
+
if (property.name === name) {
|
|
79
|
+
return property
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setProperty(name: string, value: string | CalendarDateOrTime): this {
|
|
86
|
+
for (const property of this.properties) {
|
|
87
|
+
if (property.name === name) {
|
|
88
|
+
if (typeof value === 'string') {
|
|
89
|
+
property.value = value
|
|
90
|
+
} else {
|
|
91
|
+
const prop = value.toProperty(name)
|
|
92
|
+
property.params = prop.params
|
|
93
|
+
property.value = prop.value
|
|
94
|
+
}
|
|
95
|
+
return this
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
this.properties.push(
|
|
99
|
+
typeof value === 'string'
|
|
100
|
+
? {
|
|
101
|
+
name: name,
|
|
102
|
+
params: [],
|
|
103
|
+
value: value,
|
|
104
|
+
}
|
|
105
|
+
: value.toProperty(name)
|
|
106
|
+
)
|
|
107
|
+
return this
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
hasProperty(name: string, value?: string): boolean {
|
|
111
|
+
for (const property of this.properties) {
|
|
112
|
+
if (
|
|
113
|
+
property.name === name &&
|
|
114
|
+
(value === undefined || property.value === value)
|
|
115
|
+
) {
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
removePropertiesWithName(name: string) {
|
|
123
|
+
const index = this.properties.findIndex(p => p.name === name)
|
|
124
|
+
if (index === -1) return
|
|
125
|
+
// Remove property at index
|
|
126
|
+
this.properties.splice(index, 1)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getPropertyParams(name: string): string[] | null {
|
|
130
|
+
for (const property of this.properties) {
|
|
131
|
+
if (property.name === name) {
|
|
132
|
+
return property.params
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setPropertyParams(name: string, params: string[]): this {
|
|
139
|
+
for (const property of this.properties) {
|
|
140
|
+
if (property.name === name) {
|
|
141
|
+
property.params = params
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return this
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
addComponent(component: Component): this {
|
|
148
|
+
this.components.push(component)
|
|
149
|
+
return this
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
addComponents(components: Component[]): this {
|
|
153
|
+
this.components.push(...components)
|
|
154
|
+
return this
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Remove a component from this component.
|
|
159
|
+
* @param component The component to remove.
|
|
160
|
+
* @returns `true` if the component was removed. `false` if the component was not present.
|
|
161
|
+
*/
|
|
162
|
+
removeComponent(component: Component): boolean {
|
|
163
|
+
const index = this.components.indexOf(component)
|
|
164
|
+
if (index === -1) return false
|
|
165
|
+
|
|
166
|
+
// Remove element at index from list
|
|
167
|
+
this.components.splice(index, 1)
|
|
168
|
+
|
|
169
|
+
return true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getComponentsWithName(name: string): Component[] {
|
|
173
|
+
const components: Component[] = []
|
|
174
|
+
|
|
175
|
+
for (const component of this.components) {
|
|
176
|
+
if (component.name === name) {
|
|
177
|
+
components.push(component)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return components
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Validate the component according to the rules of the iCalendar
|
|
186
|
+
* specification.
|
|
187
|
+
*
|
|
188
|
+
* This method should be overridden by subclasses to
|
|
189
|
+
* implement specific validation logic. The methods
|
|
190
|
+
* {@link validateRequiredProperty} and {@link validateOptionalProperty} may
|
|
191
|
+
* help with this.
|
|
192
|
+
* @throws {ComponentValidationError} If the component is invalid.
|
|
193
|
+
*/
|
|
194
|
+
validate(): void {}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Validate that a property exists and is valid using {@link validateProperty}.
|
|
198
|
+
* @param propertyName The name of the property to validate.
|
|
199
|
+
* @throws {MissingPropertyError} If the property doesn't exist on this component.
|
|
200
|
+
* @throws {PropertyValidationError} If the property exists but is invalid.
|
|
201
|
+
*/
|
|
202
|
+
validateRequiredProperty(propertyName: AllowedPropertyName) {
|
|
203
|
+
const property = this.getProperty(propertyName)
|
|
204
|
+
if (property == null)
|
|
205
|
+
throw new MissingPropertyError(
|
|
206
|
+
`Missing required property ${propertyName}`
|
|
207
|
+
)
|
|
208
|
+
validateProperty(property)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Validate that a property is valid using {@link validateProperty} if it exists.
|
|
213
|
+
* @param propertyName The name of the property to validate.
|
|
214
|
+
* @throws {PropertyValidationError} If the property exists and is invalid.
|
|
215
|
+
*/
|
|
216
|
+
validateOptionalProperty(propertyName: AllowedPropertyName) {
|
|
217
|
+
const property = this.getProperty(propertyName)
|
|
218
|
+
if (property != null) validateProperty(property)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Validate all properties on this component.
|
|
223
|
+
*
|
|
224
|
+
* If a property is in `requiredProperties` it is
|
|
225
|
+
* validated with {@link validateRequiredProperty}, otherwise it is
|
|
226
|
+
* validated with {@link validateOptionalProperty}.
|
|
227
|
+
* @param requiredProperties A list of property names which are required on this component.
|
|
228
|
+
*/
|
|
229
|
+
validateAllProperties(requiredProperties?: AllowedPropertyName[]) {
|
|
230
|
+
requiredProperties?.forEach(propertyName => {
|
|
231
|
+
if (!this.hasProperty(propertyName)) {
|
|
232
|
+
throw new MissingPropertyError(
|
|
233
|
+
`Missing required property ${propertyName}`
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
this.properties.forEach(property => {
|
|
239
|
+
if (
|
|
240
|
+
requiredProperties?.includes(property.name as KnownPropertyName)
|
|
241
|
+
) {
|
|
242
|
+
this.validateRequiredProperty(property.name)
|
|
243
|
+
} else {
|
|
244
|
+
this.validateOptionalProperty(property.name)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { KnownPropertyName } from '../property'
|
|
2
|
+
import { Component, ComponentValidationError } from '../component'
|
|
3
|
+
import { CalendarEvent } from './CalendarEvent'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a VCALENDAR component, the root component of an iCalendar file.
|
|
7
|
+
*/
|
|
8
|
+
export class Calendar extends Component {
|
|
9
|
+
name = 'VCALENDAR'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param prodid A unique identifier of the program creating the calendar.
|
|
13
|
+
* @example
|
|
14
|
+
* new Calendar('-//Google Inc//Google Calendar 70.9054//EN')
|
|
15
|
+
*/
|
|
16
|
+
constructor(prodid: string)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param prodid A unique identifier of the program creating the calendar.
|
|
20
|
+
* @param version The version of the iCalendar specification that this calendar uses.
|
|
21
|
+
* @example
|
|
22
|
+
* new Calendar('-//Google Inc//Google Calendar 70.9054//EN')
|
|
23
|
+
*/
|
|
24
|
+
constructor(prodid: string, version: string)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param component A VCALENDAR component.
|
|
28
|
+
*/
|
|
29
|
+
constructor(component: Component)
|
|
30
|
+
constructor(a?: string | Component, b?: string) {
|
|
31
|
+
let component: Component
|
|
32
|
+
if (a instanceof Component) {
|
|
33
|
+
component = a as Component
|
|
34
|
+
Calendar.prototype.validate.call(component)
|
|
35
|
+
} else {
|
|
36
|
+
const prodid = a as string
|
|
37
|
+
const version = (b as string) ?? '2.0'
|
|
38
|
+
component = new Component('VCALENDAR')
|
|
39
|
+
component.setProperty('PRODID', prodid)
|
|
40
|
+
component.setProperty('VERSION', version)
|
|
41
|
+
}
|
|
42
|
+
super(component.name, component.properties, component.components)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
validate() {
|
|
46
|
+
if (this.name !== 'VCALENDAR')
|
|
47
|
+
throw new ComponentValidationError(
|
|
48
|
+
'Component name must be VCALENDAR'
|
|
49
|
+
)
|
|
50
|
+
const requiredProperties: KnownPropertyName[] = ['PRODID', 'VERSION']
|
|
51
|
+
this.validateAllProperties(requiredProperties)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getEvents(): CalendarEvent[] {
|
|
55
|
+
return this.getComponentsWithName('VEVENT').map(
|
|
56
|
+
c => new CalendarEvent(c)
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
removeEvent(event: CalendarEvent): boolean
|
|
61
|
+
removeEvent(uid: string): boolean
|
|
62
|
+
removeEvent(a: CalendarEvent | string): boolean {
|
|
63
|
+
if (a instanceof CalendarEvent) {
|
|
64
|
+
const event = a as CalendarEvent
|
|
65
|
+
return this.removeComponent(event)
|
|
66
|
+
} else {
|
|
67
|
+
const uid = a as string
|
|
68
|
+
for (const event of this.getEvents()) {
|
|
69
|
+
if (event.getUid() !== uid) continue
|
|
70
|
+
return this.removeComponent(event)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getProductId(): string {
|
|
77
|
+
return this.getProperty('PRODID')!.value
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
setProductId(value: string): this {
|
|
81
|
+
return this.setProperty('PRODID', value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getVersion(): string {
|
|
85
|
+
return this.getProperty('VERSION')!.value
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setVersion(value: string): this {
|
|
89
|
+
return this.setProperty('VERSION', value)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getCalendarScale(): string | undefined {
|
|
93
|
+
return this.getProperty('CALSCALE')?.value
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setCalendarScale(value: string): this {
|
|
97
|
+
return this.setProperty('CALSCALE', value)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
removeCalendarScale() {
|
|
101
|
+
this.removePropertiesWithName('CALSCALE')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getMethod(): string | undefined {
|
|
105
|
+
return this.getProperty('METHOD')?.value
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setMethod(value: string): this {
|
|
109
|
+
return this.setProperty('METHOD', value)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
removeMethod() {
|
|
113
|
+
this.removePropertiesWithName('METHOD')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getCalendarName(): string | undefined {
|
|
117
|
+
return this.getProperty('X-WR-CALNAME')?.value
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setCalendarName(value: string): this {
|
|
121
|
+
return this.setProperty('X-WR-CALNAME', value)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
removeCalendarName() {
|
|
125
|
+
this.removePropertiesWithName('X-WR-CALNAME')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getCalendarDescription(): string | undefined {
|
|
129
|
+
return this.getProperty('X-WR-CALDESC')?.value
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
setCalendarDescription(value: string): this {
|
|
133
|
+
return this.setProperty('X-WR-CALDESC', value)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
removeCalendarDescription() {
|
|
137
|
+
this.removePropertiesWithName('X-WR-CALDESC')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* eslint-disable */
|
|
141
|
+
|
|
142
|
+
/** @deprecated use {@link getEvents} instead */
|
|
143
|
+
events = this.getEvents
|
|
144
|
+
/** @deprecated use {@link getProductId} instead */
|
|
145
|
+
prodId = this.getProductId
|
|
146
|
+
/** @deprecated use {@link setProductId} instead */
|
|
147
|
+
setProdId = this.setProductId
|
|
148
|
+
/** @deprecated use {@link getVersion} instead */
|
|
149
|
+
version = this.getVersion
|
|
150
|
+
/** @deprecated use {@link getCalendarScale} instead */
|
|
151
|
+
calScale = this.getCalendarScale
|
|
152
|
+
/** @deprecated use {@link setCalendarScale} instead */
|
|
153
|
+
setCalScale = this.setCalendarScale
|
|
154
|
+
/** @deprecated use {@link removeCalendarScale} instead */
|
|
155
|
+
removeCalScale = this.removeCalendarScale
|
|
156
|
+
/** @deprecated use {@link getMethod} instead */
|
|
157
|
+
method = this.getMethod
|
|
158
|
+
/** @deprecated use {@link getCalendarName} instead */
|
|
159
|
+
calendarName = this.getCalendarName
|
|
160
|
+
/** @deprecated use {@link getCalendarDescription} instead */
|
|
161
|
+
calendarDescription = this.getCalendarDescription
|
|
162
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { KnownPropertyName, PropertyValidationError } from '../property'
|
|
2
|
+
import { ComponentValidationError, Component } from '../component'
|
|
3
|
+
import {
|
|
4
|
+
CalendarDateTime,
|
|
5
|
+
convertDate,
|
|
6
|
+
CalendarDateOrTime,
|
|
7
|
+
parseDateProperty,
|
|
8
|
+
toDateTimeString,
|
|
9
|
+
} from '../date'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Represents a VEVENT component, representing an event in a calendar.
|
|
13
|
+
*/
|
|
14
|
+
export class CalendarEvent extends Component {
|
|
15
|
+
name = 'VEVENT'
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
uid: string,
|
|
19
|
+
dtstamp: CalendarDateTime | Date,
|
|
20
|
+
dtstart: CalendarDateOrTime | Date
|
|
21
|
+
)
|
|
22
|
+
constructor(component: Component)
|
|
23
|
+
constructor(
|
|
24
|
+
a: string | Component,
|
|
25
|
+
b?: CalendarDateTime | Date,
|
|
26
|
+
c?: CalendarDateOrTime | Date
|
|
27
|
+
) {
|
|
28
|
+
let component: Component
|
|
29
|
+
if (a instanceof Component) {
|
|
30
|
+
component = a as Component
|
|
31
|
+
CalendarEvent.prototype.validate.call(component)
|
|
32
|
+
} else {
|
|
33
|
+
const uid = a as string
|
|
34
|
+
const dtstamp = convertDate(b!, false)
|
|
35
|
+
const dtstart = convertDate(c!)
|
|
36
|
+
component = new Component('VEVENT')
|
|
37
|
+
component.setProperty('UID', uid)
|
|
38
|
+
component.setProperty('DTSTAMP', dtstamp)
|
|
39
|
+
component.setProperty('DTSTART', dtstart)
|
|
40
|
+
}
|
|
41
|
+
super(component.name, component.properties, component.components)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
serialize(): string {
|
|
45
|
+
if (!this.getEnd() && !this.duration()) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'Failed to serialize calendar event, end or duration must be set'
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
return super.serialize()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
validate() {
|
|
54
|
+
if (this.name !== 'VEVENT')
|
|
55
|
+
throw new ComponentValidationError('Component name must be VEVENT')
|
|
56
|
+
const requiredProperties: KnownPropertyName[] = [
|
|
57
|
+
'UID',
|
|
58
|
+
'DTSTAMP',
|
|
59
|
+
'DTSTART',
|
|
60
|
+
]
|
|
61
|
+
this.validateAllProperties(requiredProperties)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getStamp(): CalendarDateTime {
|
|
65
|
+
return parseDateProperty(
|
|
66
|
+
this.getProperty('DTSTAMP')!
|
|
67
|
+
) as CalendarDateTime
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setStamp(value: CalendarDateTime | Date): this {
|
|
71
|
+
const converted = convertDate(value, false)
|
|
72
|
+
if (converted.isFullDay()) {
|
|
73
|
+
throw new PropertyValidationError('DTSTAMP cannot be of type DATE')
|
|
74
|
+
}
|
|
75
|
+
return this.setProperty('DTSTAMP', converted)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getUid(): string {
|
|
79
|
+
return this.getProperty('UID')!.value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setUid(value: string): this {
|
|
83
|
+
return this.setProperty('UID', value)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getSummary(): string | undefined {
|
|
87
|
+
return this.getProperty('SUMMARY')?.value
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setSummary(value: string): this {
|
|
91
|
+
return this.setProperty('SUMMARY', value)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
removeSummary() {
|
|
95
|
+
this.removePropertiesWithName('SUMMARY')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getDescription(): string | undefined {
|
|
99
|
+
return this.getProperty('DESCRIPTION')?.value
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setDescription(value: string): this {
|
|
103
|
+
return this.setProperty('DESCRIPTION', value)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
removeDescription() {
|
|
107
|
+
this.removePropertiesWithName('DESCRIPTION')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getLocation(): string | undefined {
|
|
111
|
+
return this.getProperty('LOCATION')?.value
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setLocation(value: string): this {
|
|
115
|
+
return this.setProperty('LOCATION', value)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
removeLocation() {
|
|
119
|
+
this.removePropertiesWithName('LOCATION')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the start date or time of the event.
|
|
124
|
+
* @returns The start date or time of the event as a {@link CalendarDateOrTime}.
|
|
125
|
+
*/
|
|
126
|
+
getStart(): CalendarDateOrTime {
|
|
127
|
+
return parseDateProperty(this.getProperty('DTSTART')!)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Set the start date or time of the event.
|
|
132
|
+
* @param value The start date of the event as a {@link CalendarDateOrTime} or `Date`.
|
|
133
|
+
* @returns The CalendarEvent instance for chaining.
|
|
134
|
+
*/
|
|
135
|
+
setStart(value: CalendarDateOrTime | Date): this {
|
|
136
|
+
return this.setProperty('DTSTART', convertDate(value))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the non-inclusive end of the event.
|
|
141
|
+
* @returns The end date of the event as a {@link CalendarDateOrTime} or `undefined` if not set.
|
|
142
|
+
*/
|
|
143
|
+
getEnd(): CalendarDateOrTime | undefined {
|
|
144
|
+
const property = this.getProperty('DTEND')
|
|
145
|
+
if (!property) return
|
|
146
|
+
return parseDateProperty(property)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Set the exclusive end of the event.
|
|
151
|
+
*
|
|
152
|
+
* Will remove 'duration' if present.
|
|
153
|
+
* @param value The end date of the event as a {@link CalendarDateOrTime} or `Date`.
|
|
154
|
+
* @returns The CalendarEvent instance for chaining.
|
|
155
|
+
* @throws If the end date is a full day date and the start date is a date-time, or vice versa.
|
|
156
|
+
*/
|
|
157
|
+
setEnd(value: CalendarDateOrTime | Date): this {
|
|
158
|
+
const date = convertDate(value)
|
|
159
|
+
const start = this.getStart()
|
|
160
|
+
if (date.isFullDay() !== start.isFullDay()) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`End must be same date type as start. Start is ${start.isFullDay() ? 'date' : 'datetime'} but new end value is ${date.isFullDay() ? 'date' : 'datetime'}`
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.removeDuration()
|
|
167
|
+
return this.setProperty('DTEND', date)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Remove the end of the event.
|
|
172
|
+
*
|
|
173
|
+
* NOTE: An event must have either an end or a duration set.
|
|
174
|
+
*/
|
|
175
|
+
removeEnd() {
|
|
176
|
+
this.removePropertiesWithName('DTEND')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get the duration of the event as a string formatted according to the iCalendar specification.
|
|
181
|
+
* @returns The duration of the event, or `undefined` if not set.
|
|
182
|
+
*/
|
|
183
|
+
getDuration(): string | undefined {
|
|
184
|
+
return this.getProperty('DURATION')?.value
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Set the duration of the event.
|
|
189
|
+
*
|
|
190
|
+
* Will remove 'end' if present.
|
|
191
|
+
* @param value The duration of the event as a string in the format defined by RFC5545.
|
|
192
|
+
* @returns The CalendarEvent instance for chaining.
|
|
193
|
+
* @example
|
|
194
|
+
* // Set duration to 1 hour and 30 minutes.
|
|
195
|
+
* event.setDuration(`PT1H30M`)
|
|
196
|
+
*/
|
|
197
|
+
setDuration(value: string): this {
|
|
198
|
+
this.removeEnd()
|
|
199
|
+
return this.setProperty('DURATION', value)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Remove the duration of the event.
|
|
204
|
+
*
|
|
205
|
+
* NOTE: An event must have either an end or a duration set.
|
|
206
|
+
*/
|
|
207
|
+
removeDuration() {
|
|
208
|
+
this.removePropertiesWithName('DURATION')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getCreated(): CalendarDateOrTime | undefined {
|
|
212
|
+
const property = this.getProperty('CREATED')
|
|
213
|
+
if (!property) return
|
|
214
|
+
return parseDateProperty(property)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
setCreated(value: Date): this {
|
|
218
|
+
return this.setProperty('CREATED', toDateTimeString(value))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
removeCreated() {
|
|
222
|
+
this.removePropertiesWithName('CREATED')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
getGeographicPosition(): [number, number] | undefined {
|
|
226
|
+
const text = this.getProperty('GEO')?.value
|
|
227
|
+
if (!text) return
|
|
228
|
+
const validGeoPattern = /^[+-]?\d+(\.\d+)?;[+-]?\d+(\.\d+)?$/
|
|
229
|
+
if (!validGeoPattern.test(text))
|
|
230
|
+
throw new Error(`Failed to parse GEO property: ${text}`)
|
|
231
|
+
const [longitude, latitude] = text.split(',')
|
|
232
|
+
return [parseFloat(longitude), parseFloat(latitude)]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
setGeographicPosition(latitude: number, longitude: number): this {
|
|
236
|
+
const text = `${latitude};${longitude}`
|
|
237
|
+
return this.setProperty('GEO', text)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
removeGeographicLocation() {
|
|
241
|
+
this.removePropertiesWithName('GEO')
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* eslint-disable */
|
|
245
|
+
|
|
246
|
+
/** @deprecated use {@link getStamp} instead */
|
|
247
|
+
stamp = this.getStamp
|
|
248
|
+
/** @deprecated use {@link getUid} instead */
|
|
249
|
+
uid = this.getUid
|
|
250
|
+
/** @deprecated use {@link getSummary} instead */
|
|
251
|
+
summary = this.getSummary
|
|
252
|
+
/** @deprecated use {@link getDescription} instead */
|
|
253
|
+
description = this.getDescription
|
|
254
|
+
/** @deprecated use {@link getLocation} instead */
|
|
255
|
+
location = this.getLocation
|
|
256
|
+
/** @deprecated use {@link getStart} instead */
|
|
257
|
+
start = this.getStart
|
|
258
|
+
/** @deprecated use {@link getEnd} instead */
|
|
259
|
+
end = this.getEnd
|
|
260
|
+
/** @deprecated use {@link getDuration} instead */
|
|
261
|
+
duration = this.getDuration
|
|
262
|
+
/** @deprecated use {@link getCreated} instead */
|
|
263
|
+
created = this.getCreated
|
|
264
|
+
/** @deprecated use {@link getGeographicPosition} instead */
|
|
265
|
+
geo = this.getGeographicPosition
|
|
266
|
+
/** @deprecated use {@link setGeographicPosition} instead */
|
|
267
|
+
setGeo = this.setGeographicPosition
|
|
268
|
+
/** @deprecated use {@link removeGeographicLocation} instead */
|
|
269
|
+
removeGeo = this.removeGeographicLocation
|
|
270
|
+
}
|