ical-generator 8.1.2-develop.9 → 9.0.0-develop.2

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/calendar.ts CHANGED
@@ -1,77 +1,81 @@
1
1
  'use strict';
2
2
 
3
+ import ICalEvent, {
4
+ type ICalEventData,
5
+ type ICalEventJSONData,
6
+ } from './event.ts';
3
7
  import {
4
8
  addOrGetCustomAttributes,
5
9
  checkEnum,
6
10
  foldLines,
7
11
  generateCustomAttributes,
8
12
  isMomentDuration,
9
- toDurationString
13
+ toDurationString,
10
14
  } from './tools.ts';
11
- import ICalEvent, { type ICalEventData, type ICalEventJSONData } from './event.ts';
12
15
  import { type ICalMomentDurationStub, type ICalTimezone } from './types.ts';
13
16
 
17
+ export enum ICalCalendarMethod {
18
+ ADD = 'ADD',
19
+ CANCEL = 'CANCEL',
20
+ COUNTER = 'COUNTER',
21
+ DECLINECOUNTER = 'DECLINECOUNTER',
22
+ PUBLISH = 'PUBLISH',
23
+ REFRESH = 'REFRESH',
24
+ REPLY = 'REPLY',
25
+ REQUEST = 'REQUEST',
26
+ }
14
27
 
15
28
  export interface ICalCalendarData {
16
- prodId?: ICalCalendarProdIdData | string;
17
- method?: ICalCalendarMethod | null;
18
- name?: string | null;
19
- description?: string | null;
20
- timezone?: ICalTimezone | string | null;
21
- source?: string | null;
22
- url?: string | null;
23
- scale?: string | null;
24
- ttl?: number | ICalMomentDurationStub | null;
29
+ description?: null | string;
25
30
  events?: (ICalEvent | ICalEventData)[];
26
- x?: {key: string, value: string}[] | [string, string][] | Record<string, string>;
27
- }
28
-
29
- interface ICalCalendarInternalData {
30
- prodId: string;
31
- method: ICalCalendarMethod | null;
32
- name: string | null;
33
- description: string | null;
34
- timezone: ICalTimezone | null;
35
- source: string | null;
36
- url: string | null;
37
- scale: string | null;
38
- ttl: number | null;
39
- events: ICalEvent[];
40
- x: [string, string][];
31
+ method?: ICalCalendarMethod | null;
32
+ name?: null | string;
33
+ prodId?: ICalCalendarProdIdData | string;
34
+ scale?: null | string;
35
+ source?: null | string;
36
+ timezone?: ICalTimezone | null | string;
37
+ ttl?: ICalMomentDurationStub | null | number;
38
+ url?: null | string;
39
+ x?:
40
+ | [string, string][]
41
+ | Record<string, string>
42
+ | { key: string; value: string }[];
41
43
  }
42
44
 
43
45
  export interface ICalCalendarJSONData {
44
- prodId: string;
45
- method: ICalCalendarMethod | null;
46
- name: string | null;
47
- description: string | null;
48
- timezone: string | null;
49
- source: string | null;
50
- url: string | null;
51
- scale: string | null;
52
- ttl: number | null;
46
+ description: null | string;
53
47
  events: ICalEventJSONData[];
54
- x: {key: string, value: string}[];
48
+ method: ICalCalendarMethod | null;
49
+ name: null | string;
50
+ prodId: string;
51
+ scale: null | string;
52
+ source: null | string;
53
+ timezone: null | string;
54
+ ttl: null | number;
55
+ url: null | string;
56
+ x: { key: string; value: string }[];
55
57
  }
56
58
 
57
59
  export interface ICalCalendarProdIdData {
58
60
  company: string;
59
- product: string;
60
61
  language?: string;
62
+ product: string;
61
63
  }
62
64
 
