@hebcal/icalendar 5.0.5 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/icalendar v5.0.5 */
1
+ /*! @hebcal/icalendar v5.1.0 */
2
2
  'use strict';
3
3
 
4
4
  var core = require('@hebcal/core');
@@ -7,525 +7,483 @@ var restApi = require('@hebcal/rest-api');
7
7
  var fs = require('fs');
8
8
 
9
9
  // DO NOT EDIT THIS AUTO-GENERATED FILE!
10
- const version = '5.0.5';
10
+ const version = '5.1.0';
11
11
 
12
12
  const vtimezoneCache = new Map();
13
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,
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
29
  };
30
-
31
30
  /**
32
31
  * @private
33
- * @param {string[]} arr
34
- * @param {string} key
35
- * @param {string} val
36
32
  */
37
33
  function addOptional(arr, key, val) {
38
- if (val) {
39
- const str = IcalEvent.escape(val);
40
- arr.push(key + ':' + str);
41
- }
34
+ if (val) {
35
+ const str = IcalEvent.escape(val);
36
+ arr.push(key + ':' + str);
37
+ }
42
38
  }
43
-
44
39
  /**
45
40
  * @private
46
- * @param {string} url
47
- * @param {CalOptions} options
48
- * @return {string}
49
41
  */
50
42
  function appendTrackingToUrl(url, options) {
51
- if (!url) {
52
- return null;
53
- }
54
- const utmSource = options.utmSource || 'js';
55
- const utmMedium = options.utmMedium || 'icalendar';
56
- const utmCampaign = options.utmCampaign;
57
- return restApi.appendIsraelAndTracking(url,
58
- options.il, utmSource, utmMedium, utmCampaign);
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);
59
50
  }
60
-
61
51
  const encoder = new TextEncoder();
62
52
  const char74re = /(.{1,74})/g;
63
-
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;
64
55
  /**
65
56
  * Represents an RFC 2445 iCalendar VEVENT
66
57
  */
