iamcal 1.1.0 → 2.0.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/src/index.ts CHANGED
@@ -1,579 +1,4 @@
1
- import { parseDate, toDateString, toDateTimeString } from './parse'
2
- import { Property } from './types'
3
-
4
- // Max line length as defined by RFC 5545 3.1.
5
- const MAX_LINE_LENGTH = 75
6
-
7
- export class Component {
8
- name: string
9
- properties: Property[]
10
- components: Component[]
11
-
12
- constructor(name: string, properties?: Property[], components?: Component[]) {
13
- this.name = name
14
- if (properties) {
15
- this.properties = properties
16
- } else {
17
- this.properties = new Array()
18
- }
19
- if (components) {
20
- this.components = components
21
- } else {
22
- this.components = new Array()
23
- }
24
- }
25
-
26
- serialize(): string {
27
- // Create lines
28
- const lines = [`BEGIN:${this.name}`]
29
-
30
- for (let property of this.properties) {
31
- let line =
32
- property['name'] + //
33
- property.params.map(p => ';' + p).join('') +
34
- ':' +
35
- property['value']
36
-
37
- // Wrap lines
38
- while (line.length > MAX_LINE_LENGTH) {
39
- lines.push(line.substring(0, MAX_LINE_LENGTH))
40
- line = ' ' + line.substring(MAX_LINE_LENGTH)
41
- }
42
- lines.push(line)
43
- }
44
-
45
- for (let component of this.components) {
46
- lines.push(component.serialize())
47
- }
48
-
49
- lines.push(`END:${this.name}`)
50
-
51
- // Join lines
52
- const serialized = lines.join('\n')
53
-
54
- return serialized
55
- }
56
-
57
- getProperty(name: string): Property | null {
58
- for (const property of this.properties) {
59
- if (property.name === name) {
60
- return property
61
- }
62
- }
63
- return null
64
- }
65
-
66
- setProperty(name: string, value: string): this {
67
- for (const property of this.properties) {
68
- if (property.name === name) {
69
- property.value = value
70
- return this
71
- }
72
- }
73
- this.properties.push({
74
- name: name,
75
- params: [],
76
- value: value,
77
- })
78
- return this
79
- }
80
-
81
- removePropertiesWithName(name: string) {
82
- const index = this.properties.findIndex(p => p.name === name)
83
- if (index === -1) return this
84
- // Remove property at index
85
- this.properties.splice(index, 1)
86
- }
87
-
88
- getPropertyParams(name: string): string[] | null {
89
- for (const property of this.properties) {
90
- if (property.name === name) {
91
- return property.params
92
- }
93
- }
94
- return null
95
- }
96
-
97
- setPropertyParams(name: string, params: string[]): this {
98
- for (const property of this.properties) {
99
- if (property.name === name) {
100
- property.params = params
101
- }
102
- }
103
- return this
104
- }
105
-
106
- addComponent(component: Component): this {
107
- this.components.push(component)
108
- return this
109
- }
110
-
111
- /**
112
- * Remove a component from this component
113
- * @returns `true` if the component was removed. `false` if the component was not present
114
- */
115
- removeComponent(component: Component): boolean {
116
- const index = this.components.indexOf(component)
117
- if (index === -1) return false
118
-
119
- // Remove element at index from list
120
- this.components.splice(index, 1)
121
-
122
- return true
123
- }
124
-
125
- getComponents(name: string): Component[] {
126
- const components: Component[] = []
127
-
128
- for (let component of this.components) {
129
- if (component.name === name) {
130
- components.push(component)
131
- }
132
- }
133
-
134
- return components
135
- }
136
- }
137
-
138
- /**
139
- * Represents a VCALENDAR component, the root component of an iCalendar file.
140
- */
141
- export class Calendar extends Component {
142
- name = 'VCALENDAR'
143
-
144
- /**
145
- * @param prodid A unique identifier of the program creating the calendar.
146
- *
147
- * Example: `-//Google Inc//Google Calendar 70.9054//EN`
148
- */
149
- constructor(prodid: string)
150
- /**
151
- * @param prodid A unique identifier of the program creating the calendar.
152
- *
153
- * Example: `-//Google Inc//Google Calendar 70.9054//EN`
154
- * @param version The version of the iCalendar specification that this calendar uses.
155
- */
156
- constructor(prodid: string, version: string)
157
- /**
158
- * @param component A VCALENDAR component.
159
- */
160
- constructor(component: Component)
161
- constructor(a?: string | Component, b?: string) {
162
- var component: Component
163
- if (a instanceof Component) {
164
- component = a as Component
165
- } else {
166
- const prodid = a as string
167
- const version = (b as string) ?? '2.0'
168
- component = new Component('VCALENDAR')
169
- component.setProperty('PRODID', prodid)
170
- component.setProperty('VERSION', version)
171
- }
172
- super(component.name, component.properties, component.components)
173
- }
174
-
175
- events(): CalendarEvent[] {
176
- return this.getComponents('VEVENT').map(c => new CalendarEvent(c))
177
- }
178
-
179
- removeEvent(event: CalendarEvent): boolean
180
- removeEvent(uid: string): boolean
181
- removeEvent(a: CalendarEvent | string): boolean {
182
- if (a instanceof CalendarEvent) {
183
- const event = a as CalendarEvent
184
- return this.removeComponent(event)
185
- } else {
186
- const uid = a as string
187
- for (const event of this.events()) {
188
- if (event.uid() !== uid) continue
189
- return this.removeComponent(event)
190
- }
191
- }
192
- return false
193
- }
194
-
195
- prodId(): string {
196
- return this.getProperty('PRODID')!.value
197
- }
198
-
199
- setProdId(value: string): this {
200
- return this.setProperty('PRODID', value)
201
- }
202
-
203
- version(): string {
204
- return this.getProperty('VERSION')!.value
205
- }
206
-
207
- setVersion(value: string): this {
208
- return this.setProperty('VERSION', value)
209
- }
210
-
211
- calScale(): string | undefined {
212
- return this.getProperty('CALSCALE')?.value
213
- }
214
-
215
- setCalScale(value: string): this {
216
- return this.setProperty('CALSCALE', value)
217
- }
218
-
219
- removeCalScale() {
220
- this.removePropertiesWithName('CALSCALE')
221
- }
222
-
223
- method(): string | undefined {
224
- return this.getProperty('METHOD')?.value
225
- }
226
-
227
- setMethod(value: string) {
228
- this.setProperty('METHOD', value)
229
- }
230
-
231
- removeMethod() {
232
- this.removePropertiesWithName('METHOD')
233
- }
234
-
235
- calendarName(): string | undefined {
236
- return this.getProperty('X-WR-CALNAME')?.value
237
- }
238
-
239
- setCalendarName(value: string) {
240
- this.setProperty('X-WR-CALNAME', value)
241
- }
242
-
243
- removeCalendarName() {
244
- this.removePropertiesWithName('X-WR-CALNAME')
245
- }
246
-
247
- calendarDescription(): string | undefined {
248
- return this.getProperty('X-WR-CALDESC')?.value
249
- }
250
-
251
- setCalendarDescription(value: string): this {
252
- return this.setProperty('X-WR-CALDESC', value)
253
- }
254
-
255
- removeCalendarDescription() {
256
- this.removePropertiesWithName('X-WR-CALDESC')
257
- }
258
- }
259
-
260
- /**
261
- * Represents a VTIMEZONE component, containing time zone definitions.
262
- */
263
- export class TimeZone extends Component {
264
- constructor(id: string)
265
- constructor(component: Component)
266
- constructor(a: string | Component) {
267
- var component: Component
268
- if (a instanceof Component) {
269
- component = a as Component
270
- } else {
271
- const tzid = a as string
272
- component = new Component('VTIMEZONE')
273
- component.setProperty('TZID', tzid)
274
- }
275
- super(component.name, component.properties, component.components)
276
- }
277
-
278
- id(): string {
279
- return this.getProperty('TZID')!.value
280
- }
281
-
282
- setId(value: string): this {
283
- return this.setProperty('TZID', value)
284
- }
285
-
286
- lastMod(): Date | undefined {
287
- const text = this.getProperty('LAST-MOD')
288
- if (!text) return
289
- return parseDate(text)
290
- }
291
-
292
- setLastMod(value: Date): this {
293
- return this.setProperty('LAST-MOD', value.toISOString())
294
- }
295
-
296
- removeLastMod() {
297
- this.removePropertiesWithName('LAST-MOD')
298
- }
299
-
300
- url(): string | undefined {
301
- return this.getProperty('TZURL')?.value
302
- }
303
-
304
- setUrl(value: string): this {
305
- return this.setProperty('TZURL', value)
306
- }
307
-
308
- removeUrl() {
309
- this.removePropertiesWithName('TZURL')
310
- }
311
-
312
- /** Get all time offsets. */
313
- offsets(): TimeZoneOffset[] {
314
- const offsets: TimeZoneOffset[] = []
315
- this.components.forEach(component => {
316
- if (component.name === 'STANDARD' || component.name === 'DAYLIGHT') {
317
- offsets.push(new TimeZoneOffset(component))
318
- }
319
- })
320
- return offsets
321
- }
322
-
323
- /** Get standard/winter time offsets. */
324
- standardOffsets(): TimeZoneOffset[] {
325
- return this.getComponents('STANDARD').map(c => new TimeZoneOffset(c))
326
- }
327
-
328
- /** Get daylight savings time offsets. */
329
- daylightOffsets(): TimeZoneOffset[] {
330
- return this.getComponents('DAYLIGHT').map(c => new TimeZoneOffset(c))
331
- }
332
- }
333
-
334
- export type OffsetType = 'DAYLIGHT' | 'STANDARD'
335
- type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
336
- export type Offset = `${'-' | '+'}${Digit}${Digit}${Digit}${Digit}`
337
- /** Represents a STANDARD or DAYLIGHT component, defining a time zone offset. */
338
- class TimeZoneOffset extends Component {
339
- /**
340
- *
341
- * @param type If this is a STANDARD or DAYLIGHT component.
342
- * @param start From when this offset is active.
343
- * @param offsetFrom The offset that is in use prior to this time zone observance.
344
- * @param offsetTo The offset that is in use during this time zone observance.
345
- */
346
- constructor(type: OffsetType, start: Date, offsetFrom: Offset, offsetTo: Offset)
347
- constructor(component: Component)
348
- constructor(a: OffsetType | Component, b?: Date, c?: Offset, d?: Offset) {
349
- var component: Component
350
- if (a instanceof Component) {
351
- component = a as Component
352
- } else {
353
- const name = a as OffsetType
354
- const start = b as Date
355
- const offsetFrom = c as Offset
356
- const offsetTo = d as Offset
357
- component = new Component(name)
358
- component.setProperty('DTSTART', toDateTimeString(start))
359
- component.setProperty('TZOFFSETFROM', offsetFrom)
360
- component.setProperty('TZOFFSETTO', offsetTo)
361
- }
362
- super(component.name, component.properties, component.components)
363
- }
364
-
365
- start(): Date {
366
- return parseDate(this.getProperty('DTSTART')!)
367
- }
368
-
369
- setStart(value: Date, fullDay: boolean = false): this {
370
- if (fullDay) {
371
- this.setProperty('DTSTART', toDateString(value))
372
- this.setPropertyParams('DTSTART', ['VALUE=DATE'])
373
- } else {
374
- this.setProperty('DTSTART', toDateTimeString(value))
375
- }
376
- return this
377
- }
378
-
379
- offsetFrom(): Offset {
380
- return this.getProperty('TZOFFSETFROM')!.value as Offset
381
- }
382
-
383
- setOffsetFrom(value: Offset): this {
384
- return this.setProperty('TZOFFSETFROM', value)
385
- }
386
-
387
- offsetTo(): Offset {
388
- return this.getProperty('TZOFFSETTO')!.value as Offset
389
- }
390
-
391
- setOffsetTo(value: Offset): this {
392
- return this.setProperty('TZOFFSETTO', value)
393
- }
394
-
395
- comment(): string | undefined {
396
- return this.getProperty('COMMENT')?.value
397
- }
398
-
399
- setComment(value: string): this {
400
- return this.setProperty('COMMENT', value)
401
- }
402
-
403
- removeComment() {
404
- this.removePropertiesWithName('COMMENT')
405
- }
406
-
407
- timeZoneName(): string | undefined {
408
- return this.getProperty('TZNAME')?.value
409
- }
410
-
411
- setTimeZoneName(value: string): this {
412
- return this.setProperty('TZNAME', value)
413
- }
414
-
415
- removeTimeZoneName() {
416
- this.removePropertiesWithName('TZNAME')
417
- }
418
- }
419
-
420
- export class CalendarEvent extends Component {
421
- name = 'VEVENT'
422
-
423
- constructor(uid: string, dtstamp: Date)
424
- constructor(component: Component)
425
- constructor(a: string | Component, b?: Date) {
426
- var component: Component
427
- if (b) {
428
- const uid = a as string
429
- const dtstamp = b as Date
430
- component = new Component('VEVENT')
431
- component.setProperty('UID', uid)
432
- component.setProperty('DTSTAMP', toDateTimeString(dtstamp))
433
- } else {
434
- component = a as Component
435
- }
436
- super(component.name, component.properties, component.components)
437
- }
438
-
439
- stamp(): Date {
440
- return parseDate(this.getProperty('DTSTAMP')!)
441
- }
442
-
443
- setStamp(value: Date, fullDay: boolean = false): this {
444
- if (fullDay) {
445
- this.setProperty('DTSTAMP', toDateString(value))
446
- this.setPropertyParams('DTSTAMP', ['VALUE=DATE'])
447
- } else {
448
- this.setProperty('DTSTAMP', toDateTimeString(value))
449
- }
450
- return this
451
- }
452
-
453
- uid(): string {
454
- return this.getProperty('UID')!.value
455
- }
456
-
457
- setUid(value: string): this {
458
- return this.setProperty('UID', value)
459
- }
460
-
461
- summary(): string {
462
- return this.getProperty('SUMMARY')!.value
463
- }
464
-
465
- setSummary(value: string): this {
466
- return this.setProperty('SUMMARY', value)
467
- }
468
-
469
- removeSummary() {
470
- this.removePropertiesWithName('SUMMARY')
471
- }
472
-
473
- description(): string {
474
- return this.getProperty('DESCRIPTION')!.value
475
- }
476
-
477
- setDescription(value: string): this {
478
- return this.setProperty('DESCRIPTION', value)
479
- }
480
-
481
- removeDescription() {
482
- this.removePropertiesWithName('DESCRIPTION')
483
- }
484
-
485
- location(): string | undefined {
486
- return this.getProperty('LOCATION')?.value
487
- }
488
-
489
- setLocation(value: string): this {
490
- return this.setProperty('LOCATION', value)
491
- }
492
-
493
- removeLocation() {
494
- this.removePropertiesWithName('LOCATION')
495
- }
496
-
497
- /**
498
- * Get the start of the event.
499
- * If set as a full day the time will be at the start of the day.
500
- */
501
- start(): Date {
502
- return parseDate(this.getProperty('DTSTART')!)
503
- }
504
-
505
- setStart(value: Date, fullDay: boolean = false): this {
506
- if (fullDay) {
507
- this.setProperty('DTSTART', toDateString(value))
508
- this.setPropertyParams('DTSTART', ['VALUE=DATE'])
509
- } else {
510
- this.setProperty('DTSTART', toDateTimeString(value))
511
- }
512
- return this
513
- }
514
-
515
- removeStart() {
516
- this.removePropertiesWithName('DTSTART')
517
- }
518
-
519
- /**
520
- * Get the end of the event.
521
- * If set as a full day the time will be at the end of the day.
522
- */
523
- end(): Date {
524
- const endDate = parseDate(this.getProperty('DTEND')!)
525
- if (this.getPropertyParams('DTEND')?.includes('VALUE=DATE')) {
526
- const ONE_DAY = 24 * 60 * 60 * 1000
527
- const endTime = new Date(endDate.getTime() + ONE_DAY)
528
- return endTime
529
- } else {
530
- return endDate
531
- }
532
- }
533
-
534
- setEnd(value: Date, fullDay: boolean = false): this {
535
- if (fullDay) {
536
- this.setProperty('DTEND', toDateString(value))
537
- this.setPropertyParams('DTEND', ['VALUE=DATE'])
538
- } else {
539
- this.setProperty('DTEND', toDateTimeString(value))
540
- }
541
- return this
542
- }
543
-
544
- removeEnd() {
545
- this.removePropertiesWithName('DTEND')
546
- }
547
-
548
- created(): Date | undefined {
549
- const property = this.getProperty('CREATED')
550
- if (!property) return
551
- return parseDate(property)
552
- }
553
-
554
- setCreated(value: Date): this {
555
- return this.setProperty('CREATED', toDateTimeString(value))
556
- }
557
-
558
- removeCreated() {
559
- this.removePropertiesWithName('CREATED')
560
- }
561
-
562
- geo(): [number, number] | undefined {
563
- const text = this.getProperty('GEO')?.value
564
- if (!text) return
565
- const pattern = /^[+-]?\d+(\.\d+)?,[+-]?\d+(\.\d+)?$/
566
- if (!pattern.test(text)) throw new Error(`Failed to parse GEO property: ${text}`)
567
- const [longitude, latitude] = text.split(',')
568
- return [parseFloat(longitude), parseFloat(latitude)]
569
- }
570
-
571
- setGeo(latitude: number, longitude: number): this {
572
- const text = `${latitude},${longitude}`
573
- return this
574
- }
575
-
576
- removeGeo() {
577
- this.removePropertiesWithName('GEO')
578
- }
579
- }
1
+ export * from './component'
2
+ export * from './io'
3
+ export * from './parse'
4
+ export * from './components'
package/src/io.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import fs from 'fs'
2
2
  import readline from 'readline'
3
- import { Calendar } from '.'
3
+ import { Calendar } from './components/Calendar'
4
4
  import { deserialize } from './parse'
5
- import { resolve } from 'path'
6
5
 
7
6
  /**
8
7
  * Read a calendar from a .ical file
package/src/parse.ts CHANGED
@@ -1,7 +1,7 @@
1
+ import { Component, Property } from './component'
1
2
  import readline from 'readline'
2
3
  import { Readable } from 'stream'
3
- import { Calendar, CalendarEvent, Component } from '.'
4
- import { Property } from './types'
4
+ import { Calendar, CalendarEvent } from './components'
5
5
 
6
6
  export class DeserializationError extends Error {
7
7
  name = 'DeserializationError'
package/lib/types.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export interface Property {
2
- name: string;
3
- params: string[];
4
- value: string;
5
- }
6
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CAChB"}
package/lib/types.js DELETED
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
package/src/types.ts DELETED
@@ -1,5 +0,0 @@
1
- export interface Property {
2
- name: string
3
- params: string[]
4
- value: string
5
- }