iamcal 2.0.0 → 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 +46 -9
- package/lib/component.d.ts.map +1 -1
- package/lib/component.js +105 -17
- package/lib/components/Calendar.d.ts +35 -14
- package/lib/components/Calendar.d.ts.map +1 -1
- package/lib/components/Calendar.js +43 -15
- package/lib/components/CalendarEvent.d.ts +84 -22
- package/lib/components/CalendarEvent.d.ts.map +1 -1
- package/lib/components/CalendarEvent.js +142 -67
- package/lib/components/TimeZone.d.ts +62 -39
- package/lib/components/TimeZone.d.ts.map +1 -1
- package/lib/components/TimeZone.js +81 -86
- 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 +1 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/index.js +2 -1
- 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 +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -2
- package/lib/io.d.ts +9 -7
- package/lib/io.d.ts.map +1 -1
- package/lib/io.js +16 -11
- package/lib/parse.d.ts +32 -15
- package/lib/parse.d.ts.map +1 -1
- package/lib/parse.js +55 -53
- 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 +50 -38
- package/src/component.ts +132 -23
- package/src/components/Calendar.ts +58 -20
- package/src/components/CalendarEvent.ts +170 -66
- package/src/components/TimeZone.ts +86 -96
- package/src/components/TimeZoneOffset.ts +187 -0
- package/src/components/index.ts +2 -1
- package/src/date.ts +395 -0
- package/src/index.ts +3 -1
- package/src/io.ts +16 -11
- package/src/parse.ts +71 -51
- package/src/patterns.ts +69 -0
- package/src/property.ts +492 -0
package/src/component.ts
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
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
|
+
|
|
1
19
|
// Max line length as defined by RFC 5545 3.1.
|
|
2
20
|
const MAX_LINE_LENGTH = 75
|
|
3
21
|
|
|
4
|
-
export interface Property {
|
|
5
|
-
name: string
|
|
6
|
-
params: string[]
|
|
7
|
-
value: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
22
|
export class Component {
|
|
11
23
|
name: string
|
|
12
24
|
properties: Property[]
|
|
13
25
|
components: Component[]
|
|
14
26
|
|
|
15
|
-
constructor(
|
|
27
|
+
constructor(
|
|
28
|
+
name: string,
|
|
29
|
+
properties?: Property[],
|
|
30
|
+
components?: Component[]
|
|
31
|
+
) {
|
|
16
32
|
this.name = name
|
|
17
33
|
if (properties) {
|
|
18
34
|
this.properties = properties
|
|
19
35
|
} else {
|
|
20
|
-
this.properties =
|
|
36
|
+
this.properties = []
|
|
21
37
|
}
|
|
22
38
|
if (components) {
|
|
23
39
|
this.components = components
|
|
24
40
|
} else {
|
|
25
|
-
this.components =
|
|
41
|
+
this.components = []
|
|
26
42
|
}
|
|
27
43
|
}
|
|
28
44
|
|
|
@@ -30,7 +46,7 @@ export class Component {
|
|
|
30
46
|
// Create lines
|
|
31
47
|
const lines = [`BEGIN:${this.name}`]
|
|
32
48
|
|
|
33
|
-
for (
|
|
49
|
+
for (const property of this.properties) {
|
|
34
50
|
let line =
|
|
35
51
|
property['name'] + //
|
|
36
52
|
property.params.map(p => ';' + p).join('') +
|
|
@@ -45,7 +61,7 @@ export class Component {
|
|
|
45
61
|
lines.push(line)
|
|
46
62
|
}
|
|
47
63
|
|
|
48
|
-
for (
|
|
64
|
+
for (const component of this.components) {
|
|
49
65
|
lines.push(component.serialize())
|
|
50
66
|
}
|
|
51
67
|
|
|
@@ -66,24 +82,46 @@ export class Component {
|
|
|
66
82
|
return null
|
|
67
83
|
}
|
|
68
84
|
|
|
69
|
-
setProperty(name: string, value: string): this {
|
|
85
|
+
setProperty(name: string, value: string | CalendarDateOrTime): this {
|
|
70
86
|
for (const property of this.properties) {
|
|
71
87
|
if (property.name === name) {
|
|
72
|
-
|
|
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
|
+
}
|
|
73
95
|
return this
|
|
74
96
|
}
|
|
75
97
|
}
|
|
76
|
-
this.properties.push(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
98
|
+
this.properties.push(
|
|
99
|
+
typeof value === 'string'
|
|
100
|
+
? {
|
|
101
|
+
name: name,
|
|
102
|
+
params: [],
|
|
103
|
+
value: value,
|
|
104
|
+
}
|
|
105
|
+
: value.toProperty(name)
|
|
106
|
+
)
|
|
81
107
|
return this
|
|
82
108
|
}
|
|
83
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
|
+
|
|
84
122
|
removePropertiesWithName(name: string) {
|
|
85
123
|
const index = this.properties.findIndex(p => p.name === name)
|
|
86
|
-
if (index === -1) return
|
|
124
|
+
if (index === -1) return
|
|
87
125
|
// Remove property at index
|
|
88
126
|
this.properties.splice(index, 1)
|
|
89
127
|
}
|
|
@@ -111,9 +149,15 @@ export class Component {
|
|
|
111
149
|
return this
|
|
112
150
|
}
|
|
113
151
|
|
|
152
|
+
addComponents(components: Component[]): this {
|
|
153
|
+
this.components.push(...components)
|
|
154
|
+
return this
|
|
155
|
+
}
|
|
156
|
+
|
|
114
157
|
/**
|
|
115
|
-
* Remove a component from this component
|
|
116
|
-
* @
|
|
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.
|
|
117
161
|
*/
|
|
118
162
|
removeComponent(component: Component): boolean {
|
|
119
163
|
const index = this.components.indexOf(component)
|
|
@@ -125,10 +169,10 @@ export class Component {
|
|
|
125
169
|
return true
|
|
126
170
|
}
|
|
127
171
|
|
|
128
|
-
|
|
172
|
+
getComponentsWithName(name: string): Component[] {
|
|
129
173
|
const components: Component[] = []
|
|
130
174
|
|
|
131
|
-
for (
|
|
175
|
+
for (const component of this.components) {
|
|
132
176
|
if (component.name === name) {
|
|
133
177
|
components.push(component)
|
|
134
178
|
}
|
|
@@ -136,4 +180,69 @@ export class Component {
|
|
|
136
180
|
|
|
137
181
|
return components
|
|
138
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
|
+
}
|
|
139
248
|
}
|
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KnownPropertyName } from '../property'
|
|
2
|
+
import { Component, ComponentValidationError } from '../component'
|
|
2
3
|
import { CalendarEvent } from './CalendarEvent'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Represents a VCALENDAR component, the root component of an iCalendar file.
|
|
6
7
|
*/
|
|
7
8
|
export class Calendar extends Component {
|
|
8
|
-
name = 'VCALENDAR'
|
|
9
|
+
name = 'VCALENDAR'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @param prodid A unique identifier of the program creating the calendar.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* @example
|
|
14
|
+
* new Calendar('-//Google Inc//Google Calendar 70.9054//EN')
|
|
14
15
|
*/
|
|
15
16
|
constructor(prodid: string)
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* @param prodid A unique identifier of the program creating the calendar.
|
|
18
|
-
*
|
|
19
|
-
* Example: `-//Google Inc//Google Calendar 70.9054//EN`
|
|
20
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')
|
|
21
23
|
*/
|
|
22
24
|
constructor(prodid: string, version: string)
|
|
25
|
+
|
|
23
26
|
/**
|
|
24
27
|
* @param component A VCALENDAR component.
|
|
25
28
|
*/
|
|
26
29
|
constructor(component: Component)
|
|
27
30
|
constructor(a?: string | Component, b?: string) {
|
|
28
|
-
|
|
31
|
+
let component: Component
|
|
29
32
|
if (a instanceof Component) {
|
|
30
33
|
component = a as Component
|
|
34
|
+
Calendar.prototype.validate.call(component)
|
|
31
35
|
} else {
|
|
32
36
|
const prodid = a as string
|
|
33
37
|
const version = (b as string) ?? '2.0'
|
|
@@ -38,8 +42,19 @@ export class Calendar extends Component {
|
|
|
38
42
|
super(component.name, component.properties, component.components)
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
+
)
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
removeEvent(event: CalendarEvent): boolean
|
|
@@ -50,23 +65,23 @@ export class Calendar extends Component {
|
|
|
50
65
|
return this.removeComponent(event)
|
|
51
66
|
} else {
|
|
52
67
|
const uid = a as string
|
|
53
|
-
for (const event of this.
|
|
54
|
-
if (event.
|
|
68
|
+
for (const event of this.getEvents()) {
|
|
69
|
+
if (event.getUid() !== uid) continue
|
|
55
70
|
return this.removeComponent(event)
|
|
56
71
|
}
|
|
57
72
|
}
|
|
58
73
|
return false
|
|
59
74
|
}
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
getProductId(): string {
|
|
62
77
|
return this.getProperty('PRODID')!.value
|
|
63
78
|
}
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
setProductId(value: string): this {
|
|
66
81
|
return this.setProperty('PRODID', value)
|
|
67
82
|
}
|
|
68
83
|
|
|
69
|
-
|
|
84
|
+
getVersion(): string {
|
|
70
85
|
return this.getProperty('VERSION')!.value
|
|
71
86
|
}
|
|
72
87
|
|
|
@@ -74,19 +89,19 @@ export class Calendar extends Component {
|
|
|
74
89
|
return this.setProperty('VERSION', value)
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
|
|
92
|
+
getCalendarScale(): string | undefined {
|
|
78
93
|
return this.getProperty('CALSCALE')?.value
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
|
|
96
|
+
setCalendarScale(value: string): this {
|
|
82
97
|
return this.setProperty('CALSCALE', value)
|
|
83
98
|
}
|
|
84
99
|
|
|
85
|
-
|
|
100
|
+
removeCalendarScale() {
|
|
86
101
|
this.removePropertiesWithName('CALSCALE')
|
|
87
102
|
}
|
|
88
103
|
|
|
89
|
-
|
|
104
|
+
getMethod(): string | undefined {
|
|
90
105
|
return this.getProperty('METHOD')?.value
|
|
91
106
|
}
|
|
92
107
|
|
|
@@ -98,7 +113,7 @@ export class Calendar extends Component {
|
|
|
98
113
|
this.removePropertiesWithName('METHOD')
|
|
99
114
|
}
|
|
100
115
|
|
|
101
|
-
|
|
116
|
+
getCalendarName(): string | undefined {
|
|
102
117
|
return this.getProperty('X-WR-CALNAME')?.value
|
|
103
118
|
}
|
|
104
119
|
|
|
@@ -110,7 +125,7 @@ export class Calendar extends Component {
|
|
|
110
125
|
this.removePropertiesWithName('X-WR-CALNAME')
|
|
111
126
|
}
|
|
112
127
|
|
|
113
|
-
|
|
128
|
+
getCalendarDescription(): string | undefined {
|
|
114
129
|
return this.getProperty('X-WR-CALDESC')?.value
|
|
115
130
|
}
|
|
116
131
|
|
|
@@ -121,4 +136,27 @@ export class Calendar extends Component {
|
|
|
121
136
|
removeCalendarDescription() {
|
|
122
137
|
this.removePropertiesWithName('X-WR-CALDESC')
|
|
123
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
|
|
124
162
|
}
|