67
58
  class IcalEvent {
68
- /**
69
- * Builds an IcalEvent object from a Hebcal Event
70
- * @param {Event} ev
71
- * @param {CalOptions} options
72
- */
73
- constructor(ev, options={}) {
74
- this.ev = ev;
75
- this.options = options;
76
- this.dtstamp = options.dtstamp || IcalEvent.makeDtstamp(new Date());
77
- if (typeof ev.sequence === 'number') {
78
- this.sequence = ev.sequence;
79
- } else if (typeof options.sequence === 'number') {
80
- this.sequence = options.sequence;
81
- }
82
- const timed = this.timed = Boolean(ev.eventTime);
83
- const locale = options.locale;
84
- let subj = restApi.shouldRenderBrief(ev) ? ev.renderBrief(locale) : ev.render(locale);
85
- const mask = ev.getFlags();
86
- if (ev.locationName) {
87
- this.locationName = ev.locationName;
88
- } else if (mask & core.flags.DAF_YOMI) {
89
- this.locationName = core.Locale.gettext('Daf Yomi', locale);
90
- } else if (mask & core.flags.YERUSHALMI_YOMI) {
91
- this.locationName = core.Locale.gettext('Yerushalmi Yomi', locale);
92
- } else if (mask & core.flags.NACH_YOMI) {
93
- this.locationName = core.Locale.gettext('Nach Yomi', locale);
94
- } else if (mask & core.flags.MISHNA_YOMI) {
95
- this.locationName = core.Locale.gettext('Mishna Yomi', locale);
96
- } else if (timed && options.location) {
97
- this.locationName = options.location.getShortName();
98
- } else if (ev.category) {
99
- this.locationName = core.Locale.gettext(ev.category, locale);
100
- }
101
- const date = IcalEvent.formatYYYYMMDD(ev.getDate().greg());
102
- this.startDate = this.isoDateOnly = date;
103
- this.dtargs = '';
104
- this.transp = 'TRANSPARENT';
105
- this.busyStatus = 'FREE';
106
- if (timed) {
107
- let [hour, minute] = ev.eventTimeStr.split(':');
108
- hour = +hour;
109
- minute = +minute;
110
- this.startDate += 'T' + restApi.pad2(hour) + restApi.pad2(minute) + '00';
111
- this.endDate = this.startDate;
112
- if (options.location?.getTzid()) {
113
- this.dtargs = `;TZID=${options.location.getTzid()}`;
114
- }
115
- } else {
116
- this.endDate = IcalEvent.formatYYYYMMDD(ev.getDate().next().greg());
117
- // for all-day untimed, use DTEND;VALUE=DATE intsead of DURATION:P1D.
118
- // It's more compatible with everthing except ancient versions of
119
- // Lotus Notes circa 2004
120
- this.dtargs = ';VALUE=DATE';
121
- if (mask & core.flags.CHAG) {
122
- this.transp = 'OPAQUE';
123
- this.busyStatus = 'OOF';
124
- }
125
- }
126
-
127
- if (options.emoji) {
128
- const prefix = ev.getEmoji();
129
- if (prefix) {
130
- if (mask & core.flags.OMER_COUNT) {
131
- subj = subj + ' ' + prefix;
132
- } else {
133
- subj = prefix + ' ' + subj;
134
- }
135
- }
136
- }
137
-
138
- // make subject safe for iCalendar
139
- subj = IcalEvent.escape(subj);
140
-
141
- if (options.appendHebrewToSubject) {
142
- const hebrew = ev.renderBrief('he');
143
- if (hebrew) {
144
- subj += ` / ${hebrew}`;
145
- }
146
- }
147
- this.subj = subj;
148
- this.category = ev.category || CATEGORY[restApi.getEventCategories(ev)?.[0]];
149
- }
150
-
151
-
152
- /**
153
- * @return {string}
154
- */
155
- getAlarm() {
156
- const ev = this.ev;
157
- const mask = ev.getFlags();
158
- const evAlarm = ev.alarm;
159
- if (typeof evAlarm === 'string') {
160
- return 'TRIGGER:' + evAlarm;
161
- } else if (typeof evAlarm === 'boolean' && !evAlarm) {
162
- return null;
163
- } else if (core.greg.isDate(evAlarm)) {
164
- evAlarm.setSeconds(0);
165
- return 'TRIGGER;VALUE=DATE-TIME:' + IcalEvent.makeDtstamp(evAlarm);
166
- } else if (mask & core.flags.OMER_COUNT) {
167
- return 'TRIGGER:-P0DT3H30M0S'; // 8:30pm Omer alarm evening before
168
- } else if (mask & core.flags.USER_EVENT) {
169
- return 'TRIGGER:-P0DT12H0M0S'; // noon the day before
170
- } else if (this.timed && ev.getDesc().startsWith('Candle lighting')) {
171
- return 'TRIGGER:-P0DT0H10M0S';
172
- }
173
- return null;
174
- }
175
-
176
- /**
177
- * @return {string}
178
- */
179
- getUid() {
180
- const options = this.options;
181
- const digest = murmurhash3.murmur32HexSync(this.ev.getDesc());
182
- let uid = `hebcal-${this.isoDateOnly}-${digest}`;
183
- if (this.timed && options.location) {
184
- const loc = options.location;
185
- if (loc.getGeoId()) {
186
- uid += `-${loc.getGeoId()}`;
187
- } else if (loc.getName()) {
188
- uid += '-' + restApi.makeAnchor(loc.getName());
189
- }
190
- }
191
- return uid;
192
- }
193
-
194
- /**
195
- * @return {string[]}
196
- */
197
- getLongLines() {
198
- if (this.lines) return this.lines;
199
- const categoryLine = this.category ? [`CATEGORIES:${this.category}`] : [];
200
- const uid = this.ev.uid || this.getUid();
201
- if (this.sequence) {
202
- categoryLine.unshift(`SEQUENCE:${this.sequence}`);
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]];
203
135
  }
204
- const arr = this.lines = [
205
- 'BEGIN:VEVENT',
206
- `DTSTAMP:${this.dtstamp}`,
207
- ].concat(categoryLine).concat([
208
- `SUMMARY:${this.subj}`,
209
- `DTSTART${this.dtargs}:${this.startDate}`,
210
- `DTEND${this.dtargs}:${this.endDate}`,
211
- `UID:${uid}`,
212
- `TRANSP:${this.transp}`,
213
- `X-MICROSOFT-CDO-BUSYSTATUS:${this.busyStatus}`,
214
- ]);
215
-
216
- if (!this.timed) {
217
- arr.push('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE');
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;
218
161
  }