63
- export enum ICalCalendarMethod {
64
- PUBLISH = 'PUBLISH',
65
- REQUEST = 'REQUEST',
66
- REPLY = 'REPLY',
67
- ADD = 'ADD',
68
- CANCEL = 'CANCEL',
69
- REFRESH = 'REFRESH',
70
- COUNTER = 'COUNTER',
71
- DECLINECOUNTER = 'DECLINECOUNTER'
65
+ interface ICalCalendarInternalData {
66
+ description: null | string;
67
+ events: ICalEvent[];
68
+ method: ICalCalendarMethod | null;
69
+ name: null | string;
70
+ prodId: string;
71
+ scale: null | string;
72
+ source: null | string;
73
+ timezone: ICalTimezone | null;
74
+ ttl: null | number;
75
+ url: null | string;
76
+ x: [string, string][];
72
77
  }
73
78
 
74
-
75
79
  /**
76
80
  * Usually you get an {@link ICalCalendar} object like this:
77
81
  * ```javascript
@@ -124,17 +128,17 @@ export default class ICalCalendar {
124
128
  */
125
129
  constructor(data: ICalCalendarData = {}) {
126
130
  this.data = {
127
- prodId: '//sebbo.net//ical-generator//EN',
131
+ description: null,
132
+ events: [],
128
133
  method: null,
129
134
  name: null,
130
- description: null,
131
- timezone: null,
132
- source: null,
133
- url: null,
135
+ prodId: '//sebbo.net//ical-generator//EN',
134
136
  scale: null,
137
+ source: null,
138
+ timezone: null,
135
139
  ttl: null,
136
- events: [],
137
- x: []
140
+ url: null,
141
+ x: [],
138
142
  };
139
143
 
140
144
  if (data.prodId !== undefined) this.prodId(data.prodId);
@@ -150,61 +154,118 @@ export default class ICalCalendar {
150
154
  if (data.x !== undefined) this.x(data.x);
151
155
  }
152
156
 
157
+ /**
158
+ * Remove all events from the calendar without
159
+ * touching any other data like name or prodId.
160
+ *
161
+ * @since 2.0.0-develop.1
162
+ */
163
+ clear(): this {
164
+ this.data.events = [];
165
+ return this;
166
+ }
153
167
 
154
168
  /**
155
- * Get your feed's prodid. Will always return a string.
169
+ * Creates a new {@link ICalEvent} and returns it. Use options to prefill the event's attributes.
170
+ * Calling this method without options will create an empty event.
171
+ *
172
+ * ```javascript
173
+ * import ical from 'ical-generator';
174
+ *
175
+ * // or use require:
176
+ * // const { default: ical } = require('ical-generator');
177
+ *
178
+ * const cal = ical();
179
+ * const event = cal.createEvent({summary: 'My Event'});
180
+ *
181
+ * // overwrite event summary
182
+ * event.summary('Your Event');
183
+ * ```
184
+ *
156
185
  * @since 0.2.0
157
186
  */
158
- prodId(): string;
187
+ createEvent(data: ICalEvent | ICalEventData): ICalEvent {
188
+ const event =
189
+ data instanceof ICalEvent ? data : new ICalEvent(data, this);
190
+ this.data.events.push(event);
191
+ return event;
192
+ }
193
+ /**
194
+ * Get your feed's description
195
+ * @since 0.2.7
196
+ */
197
+ description(): null | string;
198
+ /**
199
+ * Set your feed's description
200
+ * @since 0.2.7
201
+ */
202
+ description(description: null | string): this;
203
+ description(description?: null | string): null | string | this {
204
+ if (description === undefined) {
205
+ return this.data.description;
206
+ }
159
207
 
208
+ this.data.description = description ? String(description) : null;
209
+ return this;
210
+ }
160
211
  /**
161
- * Set your feed's prodid. `prodid` can be either a
162
- * string like `//sebbo.net//ical-generator//EN` or a
163
- * valid {@link ICalCalendarProdIdData} object. `language`
164
- * is optional and defaults to `EN`.
212
+ * Returns all events of this calendar.
165
213
  *
166
214
  * ```javascript
167
- * cal.prodId({
168
- * company: 'My Company',
169
- * product: 'My Product',
170
- * language: 'EN' // optional, defaults to EN
171
- * });
215
+ * const cal = ical();
216
+ *
217
+ * cal.events([
218
+ * {
219
+ * start: new Date(),
220
+ * end: new Date(new Date().getTime() + 3600000),
221
+ * summary: 'Example Event',
222
+ * description: 'It works ;)',
223
+ * url: 'http://sebbo.net/'
224
+ * }
225
+ * ]);
226
+ *
227
+ * cal.events(); // --> [ICalEvent]
172
228
  * ```
173
229
  *
174
- * `cal.toString()` would then produce the following string:
175
- * ```text
176
- * PRODID:-//My Company//My Product//EN
230
+ * @since 0.2.0
231
+ */
232
+ events(): ICalEvent[];
233
+ /**
234
+ * Add multiple events to your calendar.
235
+ *
236
+ * ```javascript
237
+ * const cal = ical();
238
+ *
239
+ * cal.events([
240
+ * {
241
+ * start: new Date(),
242
+ * end: new Date(new Date().getTime() + 3600000),
243
+ * summary: 'Example Event',
244
+ * description: 'It works ;)',
245
+ * url: 'http://sebbo.net/'
246
+ * }
247
+ * ]);
248
+ *
249
+ * cal.events(); // --> [ICalEvent]
177
250
  * ```
178
251
  *
179
252
  * @since 0.2.0
180
253
  */
181
- prodId(prodId: ICalCalendarProdIdData | string): this;
182
- prodId(prodId?: ICalCalendarProdIdData | string): this | string {
183
- if (!prodId) {
184
- return this.data.prodId;
185
- }
186
-
187
- if (typeof prodId === 'string') {
188
- this.data.prodId = prodId;
189
- return this;
190
- }
191
-
192
- if (typeof prodId !== 'object') {
193
- throw new Error('`prodid` needs to be a string or an object!');
194
- }
195
-
196
- if (!prodId.company) {
197
- throw new Error('`prodid.company` is a mandatory item!');
198
- }
199
- if (!prodId.product) {
200
- throw new Error('`prodid.product` is a mandatory item!');
254
+ events(events: (ICalEvent | ICalEventData)[]): this;
255
+ events(events?: (ICalEvent | ICalEventData)[]): ICalEvent[] | this {
256
+ if (!events) {
257
+ return this.data.events;
201
258
  }
202
259
 
203
- const language = (prodId.language || 'EN').toUpperCase();
204
- this.data.prodId = '//' + prodId.company + '//' + prodId.product + '//' + language;
260
+ events.forEach((e: ICalEvent | ICalEventData) => this.createEvent(e));
205
261
  return this;
206
262
  }
207
-
263
+ /**
264
+ * Get the number of events added to your calendar
265
+ */
266
+ length(): number {
267
+ return this.data.events.length;
268
+ }
208
269
 
209
270
  /**
210
271
  * Get the feed method attribute.
@@ -213,7 +274,6 @@ export default class ICalCalendar {
213
274
  * @since 0.2.8
214
275
  */
215
276
  method(): ICalCalendarMethod | null;
216
-
217
277
  /**
218
278
  * Set the feed method attribute.
219
279
  * See {@link ICalCalendarMethod} for available options.
@@ -229,7 +289,9 @@ export default class ICalCalendar {
229
289
  * @since 0.2.8
230
290
  */
231
291
  method(method: ICalCalendarMethod | null): this;
232
- method(method?: ICalCalendarMethod | null): this | ICalCalendarMethod | null {
292
+ method(
293
+ method?: ICalCalendarMethod | null,
294
+ ): ICalCalendarMethod | null | this {
233
295
  if (method === undefined) {
234
296
  return this.data.method;
235
297
  }
@@ -238,17 +300,18 @@ export default class ICalCalendar {
238
300
  return this;
239
301
  }
240
302
 
241
- this.data.method = checkEnum(ICalCalendarMethod, method) as ICalCalendarMethod;
303
+ this.data.method = checkEnum(
304
+ ICalCalendarMethod,
305
+ method,
306
+ ) as ICalCalendarMethod;
242
307
  return this;
243
308
  }
244
309
 
245
-
246
310
  /**
247
311
  * Get your feed's name
248
312
  * @since 0.2.0
249
313
  */
250
- name(): string | null;
251
-
314
+ name(): null | string;
252
315
  /**
253
316
  * Set your feed's name. Is used to fill `NAME`
254
317
  * and `X-WR-CALNAME` in your iCal file.
@@ -273,8 +336,8 @@ export default class ICalCalendar {
273
336
  *
274
337
  * @since 0.2.0
275
338
  */
276
- name(name: string | null): this;
277
- name(name?: string | null): this | string | null {
339
+ name(name: null | string): this;
340
+ name(name?: null | string): null | string | this {
278
341
  if (name === undefined) {
279
342
  return this.data.name;
280
343
  }
@@ -282,51 +345,141 @@ export default class ICalCalendar {
282
345
  this.data.name = name ? String(name) : null;
283
346
  return this;
284
347
  }
285
-
286
-
287
348
  /**
288
- * Get your feed's description
289
- * @since 0.2.7
349
+ * Get your feed's prodid. Will always return a string.
350
+ * @since 0.2.0
290
351
  */
291
- description(): string | null;
292
-
352
+ prodId(): string;
293
353
  /**
294
- * Set your feed's description
295
- * @since 0.2.7
354
+ * Set your feed's prodid. `prodid` can be either a
355
+ * string like `//sebbo.net//ical-generator//EN` or a
356
+ * valid {@link ICalCalendarProdIdData} object. `language`
357
+ * is optional and defaults to `EN`.
358
+ *
359
+ * ```javascript
360
+ * cal.prodId({
361
+ * company: 'My Company',
362
+ * product: 'My Product',
363
+ * language: 'EN' // optional, defaults to EN
364
+ * });
365
+ * ```
366
+ *
367
+ * `cal.toString()` would then produce the following string:
368
+ * ```text
369
+ * PRODID:-//My Company//My Product//EN
370
+ * ```
371
+ *
372
+ * @since 0.2.0
296
373
  */
297
- description(description: string | null): this;
298
- description(description?: string | null): this | string | null {
299
- if (description === undefined) {
300
- return this.data.description;
374
+ prodId(prodId: ICalCalendarProdIdData | string): this;
375
+ prodId(prodId?: ICalCalendarProdIdData | string): string | this {
376
+ if (!prodId) {
377
+ return this.data.prodId;
301
378
  }
302
379
 
303
- this.data.description = description ? String(description) : null;
304
- return this;
305
- }
380
+ if (typeof prodId === 'string') {
381
+ this.data.prodId = prodId;
382
+ return this;
383
+ }
384
+
385
+ if (typeof prodId !== 'object') {
386
+ throw new Error('`prodid` needs to be a string or an object!');
387
+ }
306
388
 
389
+ if (!prodId.company) {
390
+ throw new Error('`prodid.company` is a mandatory item!');
391
+ }
392
+ if (!prodId.product) {
393
+ throw new Error('`prodid.product` is a mandatory item!');
394
+ }
307
395
 
396
+ const language = (prodId.language || 'EN').toUpperCase();
397
+ this.data.prodId =
398
+ '//' + prodId.company + '//' + prodId.product + '//' + language;
399
+ return this;
400
+ }
308
401
  /**
309
- * Get the current calendar timezone
310
- * @since 0.2.0
402
+ * Get current value of the `CALSCALE` attribute. It will
403
+ * return `null` if no value was set. The iCal standard
404
+ * specifies this as `GREGORIAN` if no value is present.
405
+ *
406
+ * @since 1.8.0
311
407
  */
312
- timezone(): string | null;
313
-
408
+ scale(): null | string;
314
409
  /**
315
- * Use this method to set your feed's timezone. Is used
316
- * to fill `TIMEZONE-ID` and `X-WR-TIMEZONE` in your iCal export.
317
- * Please not that all date values are treaded differently, if
318
- * a timezone was set. See {@link formatDate} for details. If no
319
- * time zone is specified, all information is output as UTC.
410
+ * Use this method to set your feed's `CALSCALE` attribute. There is no
411
+ * default value for this property and it will not appear in your iCal
412
+ * file unless set. The iCal standard specifies this as `GREGORIAN` if
413
+ * no value is present.
320
414
  *
321
415
  * ```javascript
322
- * cal.timezone('America/New_York');
416
+ * cal.scale('gregorian');
323
417
  * ```
324
418
  *
325
- * @see https://github.com/sebbo2002/ical-generator#-date-time--timezones
326
- * @since 0.2.0
419
+ * @since 1.8.0
327
420
  */
328
- timezone(timezone: string | null): this;
421
+ scale(scale: null | string): this;
422
+ scale(scale?: null | string): null | string | this {
423
+ if (scale === undefined) {
424
+ return this.data.scale;
425
+ }
426
+
427
+ if (scale === null) {
428
+ this.data.scale = null;
429
+ } else {
430
+ this.data.scale = scale.toUpperCase();
431
+ }
432
+
433
+ return this;
434
+ }
435
+ /**
436
+ * Get current value of the `SOURCE` attribute.
437
+ * @since 2.2.0-develop.1
438
+ */
439
+ source(): null | string;
440
+ /**
441
+ * Use this method to set your feed's `SOURCE` attribute.
442
+ * This tells the client where to refresh your feed.
443
+ *
444
+ * ```javascript
445
+ * cal.source('http://example.com/my/original_source.ical');
446
+ * ```
447
+ *
448
+ * ```text
449
+ * SOURCE;VALUE=URI:http://example.com/my/original_source.ical
450
+ * ```
451
+ *
452
+ * @since 2.2.0-develop.1
453
+ */
454
+ source(source: null | string): this;
455
+ source(source?: null | string): null | string | this {
456
+ if (source === undefined) {
457
+ return this.data.source;
458
+ }
329
459
 
460
+ this.data.source = source || null;
461
+ return this;
462
+ }
463
+ /**
464
+ * Get the current calendar timezone
465
+ * @since 0.2.0
466
+ */
467
+ timezone(): null | string;
468
+ /**
469
+ * Use this method to set your feed's timezone. Is used
470
+ * to fill `TIMEZONE-ID` and `X-WR-TIMEZONE` in your iCal export.
471
+ * Please not that all date values are treaded differently, if
472
+ * a timezone was set. See {@link formatDate} for details. If no
473
+ * time zone is specified, all information is output as UTC.
474
+ *
475
+ * ```javascript
476
+ * cal.timezone('America/New_York');
477
+ * ```
478
+ *
479
+ * @see https://github.com/sebbo2002/ical-generator#-date-time--timezones
480
+ * @since 0.2.0
481
+ */
482
+ timezone(timezone: null | string): this;
330
483
  /**
331
484
  * Sets the time zone to be used in this calendar file for all times of all
332
485
  * events. Please note that if the time zone is set, ical-generator assumes
@@ -361,130 +514,150 @@ export default class ICalCalendar {
361
514
  * @see https://github.com/sebbo2002/ical-generator#-date-time--timezones
362
515
  * @since 2.0.0
363
516
  */
364
- timezone(timezone: ICalTimezone | string | null): this;
365
- timezone(timezone?: ICalTimezone | string | null): this | string | null {
517
+ timezone(timezone: ICalTimezone | null | string): this;
518
+ timezone(timezone?: ICalTimezone | null | string): null | string | this {
366
519
  if (timezone === undefined) {
367
520
  return this.data.timezone?.name || null;
368
521
  }
369
522
 
370
- if(timezone === 'UTC') {
523
+ if (timezone === 'UTC') {
371
524
  this.data.timezone = null;
372
- }
373
- else if(typeof timezone === 'string') {
374
- this.data.timezone = {name: timezone};
375
- }
376
- else if(timezone === null) {
525
+ } else if (typeof timezone === 'string') {
526
+ this.data.timezone = { name: timezone };
527
+ } else if (timezone === null) {
377
528
  this.data.timezone = null;
378
- }
379
- else {
529
+ } else {
380
530
  this.data.timezone = timezone;
381
531
  }
382
532
 
383
533
  return this;
384
534
  }
385
535
 
386
-
387
536
  /**
388
- * Get current value of the `SOURCE` attribute.
389
- * @since 2.2.0-develop.1
390
- */
391
- source(): string | null;
392
-
393
- /**
394
- * Use this method to set your feed's `SOURCE` attribute.
395
- * This tells the client where to refresh your feed.
537
+ * Return a shallow copy of the calendar's options for JSON stringification.
538
+ * Third party objects like moment.js values or RRule objects are stringified
539
+ * as well. Can be used for persistence.
396
540
  *
397
541
  * ```javascript
398
- * cal.source('http://example.com/my/original_source.ical');
399
- * ```
542
+ * const cal = ical();
543
+ * const json = JSON.stringify(cal);
400
544
  *
401
- * ```text
402
- * SOURCE;VALUE=URI:http://example.com/my/original_source.ical
545
+ * // later: restore calendar data
546
+ * cal = ical(JSON.parse(json));
403
547
  * ```
404
548
  *
405
- * @since 2.2.0-develop.1
549
+ * @since 0.2.4
406
550
  */
407
- source(source: string | null): this;
408
- source(source?: string | null): this | string | null {
409
- if (source === undefined) {
410
- return this.data.source;
411
- }
412
-
413
- this.data.source = source || null;
414
- return this;
551
+ toJSON(): ICalCalendarJSONData {
552
+ return Object.assign({}, this.data, {
553
+ events: this.data.events.map((event) => event.toJSON()),
554
+ timezone: this.timezone(),
555
+ x: this.x(),
556
+ });
415
557
  }
416
558
 
417
-
418
- /**
419
- * Get your feed's URL
420
- * @since 0.2.5
421
- */
422
- url(): string | null;
423
-
424
559
  /**
425
- * Set your feed's URL
560
+ * Return generated calendar as a string.
426
561
  *
427
562
  * ```javascript
428
- * calendar.url('http://example.com/my/feed.ical');
563
+ * const cal = ical();
564
+ * console.log(cal.toString()); // → BEGIN:VCALENDAR…
429
565
  * ```
430
- *
431
- * @since 0.2.5
432
566
  */
433
- url(url: string | null): this;
434
- url(url?: string | null): this | string | null {
435
- if (url === undefined) {
436
- return this.data.url;
567
+ toString(): string {
568
+ let g = '';
569
+
570
+ // VCALENDAR and VERSION
571
+ g = 'BEGIN:VCALENDAR\r\nVERSION:2.0\r\n';
572
+
573
+ // PRODID
574
+ g += 'PRODID:-' + this.data.prodId + '\r\n';
575
+
576
+ // URL
577
+ if (this.data.url) {
578
+ g += 'URL:' + this.data.url + '\r\n';
437
579
  }
438
580
 
439
- this.data.url = url || null;
440
- return this;
441
- }
581
+ // SOURCE
582
+ if (this.data.source) {
583
+ g += 'SOURCE;VALUE=URI:' + this.data.source + '\r\n';
584
+ }
442
585
 
586
+ // CALSCALE
587
+ if (this.data.scale) {
588
+ g += 'CALSCALE:' + this.data.scale + '\r\n';
589
+ }
443
590
 
444
- /**
445
- * Get current value of the `CALSCALE` attribute. It will
446
- * return `null` if no value was set. The iCal standard
447
- * specifies this as `GREGORIAN` if no value is present.
448
- *
449
- * @since 1.8.0
450
- */
451
- scale(): string | null;
591
+ // METHOD
592
+ if (this.data.method) {
593
+ g += 'METHOD:' + this.data.method + '\r\n';
594
+ }
452
595
 
453
- /**
454
- * Use this method to set your feed's `CALSCALE` attribute. There is no
455
- * default value for this property and it will not appear in your iCal
456
- * file unless set. The iCal standard specifies this as `GREGORIAN` if
457
- * no value is present.
458
- *
459
- * ```javascript
460
- * cal.scale('gregorian');
461
- * ```
462
- *
463
- * @since 1.8.0
464
- */
465
- scale(scale: string | null): this;
466
- scale(scale?: string | null): this | string | null {
467
- if (scale === undefined) {
468
- return this.data.scale;
596
+ // NAME
597
+ if (this.data.name) {
598
+ g += 'NAME:' + this.data.name + '\r\n';
599
+ g += 'X-WR-CALNAME:' + this.data.name + '\r\n';
469
600
  }
470
601
 
471
- if (scale === null) {
472
- this.data.scale = null;
602
+ // Description
603
+ if (this.data.description) {
604
+ g += 'X-WR-CALDESC:' + this.data.description + '\r\n';
473
605
  }
474
- else {
475
- this.data.scale = scale.toUpperCase();
606
+
607
+ // Timezone
608
+ if (this.data.timezone?.generator) {
609
+ const timezones = [
610
+ ...new Set([
611
+ this.timezone(),
612
+ ...this.data.events.map((event) => event.timezone()),
613
+ ]),
614
+ ].filter((tz) => tz !== null && !tz.startsWith('/')) as string[];
615
+
616
+ timezones.forEach((tz) => {
617
+ if (!this.data.timezone?.generator) {
618
+ return;
619
+ }
620
+
621
+ const s = this.data.timezone.generator(tz);
622
+ if (!s) {
623
+ return;
624
+ }
625
+
626
+ g +=
627
+ s.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n').trim() +
628
+ '\r\n';
629
+ });
630
+ }
631
+ if (this.data.timezone?.name) {
632
+ g += 'TIMEZONE-ID:' + this.data.timezone.name + '\r\n';
633
+ g += 'X-WR-TIMEZONE:' + this.data.timezone.name + '\r\n';
476
634
  }
477
635
 
478
- return this;
479
- }
636
+ // TTL
637
+ if (this.data.ttl) {
638
+ g +=
639
+ 'REFRESH-INTERVAL;VALUE=DURATION:' +
640
+ toDurationString(this.data.ttl) +
641
+ '\r\n';
642
+ g += 'X-PUBLISHED-TTL:' + toDurationString(this.data.ttl) + '\r\n';
643
+ }
644
+
645
+ // Events
646
+ this.data.events.forEach((event) => (g += event.toString()));
647
+
648
+ // CUSTOM X ATTRIBUTES
649
+ g += generateCustomAttributes(this.data);
650
+
651
+ g += 'END:VCALENDAR';
480
652
 
653
+ return foldLines(g);
654
+ }
481
655
 
482
656
  /**
483
657
  * Get the current ttl duration in seconds
484
658
  * @since 0.2.5
485
659
  */
486
- ttl(): number | null;
487
-
660
+ ttl(): null | number;
488
661
  /**
489
662
  * Use this method to set your feed's time to live
490
663
  * (in seconds). Is used to fill `REFRESH-INTERVAL` and
@@ -499,119 +672,46 @@ export default class ICalCalendar {
499
672
  *
500
673
  * @since 0.2.5
501
674
  */
502
- ttl(ttl: number | ICalMomentDurationStub | null): this;
503
- ttl(ttl?: number | ICalMomentDurationStub | null): this | number | null {
675
+ ttl(ttl: ICalMomentDurationStub | null | number): this;
676
+ ttl(ttl?: ICalMomentDurationStub | null | number): null | number | this {
504
677
  if (ttl === undefined) {
505
678
  return this.data.ttl;
506
679
  }
507
680
 
508
681
  if (isMomentDuration(ttl)) {
509
682
  this.data.ttl = ttl.asSeconds();
510
- }
511
- else if (ttl && ttl > 0) {
683
+ } else if (ttl && ttl > 0) {
512
684
  this.data.ttl = ttl;
513
- }
514
- else {
685
+ } else {
515
686
  this.data.ttl = null;
516
687
  }
517
688
 
518
689
  return this;
519
690
  }
520
691
 
521
-
522
692
  /**
523
- * Creates a new {@link ICalEvent} and returns it. Use options to prefill the event's attributes.
524
- * Calling this method without options will create an empty event.
525
- *
526
- * ```javascript
527
- * import ical from 'ical-generator';
528
- *
529
- * // or use require:
530
- * // const { default: ical } = require('ical-generator');
531
- *
532
- * const cal = ical();
533
- * const event = cal.createEvent({summary: 'My Event'});
534
- *
535
- * // overwrite event summary
536
- * event.summary('Your Event');
537
- * ```
538
- *
539
- * @since 0.2.0
540
- */
541
- createEvent(data: ICalEvent | ICalEventData): ICalEvent {
542
- const event = data instanceof ICalEvent ? data : new ICalEvent(data, this);
543
- this.data.events.push(event);
544
- return event;
545
- }
546
-
547
-
548
- /**
549
- * Returns all events of this calendar.
550
- *
551
- * ```javascript
552
- * const cal = ical();
553
- *
554
- * cal.events([
555
- * {
556
- * start: new Date(),
557
- * end: new Date(new Date().getTime() + 3600000),
558
- * summary: 'Example Event',
559
- * description: 'It works ;)',
560
- * url: 'http://sebbo.net/'
561
- * }
562
- * ]);
563
- *
564
- * cal.events(); // --> [ICalEvent]
565
- * ```
566
- *
567
- * @since 0.2.0
693
+ * Get your feed's URL
694
+ * @since 0.2.5
568
695
  */
569
- events(): ICalEvent[];
570
-
696
+ url(): null | string;
571
697
  /**
572
- * Add multiple events to your calendar.
698
+ * Set your feed's URL
573
699
  *
574
700
  * ```javascript
575
- * const cal = ical();
576
- *
577
- * cal.events([
578
- * {
579
- * start: new Date(),
580
- * end: new Date(new Date().getTime() + 3600000),
581
- * summary: 'Example Event',
582
- * description: 'It works ;)',
583
- * url: 'http://sebbo.net/'
584
- * }
585
- * ]);
586
- *
587
- * cal.events(); // --> [ICalEvent]
701
+ * calendar.url('http://example.com/my/feed.ical');
588
702
  * ```
589
703
  *
590
- * @since 0.2.0
704
+ * @since 0.2.5
591
705
  */
592
- events(events: (ICalEvent | ICalEventData)[]): this;
593
- events(events?: (ICalEvent | ICalEventData)[]): this | ICalEvent[] {
594
- if (!events) {
595
- return this.data.events;
706
+ url(url: null | string): this;
707
+ url(url?: null | string): null | string | this {
708
+ if (url === undefined) {
709
+ return this.data.url;
596
710
  }
597
711
 
598
- events.forEach((e: ICalEvent | ICalEventData) => this.createEvent(e));
599
- return this;
600
- }
601
-
602
-
603
- /**
604
- * Remove all events from the calendar without
605
- * touching any other data like name or prodId.
606
- *
607
- * @since 2.0.0-develop.1
608
- */
609
- clear(): this {
610
- this.data.events = [];
712
+ this.data.url = url || null;
611
713
  return this;
612
714
  }
613
-
614
-
615
715
  /**
616
716
  * Set X-* attributes. Woun't filter double attributes,
617
717
  * which are also added by another method (e.g. busystatus),
@@ -644,8 +744,12 @@ export default class ICalCalendar {
644
744
  *
645
745
  * @since 1.9.0
646
746
  */
647
- x (keyOrArray: {key: string, value: string}[] | [string, string][] | Record<string, string>): this;
648
-
747
+ x(
748
+ keyOrArray:
749
+ | [string, string][]
750
+ | Record<string, string>
751
+ | { key: string; value: string }[],
752
+ ): this;
649
753
  /**
650
754
  * Set a X-* attribute. Woun't filter double attributes,
651
755
  * which are also added by another method (e.g. busystatus),
@@ -665,153 +769,32 @@ export default class ICalCalendar {
665
769
  *
666
770
  * @since 1.9.0
667
771
  */
668
- x (keyOrArray: string, value: string): this;
669
-
772
+ x(keyOrArray: string, value: string): this;
670
773
  /**
671
774
  * Get all custom X-* attributes.
672
775
  * @since 1.9.0
673
776
  */
674
- x (): {key: string, value: string}[];
675
- x (keyOrArray?: {key: string, value: string}[] | [string, string][] | Record<string, string> | string, value?: string): this | void | ({key: string, value: string})[] {
676
- if(keyOrArray === undefined) {
677
- return addOrGetCustomAttributes (this.data);
678
- }
679
-
680
- if(typeof keyOrArray === 'string' && typeof value === 'string') {
681
- addOrGetCustomAttributes (this.data, keyOrArray, value);
682
- }
683
- else if(typeof keyOrArray === 'object') {
684
- addOrGetCustomAttributes (this.data, keyOrArray);
685
- }
686
- else {
777
+ x(): { key: string; value: string }[];
778
+ x(
779
+ keyOrArray?:
780
+ | [string, string][]
781
+ | Record<string, string>
782
+ | string
783
+ | { key: string; value: string }[],
784
+ value?: string,
785
+ ): this | void | { key: string; value: string }[] {
786
+ if (keyOrArray === undefined) {
787
+ return addOrGetCustomAttributes(this.data);
788
+ }
789
+
790
+ if (typeof keyOrArray === 'string' && typeof value === 'string') {
791
+ addOrGetCustomAttributes(this.data, keyOrArray, value);
792
+ } else if (typeof keyOrArray === 'object') {
793
+ addOrGetCustomAttributes(this.data, keyOrArray);
794
+ } else {
687
795
  throw new Error('Either key or value is not a string!');
688
796
  }
689
797
 
690
798
  return this;
691
799
  }
692
-
693
-
694
- /**
695
- * Return a shallow copy of the calendar's options for JSON stringification.
696
- * Third party objects like moment.js values or RRule objects are stringified
697
- * as well. Can be used for persistence.
698
- *
699
- * ```javascript
700
- * const cal = ical();
701
- * const json = JSON.stringify(cal);
702
- *
703
- * // later: restore calendar data
704
- * cal = ical(JSON.parse(json));
705
- * ```
706
- *
707
- * @since 0.2.4
708
- */
709
- toJSON(): ICalCalendarJSONData {
710
- return Object.assign({}, this.data, {
711
- timezone: this.timezone(),
712
- events: this.data.events.map(event => event.toJSON()),
713
- x: this.x()
714
- });
715
- }
716
-
717
-
718
- /**
719
- * Get the number of events added to your calendar
720
- */
721
- length(): number {
722
- return this.data.events.length;
723
- }
724
-
725
-
726
- /**
727
- * Return generated calendar as a string.
728
- *
729
- * ```javascript
730
- * const cal = ical();
731
- * console.log(cal.toString()); // → BEGIN:VCALENDAR…
732
- * ```
733
- */
734
- toString(): string {
735
- let g = '';
736
-
737
- // VCALENDAR and VERSION
738
- g = 'BEGIN:VCALENDAR\r\nVERSION:2.0\r\n';
739
-
740
- // PRODID
741
- g += 'PRODID:-' + this.data.prodId + '\r\n';
742
-
743
- // URL
744
- if (this.data.url) {
745
- g += 'URL:' + this.data.url + '\r\n';
746
- }
747
-
748
- // SOURCE
749
- if (this.data.source) {
750
- g += 'SOURCE;VALUE=URI:' + this.data.source + '\r\n';
751
- }
752
-
753
- // CALSCALE
754
- if (this.data.scale) {
755
- g += 'CALSCALE:' + this.data.scale + '\r\n';
756
- }
757
-
758
- // METHOD
759
- if (this.data.method) {
760
- g += 'METHOD:' + this.data.method + '\r\n';
761
- }
762
-
763
- // NAME
764
- if (this.data.name) {
765
- g += 'NAME:' + this.data.name + '\r\n';
766
- g += 'X-WR-CALNAME:' + this.data.name + '\r\n';
767
- }
768
-
769
- // Description
770
- if (this.data.description) {
771
- g += 'X-WR-CALDESC:' + this.data.description + '\r\n';
772
- }
773
-
774
- // Timezone
775
- if(this.data.timezone?.generator) {
776
- const timezones = [...new Set([
777
- this.timezone(),
778
- ...this.data.events.map(event => event.timezone())
779
- ])].filter(tz => tz !== null && !tz.startsWith('/')) as string[];
780
-
781
- timezones.forEach(tz => {
782
- if(!this.data.timezone?.generator) {
783
- return;
784
- }
785
-
786
- const s = this.data.timezone.generator(tz);
787
- if(!s) {
788
- return;
789
- }
790
-
791
- g += s.replace(/\r\n/g, '\n')
792
- .replace(/\n/g, '\r\n')
793
- .trim() + '\r\n';
794
- });
795
- }
796
- if (this.data.timezone?.name) {
797
- g += 'TIMEZONE-ID:' + this.data.timezone.name + '\r\n';
798
- g += 'X-WR-TIMEZONE:' + this.data.timezone.name + '\r\n';
799
- }
800
-
801
- // TTL
802
- if (this.data.ttl) {
803
- g += 'REFRESH-INTERVAL;VALUE=DURATION:' + toDurationString(this.data.ttl) + '\r\n';
804
- g += 'X-PUBLISHED-TTL:' + toDurationString(this.data.ttl) + '\r\n';
805
- }
806
-
807
- // Events
808
- this.data.events.forEach(event => g += event.toString());
809
-
810
- // CUSTOM X ATTRIBUTES
811
- g += generateCustomAttributes(this.data);
812
-
813
- g += 'END:VCALENDAR';
814
-
815
- return foldLines(g);
816
- }
817
800
  }