@hebcal/icalendar 5.1.1 → 6.0.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/README.md +11 -54
- package/dist/icalendar.d.ts +1 -24
- package/dist/index.mjs +50 -52
- package/dist/pkgVersion.d.ts +1 -1
- package/package.json +23 -21
- package/dist/index.cjs +0 -506
package/README.md
CHANGED
|
@@ -10,68 +10,25 @@ $ npm install @hebcal/icalendar
|
|
|
10
10
|
```javascript
|
|
11
11
|
import {HebrewCalendar, Location} from '@hebcal/core';
|
|
12
12
|
import {eventsToIcalendar} from '@hebcal/icalendar';
|
|
13
|
+
import fs from 'fs';
|
|
13
14
|
|
|
14
15
|
const options = {
|
|
15
16
|
year: 2020,
|
|
16
17
|
month: 2,
|
|
17
18
|
sedrot: true,
|
|
18
19
|
candlelighting: true,
|
|
19
|
-
location: Location.lookup('
|
|
20
|
+
location: Location.lookup('Tel Aviv'),
|
|
20
21
|
};
|
|
21
22
|
const events = HebrewCalendar.calendar(options);
|
|
22
|
-
console.log(await eventsToIcalendar(ev, options));
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Functions
|
|
26
|
-
|
|
27
|
-
<dl>
|
|
28
|
-
<dt><a href="#eventToIcal">eventToIcal(e, options)</a> ⇒ <code>string</code></dt>
|
|
29
|
-
<dd><p>Transforms a single Event into a VEVENT string</p>
|
|
30
|
-
</dd>
|
|
31
|
-
<dt><a href="#eventsToIcalendarStream">eventsToIcalendarStream(readable, events, options)</a> ⇒ <code>stream.Readable</code></dt>
|
|
32
|
-
<dd><p>Generates an RFC 2445 iCalendar stream from an array of events</p>
|
|
33
|
-
</dd>
|
|
34
|
-
<dt><a href="#eventsToIcalendar">eventsToIcalendar(events, options)</a> ⇒ <code>string</code></dt>
|
|
35
|
-
<dd><p>Renders an array of events as a full RFC 2445 iCalendar string</p>
|
|
36
|
-
</dd>
|
|
37
|
-
</dl>
|
|
38
|
-
|
|
39
|
-
<a name="eventToIcal"></a>
|
|
40
|
-
|
|
41
|
-
## eventToIcal(e, options) ⇒ <code>string</code>
|
|
42
|
-
Transforms a single Event into a VEVENT string
|
|
43
|
-
|
|
44
|
-
**Kind**: global function
|
|
45
|
-
**Returns**: <code>string</code> - multi-line result, delimited by \r\n
|
|
46
|
-
|
|
47
|
-
| Param | Type |
|
|
48
|
-
| --- | --- |
|
|
49
|
-
| e | <code>Event</code> |
|
|
50
|
-
| options | <code>HebcalOptions</code> |
|
|
51
23
|
|
|
52
|
-
|
|
24
|
+
const str = await eventsToIcalendar(events, {
|
|
25
|
+
locale: 'he',
|
|
26
|
+
...options,
|
|
27
|
+
})
|
|
53
28
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| Param | Type |
|
|
60
|
-
| --- | --- |
|
|
61
|
-
| readable | <code>stream.Readable</code> |
|
|
62
|
-
| events | <code>Array.<Event></code> |
|
|
63
|
-
| options | <code>HebcalOptions</code> |
|
|
64
|
-
|
|
65
|
-
<a name="eventsToIcalendar"></a>
|
|
66
|
-
|
|
67
|
-
## eventsToIcalendar(events, options) ⇒ <code>string</code>
|
|
68
|
-
Renders an array of events as a full RFC 2445 iCalendar string
|
|
69
|
-
|
|
70
|
-
**Kind**: global function
|
|
71
|
-
**Returns**: <code>string</code> - multi-line result, delimited by \r\n
|
|
72
|
-
|
|
73
|
-
| Param | Type |
|
|
74
|
-
| --- | --- |
|
|
75
|
-
| events | <code>Array.<Event></code> |
|
|
76
|
-
| options | <code>HebcalOptions</code> |
|
|
29
|
+
const icalStream = fs.createWriteStream('feed.ics');
|
|
30
|
+
icalStream.write(str);
|
|
31
|
+
icalStream.close();
|
|
32
|
+
```
|
|
77
33
|
|
|
34
|
+
## [API Documentation](https://hebcal.github.io/api/icalendar/index.html)
|
package/dist/icalendar.d.ts
CHANGED
|
@@ -42,51 +42,28 @@ export declare class IcalEvent {
|
|
|
42
42
|
*/
|
|
43
43
|
constructor(ev: Event, options?: ICalOptions);
|
|
44
44
|
getAlarm(): string | null;
|
|
45
|
-
/**
|
|
46
|
-
* @return {string}
|
|
47
|
-
*/
|
|
48
45
|
getUid(): string;
|
|
49
|
-
/**
|
|
50
|
-
* @return {string[]}
|
|
51
|
-
*/
|
|
52
46
|
getLongLines(): string[];
|
|
53
|
-
/**
|
|
54
|
-
* @return {string}
|
|
55
|
-
*/
|
|
56
47
|
toString(): string;
|
|
57
48
|
/**
|
|
58
49
|
* fold lines to 75 characters
|
|
59
|
-
* @return {string[]}
|
|
60
50
|
*/
|
|
61
51
|
getLines(): string[];
|
|
62
52
|
/**
|
|
63
53
|
* fold line to 75 characters
|
|
64
|
-
* @param {string} line
|
|
65
|
-
* @return {string}
|
|
66
54
|
*/
|
|
67
55
|
static fold(line: string): string;
|
|
68
|
-
/**
|
|
69
|
-
* @param {string} str
|
|
70
|
-
* @return {string}
|
|
71
|
-
*/
|
|
72
56
|
static escape(str: string): string;
|
|
73
|
-
/**
|
|
74
|
-
* @param {Date} dt
|
|
75
|
-
* @return {string}
|
|
76
|
-
*/
|
|
77
57
|
static formatYYYYMMDD(dt: Date): string;
|
|
78
58
|
/**
|
|
79
59
|
* Returns UTC string for iCalendar
|
|
80
|
-
* @param {Date} dt
|
|
81
|
-
* @return {string}
|
|
82
60
|
*/
|
|
83
61
|
static makeDtstamp(dt: Date): string;
|
|
84
|
-
/** @return {string} */
|
|
85
62
|
static version(): string;
|
|
86
63
|
}
|
|
87
64
|
/**
|
|
88
65
|
* Transforms a single Event into a VEVENT string
|
|
89
|
-
* @
|
|
66
|
+
* @returns multi-line result, delimited by \r\n
|
|
90
67
|
*/
|
|
91
68
|
export declare function eventToIcal(ev: Event, options: ICalOptions): string;
|
|
92
69
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/*! @hebcal/icalendar
|
|
1
|
+
/*! @hebcal/icalendar v6.0.2 */
|
|
2
2
|
import { flags, Locale, greg } from '@hebcal/core';
|
|
3
3
|
import { murmur32HexSync } from 'murmurhash3';
|
|
4
4
|
import { shouldRenderBrief, pad2, getEventCategories, makeAnchor, pad4, getHolidayDescription, getCalendarTitle, appendIsraelAndTracking, makeTorahMemoText } from '@hebcal/rest-api';
|
|
5
5
|
import { promises } from 'fs';
|
|
6
6
|
|
|
7
7
|
// DO NOT EDIT THIS AUTO-GENERATED FILE!
|
|
8
|
-
const version = '
|
|
8
|
+
const version = '6.0.2';
|
|
9
9
|
|
|
10
10
|
const vtimezoneCache = new Map();
|
|
11
11
|
const CATEGORY = {
|
|
@@ -48,8 +48,11 @@ function appendTrackingToUrl(url, options) {
|
|
|
48
48
|
}
|
|
49
49
|
const encoder = new TextEncoder();
|
|
50
50
|
const char74re = /(.{1,74})/g;
|
|
51
|
-
const DAILY_LEARNING = flags.DAILY_LEARNING |
|
|
52
|
-
flags.
|
|
51
|
+
const DAILY_LEARNING = flags.DAILY_LEARNING |
|
|
52
|
+
flags.DAF_YOMI |
|
|
53
|
+
flags.MISHNA_YOMI |
|
|
54
|
+
flags.YERUSHALMI_YOMI |
|
|
55
|
+
flags.NACH_YOMI;
|
|
53
56
|
/**
|
|
54
57
|
* Represents an RFC 2445 iCalendar VEVENT
|
|
55
58
|
*/
|
|
@@ -69,10 +72,12 @@ class IcalEvent {
|
|
|
69
72
|
else if (typeof opts.sequence === 'number') {
|
|
70
73
|
this.sequence = opts.sequence;
|
|
71
74
|
}
|
|
72
|
-
const timed = this.timed = Boolean(ev.eventTime);
|
|
75
|
+
const timed = (this.timed = Boolean(ev.eventTime));
|
|
73
76
|
const locale = opts.locale;
|
|
74
77
|
const location = opts.location;
|
|
75
|
-
let subj = shouldRenderBrief(ev)
|
|
78
|
+
let subj = shouldRenderBrief(ev)
|
|
79
|
+
? ev.renderBrief(locale)
|
|
80
|
+
: ev.render(locale);
|
|
76
81
|
const mask = ev.getFlags();
|
|
77
82
|
if (ev.locationName) {
|
|
78
83
|
this.locationName = ev.locationName;
|
|
@@ -80,7 +85,7 @@ class IcalEvent {
|
|
|
80
85
|
else if (timed && location) {
|
|
81
86
|
this.locationName = location.getShortName();
|
|
82
87
|
}
|
|
83
|
-
else if (
|
|
88
|
+
else if (mask & DAILY_LEARNING && ev.category) {
|
|
84
89
|
this.locationName = Locale.gettext(ev.category, locale);
|
|
85
90
|
}
|
|
86
91
|
const date = IcalEvent.formatYYYYMMDD(ev.getDate().greg());
|
|
@@ -129,7 +134,8 @@ class IcalEvent {
|
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
this.subj = subj;
|
|
132
|
-
this.category =
|
|
137
|
+
this.category =
|
|
138
|
+
ev.category || CATEGORY[(_a = getEventCategories(ev)) === null || _a === void 0 ? void 0 : _a[0]];
|
|
133
139
|
}
|
|
134
140
|
getAlarm() {
|
|
135
141
|
const ev = this.ev;
|
|
@@ -157,9 +163,6 @@ class IcalEvent {
|
|
|
157
163
|
}
|
|
158
164
|
return null;
|
|
159
165
|
}
|
|
160
|
-
/**
|
|
161
|
-
* @return {string}
|
|
162
|
-
*/
|
|
163
166
|
getUid() {
|
|
164
167
|
if (this.ev.uid) {
|
|
165
168
|
return this.ev.uid;
|
|
@@ -178,9 +181,6 @@ class IcalEvent {
|
|
|
178
181
|
}
|
|
179
182
|
return uid;
|
|
180
183
|
}
|
|
181
|
-
/**
|
|
182
|
-
* @return {string[]}
|
|
183
|
-
*/
|
|
184
184
|
getLongLines() {
|
|
185
185
|
if (this.lines)
|
|
186
186
|
return this.lines;
|
|
@@ -189,17 +189,16 @@ class IcalEvent {
|
|
|
189
189
|
if (this.sequence) {
|
|
190
190
|
categoryLine.unshift(`SEQUENCE:${this.sequence}`);
|
|
191
191
|
}
|
|
192
|
-
const arr = this.lines = [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
].concat(categoryLine).concat([
|
|
192
|
+
const arr = (this.lines = ['BEGIN:VEVENT', `DTSTAMP:${this.dtstamp}`]
|
|
193
|
+
.concat(categoryLine)
|
|
194
|
+
.concat([
|
|
196
195
|
`SUMMARY:${this.subj}`,
|
|
197
196
|
`DTSTART${this.dtargs}:${this.startDate}`,
|
|
198
197
|
`DTEND${this.dtargs}:${this.endDate}`,
|
|
199
198
|
`UID:${uid}`,
|
|
200
199
|
`TRANSP:${this.transp}`,
|
|
201
200
|
`X-MICROSOFT-CDO-BUSYSTATUS:${this.busyStatus}`,
|
|
202
|
-
]);
|
|
201
|
+
]));
|
|
203
202
|
if (!this.timed) {
|
|
204
203
|
arr.push('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE');
|
|
205
204
|
}
|
|
@@ -233,23 +232,17 @@ class IcalEvent {
|
|
|
233
232
|
arr.push('END:VEVENT');
|
|
234
233
|
return arr;
|
|
235
234
|
}
|
|
236
|
-
/**
|
|
237
|
-
* @return {string}
|
|
238
|
-
*/
|
|
239
235
|
toString() {
|
|
240
236
|
return this.getLines().join('\r\n');
|
|
241
237
|
}
|
|
242
238
|
/**
|
|
243
239
|
* fold lines to 75 characters
|
|
244
|
-
* @return {string[]}
|
|
245
240
|
*/
|
|
246
241
|
getLines() {
|
|
247
242
|
return this.getLongLines().map(IcalEvent.fold);
|
|
248
243
|
}
|
|
249
244
|
/**
|
|
250
245
|
* fold line to 75 characters
|
|
251
|
-
* @param {string} line
|
|
252
|
-
* @return {string}
|
|
253
246
|
*/
|
|
254
247
|
static fold(line) {
|
|
255
248
|
let isASCII = true;
|
|
@@ -292,10 +285,6 @@ class IcalEvent {
|
|
|
292
285
|
}
|
|
293
286
|
return result + current;
|
|
294
287
|
}
|
|
295
|
-
/**
|
|
296
|
-
* @param {string} str
|
|
297
|
-
* @return {string}
|
|
298
|
-
*/
|
|
299
288
|
static escape(str) {
|
|
300
289
|
if (str.indexOf(',') !== -1) {
|
|
301
290
|
str = str.replace(/,/g, '\\,');
|
|
@@ -305,46 +294,45 @@ class IcalEvent {
|
|
|
305
294
|
}
|
|
306
295
|
return str;
|
|
307
296
|
}
|
|
308
|
-
/**
|
|
309
|
-
* @param {Date} dt
|
|
310
|
-
* @return {string}
|
|
311
|
-
*/
|
|
312
297
|
static formatYYYYMMDD(dt) {
|
|
313
|
-
return pad4(dt.getFullYear()) +
|
|
314
|
-
pad2(dt.getMonth() + 1) + pad2(dt.getDate());
|
|
298
|
+
return (pad4(dt.getFullYear()) + pad2(dt.getMonth() + 1) + pad2(dt.getDate()));
|
|
315
299
|
}
|
|
316
300
|
/**
|
|
317
301
|
* Returns UTC string for iCalendar
|
|
318
|
-
* @param {Date} dt
|
|
319
|
-
* @return {string}
|
|
320
302
|
*/
|
|
321
303
|
static makeDtstamp(dt) {
|
|
322
304
|
const s = dt.toISOString();
|
|
323
|
-
return s.slice(0, 4) +
|
|
324
|
-
s.slice(
|
|
305
|
+
return (s.slice(0, 4) +
|
|
306
|
+
s.slice(5, 7) +
|
|
307
|
+
s.slice(8, 13) +
|
|
308
|
+
s.slice(14, 16) +
|
|
309
|
+
s.slice(17, 19) +
|
|
310
|
+
'Z');
|
|
325
311
|
}
|
|
326
|
-
/** @return {string} */
|
|
327
312
|
static version() {
|
|
328
313
|
return version;
|
|
329
314
|
}
|
|
330
315
|
}
|
|
331
316
|
/**
|
|
332
317
|
* Transforms a single Event into a VEVENT string
|
|
333
|
-
* @
|
|
318
|
+
* @returns multi-line result, delimited by \r\n
|
|
334
319
|
*/
|
|
335
320
|
function eventToIcal(ev, options) {
|
|
336
321
|
const ical = new IcalEvent(ev, options);
|
|
337
322
|
return ical.toString();
|
|
338
323
|
}
|
|
339
324
|
const torahMemoCache = new Map();
|
|
340
|
-
const HOLIDAY_IGNORE_MASK = DAILY_LEARNING |
|
|
341
|
-
flags.
|
|
325
|
+
const HOLIDAY_IGNORE_MASK = DAILY_LEARNING |
|
|
326
|
+
flags.OMER_COUNT |
|
|
327
|
+
flags.SHABBAT_MEVARCHIM |
|
|
328
|
+
flags.MOLAD |
|
|
329
|
+
flags.USER_EVENT |
|
|
342
330
|
flags.HEBREW_DATE;
|
|
343
331
|
/**
|
|
344
332
|
* @private
|
|
345
333
|
*/
|
|
346
334
|
function makeTorahMemo(ev, il) {
|
|
347
|
-
if (
|
|
335
|
+
if (ev.getFlags() & HOLIDAY_IGNORE_MASK || ev.eventTime) {
|
|
348
336
|
return '';
|
|
349
337
|
}
|
|
350
338
|
const hd = ev.getDate();
|
|
@@ -375,8 +363,16 @@ function createMemo(ev, options) {
|
|
|
375
363
|
const mask = ev.getFlags();
|
|
376
364
|
if (mask & flags.OMER_COUNT) {
|
|
377
365
|
const omerEv = ev;
|
|
378
|
-
const sefira = [
|
|
379
|
-
|
|
366
|
+
const sefira = [
|
|
367
|
+
omerEv.sefira('en'),
|
|
368
|
+
omerEv.sefira('he'),
|
|
369
|
+
omerEv.sefira('translit'),
|
|
370
|
+
].join('\\n');
|
|
371
|
+
return (omerEv.getTodayIs('en') +
|
|
372
|
+
'\\n\\n' +
|
|
373
|
+
omerEv.getTodayIs('he') +
|
|
374
|
+
'\\n\\n' +
|
|
375
|
+
sefira);
|
|
380
376
|
}
|
|
381
377
|
const url = appendTrackingToUrl(ev.url(), options);
|
|
382
378
|
const torahMemo = makeTorahMemo(ev, options.il);
|
|
@@ -415,7 +411,7 @@ async function eventsToIcalendar(events, options) {
|
|
|
415
411
|
if (!opts.title) {
|
|
416
412
|
opts.title = getCalendarTitle(events, opts);
|
|
417
413
|
}
|
|
418
|
-
const icals = events.map(
|
|
414
|
+
const icals = events.map(ev => new IcalEvent(ev, opts));
|
|
419
415
|
return icalEventsToString(icals, opts);
|
|
420
416
|
}
|
|
421
417
|
/**
|
|
@@ -432,11 +428,13 @@ async function icalEventsToString(icals, options) {
|
|
|
432
428
|
const opts = Object.assign({}, options);
|
|
433
429
|
opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
|
|
434
430
|
const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
|
|
435
|
-
const caldesc = opts.caldesc
|
|
436
|
-
opts.
|
|
437
|
-
|
|
438
|
-
'
|
|
439
|
-
|
|
431
|
+
const caldesc = opts.caldesc
|
|
432
|
+
? IcalEvent.escape(opts.caldesc)
|
|
433
|
+
: opts.yahrzeit
|
|
434
|
+
? 'Yahrzeits + Anniversaries from www.hebcal.com'
|
|
435
|
+
: 'Jewish Holidays from www.hebcal.com';
|
|
436
|
+
const prodid = opts.prodid ||
|
|
437
|
+
`-//hebcal.com/NONSGML Hebcal Calendar v1${version}//${uclang}`;
|
|
440
438
|
const preamble = [
|
|
441
439
|
'BEGIN:VCALENDAR',
|
|
442
440
|
'VERSION:2.0',
|
package/dist/pkgVersion.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "
|
|
1
|
+
export declare const version = "6.0.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hebcal/icalendar",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"author": "Michael J. Radwin (https://github.com/mjradwin)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ical",
|
|
@@ -9,15 +9,16 @@
|
|
|
9
9
|
"hebcal"
|
|
10
10
|
],
|
|
11
11
|
"description": "Jewish holidays and Hebrew calendar as iCalendar RFC 2445",
|
|
12
|
-
"main": "dist/index.cjs",
|
|
13
12
|
"module": "dist/index.mjs",
|
|
14
13
|
"type": "module",
|
|
15
14
|
"exports": {
|
|
16
15
|
"import": "./dist/index.mjs",
|
|
17
|
-
"require": "./dist/index.cjs",
|
|
18
16
|
"types": "./dist/icalendar.d.ts"
|
|
19
17
|
},
|
|
20
18
|
"typings": "dist/icalendar.d.ts",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">= 18.0.0"
|
|
21
|
+
},
|
|
21
22
|
"files": [
|
|
22
23
|
"dist"
|
|
23
24
|
],
|
|
@@ -29,10 +30,10 @@
|
|
|
29
30
|
"url": "https://github.com/hebcal/hebcal-icalendar/issues"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@hebcal/core": "^5.
|
|
33
|
-
"@hebcal/rest-api": "^5.0
|
|
33
|
+
"@hebcal/core": "^5.8.0",
|
|
34
|
+
"@hebcal/rest-api": "^5.2.0",
|
|
34
35
|
"murmurhash3": "^0.5.0",
|
|
35
|
-
"tslib": "^2.
|
|
36
|
+
"tslib": "^2.8.1"
|
|
36
37
|
},
|
|
37
38
|
"scripts": {
|
|
38
39
|
"build:rollup": "rollup -c",
|
|
@@ -40,27 +41,28 @@
|
|
|
40
41
|
"build": "npm run build:version && npm run build:rollup",
|
|
41
42
|
"prepare": "npm run build",
|
|
42
43
|
"pretest": "npm run build",
|
|
43
|
-
"
|
|
44
|
+
"docs": "typedoc",
|
|
44
45
|
"coverage": "jest --coverage",
|
|
45
|
-
"test": "jest"
|
|
46
|
+
"test": "jest",
|
|
47
|
+
"lint": "gts lint",
|
|
48
|
+
"clean": "gts clean",
|
|
49
|
+
"compile": "tsc",
|
|
50
|
+
"fix": "gts fix"
|
|
46
51
|
},
|
|
47
52
|
"license": "BSD-2-Clause",
|
|
48
53
|
"devDependencies": {
|
|
49
|
-
"@babel/preset-env": "^7.
|
|
50
|
-
"@babel/preset-typescript": "^7.
|
|
51
|
-
"@hebcal/learning": "^5.
|
|
52
|
-
"@rollup/plugin-commonjs": "^
|
|
54
|
+
"@babel/preset-env": "^7.26.0",
|
|
55
|
+
"@babel/preset-typescript": "^7.26.0",
|
|
56
|
+
"@hebcal/learning": "^5.1.2",
|
|
57
|
+
"@rollup/plugin-commonjs": "^28.0.1",
|
|
53
58
|
"@rollup/plugin-json": "^6.1.0",
|
|
54
|
-
"@rollup/plugin-typescript": "^
|
|
55
|
-
"@types/jest": "^29.5.
|
|
56
|
-
"
|
|
57
|
-
"eslint-config-google": "^0.14.0",
|
|
59
|
+
"@rollup/plugin-typescript": "^12.1.1",
|
|
60
|
+
"@types/jest": "^29.5.14",
|
|
61
|
+
"@types/node": "22.9.0",
|
|
58
62
|
"gts": "^5.3.1",
|
|
59
63
|
"jest": "^29.7.0",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"ts-jest": "^29.1.5",
|
|
64
|
-
"typescript": "^5.5.3"
|
|
64
|
+
"rollup": "^4.25.0",
|
|
65
|
+
"typedoc": "^0.26.11",
|
|
66
|
+
"typescript": "^5.6.3"
|
|
65
67
|
}
|
|
66
68
|
}
|
package/dist/index.cjs
DELETED
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
/*! @hebcal/icalendar v5.1.1 */
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var core = require('@hebcal/core');
|
|
5
|
-
var murmurhash3 = require('murmurhash3');
|
|
6
|
-
var restApi = require('@hebcal/rest-api');
|
|
7
|
-
var fs = require('fs');
|
|
8
|
-
|
|
9
|
-
// DO NOT EDIT THIS AUTO-GENERATED FILE!
|
|
10
|
-
const version = '5.1.1';
|
|
11
|
-
|
|
12
|
-
const vtimezoneCache = new Map();
|
|
13
|
-
const CATEGORY = {
|
|
14
|
-
candles: 'Holiday',
|
|
15
|
-
dafyomi: 'Daf Yomi',
|
|
16
|
-
mishnayomi: 'Mishna Yomi',
|
|
17
|
-
nachyomi: 'Nach Yomi',
|
|
18
|
-
yerushalmi: 'Yerushalmi Yomi',
|
|
19
|
-
havdalah: 'Holiday',
|
|
20
|
-
hebdate: null,
|
|
21
|
-
holiday: 'Holiday',
|
|
22
|
-
mevarchim: null,
|
|
23
|
-
molad: null,
|
|
24
|
-
omer: null,
|
|
25
|
-
parashat: 'Parsha',
|
|
26
|
-
roshchodesh: 'Holiday',
|
|
27
|
-
user: 'Personal',
|
|
28
|
-
zmanim: null,
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* @private
|
|
32
|
-
*/
|
|
33
|
-
function addOptional(arr, key, val) {
|
|
34
|
-
if (val) {
|
|
35
|
-
const str = IcalEvent.escape(val);
|
|
36
|
-
arr.push(key + ':' + str);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* @private
|
|
41
|
-
*/
|
|
42
|
-
function appendTrackingToUrl(url, options) {
|
|
43
|
-
if (!url) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
const utmSource = options.utmSource || 'js';
|
|
47
|
-
const utmMedium = options.utmMedium || 'icalendar';
|
|
48
|
-
const utmCampaign = options.utmCampaign;
|
|
49
|
-
return restApi.appendIsraelAndTracking(url, options.il, utmSource, utmMedium, utmCampaign);
|
|
50
|
-
}
|
|
51
|
-
const encoder = new TextEncoder();
|
|
52
|
-
const char74re = /(.{1,74})/g;
|
|
53
|
-
const DAILY_LEARNING = core.flags.DAILY_LEARNING | core.flags.DAF_YOMI |
|
|
54
|
-
core.flags.MISHNA_YOMI | core.flags.YERUSHALMI_YOMI | core.flags.NACH_YOMI;
|
|
55
|
-
/**
|
|
56
|
-
* Represents an RFC 2445 iCalendar VEVENT
|
|
57
|
-
*/
|
|
58
|
-
class IcalEvent {
|
|
59
|
-
/**
|
|
60
|
-
* Builds an IcalEvent object from a Hebcal Event
|
|
61
|
-
*/
|
|
62
|
-
constructor(ev, options = {}) {
|
|
63
|
-
var _a;
|
|
64
|
-
this.ev = ev;
|
|
65
|
-
const opts = Object.assign({}, options);
|
|
66
|
-
this.options = opts;
|
|
67
|
-
this.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
|
|
68
|
-
if (typeof ev.sequence === 'number') {
|
|
69
|
-
this.sequence = ev.sequence;
|
|
70
|
-
}
|
|
71
|
-
else if (typeof opts.sequence === 'number') {
|
|
72
|
-
this.sequence = opts.sequence;
|
|
73
|
-
}
|
|
74
|
-
const timed = this.timed = Boolean(ev.eventTime);
|
|
75
|
-
const locale = opts.locale;
|
|
76
|
-
const location = opts.location;
|
|
77
|
-
let subj = restApi.shouldRenderBrief(ev) ? ev.renderBrief(locale) : ev.render(locale);
|
|
78
|
-
const mask = ev.getFlags();
|
|
79
|
-
if (ev.locationName) {
|
|
80
|
-
this.locationName = ev.locationName;
|
|
81
|
-
}
|
|
82
|
-
else if (timed && location) {
|
|
83
|
-
this.locationName = location.getShortName();
|
|
84
|
-
}
|
|
85
|
-
else if ((mask & DAILY_LEARNING) && ev.category) {
|
|
86
|
-
this.locationName = core.Locale.gettext(ev.category, locale);
|
|
87
|
-
}
|
|
88
|
-
const date = IcalEvent.formatYYYYMMDD(ev.getDate().greg());
|
|
89
|
-
this.startDate = this.isoDateOnly = date;
|
|
90
|
-
this.dtargs = '';
|
|
91
|
-
this.transp = 'TRANSPARENT';
|
|
92
|
-
this.busyStatus = 'FREE';
|
|
93
|
-
if (timed) {
|
|
94
|
-
let [hour, minute] = ev.eventTimeStr.split(':');
|
|
95
|
-
hour = +hour;
|
|
96
|
-
minute = +minute;
|
|
97
|
-
this.startDate += 'T' + restApi.pad2(hour) + restApi.pad2(minute) + '00';
|
|
98
|
-
this.endDate = this.startDate;
|
|
99
|
-
if (location === null || location === void 0 ? void 0 : location.getTzid()) {
|
|
100
|
-
this.dtargs = `;TZID=${location.getTzid()}`;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
this.endDate = IcalEvent.formatYYYYMMDD(ev.getDate().next().greg());
|
|
105
|
-
// for all-day untimed, use DTEND;VALUE=DATE intsead of DURATION:P1D.
|
|
106
|
-
// It's more compatible with everthing except ancient versions of
|
|
107
|
-
// Lotus Notes circa 2004
|
|
108
|
-
this.dtargs = ';VALUE=DATE';
|
|
109
|
-
if (mask & core.flags.CHAG) {
|
|
110
|
-
this.transp = 'OPAQUE';
|
|
111
|
-
this.busyStatus = 'OOF';
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (opts.emoji) {
|
|
115
|
-
const prefix = ev.getEmoji();
|
|
116
|
-
if (prefix) {
|
|
117
|
-
if (mask & core.flags.OMER_COUNT) {
|
|
118
|
-
subj = subj + ' ' + prefix;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
subj = prefix + ' ' + subj;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// make subject safe for iCalendar
|
|
126
|
-
subj = IcalEvent.escape(subj);
|
|
127
|
-
if (opts.appendHebrewToSubject) {
|
|
128
|
-
const hebrew = ev.renderBrief('he');
|
|
129
|
-
if (hebrew) {
|
|
130
|
-
subj += ` / ${hebrew}`;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
this.subj = subj;
|
|
134
|
-
this.category = ev.category || CATEGORY[(_a = restApi.getEventCategories(ev)) === null || _a === void 0 ? void 0 : _a[0]];
|
|
135
|
-
}
|
|
136
|
-
getAlarm() {
|
|
137
|
-
const ev = this.ev;
|
|
138
|
-
const mask = ev.getFlags();
|
|
139
|
-
const evAlarm = ev.alarm;
|
|
140
|
-
if (typeof evAlarm === 'string') {
|
|
141
|
-
return 'TRIGGER:' + evAlarm;
|
|
142
|
-
}
|
|
143
|
-
else if (typeof evAlarm === 'boolean' && !evAlarm) {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
else if (core.greg.isDate(evAlarm)) {
|
|
147
|
-
const alarmDt = evAlarm;
|
|
148
|
-
alarmDt.setSeconds(0);
|
|
149
|
-
return 'TRIGGER;VALUE=DATE-TIME:' + IcalEvent.makeDtstamp(alarmDt);
|
|
150
|
-
}
|
|
151
|
-
else if (mask & core.flags.OMER_COUNT) {
|
|
152
|
-
return 'TRIGGER:-P0DT3H30M0S'; // 8:30pm Omer alarm evening before
|
|
153
|
-
}
|
|
154
|
-
else if (mask & core.flags.USER_EVENT) {
|
|
155
|
-
return 'TRIGGER:-P0DT12H0M0S'; // noon the day before
|
|
156
|
-
}
|
|
157
|
-
else if (this.timed && ev.getDesc().startsWith('Candle lighting')) {
|
|
158
|
-
return 'TRIGGER:-P0DT0H10M0S';
|
|
159
|
-
}
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* @return {string}
|
|
164
|
-
*/
|
|
165
|
-
getUid() {
|
|
166
|
-
if (this.ev.uid) {
|
|
167
|
-
return this.ev.uid;
|
|
168
|
-
}
|
|
169
|
-
const options = this.options;
|
|
170
|
-
const digest = murmurhash3.murmur32HexSync(this.ev.getDesc());
|
|
171
|
-
let uid = `hebcal-${this.isoDateOnly}-${digest}`;
|
|
172
|
-
const loc = options.location;
|
|
173
|
-
if (this.timed && loc) {
|
|
174
|
-
if (loc.getGeoId()) {
|
|
175
|
-
uid += `-${loc.getGeoId()}`;
|
|
176
|
-
}
|
|
177
|
-
else if (loc.getName()) {
|
|
178
|
-
uid += '-' + restApi.makeAnchor(loc.getName());
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return uid;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* @return {string[]}
|
|
185
|
-
*/
|
|
186
|
-
getLongLines() {
|
|
187
|
-
if (this.lines)
|
|
188
|
-
return this.lines;
|
|
189
|
-
const categoryLine = this.category ? [`CATEGORIES:${this.category}`] : [];
|
|
190
|
-
const uid = this.getUid();
|
|
191
|
-
if (this.sequence) {
|
|
192
|
-
categoryLine.unshift(`SEQUENCE:${this.sequence}`);
|
|
193
|
-
}
|
|
194
|
-
const arr = this.lines = [
|
|
195
|
-
'BEGIN:VEVENT',
|
|
196
|
-
`DTSTAMP:${this.dtstamp}`,
|
|
197
|
-
].concat(categoryLine).concat([
|
|
198
|
-
`SUMMARY:${this.subj}`,
|
|
199
|
-
`DTSTART${this.dtargs}:${this.startDate}`,
|
|
200
|
-
`DTEND${this.dtargs}:${this.endDate}`,
|
|
201
|
-
`UID:${uid}`,
|
|
202
|
-
`TRANSP:${this.transp}`,
|
|
203
|
-
`X-MICROSOFT-CDO-BUSYSTATUS:${this.busyStatus}`,
|
|
204
|
-
]);
|
|
205
|
-
if (!this.timed) {
|
|
206
|
-
arr.push('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE');
|
|
207
|
-
}
|
|
208
|
-
const ev = this.ev;
|
|
209
|
-
const mask = ev.getFlags();
|
|
210
|
-
const isUserEvent = Boolean(mask & core.flags.USER_EVENT);
|
|
211
|
-
if (!isUserEvent) {
|
|
212
|
-
arr.push('CLASS:PUBLIC');
|
|
213
|
-
}
|
|
214
|
-
const options = this.options;
|
|
215
|
-
// create memo (holiday descr, Torah, etc)
|
|
216
|
-
const memo = createMemo(ev, options);
|
|
217
|
-
addOptional(arr, 'DESCRIPTION', memo);
|
|
218
|
-
addOptional(arr, 'LOCATION', this.locationName);
|
|
219
|
-
const loc = options.location;
|
|
220
|
-
if (this.timed && loc) {
|
|
221
|
-
arr.push('GEO:' + loc.getLatitude() + ';' + loc.getLongitude());
|
|
222
|
-
}
|
|
223
|
-
// In addition to the URL being part of the DESCRIPTION field,
|
|
224
|
-
// should we also generate an RFC 5545 URL property?
|
|
225
|
-
if (options.url) {
|
|
226
|
-
const url = ev.url();
|
|
227
|
-
if (url) {
|
|
228
|
-
arr.push('URL:' + appendTrackingToUrl(url, options));
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const trigger = this.getAlarm();
|
|
232
|
-
if (trigger) {
|
|
233
|
-
arr.push('BEGIN:VALARM', 'ACTION:DISPLAY', 'DESCRIPTION:Event reminder', `${trigger}`, 'END:VALARM');
|
|
234
|
-
}
|
|
235
|
-
arr.push('END:VEVENT');
|
|
236
|
-
return arr;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* @return {string}
|
|
240
|
-
*/
|
|
241
|
-
toString() {
|
|
242
|
-
return this.getLines().join('\r\n');
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* fold lines to 75 characters
|
|
246
|
-
* @return {string[]}
|
|
247
|
-
*/
|
|
248
|
-
getLines() {
|
|
249
|
-
return this.getLongLines().map(IcalEvent.fold);
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* fold line to 75 characters
|
|
253
|
-
* @param {string} line
|
|
254
|
-
* @return {string}
|
|
255
|
-
*/
|
|
256
|
-
static fold(line) {
|
|
257
|
-
let isASCII = true;
|
|
258
|
-
for (let i = 0; i < line.length; i++) {
|
|
259
|
-
if (line.charCodeAt(i) > 255) {
|
|
260
|
-
isASCII = false;
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
if (isASCII) {
|
|
265
|
-
if (line.length <= 74) {
|
|
266
|
-
return line;
|
|
267
|
-
}
|
|
268
|
-
const matches = line.match(char74re);
|
|
269
|
-
return matches.join('\r\n ');
|
|
270
|
-
}
|
|
271
|
-
if (encoder.encode(line).length <= 74) {
|
|
272
|
-
return line;
|
|
273
|
-
}
|
|
274
|
-
// iterate unicode character by character, making sure
|
|
275
|
-
// that adding a new character would keep the line <= 75 octets
|
|
276
|
-
let result = '';
|
|
277
|
-
let current = '';
|
|
278
|
-
let len = 0;
|
|
279
|
-
for (let i = 0; i < line.length; i++) {
|
|
280
|
-
const char = line[i];
|
|
281
|
-
const octets = char.charCodeAt(0) < 256 ? 1 : encoder.encode(char).length;
|
|
282
|
-
const newlen = len + octets;
|
|
283
|
-
if (newlen < 75) {
|
|
284
|
-
current += char;
|
|
285
|
-
len = newlen;
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
result += current + '\r\n ';
|
|
289
|
-
line = line.substring(i);
|
|
290
|
-
current = '';
|
|
291
|
-
len = 0;
|
|
292
|
-
i = -1;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return result + current;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* @param {string} str
|
|
299
|
-
* @return {string}
|
|
300
|
-
*/
|
|
301
|
-
static escape(str) {
|
|
302
|
-
if (str.indexOf(',') !== -1) {
|
|
303
|
-
str = str.replace(/,/g, '\\,');
|
|
304
|
-
}
|
|
305
|
-
if (str.indexOf(';') !== -1) {
|
|
306
|
-
str = str.replace(/;/g, '\\;');
|
|
307
|
-
}
|
|
308
|
-
return str;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* @param {Date} dt
|
|
312
|
-
* @return {string}
|
|
313
|
-
*/
|
|
314
|
-
static formatYYYYMMDD(dt) {
|
|
315
|
-
return restApi.pad4(dt.getFullYear()) +
|
|
316
|
-
restApi.pad2(dt.getMonth() + 1) + restApi.pad2(dt.getDate());
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Returns UTC string for iCalendar
|
|
320
|
-
* @param {Date} dt
|
|
321
|
-
* @return {string}
|
|
322
|
-
*/
|
|
323
|
-
static makeDtstamp(dt) {
|
|
324
|
-
const s = dt.toISOString();
|
|
325
|
-
return s.slice(0, 4) + s.slice(5, 7) + s.slice(8, 13) +
|
|
326
|
-
s.slice(14, 16) + s.slice(17, 19) + 'Z';
|
|
327
|
-
}
|
|
328
|
-
/** @return {string} */
|
|
329
|
-
static version() {
|
|
330
|
-
return version;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Transforms a single Event into a VEVENT string
|
|
335
|
-
* @return {string} multi-line result, delimited by \r\n
|
|
336
|
-
*/
|
|
337
|
-
function eventToIcal(ev, options) {
|
|
338
|
-
const ical = new IcalEvent(ev, options);
|
|
339
|
-
return ical.toString();
|
|
340
|
-
}
|
|
341
|
-
const torahMemoCache = new Map();
|
|
342
|
-
const HOLIDAY_IGNORE_MASK = DAILY_LEARNING | core.flags.OMER_COUNT |
|
|
343
|
-
core.flags.SHABBAT_MEVARCHIM | core.flags.MOLAD | core.flags.USER_EVENT |
|
|
344
|
-
core.flags.HEBREW_DATE;
|
|
345
|
-
/**
|
|
346
|
-
* @private
|
|
347
|
-
*/
|
|
348
|
-
function makeTorahMemo(ev, il) {
|
|
349
|
-
if ((ev.getFlags() & HOLIDAY_IGNORE_MASK) || ev.eventTime) {
|
|
350
|
-
return '';
|
|
351
|
-
}
|
|
352
|
-
const hd = ev.getDate();
|
|
353
|
-
const yy = hd.getFullYear();
|
|
354
|
-
const mm = hd.getMonth();
|
|
355
|
-
const dd = hd.getDate();
|
|
356
|
-
const key = [yy, mm, dd, il ? '1' : '0', ev.getDesc()].join('-');
|
|
357
|
-
let memo = torahMemoCache.get(key);
|
|
358
|
-
if (typeof memo === 'string') {
|
|
359
|
-
return memo;
|
|
360
|
-
}
|
|
361
|
-
memo = restApi.makeTorahMemoText(ev, il).replace(/\n/g, '\\n');
|
|
362
|
-
torahMemoCache.set(key, memo);
|
|
363
|
-
return memo;
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* @private
|
|
367
|
-
*/
|
|
368
|
-
function createMemo(ev, options) {
|
|
369
|
-
let memo = ev.memo;
|
|
370
|
-
if (typeof memo === 'string' && memo.length && memo.indexOf('\n') !== -1) {
|
|
371
|
-
memo = memo.replace(/\n/g, '\\n');
|
|
372
|
-
}
|
|
373
|
-
const desc = ev.getDesc();
|
|
374
|
-
if (desc === 'Havdalah' || desc === 'Candle lighting') {
|
|
375
|
-
return memo || '';
|
|
376
|
-
}
|
|
377
|
-
const mask = ev.getFlags();
|
|
378
|
-
if (mask & core.flags.OMER_COUNT) {
|
|
379
|
-
const omerEv = ev;
|
|
380
|
-
const sefira = [omerEv.sefira('en'), omerEv.sefira('he'), omerEv.sefira('translit')].join('\\n');
|
|
381
|
-
return omerEv.getTodayIs('en') + '\\n\\n' + omerEv.getTodayIs('he') + '\\n\\n' + sefira;
|
|
382
|
-
}
|
|
383
|
-
const url = appendTrackingToUrl(ev.url(), options);
|
|
384
|
-
const torahMemo = makeTorahMemo(ev, options.il);
|
|
385
|
-
if (!memo) {
|
|
386
|
-
if (typeof ev.linkedEvent !== 'undefined') {
|
|
387
|
-
memo = ev.linkedEvent.render(options.locale);
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
memo = restApi.getHolidayDescription(ev);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
if (torahMemo) {
|
|
394
|
-
if (memo.length) {
|
|
395
|
-
memo += '\\n\\n';
|
|
396
|
-
}
|
|
397
|
-
memo += torahMemo;
|
|
398
|
-
}
|
|
399
|
-
if (url) {
|
|
400
|
-
if (memo.length) {
|
|
401
|
-
memo += '\\n\\n';
|
|
402
|
-
}
|
|
403
|
-
memo += url;
|
|
404
|
-
}
|
|
405
|
-
return memo;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Generates an RFC 2445 iCalendar string from an array of events
|
|
409
|
-
*/
|
|
410
|
-
async function eventsToIcalendar(events, options) {
|
|
411
|
-
if (!events.length)
|
|
412
|
-
throw new RangeError('Events can not be empty');
|
|
413
|
-
if (!options)
|
|
414
|
-
throw new TypeError('Invalid options object');
|
|
415
|
-
const opts = Object.assign({}, options);
|
|
416
|
-
opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
|
|
417
|
-
if (!opts.title) {
|
|
418
|
-
opts.title = restApi.getCalendarTitle(events, opts);
|
|
419
|
-
}
|
|
420
|
-
const icals = events.map((ev) => new IcalEvent(ev, opts));
|
|
421
|
-
return icalEventsToString(icals, opts);
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Generates an RFC 2445 iCalendar string from an array of IcalEvents
|
|
425
|
-
*/
|
|
426
|
-
async function icalEventsToString(icals, options) {
|
|
427
|
-
if (!icals.length)
|
|
428
|
-
throw new RangeError('Events can not be empty');
|
|
429
|
-
if (!options)
|
|
430
|
-
throw new TypeError('Invalid options object');
|
|
431
|
-
const stream = [];
|
|
432
|
-
const locale = options.locale || core.Locale.getLocaleName();
|
|
433
|
-
const uclang = locale.toUpperCase();
|
|
434
|
-
const opts = Object.assign({}, options);
|
|
435
|
-
opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
|
|
436
|
-
const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
|
|
437
|
-
const caldesc = opts.caldesc ? IcalEvent.escape(opts.caldesc) :
|
|
438
|
-
opts.yahrzeit ?
|
|
439
|
-
'Yahrzeits + Anniversaries from www.hebcal.com' :
|
|
440
|
-
'Jewish Holidays from www.hebcal.com';
|
|
441
|
-
const prodid = opts.prodid || `-//hebcal.com/NONSGML Hebcal Calendar v1${version}//${uclang}`;
|
|
442
|
-
const preamble = [
|
|
443
|
-
'BEGIN:VCALENDAR',
|
|
444
|
-
'VERSION:2.0',
|
|
445
|
-
`PRODID:${prodid}`,
|
|
446
|
-
'CALSCALE:GREGORIAN',
|
|
447
|
-
'METHOD:PUBLISH',
|
|
448
|
-
'X-LOTUS-CHARSET:UTF-8',
|
|
449
|
-
];
|
|
450
|
-
if (opts.publishedTTL !== false) {
|
|
451
|
-
const publishedTTL = opts.publishedTTL || 'PT7D';
|
|
452
|
-
preamble.push(`X-PUBLISHED-TTL:${publishedTTL}`);
|
|
453
|
-
}
|
|
454
|
-
preamble.push(`X-WR-CALNAME:${title}`);
|
|
455
|
-
preamble.push(`X-WR-CALDESC:${caldesc}`);
|
|
456
|
-
for (const line of preamble.map(IcalEvent.fold)) {
|
|
457
|
-
stream.push(line);
|
|
458
|
-
stream.push('\r\n');
|
|
459
|
-
}
|
|
460
|
-
if (opts.relcalid) {
|
|
461
|
-
stream.push(IcalEvent.fold(`X-WR-RELCALID:${opts.relcalid}`));
|
|
462
|
-
stream.push('\r\n');
|
|
463
|
-
}
|
|
464
|
-
if (opts.calendarColor) {
|
|
465
|
-
stream.push(`X-APPLE-CALENDAR-COLOR:${opts.calendarColor}\r\n`);
|
|
466
|
-
}
|
|
467
|
-
const location = opts.location;
|
|
468
|
-
const tzid = location === null || location === void 0 ? void 0 : location.getTzid();
|
|
469
|
-
if (tzid) {
|
|
470
|
-
stream.push(`X-WR-TIMEZONE;VALUE=TEXT:${tzid}\r\n`);
|
|
471
|
-
const vtz = vtimezoneCache.get(tzid);
|
|
472
|
-
if (typeof vtz === 'string') {
|
|
473
|
-
stream.push(vtz);
|
|
474
|
-
stream.push('\r\n');
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
const vtimezoneFilename = `./zoneinfo/${tzid}.ics`;
|
|
478
|
-
try {
|
|
479
|
-
const vtimezoneIcs = await fs.promises.readFile(vtimezoneFilename, 'utf-8');
|
|
480
|
-
const lines = vtimezoneIcs.split('\r\n');
|
|
481
|
-
// ignore first 3 and last 1 lines
|
|
482
|
-
const str = lines.slice(3, lines.length - 2).join('\r\n');
|
|
483
|
-
stream.push(str);
|
|
484
|
-
stream.push('\r\n');
|
|
485
|
-
vtimezoneCache.set(tzid, str);
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
// ignore failure when no timezone definition to read
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
for (const ical of icals) {
|
|
493
|
-
const lines = ical.getLines();
|
|
494
|
-
for (const line of lines) {
|
|
495
|
-
stream.push(line);
|
|
496
|
-
stream.push('\r\n');
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
stream.push('END:VCALENDAR\r\n');
|
|
500
|
-
return stream.join('');
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
exports.IcalEvent = IcalEvent;
|
|
504
|
-
exports.eventToIcal = eventToIcal;
|
|
505
|
-
exports.eventsToIcalendar = eventsToIcalendar;
|
|
506
|
-
exports.icalEventsToString = icalEventsToString;
|