219
-
220
- const ev = this.ev;
221
- const mask = ev.getFlags();
222
- const isUserEvent = Boolean(mask & core.flags.USER_EVENT);
223
- if (!isUserEvent) {
224
- arr.push('CLASS:PUBLIC');
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;
225
182
  }
226
-
227
- const options = this.options;
228
- // create memo (holiday descr, Torah, etc)
229
- const memo = createMemo(ev, options);
230
- addOptional(arr, 'DESCRIPTION', memo);
231
- addOptional(arr, 'LOCATION', this.locationName);
232
- if (this.timed && options.location) {
233
- arr.push('GEO:' + options.location.getLatitude() + ';' + options.location.getLongitude());
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
+ const trigger = this.getAlarm();
224
+ if (trigger) {
225
+ arr.push('BEGIN:VALARM', 'ACTION:DISPLAY', 'DESCRIPTION:Event reminder', `${trigger}`, 'END:VALARM');
226
+ }
227
+ arr.push('END:VEVENT');
228
+ return arr;
234
229
  }
235
-
236
- const trigger = this.getAlarm();
237
- if (trigger) {
238
- arr.push(
239
- 'BEGIN:VALARM',
240
- 'ACTION:DISPLAY',
241
- 'DESCRIPTION:Event reminder',
242
- `${trigger}`,
243
- 'END:VALARM',
244
- );
230
+ /**
231
+ * @return {string}
232
+ */
233
+ toString() {
234
+ return this.getLines().join('\r\n');
245
235
  }
246
-
247
- arr.push('END:VEVENT');
248
-
249
- return arr;
250
- }
251
-
252
- /**
253
- * @return {string}
254
- */
255
- toString() {
256
- return this.getLines().join('\r\n');
257
- }
258
-
259
- /**
260
- * fold lines to 75 characters
261
- * @return {string[]}
262
- */
263
- getLines() {
264
- return this.getLongLines().map(IcalEvent.fold);
265
- }
266
-
267
- /**
268
- * fold line to 75 characters
269
- * @param {string} line
270
- * @return {string}
271
- */
272
- static fold(line) {
273
- let isASCII = true;
274
- for (let i = 0; i < line.length; i++) {
275
- if (line.charCodeAt(i) > 255) {
276
- isASCII = false;
277
- break;
278
- }
236
+ /**
237
+ * fold lines to 75 characters
238
+ * @return {string[]}
239
+ */
240
+ getLines() {
241
+ return this.getLongLines().map(IcalEvent.fold);
279
242
  }
280
- if (isASCII) {
281
- return line.length <= 74 ? line : line.match(char74re).join('\r\n ');
243
+ /**
244
+ * fold line to 75 characters
245
+ * @param {string} line
246
+ * @return {string}
247
+ */
248
+ static fold(line) {
249
+ let isASCII = true;
250
+ for (let i = 0; i < line.length; i++) {
251
+ if (line.charCodeAt(i) > 255) {
252
+ isASCII = false;
253
+ break;
254
+ }
255
+ }
256
+ if (isASCII) {
257
+ if (line.length <= 74) {
258
+ return line;
259
+ }
260
+ const matches = line.match(char74re);
261
+ return matches.join('\r\n ');
262
+ }
263
+ if (encoder.encode(line).length <= 74) {
264
+ return line;
265
+ }
266
+ // iterate unicode character by character, making sure
267
+ // that adding a new character would keep the line <= 75 octets
268
+ let result = '';
269
+ let current = '';
270
+ let len = 0;
271
+ for (let i = 0; i < line.length; i++) {
272
+ const char = line[i];
273
+ const octets = char.charCodeAt(0) < 256 ? 1 : encoder.encode(char).length;
274
+ const newlen = len + octets;
275
+ if (newlen < 75) {
276
+ current += char;
277
+ len = newlen;
278
+ }
279
+ else {
280
+ result += current + '\r\n ';
281
+ line = line.substring(i);
282
+ current = '';
283
+ len = 0;
284
+ i = -1;
285
+ }
286
+ }
287
+ return result + current;
282
288
  }
283
- if (encoder.encode(line).length <= 74) {
284
- return line;
289
+ /**
290
+ * @param {string} str
291
+ * @return {string}
292
+ */
293
+ static escape(str) {
294
+ if (str.indexOf(',') !== -1) {
295
+ str = str.replace(/,/g, '\\,');
296
+ }
297
+ if (str.indexOf(';') !== -1) {
298
+ str = str.replace(/;/g, '\\;');
299
+ }
300
+ return str;
285
301
  }
286
- // iterate unicode character by character, making sure
287
- // that adding a new character would keep the line <= 75 octets
288
- let result = '';
289
- let current = '';
290
- let len = 0;
291
- for (let i = 0; i < line.length; i++) {
292
- const char = line[i];
293
- const octets = char.charCodeAt(0) < 256 ? 1 : encoder.encode(char).length;
294
- const newlen = len + octets;
295
- if (newlen < 75) {
296
- current += char;
297
- len = newlen;
298
- } else {
299
- result += current + '\r\n ';
300
- line = line.substring(i);
301
- current = '';
302
- len = 0;
303
- i = -1;
304
- }
302
+ /**
303
+ * @param {Date} dt
304
+ * @return {string}
305
+ */
306
+ static formatYYYYMMDD(dt) {
307
+ return restApi.pad4(dt.getFullYear()) +
308
+ restApi.pad2(dt.getMonth() + 1) + restApi.pad2(dt.getDate());
305
309
  }
306
- return result + current;
307
- }
308
-
309
- /**
310
- * @param {string} str
311
- * @return {string}
312
- */
313
- static escape(str) {
314
- if (str.indexOf(',') !== -1) {
315
- str = str.replace(/,/g, '\\,');
310
+ /**
311
+ * Returns UTC string for iCalendar
312
+ * @param {Date} dt
313
+ * @return {string}
314
+ */
315
+ static makeDtstamp(dt) {
316
+ const s = dt.toISOString();
317
+ return s.slice(0, 4) + s.slice(5, 7) + s.slice(8, 13) +
318
+ s.slice(14, 16) + s.slice(17, 19) + 'Z';
316
319
  }
317
- if (str.indexOf(';') !== -1) {
318
- str = str.replace(/;/g, '\\;');
320
+ /** @return {string} */
321
+ static version() {
322
+ return version;
319
323
  }
320
- return str;
321
- }
322
-
323
- /**
324
- * @param {Date} dt
325
- * @return {string}
326
- */
327
- static formatYYYYMMDD(dt) {
328
- return restApi.pad4(dt.getFullYear()) +
329
- restApi.pad2(dt.getMonth() + 1) + restApi.pad2(dt.getDate());
330
- }
331
-
332
- /**
333
- * Returns UTC string for iCalendar
334
- * @param {Date} dt
335
- * @return {string}
336
- */
337
- static makeDtstamp(dt) {
338
- const s = dt.toISOString();
339
- return s.slice(0, 4) + s.slice(5, 7) + s.slice(8, 13) +
340
- s.slice(14, 16) + s.slice(17, 19) + 'Z';
341
- }
342
-
343
- /** @return {string} */
344
- static version() {
345
- return version;
346
- }
347
324
  }
348
-
349
325
  /**
350
326
  * Transforms a single Event into a VEVENT string
351
- * @param {Event} ev
352
- * @param {CalOptions} options
353
327
  * @return {string} multi-line result, delimited by \r\n
354
328
  */
355
329
  function eventToIcal(ev, options) {
356
- const ical = new IcalEvent(ev, options);
357
- return ical.toString();
330
+ const ical = new IcalEvent(ev, options);
331
+ return ical.toString();
358
332
  }
359
-
360
333
  const torahMemoCache = new Map();
361
-
362
- const HOLIDAY_IGNORE_MASK = core.flags.DAF_YOMI | core.flags.OMER_COUNT |
363
- core.flags.SHABBAT_MEVARCHIM | core.flags.MOLAD | core.flags.USER_EVENT |
364
- core.flags.MISHNA_YOMI | core.flags.YERUSHALMI_YOMI | core.flags.NACH_YOMI |
365
- core.flags.HEBREW_DATE | core.flags.DAILY_LEARNING;
366
-
334
+ const HOLIDAY_IGNORE_MASK = DAILY_LEARNING | core.flags.OMER_COUNT |
335
+ core.flags.SHABBAT_MEVARCHIM | core.flags.MOLAD | core.flags.USER_EVENT |
336
+ core.flags.HEBREW_DATE;
367
337
  /**
368
338
  * @private
369
- * @param {Event} ev
370
- * @param {boolean} il
371
- * @return {string}
372
339
  */
373
340
  function makeTorahMemo(ev, il) {
374
- if ((ev.getFlags() & HOLIDAY_IGNORE_MASK) || ev.eventTime) {
375
- return '';
376
- }
377
- const hd = ev.getDate();
378
- const yy = hd.getFullYear();
379
- const mm = hd.getMonth();
380
- const dd = hd.getDate();
381
- const key = [yy, mm, dd, il ? '1' : '0', ev.getDesc()].join('-');
382
- let memo = torahMemoCache.get(key);
383
- if (typeof memo === 'string') {
341
+ if ((ev.getFlags() & HOLIDAY_IGNORE_MASK) || ev.eventTime) {
342
+ return '';
343
+ }
344
+ const hd = ev.getDate();
345
+ const yy = hd.getFullYear();
346
+ const mm = hd.getMonth();
347
+ const dd = hd.getDate();
348
+ const key = [yy, mm, dd, il ? '1' : '0', ev.getDesc()].join('-');
349
+ let memo = torahMemoCache.get(key);
350
+ if (typeof memo === 'string') {
351
+ return memo;
352
+ }
353
+ memo = restApi.makeTorahMemoText(ev, il).replace(/\n/g, '\\n');
354
+ torahMemoCache.set(key, memo);
384
355
  return memo;
385
- }
386
- memo = restApi.makeTorahMemoText(ev, il).replace(/\n/g, '\\n');
387
- torahMemoCache.set(key, memo);
388
- return memo;
389
356
  }
390
-
391
357
  /**
392
358
  * @private
393
- * @param {Event} e
394
- * @param {CalOptions} options
395
- * @return {string}
396
359
  */
397
- function createMemo(e, options) {
398
- let memo = e.memo;
399
- if (typeof memo === 'string' && memo.length && memo.indexOf('\n') !== -1) {
400
- memo = memo.replace(/\n/g, '\\n');
401
- }
402
- const desc = e.getDesc();
403
- if (desc === 'Havdalah' || desc === 'Candle lighting') {
404
- return memo || '';
405
- }
406
- const mask = e.getFlags();
407
- if (mask & core.flags.OMER_COUNT) {
408
- const sefira = [e.sefira('en'), e.sefira('he'), e.sefira('translit')].join('\\n');
409
- return e.getTodayIs('en') + '\\n\\n' + e.getTodayIs('he') + '\\n\\n' + sefira;
410
- }
411
- const url = appendTrackingToUrl(e.url(), options);
412
- const torahMemo = makeTorahMemo(e, options.il);
413
- if (!memo) {
414
- if (typeof e.linkedEvent !== 'undefined') {
415
- memo = e.linkedEvent.render(options.locale);
416
- } else {
417
- memo = restApi.getHolidayDescription(e);
360
+ function createMemo(ev, options) {
361
+ let memo = ev.memo;
362
+ if (typeof memo === 'string' && memo.length && memo.indexOf('\n') !== -1) {
363
+ memo = memo.replace(/\n/g, '\\n');
364
+ }
365
+ const desc = ev.getDesc();
366
+ if (desc === 'Havdalah' || desc === 'Candle lighting') {
367
+ return memo || '';
368
+ }
369
+ const mask = ev.getFlags();
370
+ if (mask & core.flags.OMER_COUNT) {
371
+ const omerEv = ev;
372
+ const sefira = [omerEv.sefira('en'), omerEv.sefira('he'), omerEv.sefira('translit')].join('\\n');
373
+ return omerEv.getTodayIs('en') + '\\n\\n' + omerEv.getTodayIs('he') + '\\n\\n' + sefira;
374
+ }
375
+ const url = appendTrackingToUrl(ev.url(), options);
376
+ const torahMemo = makeTorahMemo(ev, options.il);
377
+ if (!memo) {
378
+ if (typeof ev.linkedEvent !== 'undefined') {
379
+ memo = ev.linkedEvent.render(options.locale);
380
+ }
381
+ else {
382
+ memo = restApi.getHolidayDescription(ev);
383
+ }
418
384
  }
419
- }
420
- if (torahMemo) {
421
- if (memo.length) {
422
- memo += '\\n\\n';
385
+ if (torahMemo) {
386
+ if (memo.length) {
387
+ memo += '\\n\\n';
388
+ }
389
+ memo += torahMemo;
423
390
  }
424
- memo += torahMemo;
425
- }
426
- if (url) {
427
- if (memo.length) {
428
- memo += '\\n\\n';
391
+ if (url) {
392
+ if (memo.length) {
393
+ memo += '\\n\\n';
394
+ }
395
+ memo += url;
429
396
  }
430
- memo += url;
431
- }
432
- return memo;
397
+ return memo;
433
398
  }
434
-
435
399
  /**
436
400
  * Generates an RFC 2445 iCalendar string from an array of events
437
- * @param {Event[]} events
438
- * @param {CalOptions} options
439
- * @return {Promise<string>}
440
401
  */
441
402
  async function eventsToIcalendar(events, options) {
442
- if (!events.length) throw new RangeError('Events can not be empty');
443
- if (!options) throw new TypeError('Invalid options object');
444
- const opts = Object.assign({}, options);
445
- opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
446
- if (!opts.title) {
447
- opts.title = restApi.getCalendarTitle(events, opts);
448
- }
449
- const icals = events.map((ev) => new IcalEvent(ev, opts));
450
- return icalEventsToString(icals, opts);
403
+ if (!options)
404
+ throw new TypeError('Invalid options object');
405
+ const opts = Object.assign({}, options);
406
+ opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
407
+ if (!opts.title) {
408
+ opts.title = restApi.getCalendarTitle(events, opts);
409
+ }
410
+ const icals = events.map((ev) => new IcalEvent(ev, opts));
411
+ return icalEventsToString(icals, opts);
451
412
  }
452
-
453
413
  /**
454
414
  * Generates an RFC 2445 iCalendar string from an array of IcalEvents
455
- * @param {IcalEvent[]} icals
456
- * @param {CalOptions} options
457
- * @return {Promise<string>}
458
415
  */
459
416
  async function icalEventsToString(icals, options) {
460
- const stream = [];
461
- const locale = options.locale || core.Locale.getLocaleName();
462
- const uclang = locale.toUpperCase();
463
- const opts = Object.assign({}, options);
464
- opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
465
- const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
466
- const caldesc = opts.caldesc ? IcalEvent.escape(opts.caldesc) :
467
- opts.yahrzeit ?
468
- 'Yahrzeits + Anniversaries from www.hebcal.com' :
469
- 'Jewish Holidays from www.hebcal.com';
470
- const prodid = opts.prodid || `-//hebcal.com/NONSGML Hebcal Calendar v1${version}//${uclang}`;
471
- const preamble = [
472
- 'BEGIN:VCALENDAR',
473
- 'VERSION:2.0',
474
- `PRODID:${prodid}`,
475
- 'CALSCALE:GREGORIAN',
476
- 'METHOD:PUBLISH',
477
- 'X-LOTUS-CHARSET:UTF-8',
478
- ];
479
- if (opts.publishedTTL !== false) {
480
- const publishedTTL = opts.publishedTTL || 'PT7D';
481
- preamble.push(`X-PUBLISHED-TTL:${publishedTTL}`);
482
- }
483
- preamble.push(`X-WR-CALNAME:${title}`);
484
- preamble.push(`X-WR-CALDESC:${caldesc}`);
485
- for (const line of preamble.map(IcalEvent.fold)) {
486
- stream.push(line);
487
- stream.push('\r\n');
488
- }
489
- if (opts.relcalid) {
490
- stream.push(IcalEvent.fold(`X-WR-RELCALID:${opts.relcalid}`));
491
- stream.push('\r\n');
492
- }
493
- if (opts.calendarColor) {
494
- stream.push(`X-APPLE-CALENDAR-COLOR:${opts.calendarColor}\r\n`);
495
- }
496
- const location = opts.location;
497
- const tzid = location?.getTzid();
498
- if (tzid) {
499
- stream.push(`X-WR-TIMEZONE;VALUE=TEXT:${tzid}\r\n`);
500
- const vtz = vtimezoneCache.get(tzid);
501
- if (typeof vtz === 'string') {
502
- stream.push(vtz);
503
- stream.push('\r\n');
504
- } else {
505
- const vtimezoneFilename = `./zoneinfo/${tzid}.ics`;
506
- try {
507
- const vtimezoneIcs = await fs.promises.readFile(vtimezoneFilename, 'utf-8');
508
- const lines = vtimezoneIcs.split('\r\n');
509
- // ignore first 3 and last 1 lines
510
- const str = lines.slice(3, lines.length - 2).join('\r\n');
511
- stream.push(str);
417
+ const stream = [];
418
+ const locale = options.locale || core.Locale.getLocaleName();
419
+ const uclang = locale.toUpperCase();
420
+ const opts = Object.assign({}, options);
421
+ opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
422
+ const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
423
+ const caldesc = opts.caldesc ? IcalEvent.escape(opts.caldesc) :
424
+ opts.yahrzeit ?
425
+ 'Yahrzeits + Anniversaries from www.hebcal.com' :
426
+ 'Jewish Holidays from www.hebcal.com';
427
+ const prodid = opts.prodid || `-//hebcal.com/NONSGML Hebcal Calendar v1${version}//${uclang}`;
428
+ const preamble = [
429
+ 'BEGIN:VCALENDAR',
430
+ 'VERSION:2.0',
431
+ `PRODID:${prodid}`,
432
+ 'CALSCALE:GREGORIAN',
433
+ 'METHOD:PUBLISH',
434
+ 'X-LOTUS-CHARSET:UTF-8',
435
+ ];
436
+ if (opts.publishedTTL !== false) {
437
+ const publishedTTL = opts.publishedTTL || 'PT7D';
438
+ preamble.push(`X-PUBLISHED-TTL:${publishedTTL}`);
439
+ }
440
+ preamble.push(`X-WR-CALNAME:${title}`);
441
+ preamble.push(`X-WR-CALDESC:${caldesc}`);
442
+ for (const line of preamble.map(IcalEvent.fold)) {
443
+ stream.push(line);
512
444
  stream.push('\r\n');
513
- vtimezoneCache.set(tzid, str);
514
- } catch (error) {
515
- // ignore failure when no timezone definition to read
516
- }
517
445
  }
518
- }
519
-
520
- for (const ical of icals) {
521
- const lines = ical.getLines();
522
- for (const line of lines) {
523
- stream.push(line);
524
- stream.push('\r\n');
446
+ if (opts.relcalid) {
447
+ stream.push(IcalEvent.fold(`X-WR-RELCALID:${opts.relcalid}`));
448
+ stream.push('\r\n');
449
+ }
450
+ if (opts.calendarColor) {
451
+ stream.push(`X-APPLE-CALENDAR-COLOR:${opts.calendarColor}\r\n`);
452
+ }
453
+ const location = opts.location;
454
+ const tzid = location === null || location === void 0 ? void 0 : location.getTzid();
455
+ if (tzid) {
456
+ stream.push(`X-WR-TIMEZONE;VALUE=TEXT:${tzid}\r\n`);
457
+ const vtz = vtimezoneCache.get(tzid);
458
+ if (typeof vtz === 'string') {
459
+ stream.push(vtz);
460
+ stream.push('\r\n');
461
+ }
462
+ else {
463
+ const vtimezoneFilename = `./zoneinfo/${tzid}.ics`;
464
+ try {
465
+ const vtimezoneIcs = await fs.promises.readFile(vtimezoneFilename, 'utf-8');
466
+ const lines = vtimezoneIcs.split('\r\n');
467
+ // ignore first 3 and last 1 lines
468
+ const str = lines.slice(3, lines.length - 2).join('\r\n');
469
+ stream.push(str);
470
+ stream.push('\r\n');
471
+ vtimezoneCache.set(tzid, str);
472
+ }
473
+ catch (error) {
474
+ // ignore failure when no timezone definition to read
475
+ }
476
+ }
477
+ }
478
+ for (const ical of icals) {
479
+ const lines = ical.getLines();
480
+ for (const line of lines) {
481
+ stream.push(line);
482
+ stream.push('\r\n');
483
+ }
525
484
  }
526
- }
527
- stream.push('END:VCALENDAR\r\n');
528
- return stream.join('');
485
+ stream.push('END:VCALENDAR\r\n');
486
+ return stream.join('');
529
487
  }
530
488
 
531
489
  exports.IcalEvent = IcalEvent;