ical-generator 3.5.1 → 3.5.2-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.
@@ -0,0 +1,614 @@
1
+ 'use strict';
2
+
3
+
4
+ import {addOrGetCustomAttributes, checkEnum, checkNameAndMail, escape} from './tools';
5
+ import ICalEvent from './event';
6
+
7
+
8
+ interface ICalInternalAttendeeData {
9
+ name: string | null;
10
+ email: string | null;
11
+ mailto: string | null;
12
+ sentBy: string | null;
13
+ status: ICalAttendeeStatus | null;
14
+ role: ICalAttendeeRole;
15
+ rsvp: boolean | null;
16
+ type: ICalAttendeeType | null;
17
+ delegatedTo: ICalAttendee | null;
18
+ delegatedFrom: ICalAttendee | null;
19
+ x: [string, string][];
20
+ }
21
+
22
+ export interface ICalAttendeeData {
23
+ name?: string | null;
24
+ email?: string | null;
25
+ mailto?: string | null;
26
+ sentBy?: string | null;
27
+ status?: ICalAttendeeStatus | null;
28
+ role?: ICalAttendeeRole;
29
+ rsvp?: boolean | null;
30
+ type?: ICalAttendeeType | null;
31
+ delegatedTo?: ICalAttendee | ICalAttendeeData | string | null;
32
+ delegatedFrom?: ICalAttendee | ICalAttendeeData | string | null;
33
+ delegatesTo?: ICalAttendee | ICalAttendeeData | string | null;
34
+ delegatesFrom?: ICalAttendee | ICalAttendeeData | string | null;
35
+ x?: {key: string, value: string}[] | [string, string][] | Record<string, string>;
36
+ }
37
+
38
+ export interface ICalAttendeeJSONData {
39
+ name: string | null;
40
+ email: string | null;
41
+ mailto: string | null;
42
+ sentBy: string | null;
43
+ status: ICalAttendeeStatus | null;
44
+ role: ICalAttendeeRole;
45
+ rsvp: boolean | null;
46
+ type: ICalAttendeeType | null;
47
+ delegatedTo: string | null;
48
+ delegatedFrom: string | null;
49
+ x: {key: string, value: string}[];
50
+ }
51
+
52
+ export enum ICalAttendeeRole {
53
+ CHAIR = 'CHAIR',
54
+ REQ = 'REQ-PARTICIPANT',
55
+ OPT = 'OPT-PARTICIPANT',
56
+ NON = 'NON-PARTICIPANT'
57
+ }
58
+
59
+ export enum ICalAttendeeStatus {
60
+ ACCEPTED = 'ACCEPTED',
61
+ TENTATIVE = 'TENTATIVE',
62
+ DECLINED = 'DECLINED',
63
+ DELEGATED = 'DELEGATED',
64
+ NEEDSACTION = 'NEEDS-ACTION'
65
+ }
66
+
67
+ // ref: https://tools.ietf.org/html/rfc2445#section-4.2.3
68
+ export enum ICalAttendeeType {
69
+ INDIVIDUAL = 'INDIVIDUAL',
70
+ GROUP = 'GROUP',
71
+ RESOURCE = 'RESOURCE',
72
+ ROOM = 'ROOM',
73
+ UNKNOWN = 'UNKNOWN'
74
+ }
75
+
76
+
77
+ /**
78
+ * Usually you get an `ICalAttendee` object like this:
79
+ *
80
+ * ```javascript
81
+ * import ical from 'ical-generator';
82
+ * const calendar = ical();
83
+ * const event = calendar.createEvent();
84
+ * const attendee = event.createAttendee();
85
+ * ```
86
+ *
87
+ * You can also use the [[`ICalAttendee`]] object directly:
88
+ *
89
+ * ```javascript
90
+ * import ical, {ICalAttendee} from 'ical-generator';
91
+ * const attendee = new ICalAttendee();
92
+ * event.attendees([attendee]);
93
+ * ```
94
+ */
95
+ export default class ICalAttendee {
96
+ private readonly data: ICalInternalAttendeeData;
97
+ private readonly event: ICalEvent;
98
+
99
+ /**
100
+ * Constructor of [[`ICalAttendee`]]. The event reference is
101
+ * required to query the calendar's timezone when required.
102
+ *
103
+ * @param data Attendee Data
104
+ * @param calendar Reference to ICalEvent object
105
+ */
106
+ constructor(data: ICalAttendeeData, event: ICalEvent) {
107
+ this.data = {
108
+ name: null,
109
+ email: null,
110
+ mailto: null,
111
+ sentBy: null,
112
+ status: null,
113
+ role: ICalAttendeeRole.REQ,
114
+ rsvp: null,
115
+ type: null,
116
+ delegatedTo: null,
117
+ delegatedFrom: null,
118
+ x: []
119
+ };
120
+ this.event = event;
121
+ if (!this.event) {
122
+ throw new Error('`event` option required!');
123
+ }
124
+
125
+ data.name !== undefined && this.name(data.name);
126
+ data.email !== undefined && this.email(data.email);
127
+ data.mailto !== undefined && this.mailto(data.mailto);
128
+ data.sentBy !== undefined && this.sentBy(data.sentBy);
129
+ data.status !== undefined && this.status(data.status);
130
+ data.role !== undefined && this.role(data.role);
131
+ data.rsvp !== undefined && this.rsvp(data.rsvp);
132
+ data.type !== undefined && this.type(data.type);
133
+ data.delegatedTo !== undefined && this.delegatedTo(data.delegatedTo);
134
+ data.delegatedFrom !== undefined && this.delegatedFrom(data.delegatedFrom);
135
+ data.delegatesTo && this.delegatesTo(data.delegatesTo);
136
+ data.delegatesFrom && this.delegatesFrom(data.delegatesFrom);
137
+ data.x !== undefined && this.x(data.x);
138
+ }
139
+
140
+
141
+ /**
142
+ * Get the attendee's name
143
+ * @since 0.2.0
144
+ */
145
+ name(): string | null;
146
+
147
+ /**
148
+ * Set the attendee's name
149
+ * @since 0.2.0
150
+ */
151
+ name(name: string | null): this;
152
+ name(name?: string | null): this | string | null {
153
+ if (name === undefined) {
154
+ return this.data.name;
155
+ }
156
+
157
+ this.data.name = name || null;
158
+ return this;
159
+ }
160
+
161
+
162
+ /**
163
+ * Get the attendee's email address
164
+ * @since 0.2.0
165
+ */
166
+ email(): string | null;
167
+
168
+ /**
169
+ * Set the attendee's email address
170
+ * @since 0.2.0
171
+ */
172
+ email(email: string | null): this;
173
+ email(email?: string | null): this | string | null {
174
+ if (!email) {
175
+ return this.data.email;
176
+ }
177
+
178
+ this.data.email = email;
179
+ return this;
180
+ }
181
+
182
+ /**
183
+ * Get the attendee's email address
184
+ * @since 1.3.0
185
+ */
186
+ mailto(): string | null;
187
+
188
+ /**
189
+ * Set the attendee's email address
190
+ * @since 1.3.0
191
+ */
192
+ mailto(mailto: string | null): this;
193
+ mailto(mailto?: string | null): this | string | null {
194
+ if (mailto === undefined) {
195
+ return this.data.mailto;
196
+ }
197
+
198
+ this.data.mailto = mailto || null;
199
+ return this;
200
+ }
201
+
202
+
203
+ /**
204
+ * Get the acting user's email adress
205
+ * @since 3.3.0
206
+ */
207
+ sentBy(): string | null;
208
+
209
+ /**
210
+ * Set the acting user's email adress
211
+ * @since 3.3.0
212
+ */
213
+ sentBy(email: string | null): this;
214
+ sentBy(email?: string | null): this | string | null {
215
+ if (!email) {
216
+ return this.data.sentBy;
217
+ }
218
+
219
+ this.data.sentBy = email;
220
+ return this;
221
+ }
222
+
223
+
224
+ /**
225
+ * Get attendee's role
226
+ * @since 0.2.0
227
+ */
228
+ role(): ICalAttendeeRole;
229
+
230
+ /**
231
+ * Set the attendee's role, defaults to `REQ` / `REQ-PARTICIPANT`.
232
+ * Checkout [[`ICalAttendeeRole`]] for available roles.
233
+ *
234
+ * @since 0.2.0
235
+ */
236
+ role(role: ICalAttendeeRole): this;
237
+ role(role?: ICalAttendeeRole): this | ICalAttendeeRole {
238
+ if (role === undefined) {
239
+ return this.data.role;
240
+ }
241
+
242
+ this.data.role = checkEnum(ICalAttendeeRole, role) as ICalAttendeeRole;
243
+ return this;
244
+ }
245
+
246
+
247
+ /**
248
+ * Get attendee's RSVP expectation
249
+ * @since 0.2.1
250
+ */
251
+ rsvp(): boolean | null;
252
+
253
+ /**
254
+ * Set the attendee's RSVP expectation
255
+ * @since 0.2.1
256
+ */
257
+ rsvp(rsvp: boolean | null): this;
258
+ rsvp(rsvp?: boolean | null): this | boolean | null {
259
+ if (rsvp === undefined) {
260
+ return this.data.rsvp;
261
+ }
262
+ if (rsvp === null) {
263
+ this.data.rsvp = null;
264
+ return this;
265
+ }
266
+
267
+ this.data.rsvp = Boolean(rsvp);
268
+ return this;
269
+ }
270
+
271
+
272
+ /**
273
+ * Get attendee's status
274
+ * @since 0.2.0
275
+ */
276
+ status(): ICalAttendeeStatus | null;
277
+
278
+ /**
279
+ * Set the attendee's status. See [[`ICalAttendeeStatus`]]
280
+ * for available status options.
281
+ *
282
+ * @since 0.2.0
283
+ */
284
+ status(status: ICalAttendeeStatus | null): this;
285
+ status(status?: ICalAttendeeStatus | null): this | ICalAttendeeStatus | null {
286
+ if (status === undefined) {
287
+ return this.data.status;
288
+ }
289
+ if (!status) {
290
+ this.data.status = null;
291
+ return this;
292
+ }
293
+
294
+ this.data.status = checkEnum(ICalAttendeeStatus, status) as ICalAttendeeStatus;
295
+ return this;
296
+ }
297
+
298
+
299
+ /**
300
+ * Get attendee's type (a.k.a. CUTYPE)
301
+ * @since 0.2.3
302
+ */
303
+ type(): ICalAttendeeType;
304
+
305
+ /**
306
+ * Set attendee's type (a.k.a. CUTYPE).
307
+ * See [[`ICalAttendeeType`]] for available status options.
308
+ *
309
+ * @since 0.2.3
310
+ */
311
+ type(type: ICalAttendeeType | null): this;
312
+ type(type?: ICalAttendeeType | null): this | ICalAttendeeType | null {
313
+ if (type === undefined) {
314
+ return this.data.type;
315
+ }
316
+ if (!type) {
317
+ this.data.type = null;
318
+ return this;
319
+ }
320
+
321
+ this.data.type = checkEnum(ICalAttendeeType, type) as ICalAttendeeType;
322
+ return this;
323
+ }
324
+
325
+
326
+ /**
327
+ * Get the attendee's delegated-to value.
328
+ * @since 0.2.0
329
+ */
330
+ delegatedTo(): ICalAttendee | null;
331
+
332
+ /**
333
+ * Set the attendee's delegated-to field.
334
+ *
335
+ * Creates a new Attendee if the passed object is not already a
336
+ * [[`ICalAttendee`]] object. Will set the `delegatedTo` and
337
+ * `delegatedFrom` attributes.
338
+ *
339
+ * Will also set the `status` to `DELEGATED`, if attribute is set.
340
+ *
341
+ * ```javascript
342
+ * const cal = ical();
343
+ * const event = cal.createEvent();
344
+ * const attendee = cal.createAttendee();
345
+ *
346
+ * attendee.delegatesTo({email: 'foo@bar.com', name: 'Foo'});
347
+ ```
348
+ *
349
+ * @since 0.2.0
350
+ */
351
+ delegatedTo(delegatedTo: ICalAttendee | ICalAttendeeData | string | null): this;
352
+ delegatedTo(delegatedTo?: ICalAttendee | ICalAttendeeData | string | null): this | ICalAttendee | null {
353
+ if (delegatedTo === undefined) {
354
+ return this.data.delegatedTo;
355
+ }
356
+ if (!delegatedTo) {
357
+ this.data.delegatedTo = null;
358
+ if (this.data.status === ICalAttendeeStatus.DELEGATED) {
359
+ this.data.status = null;
360
+ }
361
+ return this;
362
+ }
363
+
364
+ if(typeof delegatedTo === 'string') {
365
+ this.data.delegatedTo = new ICalAttendee(
366
+ checkNameAndMail('delegatedTo', delegatedTo),
367
+ this.event,
368
+ );
369
+ }
370
+ else if(delegatedTo instanceof ICalAttendee) {
371
+ this.data.delegatedTo = delegatedTo;
372
+ }
373
+ else {
374
+ this.data.delegatedTo = new ICalAttendee(delegatedTo, this.event);
375
+ }
376
+
377
+ this.data.status = ICalAttendeeStatus.DELEGATED;
378
+ return this;
379
+ }
380
+
381
+
382
+ /**
383
+ * Get the attendee's delegated-from field
384
+ * @since 0.2.0
385
+ */
386
+ delegatedFrom (): ICalAttendee | null;
387
+
388
+ /**
389
+ * Set the attendee's delegated-from field
390
+ *
391
+ * Creates a new Attendee if the passed object is not already a
392
+ * [[`ICalAttendee`]] object. Will set the `delegatedTo` and
393
+ * `delegatedFrom` attributes.
394
+ *
395
+ * @param delegatedFrom
396
+ */
397
+ delegatedFrom (delegatedFrom: ICalAttendee | ICalAttendeeData | string | null): this;
398
+ delegatedFrom(delegatedFrom?: ICalAttendee | ICalAttendeeData | string | null): this | ICalAttendee | null {
399
+ if (delegatedFrom === undefined) {
400
+ return this.data.delegatedFrom;
401
+ }
402
+
403
+ if (!delegatedFrom) {
404
+ this.data.delegatedFrom = null;
405
+ }
406
+ else if(typeof delegatedFrom === 'string') {
407
+ this.data.delegatedFrom = new ICalAttendee(
408
+ checkNameAndMail('delegatedFrom', delegatedFrom),
409
+ this.event,
410
+ );
411
+ }
412
+ else if(delegatedFrom instanceof ICalAttendee) {
413
+ this.data.delegatedFrom = delegatedFrom;
414
+ }
415
+ else {
416
+ this.data.delegatedFrom = new ICalAttendee(delegatedFrom, this.event);
417
+ }
418
+
419
+ return this;
420
+ }
421
+
422
+
423
+ /**
424
+ * Create a new attendee this attendee delegates to and returns
425
+ * this new attendee. Creates a new attendee if the passed object
426
+ * is not already an [[`ICalAttendee`]].
427
+ *
428
+ * ```javascript
429
+ * const cal = ical();
430
+ * const event = cal.createEvent();
431
+ * const attendee = cal.createAttendee();
432
+ *
433
+ * attendee.delegatesTo({email: 'foo@bar.com', name: 'Foo'});
434
+ * ```
435
+ *
436
+ * @since 0.2.0
437
+ */
438
+ delegatesTo (options: ICalAttendee | ICalAttendeeData | string): ICalAttendee {
439
+ const a = options instanceof ICalAttendee ? options : this.event.createAttendee(options);
440
+ this.delegatedTo(a);
441
+ a.delegatedFrom(this);
442
+ return a;
443
+ }
444
+
445
+
446
+ /**
447
+ * Create a new attendee this attendee delegates from and returns
448
+ * this new attendee. Creates a new attendee if the passed object
449
+ * is not already an [[`ICalAttendee`]].
450
+ *
451
+ * ```javascript
452
+ * const cal = ical();
453
+ * const event = cal.createEvent();
454
+ * const attendee = cal.createAttendee();
455
+ *
456
+ * attendee.delegatesFrom({email: 'foo@bar.com', name: 'Foo'});
457
+ * ```
458
+ *
459
+ * @since 0.2.0
460
+ */
461
+ delegatesFrom (options: ICalAttendee | ICalAttendeeData | string): ICalAttendee {
462
+ const a = options instanceof ICalAttendee ? options : this.event.createAttendee(options);
463
+ this.delegatedFrom(a);
464
+ a.delegatedTo(this);
465
+ return a;
466
+ }
467
+
468
+ /**
469
+ * Set X-* attributes. Woun't filter double attributes,
470
+ * which are also added by another method (e.g. status),
471
+ * so these attributes may be inserted twice.
472
+ *
473
+ * ```javascript
474
+ * attendee.x([
475
+ * {
476
+ * key: "X-MY-CUSTOM-ATTR",
477
+ * value: "1337!"
478
+ * }
479
+ * ]);
480
+ *
481
+ * attendee.x([
482
+ * ["X-MY-CUSTOM-ATTR", "1337!"]
483
+ * ]);
484
+ *
485
+ * attendee.x({
486
+ * "X-MY-CUSTOM-ATTR": "1337!"
487
+ * });
488
+ * ```
489
+ *
490
+ * @since 1.9.0
491
+ */
492
+ x (keyOrArray: {key: string, value: string}[] | [string, string][] | Record<string, string>): this;
493
+
494
+ /**
495
+ * Set a X-* attribute. Woun't filter double attributes,
496
+ * which are also added by another method (e.g. status),
497
+ * so these attributes may be inserted twice.
498
+ *
499
+ * ```javascript
500
+ * attendee.x("X-MY-CUSTOM-ATTR", "1337!");
501
+ * ```
502
+ *
503
+ * @since 1.9.0
504
+ */
505
+ x (keyOrArray: string, value: string): this;
506
+
507
+ /**
508
+ * Get all custom X-* attributes.
509
+ * @since 1.9.0
510
+ */
511
+ x (): {key: string, value: string}[];
512
+ x (keyOrArray?: ({key: string, value: string})[] | [string, string][] | Record<string, string> | string, value?: string): this | void | ({key: string, value: string})[] {
513
+ if(keyOrArray === undefined) {
514
+ return addOrGetCustomAttributes (this.data);
515
+ }
516
+
517
+ if(typeof keyOrArray === 'string' && typeof value === 'string') {
518
+ addOrGetCustomAttributes (this.data, keyOrArray, value);
519
+ }
520
+ else if(typeof keyOrArray === 'object') {
521
+ addOrGetCustomAttributes (this.data, keyOrArray);
522
+ }
523
+ else {
524
+ throw new Error('Either key or value is not a string!');
525
+ }
526
+
527
+ return this;
528
+ }
529
+
530
+
531
+ /**
532
+ * Return a shallow copy of the attendee's options for JSON stringification.
533
+ * Can be used for persistence.
534
+ *
535
+ * @since 0.2.4
536
+ */
537
+ toJSON(): ICalAttendeeJSONData {
538
+ return Object.assign({}, this.data, {
539
+ delegatedTo: this.data.delegatedTo?.email() || null,
540
+ delegatedFrom: this.data.delegatedFrom?.email() || null,
541
+ x: this.x()
542
+ });
543
+ }
544
+
545
+
546
+ /**
547
+ * Return generated attendee as a string.
548
+ *
549
+ * ```javascript
550
+ * console.log(attendee.toString()); // → ATTENDEE;ROLE=…
551
+ * ```
552
+ */
553
+ toString (): string {
554
+ let g = 'ATTENDEE';
555
+
556
+ if (!this.data.email) {
557
+ throw new Error('No value for `email` in ICalAttendee given!');
558
+ }
559
+
560
+ // ROLE
561
+ g += ';ROLE=' + this.data.role;
562
+
563
+ // TYPE
564
+ if (this.data.type) {
565
+ g += ';CUTYPE=' + this.data.type;
566
+ }
567
+
568
+ // PARTSTAT
569
+ if (this.data.status) {
570
+ g += ';PARTSTAT=' + this.data.status;
571
+ }
572
+
573
+ // RSVP
574
+ if (this.data.rsvp !== null) {
575
+ g += ';RSVP=' + this.data.rsvp.toString().toUpperCase();
576
+ }
577
+
578
+ // SENT-BY
579
+ if (this.data.sentBy !== null) {
580
+ g += ';SENT-BY="mailto:' + this.data.sentBy + '"';
581
+ }
582
+
583
+ // DELEGATED-TO
584
+ if (this.data.delegatedTo) {
585
+ g += ';DELEGATED-TO="' + this.data.delegatedTo.email() + '"';
586
+ }
587
+
588
+ // DELEGATED-FROM
589
+ if (this.data.delegatedFrom) {
590
+ g += ';DELEGATED-FROM="' + this.data.delegatedFrom.email() + '"';
591
+ }
592
+
593
+ // CN / Name
594
+ if (this.data.name) {
595
+ g += ';CN="' + escape(this.data.name, true) + '"';
596
+ }
597
+
598
+ // EMAIL
599
+ if (this.data.email && this.data.mailto) {
600
+ g += ';EMAIL=' + escape(this.data.email, false);
601
+ }
602
+
603
+ // CUSTOM X ATTRIBUTES
604
+ if(this.data.x.length) {
605
+ g += ';' + this.data.x
606
+ .map(([key, value]) => key.toUpperCase() + '=' + escape(value, false))
607
+ .join(';');
608
+ }
609
+
610
+ g += ':MAILTO:' + escape(this.data.mailto || this.data.email, false) + '\r\n';
611
+
612
+ return g;
613
+ }
614
+ }