PageSourceSearch

https://www.millingtons.com.au/wp-content/plugins/fcrm-tributes/public/js/atcb.js?ver=2.2.4

js millingtons.com.au collected 2026-10-02 12:11:52 UTC 196,628 bytes, 4,087 lines download raw bytes

1/**
2 * ++++++++++++++++++++++++++++++++++++++
3 * Add to Calendar TimeZones iCal Library
4 * ++++++++++++++++++++++++++++++++++++++
5 */
6const tzlibVersion = '1.3.2';
7/* Creator: Jens Kuerschner (https://jenskuerschner.de)
8 * Project: https://github.com/add2cal/timezones-ical-library
9 * License: Apache-2.0
10 *
11 */
12
13// DEFINING THE DB DATA - WILL GET RE-WRITTEN WITH THE ACTUAL DATA ON BUILD
14const tzlibZonesDB = {
15  Africa: {
16    Abidjan: ['', 0],
17    Accra: ['Africa/Abidjan', 0],
18    Addis_Ababa: ['Africa/Nairobi', 1],
19    Algiers: ['', 2],
20    Asmara: ['Africa/Nairobi', 1],
21    Asmera: ['Africa/Nairobi', 1],
22    Bamako: ['Africa/Abidjan', 0],
23    Bangui: ['Africa/Lagos', 3],
24    Banjul: ['Africa/Abidjan', 0],
25    Bissau: ['', 0],
26    Blantyre: ['Africa/Maputo', 4],
27    Brazzaville: ['Africa/Lagos', 3],
28    Bujumbura: ['Africa/Maputo', 4],
29    Cairo: ['', 5],
30    Casablanca: ['', 6],
31    Ceuta: ['', 7],
32    Conakry: ['Africa/Abidjan', 0],
33    Dakar: ['Africa/Abidjan', 0],
34    Dar_es_Salaam: ['Africa/Nairobi', 1],
35    Djibouti: ['Africa/Nairobi', 1],
36    Douala: ['Africa/Lagos', 3],
37    El_Aaiun: ['', 6],
38    Freetown: ['Africa/Abidjan', 0],
39    Gaborone: ['Africa/Maputo', 4],
40    Harare: ['Africa/Maputo', 4],
41    Johannesburg: ['', 8],
42    Juba: ['', 4],
43    Kampala: ['Africa/Nairobi', 1],
44    Khartoum: ['', 4],
45    Kigali: ['Africa/Maputo', 4],
46    Kinshasa: ['Africa/Lagos', 3],
47    Lagos: ['', 3],
48    Libreville: ['Africa/Lagos', 3],
49    Lome: ['Africa/Abidjan', 0],
50    Luanda: ['Africa/Lagos', 3],
51    Lubumbashi: ['Africa/Maputo', 4],
52    Lusaka: ['Africa/Maputo', 4],
53    Malabo: ['Africa/Lagos', 3],
54    Maputo: ['', 4],
55    Maseru: ['Africa/Johannesburg', 8],
56    Mbabane: ['Africa/Johannesburg', 8],
57    Mogadishu: ['Africa/Nairobi', 1],
58    Monrovia: ['', 0],
59    Nairobi: ['', 1],
60    Ndjamena: ['', 3],
61    Niamey: ['Africa/Lagos', 3],
62    Nouakchott: ['Africa/Abidjan', 0],
63    Ouagadougou: ['Africa/Abidjan', 0],
64    'Porto-Novo': ['Africa/Lagos', 3],
65    Sao_Tome: ['', 0],
66    Timbuktu: ['Africa/Abidjan', 0],
67    Tripoli: ['', 5],
68    Tunis: ['', 2],
69    Windhoek: ['', 4],
70  },
71  America: {
72    Adak: ['', 9],
73    Anchorage: ['', 10],
74    Anguilla: ['America/Puerto_Rico', 11],
75    Antigua: ['America/Puerto_Rico', 11],
76    Araguaina: ['', 12],
77    Argentina: {
78      Buenos_Aires: ['', 12],
79      Catamarca: ['', 12],
80      ComodRivadavia: ['America/Argentina/Catamarca', 12],
81      Cordoba: ['', 12],
82      Jujuy: ['', 12],
83      La_Rioja: ['', 12],
84      Mendoza: ['', 12],
85      Rio_Gallegos: ['', 12],
86      Salta: ['', 12],
87      San_Juan: ['', 12],
88      San_Luis: ['', 12],
89      Tucuman: ['', 12],
90      Ushuaia: ['', 12],
91    },
92    Aruba: ['America/Puerto_Rico', 11],
93    Asuncion: ['', 13],
94    Atikokan: ['America/Panama', 14],
95    Atka: ['America/Adak', 9],
96    Bahia_Banderas: ['', 15],
97    Bahia: ['', 12],
98    Barbados: ['', 11],
99    Belem: ['', 12],
100    Belize: ['', 16],
101    'Blanc-Sablon': ['America/Puerto_Rico', 11],
102    Boa_Vista: ['', 17],
103    Bogota: ['', 18],
104    Boise: ['', 19],
105    Buenos_Aires: ['America/Argentina/Buenos_Aires', 12],
106    Cambridge_Bay: ['', 19],
107    Campo_Grande: ['', 17],
108    Cancun: ['', 14],
109    Caracas: ['', 17],
110    Catamarca: ['America/Argentina/Catamarca', 12],
111    Cayenne: ['', 12],
112    Cayman: ['America/Panama', 14],
113    Chicago: ['', 20],
114    Chihuahua: ['', 21],
115    Coral_Harbour: ['America/Panama', 14],
116    Cordoba: ['America/Argentina/Cordoba', 12],
117    Costa_Rica: ['', 16],
118    Creston: ['America/Phoenix', 22],
119    Cuiaba: ['', 17],
120    Curacao: ['America/Puerto_Rico', 11],
121    Danmarkshavn: ['', 0],
122    Dawson_Creek: ['', 22],
123    Dawson: ['', 22],
124    Denver: ['', 19],
125    Detroit: ['', 23],
126    Dominica: ['America/Puerto_Rico', 11],
127    Edmonton: ['', 19],
128    Eirunepe: ['', 18],
129    El_Salvador: ['', 16],
130    Ensenada: ['America/Tijuana', 24],
131    Fort_Nelson: ['', 22],
132    Fort_Wayne: ['America/Indiana/Indianapolis', 23],
133    Fortaleza: ['', 12],
134    Glace_Bay: ['', 25],
135    Godthab: ['America/Nuuk', 26],
136    Goose_Bay: ['', 27],
137    Grand_Turk: ['', 28],
138    Grenada: ['America/Puerto_Rico', 11],
139    Guadeloupe: ['America/Puerto_Rico', 11],
140    Guatemala: ['', 16],
141    Guayaquil: ['', 18],
142    Guyana: ['', 17],
143    Halifax: ['', 25],
144    Havana: ['', 29],
145    Hermosillo: ['', 22],
146    Indiana: {
147      Indianapolis: ['', 23],
148      Knox: ['', 20],
149      Marengo: ['', 23],
150      Petersburg: ['', 23],
151      Tell_City: ['', 20],
152      Vevay: ['', 23],
153      Vincennes: ['', 23],
154      Winamac: ['', 28],
155    },
156    Indianapolis: ['America/Indiana/Indianapolis', 23],
157    Inuvik: ['', 19],
158    Iqaluit: ['', 23],
159    Jamaica: ['', 14],
160    Jujuy: ['America/Argentina/Jujuy', 12],
161    Juneau: ['', 10],
162    Kentucky: { Louisville: ['', 23], Monticello: ['', 23] },
163    Knox_IN: ['America/Indiana/Knox', 20],
164    Kralendijk: ['America/Puerto_Rico', 11],
165    La_Paz: ['', 17],
166    Lima: ['', 18],
167    Los_Angeles: ['', 24],
168    Louisville: ['America/Kentucky/Louisville', 23],
169    Lower_Princes: ['America/Puerto_Rico', 11],
170    Maceio: ['', 12],
171    Managua: ['', 16],
172    Manaus: ['', 17],
173    Marigot: ['America/Puerto_Rico', 11],
174    Martinique: ['', 11],
175    Matamoros: ['', 20],
176    Mazatlan: ['', 21],
177    Mendoza: ['America/Argentina/Mendoza', 12],
178    Menominee: ['', 20],
179    Merida: ['', 30],
180    Metlakatla: ['', 10],
181    Mexico_City: ['', 30],
182    Miquelon: ['', 31],
183    Moncton: ['', 25],
184    Monterrey: ['', 30],
185    Montevideo: ['', 12],
186    Montreal: ['America/Toronto', 23],
187    Montserrat: ['America/Puerto_Rico', 11],
188    Nassau: ['America/Toronto', 23],
189    New_York: ['', 23],
190    Nipigon: ['', 23],
191    Nome: ['', 10],
192    Noronha: ['', 32],
193    North_Dakota: { Beulah: ['', 20], Center: ['', 20], New_Salem: ['', 20] },
194    Nuuk: ['', 26],
195    Ojinaga: ['', 19],
196    Panama: ['', 14],
197    Pangnirtung: ['', 23],
198    Paramaribo: ['', 12],
199    Phoenix: ['', 22],
200    Port_of_Spain: ['America/Puerto_Rico', 11],
201    'Port-au-Prince': ['', 23],
202    Porto_Acre: ['America/Rio_Branco', 18],
203    Porto_Velho: ['', 17],
204    Puerto_Rico: ['', 11],
205    Punta_Arenas: ['', 12],
206    Rainy_River: ['', 20],
207    Rankin_Inlet: ['', 20],
208    Recife: ['', 12],
209    Regina: ['', 16],
210    Resolute: ['', 33],
211    Rio_Branco: ['', 18],
212    Rosario: ['America/Argentina/Cordoba', 12],
213    Santa_Isabel: ['America/Tijuana', 24],
214    Santarem: ['', 12],
215    Santiago: ['', 34],
216    Santo_Domingo: ['', 11],
217    Sao_Paulo: ['', 12],
218    Scoresbysund: ['', 35],
219    Shiprock: ['America/Denver', 19],
220    Sitka: ['', 10],
221    St_Barthelemy: ['America/Puerto_Rico', 11],
222    St_Johns: ['', 36],
223    St_Kitts: ['America/Puerto_Rico', 11],
224    St_Lucia: ['America/Puerto_Rico', 11],
225    St_Thomas: ['America/Puerto_Rico', 11],
226    St_Vincent: ['America/Puerto_Rico', 11],
227    Swift_Current: ['', 16],
228    Tegucigalpa: ['', 16],
229    Thule: ['', 25],
230    Thunder_Bay: ['', 23],
231    Tijuana: ['', 24],
232    Toronto: ['', 23],
233    Tortola: ['America/Puerto_Rico', 11],
234    Vancouver: ['', 24],
235    Virgin: ['America/Puerto_Rico', 11],
236    Whitehorse: ['', 22],
237    Winnipeg: ['', 20],
238    Yakutat: ['', 10],
239    Yellowknife: ['', 19],
240  },
241  Antarctica: {
242    Casey: ['', 37],
243    Davis: ['', 38],
244    DumontDUrville: ['Pacific/Port_Moresby', 39],
245    Macquarie: ['', 40],
246    Mawson: ['', 41],
247    McMurdo: ['Pacific/Auckland', 42],
248    Palmer: ['', 12],
249    Rothera: ['', 12],
250    South_Pole: ['Pacific/Auckland', 42],
251    Syowa: ['Asia/Riyadh', 43],
252    Troll: ['', 44],
253    Vostok: ['Asia/Urumqi', 45],
254  },
255  Arctic: { Longyearbyen: ['Europe/Berlin', 7] },
256  Asia: {
257    Aden: ['Asia/Riyadh', 43],
258    Almaty: ['', 45],
259    Amman: ['', 46],
260    Anadyr: ['', 47],
261    Aqtau: ['', 41],
262    Aqtobe: ['', 41],
263    Ashgabat: ['', 41],
264    Ashkhabad: ['Asia/Ashgabat', 41],
265    Atyrau: ['', 41],
266    Baghdad: ['', 43],
267    Bahrain: ['Asia/Qatar', 43],
268    Baku: ['', 48],
269    Bangkok: ['', 38],
270    Barnaul: ['', 38],
271    Beirut: ['', 49],
272    Bishkek: ['', 45],
273    Brunei: ['Asia/Kuching', 50],
274    Calcutta: ['Asia/Kolkata', 51],
275    Chita: ['', 52],
276    Choibalsan: ['', 50],
277    Chongqing: ['Asia/Shanghai', 53],
278    Chungking: ['Asia/Shanghai', 53],
279    Colombo: ['', 54],
280    Dacca: ['Asia/Dhaka', 45],
281    Damascus: ['', 55],
282    Dhaka: ['', 45],
283    Dili: ['', 52],
284    Dubai: ['', 48],
285    Dushanbe: ['', 41],
286    Famagusta: ['', 56],
287    Gaza: ['', 57],
288    Harbin: ['Asia/Shanghai', 53],
289    Hebron: ['', 57],
290    Ho_Chi_Minh: ['', 38],
291    Hong_Kong: ['', 58],
292    Hovd: ['', 38],
293    Irkutsk: ['', 50],
294    Istanbul: ['Europe/Istanbul', 43],
295    Jakarta: ['', 59],
296    Jayapura: ['', 60],
297    Jerusalem: ['', 61],
298    Kabul: ['', 62],
299    Kamchatka: ['', 47],
300    Karachi: ['', 63],
301    Kashgar: ['Asia/Urumqi', 45],
302    Kathmandu: ['', 64],
303    Katmandu: ['Asia/Kathmandu', 64],
304    Khandyga: ['', 52],
305    Kolkata: ['', 51],
306    Krasnoyarsk: ['', 38],
307    Kuala_Lumpur: ['Asia/Singapore', 50],
308    Kuching: ['', 50],
309    Kuwait: ['Asia/Riyadh', 43],
310    Macao: ['Asia/Macau', 53],
311    Macau: ['', 53],
312    Magadan: ['', 37],
313    Makassar: ['', 65],
314    Manila: ['', 66],
315    Muscat: ['Asia/Dubai', 48],
316    Nicosia: ['', 67],
317    Novokuznetsk: ['', 38],
318    Novosibirsk: ['', 38],
319    Omsk: ['', 45],
320    Oral: ['', 41],
321    Phnom_Penh: ['Asia/Bangkok', 38],
322    Pontianak: ['', 59],
323    Pyongyang: ['', 68],
324    Qatar: ['', 43],
325    Qostanay: ['', 45],
326    Qyzylorda: ['', 41],
327    Rangoon: ['Asia/Yangon', 69],
328    Riyadh: ['', 43],
329    Saigon: ['Asia/Ho_Chi_Minh', 38],
330    Sakhalin: ['', 37],
331    Samarkand: ['', 41],
332    Seoul: ['', 68],
333    Shanghai: ['', 53],
334    Singapore: ['', 50],
335    Srednekolymsk: ['', 37],
336    Taipei: ['', 53],
337    Tashkent: ['', 41],
338    Tbilisi: ['', 48],
339    Tehran: ['', 70],
340    Tel_Aviv: ['Asia/Jerusalem', 61],
341    Thimbu: ['Asia/Thimphu', 45],
342    Thimphu: ['', 45],
343    Tokyo: ['', 71],
344    Tomsk: ['', 38],
345    Ujung_Pandang: ['Asia/Makassar', 65],
346    Ulaanbaatar: ['', 50],
347    Ulan_Bator: ['Asia/Ulaanbaatar', 50],
348    Urumqi: ['', 45],
349    'Ust-Nera': ['', 39],
350    Vientiane: ['Asia/Bangkok', 38],
351    Vladivostok: ['', 39],
352    Yakutsk: ['', 52],
353    Yangon: ['', 69],
354    Yekaterinburg: ['', 41],
355    Yerevan: ['', 48],
356  },
357  Atlantic: {
358    Azores: ['', 35],
359    Bermuda: ['', 25],
360    Canary: ['', 72],
361    Cape_Verde: ['', 73],
362    Faeroe: ['Atlantic/Faroe', 72],
363    Faroe: ['', 72],
364    Jan_Mayen: ['Europe/Berlin', 7],
365    Madeira: ['', 72],
366    Reykjavik: ['Africa/Abidjan', 0],
367    South_Georgia: ['', 32],
368    St_Helena: ['Africa/Abidjan', 0],
369    Stanley: ['', 12],
370  },
371  Australia: {
372    ACT: ['Australia/Sydney', 40],
373    Adelaide: ['', 74],
374    Brisbane: ['', 75],
375    Broken_Hill: ['', 74],
376    Canberra: ['Australia/Sydney', 40],
377    Currie: ['Australia/Hobart', 76],
378    Darwin: ['', 77],
379    Eucla: ['', 78],
380    Hobart: ['', 76],
381    LHI: ['Australia/Lord_Howe', 79],
382    Lindeman: ['', 75],
383    Lord_Howe: ['', 79],
384    Melbourne: ['', 40],
385    North: ['Australia/Darwin', 77],
386    NSW: ['Australia/Sydney', 40],
387    Perth: ['', 80],
388    Queensland: ['Australia/Brisbane', 75],
389    South: ['Australia/Adelaide', 74],
390    Sydney: ['', 40],
391    Tasmania: ['Australia/Hobart', 76],
392    Victoria: ['Australia/Melbourne', 40],
393    West: ['Australia/Perth', 80],
394    Yancowinna: ['Australia/Broken_Hill', 74],
395  },
396  Brazil: {
397    Acre: ['America/Rio_Branco', 18],
398    DeNoronha: ['America/Noronha', 32],
399    East: ['America/Sao_Paulo', 12],
400    West: ['America/Manaus', 17],
401  },
402  Canada: {
403    Atlantic: ['America/Halifax', 25],
404    Central: ['America/Winnipeg', 20],
405    Eastern: ['America/Toronto', 23],
406    Mountain: ['America/Edmonton', 19],
407    Newfoundland: ['America/St_Johns', 36],
408    Pacific: ['America/Vancouver', 24],
409    Saskatchewan: ['America/Regina', 16],
410    Yukon: ['America/Whitehorse', 22],
411  },
412  CET: ['', 7],
413  Chile: { Continental: ['America/Santiago', 34], EasterIsland: ['Pacific/Easter', 81] },
414  CST6CDT: ['', 20],
415  Cuba: ['America/Havana', 29],
416  EET: ['', 56],
417  Egypt: ['Africa/Cairo', 5],
418  Eire: ['Europe/Dublin', 82],
419  EST: ['', 14],
420  EST5EDT: ['', 23],
421  Etc: {
422    'GMT-0': ['Etc/GMT', 0],
423    'GMT-1': ['', 6],
424    'GMT-10': ['', 39],
425    'GMT-11': ['', 37],
426    'GMT-12': ['', 47],
427    'GMT-13': ['', 83],
428    'GMT-14': ['', 84],
429    'GMT-2': ['', 85],
430    'GMT-3': ['', 43],
431    'GMT-4': ['', 48],
432    'GMT-5': ['', 41],
433    'GMT-6': ['', 45],
434    'GMT-7': ['', 38],
435    'GMT-8': ['', 50],
436    'GMT-9': ['', 52],
437    GMT: ['', 0],
438    'GMT+0': ['Etc/GMT', 0],
439    'GMT+1': ['', 73],
440    'GMT+10': ['', 86],
441    'GMT+11': ['', 87],
442    'GMT+12': ['', 88],
443    'GMT+2': ['', 32],
444    'GMT+3': ['', 12],
445    'GMT+4': ['', 17],
446    'GMT+5': ['', 18],
447    'GMT+6': ['', 89],
448    'GMT+7': ['', 90],
449    'GMT+8': ['', 91],
450    'GMT+9': ['', 92],
451    GMT0: ['Etc/GMT', 0],
452    Greenwich: ['Etc/GMT', 0],
453    UCT: ['Etc/UTC', 93],
454    Universal: ['Etc/UTC', 93],
455    UTC: ['', 93],
456    Zulu: ['Etc/UTC', 93],
457  },
458  Europe: {
459    Amsterdam: ['Europe/Brussels', 7],
460    Andorra: ['', 7],
461    Astrakhan: ['', 48],
462    Athens: ['', 56],
463    Belfast: ['Europe/London', 94],
464    Belgrade: ['', 7],
465    Berlin: ['', 7],
466    Bratislava: ['Europe/Prague', 7],
467    Brussels: ['', 7],
468    Bucharest: ['', 56],
469    Budapest: ['', 7],
470    Busingen: ['Europe/Zurich', 7],
471    Chisinau: ['', 95],
472    Copenhagen: ['Europe/Berlin', 7],
473    Dublin: ['', 82],
474    Gibraltar: ['', 7],
475    Guernsey: ['Europe/London', 94],
476    Helsinki: ['', 56],
477    Isle_of_Man: ['Europe/London', 94],
478    Istanbul: ['', 43],
479    Jersey: ['Europe/London', 94],
480    Kaliningrad: ['', 5],
481    Kiev: ['Europe/Kyiv', 67],
482    Kirov: ['', 43],
483    Kyiv: ['', 67],
484    Lisbon: ['', 96],
485    Ljubljana: ['Europe/Belgrade', 7],
486    London: ['', 94],
487    Luxembourg: ['Europe/Brussels', 7],
488    Madrid: ['', 7],
489    Malta: ['', 7],
490    Mariehamn: ['Europe/Helsinki', 56],
491    Minsk: ['', 43],
492    Monaco: ['Europe/Paris', 7],
493    Moscow: ['', 97],
494    Nicosia: ['Asia/Nicosia', 67],
495    Oslo: ['Europe/Berlin', 7],
496    Paris: ['', 7],
497    Podgorica: ['Europe/Belgrade', 7],
498    Prague: ['', 7],
499    Riga: ['', 56],
500    Rome: ['', 7],
501    Samara: ['', 48],
502    San_Marino: ['Europe/Rome', 7],
503    Sarajevo: ['Europe/Belgrade', 7],
504    Saratov: ['', 48],
505    Simferopol: ['', 97],
506    Skopje: ['Europe/Belgrade', 7],
507    Sofia: ['', 56],
508    Stockholm: ['Europe/Berlin', 7],
509    Tallinn: ['', 56],
510    Tirane: ['', 7],
511    Tiraspol: ['Europe/Chisinau', 95],
512    Ulyanovsk: ['', 48],
513    Uzhgorod: ['Europe/Kyiv', 67],
514    Vaduz: ['Europe/Zurich', 7],
515    Vatican: ['Europe/Rome', 7],
516    Vienna: ['', 7],
517    Vilnius: ['', 56],
518    Volgograd: ['', 43],
519    Warsaw: ['', 7],
520    Zagreb: ['Europe/Belgrade', 7],
521    Zaporozhye: ['Europe/Kyiv', 67],
522    Zurich: ['', 7],
523  },
524  'GB-Eire': ['Europe/London', 94],
525  GB: ['Europe/London', 94],
526  'GMT-0': ['Etc/GMT', 0],
527  GMT: ['Etc/GMT', 0],
528  'GMT+0': ['Etc/GMT', 0],
529  GMT0: ['Etc/GMT', 0],
530  Greenwich: ['Etc/GMT', 0],
531  Hongkong: ['Asia/Hong_Kong', 58],
532  HST: ['', 98],
533  Iceland: ['Africa/Abidjan', 0],
534  Indian: {
535    Antananarivo: ['Africa/Nairobi', 1],
536    Chagos: ['', 45],
537    Christmas: ['Asia/Bangkok', 38],
538    Cocos: ['Asia/Yangon', 69],
539    Comoro: ['Africa/Nairobi', 1],
540    Kerguelen: ['Indian/Maldives', 41],
541    Mahe: ['Asia/Dubai', 48],
542    Maldives: ['', 41],
543    Mauritius: ['', 48],
544    Mayotte: ['Africa/Nairobi', 1],
545    Reunion: ['Asia/Dubai', 48],
546  },
547  Iran: ['Asia/Tehran', 70],
548  Israel: ['Asia/Jerusalem', 61],
549  Jamaica: ['America/Jamaica', 14],
550  Japan: ['Asia/Tokyo', 71],
551  Kwajalein: ['Pacific/Kwajalein', 47],
552  Libya: ['Africa/Tripoli', 5],
553  MET: ['', 99],
554  Mexico: {
555    BajaNorte: ['America/Tijuana', 24],
556    BajaSur: ['America/Mazatlan', 21],
557    General: ['America/Mexico_City', 30],
558  },
559  MST: ['', 22],
560  MST7MDT: ['', 19],
561  Navajo: ['America/Denver', 19],
562  'NZ-CHAT': ['Pacific/Chatham', 100],
563  NZ: ['Pacific/Auckland', 42],
564  Pacific: {
565    Apia: ['', 83],
566    Auckland: ['', 42],
567    Bougainville: ['', 37],
568    Chatham: ['', 100],
569    Chuuk: ['Pacific/Port_Moresby', 39],
570    Easter: ['', 81],
571    Efate: ['', 37],
572    Enderbury: ['Pacific/Kanton', 83],
573    Fakaofo: ['', 83],
574    Fiji: ['', 101],
575    Funafuti: ['Pacific/Tarawa', 47],
576    Galapagos: ['', 89],
577    Gambier: ['', 92],
578    Guadalcanal: ['', 37],
579    Guam: ['', 102],
580    Honolulu: ['', 98],
581    Johnston: ['Pacific/Honolulu', 98],
582    Kanton: ['', 83],
583    Kiritimati: ['', 84],
584    Kosrae: ['', 37],
585    Kwajalein: ['', 47],
586    Majuro: ['Pacific/Tarawa', 47],
587    Marquesas: ['', 103],
588    Midway: ['Pacific/Pago_Pago', 104],
589    Nauru: ['', 47],
590    Niue: ['', 87],
591    Norfolk: ['', 105],
592    Noumea: ['', 37],
593    Pago_Pago: ['', 104],
594    Palau: ['', 52],
595    Pitcairn: ['', 91],
596    Pohnpei: ['Pacific/Guadalcanal', 37],
597    Ponape: ['Pacific/Guadalcanal', 37],
598    Port_Moresby: ['', 39],
599    Rarotonga: ['', 86],
600    Saipan: ['Pacific/Guam', 102],
601    Samoa: ['Pacific/Pago_Pago', 104],
602    Tahiti: ['', 86],
603    Tarawa: ['', 47],
604    Tongatapu: ['', 83],
605    Truk: ['Pacific/Port_Moresby', 39],
606    Wake: ['Pacific/Tarawa', 47],
607    Wallis: ['Pacific/Tarawa', 47],
608    Yap: ['Pacific/Port_Moresby', 39],
609  },
610  Poland: ['Europe/Warsaw', 7],
611  Portugal: ['Europe/Lisbon', 96],
612  PRC: ['Asia/Shanghai', 53],
613  PST8PDT: ['', 24],
614  ROC: ['Asia/Taipei', 53],
615  ROK: ['Asia/Seoul', 68],
616  Singapore: ['Asia/Singapore', 50],
617  Turkey: ['Europe/Istanbul', 43],
618  UCT: ['Etc/UTC', 93],
619  Universal: ['Etc/UTC', 93],
620  US: {
621    Alaska: ['America/Anchorage', 10],
622    Aleutian: ['America/Adak', 9],
623    Arizona: ['America/Phoenix', 22],
624    Central: ['America/Chicago', 20],
625    'East-Indiana': ['America/Indiana/Indianapolis', 23],
626    Eastern: ['America/New_York', 23],
627    Hawaii: ['Pacific/Honolulu', 98],
628    'Indiana-Starke': ['America/Indiana/Knox', 20],
629    Michigan: ['America/Detroit', 23],
630    Mountain: ['America/Denver', 19],
631    Pacific: ['America/Los_Angeles', 24],
632    Samoa: ['Pacific/Pago_Pago', 104],
633  },
634  UTC: ['Etc/UTC', 93],
635  'W-SU': ['Europe/Moscow', 97],
636  WET: ['', 72],
637  Zulu: ['Etc/UTC', 93],
638};
639
640const tzlibZonesDetailsDB = [
641  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:GMT<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
642  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EAT<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0300<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
643  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CET<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
644  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:WAT<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
645  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CAT<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
646  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
647  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+01<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
648  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:CEST<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0200<br>DTSTART:19700329T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:CET<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0100<br>DTSTART:19701025T030000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
649  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:SAST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
650  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:HDT<br>TZOFFSETFROM:-1000<br>TZOFFSETTO:-0900<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:HST<br>TZOFFSETFROM:-0900<br>TZOFFSETTO:-1000<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
651  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:AKDT<br>TZOFFSETFROM:-0900<br>TZOFFSETTO:-0800<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:AKST<br>TZOFFSETFROM:-0800<br>TZOFFSETTO:-0900<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
652  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:AST<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0400<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
653  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-03<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0300<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
654  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:-03<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0300<br>DTSTART:19701004T000000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:-04<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0400<br>DTSTART:19700322T000000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=4SU<br>END:STANDARD<br>',
655  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EST<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0500<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
656  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0600<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:CDT<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0500<br>DTSTART:19700405T020000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:DAYLIGHT<br>',
657  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0600<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
658  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-04<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0400<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
659  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-05<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0500<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
660  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:MDT<br>TZOFFSETFROM:-0700<br>TZOFFSETTO:-0600<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:MST<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0700<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
661  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:CDT<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0500<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0600<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
662  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:MDT<br>TZOFFSETFROM:-0700<br>TZOFFSETTO:-0600<br>DTSTART:19700405T020000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:MST<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0700<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
663  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:MST<br>TZOFFSETFROM:-0700<br>TZOFFSETTO:-0700<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
664  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:EDT<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0400<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:EST<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0500<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
665  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:PDT<br>TZOFFSETFROM:-0800<br>TZOFFSETTO:-0700<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:PST<br>TZOFFSETFROM:-0700<br>TZOFFSETTO:-0800<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
666  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:ADT<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0300<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:AST<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0400<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
667  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:-02<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0200<br>DTSTART:19700328T220000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SA<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:-03<br>TZOFFSETFROM:-0200<br>TZOFFSETTO:-0300<br>DTSTART:19701024T230000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SA<br>END:STANDARD<br>',
668  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:AST<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0400<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:ADT<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0300<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>',
669  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EST<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0500<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:EDT<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0400<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>',
670  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0500<br>DTSTART:19701101T010000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:CDT<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0400<br>DTSTART:19700308T000000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>',
671  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:CDT<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0500<br>DTSTART:19700405T020000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0600<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
672  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:-02<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0200<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:-03<br>TZOFFSETFROM:-0200<br>TZOFFSETTO:-0300<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>',
673  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-02<br>TZOFFSETFROM:-0200<br>TZOFFSETTO:-0200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
674  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0600<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:CDT<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0500<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>',
675  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-04<br>TZOFFSETFROM:-0300<br>TZOFFSETTO:-0400<br>DTSTART:19700405T000000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:-03<br>TZOFFSETFROM:-0400<br>TZOFFSETTO:-0300<br>DTSTART:19700906T000000<br>RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=1SU<br>END:DAYLIGHT<br>',
676  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:+00<br>TZOFFSETFROM:-0100<br>TZOFFSETTO:+0000<br>DTSTART:19700329T000000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:-01<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:-0100<br>DTSTART:19701025T010000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
677  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:NST<br>TZOFFSETFROM:-0230<br>TZOFFSETTO:-0330<br>DTSTART:19701101T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:NDT<br>TZOFFSETFROM:-0330<br>TZOFFSETTO:-0230<br>DTSTART:19700308T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU<br>END:DAYLIGHT<br>',
678  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+11<br>TZOFFSETFROM:+1100<br>TZOFFSETTO:+1100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
679  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+07<br>TZOFFSETFROM:+0700<br>TZOFFSETTO:+0700<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
680  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+10<br>TZOFFSETFROM:+1000<br>TZOFFSETTO:+1000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
681  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:AEST<br>TZOFFSETFROM:+1100<br>TZOFFSETTO:+1000<br>DTSTART:19700405T030000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:AEDT<br>TZOFFSETFROM:+1000<br>TZOFFSETTO:+1100<br>DTSTART:19701004T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU<br>END:DAYLIGHT<br>',
682  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+05<br>TZOFFSETFROM:+0500<br>TZOFFSETTO:+0500<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
683  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:NZDT<br>TZOFFSETFROM:+1200<br>TZOFFSETTO:+1300<br>DTSTART:19700927T020000<br>RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:NZST<br>TZOFFSETFROM:+1300<br>TZOFFSETTO:+1200<br>DTSTART:19700405T030000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>',
684  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+03<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0300<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
685  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:+02<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0200<br>DTSTART:19700329T010000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:+00<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0000<br>DTSTART:19701025T030000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
686  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+06<br>TZOFFSETFROM:+0600<br>TZOFFSETTO:+0600<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
687  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701030T010000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1FR<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700227T000000<br>RRULE:FREQ=YEARLY;BYMONTH=2;BYDAY=-1FR<br>END:DAYLIGHT<br>',
688  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+12<br>TZOFFSETFROM:+1200<br>TZOFFSETTO:+1200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
689  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+04<br>TZOFFSETFROM:+0400<br>TZOFFSETTO:+0400<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
690  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700329T000000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701025T000000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
691  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+08<br>TZOFFSETFROM:+0800<br>TZOFFSETTO:+0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
692  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:IST<br>TZOFFSETFROM:+0530<br>TZOFFSETTO:+0530<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
693  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+09<br>TZOFFSETFROM:+0900<br>TZOFFSETTO:+0900<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
694  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:CST<br>TZOFFSETFROM:+0800<br>TZOFFSETTO:+0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
695  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+0530<br>TZOFFSETFROM:+0530<br>TZOFFSETTO:+0530<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
696  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701030T000000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1FR<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700327T000000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1FR<br>END:DAYLIGHT<br>',
697  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700329T030000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701025T040000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
698  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701024T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SA<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700328T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SA<br>END:DAYLIGHT<br>',
699  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:HKT<br>TZOFFSETFROM:+0800<br>TZOFFSETTO:+0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
700  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:WIB<br>TZOFFSETFROM:+0700<br>TZOFFSETTO:+0700<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
701  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:WIT<br>TZOFFSETFROM:+0900<br>TZOFFSETTO:+0900<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
702  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:IDT<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700327T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1FR<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:IST<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
703  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+0430<br>TZOFFSETFROM:+0430<br>TZOFFSETTO:+0430<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
704  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:PKT<br>TZOFFSETFROM:+0500<br>TZOFFSETTO:+0500<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
705  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+0545<br>TZOFFSETFROM:+0545<br>TZOFFSETTO:+0545<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
706  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:WITA<br>TZOFFSETFROM:+0800<br>TZOFFSETTO:+0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
707  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:PST<br>TZOFFSETFROM:+0800<br>TZOFFSETTO:+0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
708  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701025T040000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700329T030000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLI
708GHT<br>',
709  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:KST<br>TZOFFSETFROM:+0900<br>TZOFFSETTO:+0900<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
710  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+0630<br>TZOFFSETFROM:+0630<br>TZOFFSETTO:+0630<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
711  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+0330<br>TZOFFSETFROM:+0330<br>TZOFFSETTO:+0330<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
712  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:JST<br>TZOFFSETFROM:+0900<br>TZOFFSETTO:+0900<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
713  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:WEST<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0100<br>DTSTART:19700329T010000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:WET<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0000<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
714  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-01<br>TZOFFSETFROM:-0100<br>TZOFFSETTO:-0100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
715  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:ACST<br>TZOFFSETFROM:+1030<br>TZOFFSETTO:+0930<br>DTSTART:19700405T030000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:ACDT<br>TZOFFSETFROM:+0930<br>TZOFFSETTO:+1030<br>DTSTART:19701004T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU<br>END:DAYLIGHT<br>',
716  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:AEST<br>TZOFFSETFROM:+1000<br>TZOFFSETTO:+1000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
717  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:AEDT<br>TZOFFSETFROM:+1000<br>TZOFFSETTO:+1100<br>DTSTART:19701004T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:AEST<br>TZOFFSETFROM:+1100<br>TZOFFSETTO:+1000<br>DTSTART:19700405T030000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>',
718  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:ACST<br>TZOFFSETFROM:+0930<br>TZOFFSETTO:+0930<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
719  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+0845<br>TZOFFSETFROM:+0845<br>TZOFFSETTO:+0845<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
720  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+1030<br>TZOFFSETFROM:+1100<br>TZOFFSETTO:+1030<br>DTSTART:19700405T020000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:+11<br>TZOFFSETFROM:+1030<br>TZOFFSETTO:+1100<br>DTSTART:19701004T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU<br>END:DAYLIGHT<br>',
721  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:AWST<br>TZOFFSETFROM:+0800<br>TZOFFSETTO:+0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
722  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-06<br>TZOFFSETFROM:-0500<br>TZOFFSETTO:-0600<br>DTSTART:19700404T220000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SA<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:-05<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0500<br>DTSTART:19700905T220000<br>RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=1SA<br>END:DAYLIGHT<br>',
723  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:IST<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0100<br>DTSTART:19700329T010000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:GMT<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0000<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:DAYLIGHT<br>',
724  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+13<br>TZOFFSETFROM:+1300<br>TZOFFSETTO:+1300<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
725  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+14<br>TZOFFSETFROM:+1400<br>TZOFFSETTO:+1400<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
726  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:+02<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
727  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-10<br>TZOFFSETFROM:-1000<br>TZOFFSETTO:-1000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
728  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-11<br>TZOFFSETFROM:-1100<br>TZOFFSETTO:-1100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
729  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-12<br>TZOFFSETFROM:-1200<br>TZOFFSETTO:-1200<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
730  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-06<br>TZOFFSETFROM:-0600<br>TZOFFSETTO:-0600<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
731  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-07<br>TZOFFSETFROM:-0700<br>TZOFFSETTO:-0700<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
732  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-08<br>TZOFFSETFROM:-0800<br>TZOFFSETTO:-0800<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
733  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-09<br>TZOFFSETFROM:-0900<br>TZOFFSETTO:-0900<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
734  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:UTC<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
735  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:BST<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0100<br>DTSTART:19700329T010000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:GMT<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0000<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
736  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:EEST<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0300<br>DTSTART:19700329T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:EET<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0200<br>DTSTART:19701025T030000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
737  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:WET<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0000<br>DTSTART:19701025T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>BEGIN:DAYLIGHT<br>TZNAME:WEST<br>TZOFFSETFROM:+0000<br>TZOFFSETTO:+0100<br>DTSTART:19700329T010000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>',
738  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:MSK<br>TZOFFSETFROM:+0300<br>TZOFFSETTO:+0300<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
739  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:HST<br>TZOFFSETFROM:-1000<br>TZOFFSETTO:-1000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
740  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:MEST<br>TZOFFSETFROM:+0100<br>TZOFFSETTO:+0200<br>DTSTART:19700329T020000<br>RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:MET<br>TZOFFSETFROM:+0200<br>TZOFFSETTO:+0100<br>DTSTART:19701025T030000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU<br>END:STANDARD<br>',
741  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:+1345<br>TZOFFSETFROM:+1245<br>TZOFFSETTO:+1345<br>DTSTART:19700927T024500<br>RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:+1245<br>TZOFFSETFROM:+1345<br>TZOFFSETTO:+1245<br>DTSTART:19700405T034500<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>',
742  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:+13<br>TZOFFSETFROM:+1200<br>TZOFFSETTO:+1300<br>DTSTART:19701108T020000<br>RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=2SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:+12<br>TZOFFSETFROM:+1300<br>TZOFFSETTO:+1200<br>DTSTART:19700118T030000<br>RRULE:FREQ=YEARLY;BYMONTH=1;BYDAY=-2SU<br>END:STANDARD<br>',
743  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:ChST<br>TZOFFSETFROM:+1000<br>TZOFFSETTO:+1000<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
744  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:-0930<br>TZOFFSETFROM:-0930<br>TZOFFSETTO:-0930<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
745  '20220929T150625Z<br>BEGIN:STANDARD<br>TZNAME:SST<br>TZOFFSETFROM:-1100<br>TZOFFSETTO:-1100<br>DTSTART:19700101T000000<br>END:STANDARD<br>',
746  '20220929T150625Z<br>BEGIN:DAYLIGHT<br>TZNAME:+12<br>TZOFFSETFROM:+1100<br>TZOFFSETTO:+1200<br>DTSTART:19701004T020000<br>RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU<br>END:DAYLIGHT<br>BEGIN:STANDARD<br>TZNAME:+11<br>TZOFFSETFROM:+1200<br>TZOFFSETTO:+1100<br>DTSTART:19700405T030000<br>RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU<br>END:STANDARD<br>',
747];
748
749
750// SHARED FUNCTION TO GET THE TZ CONTENT FROM THE INTERNAL DATABASE
751function tzlib_get_content(tzName) {
752  // get timezone parts
753  const nameParts = tzName.split('/');
754  // validate timezone
755  // TODO: Make this a little bit smarter (depending on the future db structure)
756  if (
757    (nameParts.length === 3 &&
758      (!tzlibZonesDB[`${nameParts[0]}`] ||
759        !tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`] ||
760        !tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`][`${nameParts[2]}`])) ||
761    (nameParts.length === 2 &&
762      (!tzlibZonesDB[`${nameParts[0]}`] || !tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`])) ||
763    (nameParts.length === 1 && !tzlibZonesDB[`${nameParts[0]}`])
764  ) {
765    console.error('Given timezone not valid.');
766    return '';
767  }
768  // create the output
769  if (nameParts.length === 3) {
770    return [
771      tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`][`${nameParts[2]}`][0],
772      tzlibZonesDetailsDB[tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`][`${nameParts[2]}`][1]],
773    ];
774  }
775  if (nameParts.length === 2) {
776    return [
777      tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`][0],
778      tzlibZonesDetailsDB[tzlibZonesDB[`${nameParts[0]}`][`${nameParts[1]}`][1]],
779    ];
780  }
781  return [tzlibZonesDB[`${nameParts[0]}`][0], tzlibZonesDetailsDB[tzlibZonesDB[`${nameParts[0]}`][1]]];
782}
783
784// LOADING THE RIGHT CODE BLOCK
785// eslint-disable-next-line @typescript-eslint/no-unused-vars
786function tzlib_get_ical_block(tzName, jsonType = false) {
787  const tzBlock = tzlib_get_content(tzName);
788  if (tzBlock[1] == null || tzBlock[1] == '') {
789    return '';
790  }
791  // create the output
792  const location = (function () {
793    if (tzBlock[0] == '') {
794      return tzName;
795    } else {
796      return tzBlock[0];
797    }
798  })();
799  const tzidLine = 'TZID=' + location;
800  const output = [
801    'BEGIN:VTIMEZONE\r\nTZID:' +
802      location +
803      '\r\nX-LIC-LOCATION:' +
804      location +
805      '\r\nLAST-MODIFIED:' +
806      tzBlock[1].replace(/[^\w_\-:,;=+/<br>]/g, '').replace(/<br>/g, '\r\n') +
807      'END:VTIMEZONE',
808    tzidLine,
809  ];
810  // return
811  if (jsonType) {
812    return JSON.stringify(output);
813  }
814  return output;
815}
816
817// PROVIDING THE OFFSET BASED ON A GIVEN DATE AND TIME (YYYY-MM-DD and hh:mm as per ISO-8601).
818// eslint-disable-next-line @typescript-eslint/no-unused-vars
819function tzlib_get_offset(tzName, isoDate, isoTime) {
820  const tzBlock = tzlib_get_content(tzName);
821  if (tzBlock[1] == null || tzBlock[1] == '') {
822    return '';
823  }
824  // validate date
825  if (!isoDate.match(/^\d{4}-\d{2}-\d{2}$/)) {
826    console.error('offset calculation failed: date misspelled [-> YYYY-MM-DD]');
827    return '';
828  }
829  // validate time
830  if (!isoTime.match(/^\d{2}:\d{2}$/)) {
831    console.error('offset calculation failed: time misspelled [-> hh:mm]');
832    return '';
833  }
834  // return early if there are no daylight changes
835  if (!tzBlock[1].match(/BEGIN:DAYLIGHT/i)) {
836    return tzBlock[1].match(/TZOFFSETTO:([+|-]\d{4})/i)[1];
837  }
838  // otherwise, calculate offset
839  // creating a JS date from the input
840  const dateString = isoDate + 'T' + isoTime + ':00';
841  const date = new Date(dateString);
842  const dateYear = date.getFullYear();
843  const dateMonth = date.getMonth() + 1;
844  const dateDay = date.getDate();
845  const dateHour = date.getHours();
846  // preparing the tz data
847  const timezoneData = tzBlock[1].replace(/[^\w_\-:,;=+/<br>]/g, '').split('<br>');
848  // collect timezone breakpoints (exactly 2)
849  const tzBreakpoints = { 1: {}, 2: {} };
850  let breakpointCount = 0;
851  for (let i = 0; i < timezoneData.length; i++) {
852    // always first and therefore drives the counter
853    if (timezoneData[`${i}`].startsWith('TZOFFSETTO')) {
854      breakpointCount++;
855      tzBreakpoints[`${breakpointCount}`].offset = timezoneData[`${i}`].split(':')[1];
856    }
857    // only required for the critical hour
858    if (timezoneData[`${i}`].startsWith('DTSTART')) {
859      tzBreakpoints[`${breakpointCount}`].hour = parseInt(timezoneData[`${i}`].substr(17, 2));
860    }
861    // the RRULE is deciding when the switch happens (excluding the hour information from DTSTART)
862    if (timezoneData[`${i}`].startsWith('RRULE')) {
863      let rruleParts = timezoneData[`${i}`].split(';');
864      let rruleMonth = parseInt(rruleParts[1].split('=')[1]);
865      tzBreakpoints[`${breakpointCount}`].month = parseInt(rruleMonth);
866      tzBreakpoints[`${breakpointCount}`].day = rruleParts[2].split('=')[1];
867    }
868  }
869  // swap objects, if larger one comes first
870  if (tzBreakpoints[1].month > tzBreakpoints[2].month) {
871    [tzBreakpoints[1], tzBreakpoints[2]] = [tzBreakpoints[2], tzBreakpoints[1]];
872  }
873  // check for easy cases where the month is cleary in between
874  if (dateMonth != tzBreakpoints[1].month && dateMonth != tzBreakpoints[2].month) {
875    if (dateMonth < tzBreakpoints[1].month || dateMonth > tzBreakpoints[2].month) {
876      return tzBreakpoints[2].offset;
877    } else {
878      return tzBreakpoints[1].offset;
879    }
880  }
881  // in other cases, validate where we are exactly and pick the right offset
882  // defining the critical case, we need to evaluate (the breakpoint we are matching by month)
883  const theCase = (function () {
884    return Object.keys(tzBreakpoints).find((key) => tzBreakpoints[`${key}`].month == dateMonth);
885  })();
886  // determining the actual day
887  const helperArrayWeekdays = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
888  const numberDays = new Date(dateYear, dateMonth, 0).getDate();
889  let weekdayCount = new Date(dateYear, dateMonth - 1, 1).getDay();
890  const weekdays = { SU: {}, MO: {}, TU: {}, WE: {}, TH: {}, FR: {}, SA: {} };
891  for (let d = 1; d <= numberDays; d++) {
892    const occurence = Object.keys(weekdays[helperArrayWeekdays[`${weekdayCount}`]]).length + 1;
893    weekdays[helperArrayWeekdays[`${weekdayCount}`]][`${occurence}`] = d;
894    weekdayCount++;
895    if (weekdayCount == 7) {
896      weekdayCount = 0;
897    }
898  }
899  const actualDay = (function () {
900    if (tzBreakpoints[`${theCase}`].day[0] == '-') {
901      const breakpointWeekday = tzBreakpoints[`${theCase}`].day.substr(2, 2);
902      const dayIndex =
903        Object.keys(weekdays[`${breakpointWeekday}`]).length +
904        1 -
905        parseInt(tzBreakpoints[`${theCase}`].day[1]);
906      return weekdays[`${breakpointWeekday}`][`${dayIndex}`];
907    } else {
908      const breakpointWeekday = tzBreakpoints[`${theCase}`].day.substr(1, 2);
909      return weekdays[`${breakpointWeekday}`][tzBreakpoints[`${theCase}`].day[0]];
910    }
911  })();
912  // finally identifying the right offset
913  if (dateDay > actualDay || (dateDay == actualDay && dateHour >= tzBreakpoints[`${theCase}`].hour)) {
914    return tzBreakpoints[`${theCase}`].offset;
915  }
916  const fallbackCase = (function () {
917    if (theCase == 1) {
918      return 2;
919    } else {
920      return 1;
921    }
922  })();
923  return tzBreakpoints[`${fallbackCase}`].offset;
924}
925
926// PROVIDE ALL TIMEZONES
927// eslint-disable-next-line @typescript-eslint/no-unused-vars
928function tzlib_get_timezones(jsonType = false) {
929  const tzlibZoneNames = [
930  "Africa/Abidjan",
931  "Africa/Accra",
932  "Africa/Addis_Ababa",
933  "Africa/Algiers",
934  "Africa/Asmara",
935  "Africa/Asmera",
936  "Africa/Bamako",
937  "Africa/Bangui",
938  "Africa/Banjul",
939  "Africa/Bissau",
940  "Africa/Blantyre",
941  "Africa/Brazzaville",
942  "Africa/Bujumbura",
943  "Africa/Cairo",
944  "Africa/Casablanca",
945  "Africa/Ceuta",
946  "Africa/Conakry",
947  "Africa/Dakar",
948  "Africa/Dar_es_Salaam",
949  "Africa/Djibouti",
950  "Africa/Douala",
951  "Africa/El_Aaiun",
952  "Africa/Freetown",
953  "Africa/Gaborone",
954  "Africa/Harare",
955  "Africa/Johannesburg",
956  "Africa/Juba",
957  "Africa/Kampala",
958  "Africa/Khartoum",
959  "Africa/Kigali",
960  "Africa/Kinshasa",
961  "Africa/Lagos",
962  "Africa/Libreville",
963  "Africa/Lome",
964  "Africa/Luanda",
965  "Africa/Lubumbashi",
966  "Africa/Lusaka",
967  "Africa/Malabo",
968  "Africa/Maputo",
969  "Africa/Maseru",
970  "Africa/Mbabane",
971  "Africa/Mogadishu",
972  "Africa/Monrovia",
973  "Africa/Nairobi",
974  "Africa/Ndjamena",
975  "Africa/Niamey",
976  "Africa/Nouakchott",
977  "Africa/Ouagadougou",
978  "Africa/Porto-Novo",
979  "Africa/Sao_Tome",
980  "Africa/Timbuktu",
981  "Africa/Tripoli",
982  "Africa/Tunis",
983  "Africa/Windhoek",
984  "America/Adak",
985  "America/Anchorage",
986  "America/Anguilla",
987  "America/Antigua",
988  "America/Araguaina",
989  "America/Argentina/Buenos_Aires",
990  "America/Argentina/Catamarca",
991  "America/Argentina/ComodRivadavia",
992  "America/Argentina/Cordoba",
993  "America/Argentina/Jujuy",
994  "America/Argentina/La_Rioja",
995  "America/Argentina/Mendoza",
996  "America/Argentina/Rio_Gallegos",
997  "America/Argentina/Salta",
998  "America/Argentina/San_Juan",
999  "America/Argentina/San_Luis",
1000  "America/Argentina/Tucuman",
1001  "America/Argentina/Ushuaia",
1002  "America/Aruba",
1003  "America/Asuncion",
1004  "America/Atikokan",
1005  "America/Atka",
1006  "America/Bahia_Banderas",
1007  "America/Bahia",
1008  "America/Barbados",
1009  "America/Belem",
1010  "America/Belize",
1011  "America/Blanc-Sablon",
1012  "America/Boa_Vista",
1013  "America/Bogota",
1014  "America/Boise",
1015  "America/Buenos_Aires",
1016  "America/Cambridge_Bay",
1017  "America/Campo_Grande",
1018  "America/Cancun",
1019  "America/Caracas",
1020  "America/Catamarca",
1021  "America/Cayenne",
1022  "America/Cayman",
1023  "America/Chicago",
1024  "America/Chihuahua",
1025  "America/Coral_Harbour",
1026  "America/Cordoba",
1027  "America/Costa_Rica",
1028  "America/Creston",
1029  "America/Cuiaba",
1030  "America/Curacao",
1031  "America/Danmarkshavn",
1032  "America/Dawson_Creek",
1033  "America/Dawson",
1034  "America/Denver",
1035  "America/Detroit",
1036  "America/Dominica",
1037  "America/Edmonton",
1038  "America/Eirunepe",
1039  "America/El_Salvador",
1040  "America/Ensenada",
1041  "America/Fort_Nelson",
1042  "America/Fort_Wayne",
1043  "America/Fortaleza",
1044  "America/Glace_Bay",
1045  "America/Godthab",
1046  "America/Goose_Bay",
1047  "America/Grand_Turk",
1048  "America/Grenada",
1049  "America/Guadeloupe",
1050  "America/Guatemala",
1051  "America/Guayaquil",
1052  "America/Guyana",
1053  "America/Halifax",
1054  "America/Havana",
1055  "America/Hermosillo",
1056  "America/Indiana/Indianapolis",
1057  "America/Indiana/Knox",
1058  "America/Indiana/Marengo",
1059  "America/Indiana/Petersburg",
1060  "America/Indiana/Tell_City",
1061  "America/Indiana/Vevay",
1062  "America/Indiana/Vincennes",
1063  "America/Indiana/Winamac",
1064  "America/Indianapolis",
1065  "America/Inuvik",
1066  "America/Iqaluit",
1067  "America/Jamaica",
1068  "America/Jujuy",
1069  "America/Juneau",
1070  "America/Kentucky/Louisville",
1071  "America/Kentucky/Monticello",
1072  "America/Knox_IN",
1073  "America/Kralendijk",
1074  "America/La_Paz",
1075  "America/Lima",
1076  "America/Los_Angeles",
1077  "America/Louisville",
1078  "America/Lower_Princes",
1079  "America/Maceio",
1080  "America/Managua",
1081  "America/Manaus",
1082  "America/Marigot",
1083  "America/Martinique",
1084  "America/Matamoros",
1085  "America/Mazatlan",
1086  "America/Mendoza",
1087  "America/Menominee",
1088  "America/Merida",
1089  "America/Metlakatla",
1090  "America/Mexico_City",
1091  "America/Miquelon",
1092  "America/Moncton",
1093  "America/Monterrey",
1094  "America/Montevideo",
1095  "America/Montreal",
1096  "America/Montserrat",
1097  "America/Nassau",
1098  "America/New_York",
1099  "America/Nipigon",
1100  "America/Nome",
1101  "America/Noronha",
1102  "America/North_Dakota/Beulah",
1103  "America/North_Dakota/Center",
1104  "America/North_Dakota/New_Salem",
1105  "America/Nuuk",
1106  "America/Ojinaga",
1107  "America/Panama",
1108  "America/Pangnirtung",
1109  "America/Paramaribo",
1110  "America/Phoenix",
1111  "America/Port_of_Spain",
1112  "America/Port-au-Prince",
1113  "America/Porto_Acre",
1114  "America/Porto_Velho",
1115  "America/Puerto_Rico",
1116  "America/Punta_Arenas",
1117  "America/Rainy_River",
1118  "America/Rankin_Inlet",
1119  "America/Recife",
1120  "America/Regina",
1121  "America/Resolute",
1122  "America/Rio_Branco",
1123  "America/Rosario",
1124  "America/Santa_Isabel",
1125  "America/Santarem",
1126  "America/Santiago",
1127  "America/Santo_Domingo",
1128  "America/Sao_Paulo",
1129  "America/Scoresbysund",
1130  "America/Shiprock",
1131  "America/Sitka",
1132  "America/St_Barthelemy",
1133  "America/St_Johns",
1134  "America/St_Kitts",
1135  "America/St_Lucia",
1136  "America/St_Thomas",
1137  "America/St_Vincent",
1138  "America/Swift_Current",
1139  "America/Tegucigalpa",
1140  "America/Thule",
1141  "America/Thunder_Bay",
1142  "America/Tijuana",
1143  "America/Toronto",
1144  "America/Tortola",
1145  "America/Vancouver",
1146  "America/Virgin",
1147  "America/Whitehorse",
1148  "America/Winnipeg",
1149  "America/Yakutat",
1150  "America/Yellowknife",
1151  "Antarctica/Casey",
1152  "Antarctica/Davis",
1153  "Antarctica/DumontDUrville",
1154  "Antarctica/Macquarie",
1155  "Antarctica/Mawson",
1156  "Antarctica/McMurdo",
1157  "Antarctica/Palmer",
1158  "Antarctica/Rothera",
1159  "Antarctica/South_Pole",
1160  "Antarctica/Syowa",
1161  "Antarctica/Troll",
1162  "Antarctica/Vostok",
1163  "Arctic/Longyearbyen",
1164  "Asia/Aden",
1165  "Asia/Almaty",
1166  "Asia/Amman",
1167  "Asia/Anadyr",
1168  "Asia/Aqtau",
1169  "Asia/Aqtobe",
1170  "Asia/Ashgabat",
1171  "Asia/Ashkhabad",
1172  "Asia/Atyrau",
1173  "Asia/Baghdad",
1174  "Asia/Bahrain",
1175  "Asia/Baku",
1176  "Asia/Bangkok",
1177  "Asia/Barnaul",
1178  "Asia/Beirut",
1179  "Asia/Bishkek",
1180  "Asia/Brunei",
1181  "Asia/Calcutta",
1182  "Asia/Chita",
1183  "Asia/Choibalsan",
1184  "Asia/Chongqing",
1185  "Asia/Chungking",
1186  "Asia/Colombo",
1187  "Asia/Dacca",
1188  "Asia/Damascus",
1189  "Asia/Dhaka",
1190  "Asia/Dili",
1191  "Asia/Dubai",
1192  "Asia/Dushanbe",
1193  "Asia/Famagusta",
1194  "Asia/Gaza",
1195  "Asia/Harbin",
1196  "Asia/Hebron",
1197  "Asia/Ho_Chi_Minh",
1198  "Asia/Hong_Kong",
1199  "Asia/Hovd",
1200  "Asia/Irkutsk",
1201  "Asia/Istanbul",
1202  "Asia/Jakarta",
1203  "Asia/Jayapura",
1204  "Asia/Jerusalem",
1205  "Asia/Kabul",
1206  "Asia/Kamchatka",
1207  "Asia/Karachi",
1208  "Asia/Kashgar",
1209  "Asia/Kathmandu",
1210  "Asia/Katmandu",
1211  "Asia/Khandyga",
1212  "Asia/Kolkata",
1213  "Asia/Krasnoyarsk",
1214  "Asia/Kuala_Lumpur",
1215  "Asia/Kuching",
1216  "Asia/Kuwait",
1217  "Asia/Macao",
1218  "Asia/Macau",
1219  "Asia/Magadan",
1220  "Asia/Makassar",
1221  "Asia/Manila",
1222  "Asia/Muscat",
1223  "Asia/Nicosia",
1224  "Asia/Novokuznetsk",
1225  "Asia/Novosibirsk",
1226  "Asia/Omsk",
1227  "Asia/Oral",
1228  "Asia/Phnom_Penh",
1229  "Asia/Pontianak",
1230  "Asia/Pyongyang",
1231  "Asia/Qatar",
1232  "Asia/Qostanay",
1233  "Asia/Qyzylorda",
1234  "Asia/Rangoon",
1235  "Asia/Riyadh",
1236  "Asia/Saigon",
1237  "Asia/Sakhalin",
1238  "Asia/Samarkand",
1239  "Asia/Seoul",
1240  "Asia/Shanghai",
1241  "Asia/Singapore",
1242  "Asia/Srednekolymsk",
1243  "Asia/Taipei",
1244  "Asia/Tashkent",
1245  "Asia/Tbilisi",
1246  "Asia/Tehran",
1247  "Asia/Tel_Aviv",
1248  "Asia/Thimbu",
1249  "Asia/Thimphu",
1250  "Asia/Tokyo",
1251  "Asia/Tomsk",
1252  "Asia/Ujung_Pandang",
1253  "Asia/Ulaanbaatar",
1254  "Asia/Ulan_Bator",
1255  "Asia/Urumqi",
1256  "Asia/Ust-Nera",
1257  "Asia/Vientiane",
1258  "Asia/Vladivostok",
1259  "Asia/Yakutsk",
1260  "Asia/Yangon",
1261  "Asia/Yekaterinburg",
1262  "Asia/Yerevan",
1263  "Atlantic/Azores",
1264  "Atlantic/Bermuda",
1265  "Atlantic/Canary",
1266  "Atlantic/Cape_Verde",
1267  "Atlantic/Faeroe",
1268  "Atlantic/Faroe",
1269  "Atlantic/Jan_Mayen",
1270  "Atlantic/Madeira",
1271  "Atlantic/Reykjavik",
1272  "Atlantic/South_Georgia",
1273  "Atlantic/St_Helena",
1274  "Atlantic/Stanley",
1275  "Australia/ACT",
1276  "Australia/Adelaide",
1277  "Australia/Brisbane",
1278  "Australia/Broken_Hill",
1279  "Australia/Canberra",
1280  "Australia/Currie",
1281  "Australia/Darwin",
1282  "Australia/Eucla",
1283  "Australia/Hobart",
1284  "Australia/LHI",
1285  "Australia/Lindeman",
1286  "Australia/Lord_Howe",
1287  "Australia/Melbourne",
1288  "Australia/North",
1289  "Australia/NSW",
1290  "Australia/Perth",
1291  "Australia/Queensland",
1292  "Australia/South",
1293  "Australia/Sydney",
1294  "Australia/Tasmania",
1295  "Australia/Victoria",
1296  "Australia/West",
1297  "Australia/Yancowinna",
1298  "Brazil/Acre",
1299  "Brazil/DeNoronha",
1300  "Brazil/East",
1301  "Brazil/West",
1302  "Canada/Atlantic",
1303  "Canada/Central",
1304  "Canada/Eastern",
1305  "Canada/Mountain",
1306  "Canada/Newfoundland",
1307  "Canada/Pacific",
1308  "Canada/Saskatchewan",
1309  "Canada/Yukon",
1310  "CET",
1311  "Chile/Continental",
1312  "Chile/EasterIsland",
1313  "CST6CDT",
1314  "Cuba",
1315  "EET",
1316  "Egypt",
1317  "Eire",
1318  "EST",
1319  "EST5EDT",
1320  "Etc/GMT-0",
1321  "Etc/GMT-1",
1322  "Etc/GMT-10",
1323  "Etc/GMT-11",
1324  "Etc/GMT-12",
1325  "Etc/GMT-13",
1326  "Etc/GMT-14",
1327  "Etc/GMT-2",
1328  "Etc/GMT-3",
1329  "Etc/GMT-4",
1330  "Etc/GMT-5",
1331  "Etc/GMT-6",
1332  "Etc/GMT-7",
1333  "Etc/GMT-8",
1334  "Etc/GMT-9",
1335  "Etc/GMT",
1336  "Etc/GMT+0",
1337  "Etc/GMT+1",
1338  "Etc/GMT+10",
1339  "Etc/GMT+11",
1340  "Etc/GMT+12",
1341  "Etc/GMT+2",
1342  "Etc/GMT+3",
1343  "Etc/GMT+4",
1344  "Etc/GMT+5",
1345  "Etc/GMT+6",
1346  "Etc/GMT+7",
1347  "Etc/GMT+8",
1348  "Etc/GMT+9",
1349  "Etc/GMT0",
1350  "Etc/Greenwich",
1351  "Etc/UCT",
1352  "Etc/Universal",
1353  "Etc/UTC",
1354  "Etc/Zulu",
1355  "Europe/Amsterdam",
1356  "Europe/Andorra",
1357  "Europe/Astrakhan",
1358  "Europe/Athens",
1359  "Europe/Belfast",
1360  "Europe/Belgrade",
1361  "Europe/Berlin",
1362  "Europe/Bratislava",
1363  "Europe/Brussels",
1364  "Europe/Bucharest",
1365  "Europe/Budapest",
1366  "Europe/Busingen",
1367  "Europe/Chisinau",
1368  "Europe/Copenhagen",
1369  "Europe/Dublin",
1370  "Europe/Gibraltar",
1371  "Europe/Guernsey",
1372  "Europe/Helsinki",
1373  "Europe/Isle_of_Man",
1374  "Europe/Istanbul",
1375  "Europe/Jersey",
1376  "Europe/Kaliningrad",
1377  "Europe/Kiev",
1378  "Europe/Kirov",
1379  "Europe/Kyiv",
1380  "Europe/Lisbon",
1381  "Europe/Ljubljana",
1382  "Europe/London",
1383  "Europe/Luxembourg",
1384  "Europe/Madrid",
1385  "Europe/Malta",
1386  "Europe/Mariehamn",
1387  "Europe/Minsk",
1388  "Europe/Monaco",
1389  "Europe/Moscow",
1390  "Europe/Nicosia",
1391  "Europe/Oslo",
1392  "Europe/Paris",
1393  "Europe/Podgorica",
1394  "Europe/Prague",
1395  "Europe/Riga",
1396  "Europe/Rome",
1397  "Europe/Samara",
1398  "Europe/San_Marino",
1399  "Europe/Sarajevo",
1400  "Europe/Saratov",
1401  "Europe/Simferopol",
1402  "Europe/Skopje",
1403  "Europe/Sofia",
1404  "Europe/Stockholm",
1405  "Europe/Tallinn",
1406  "Europe/Tirane",
1407  "Europe/Tiraspol",
1408  "Europe/Ulyanovsk",
1409  "Europe/Uzhgorod",
1410  "Europe/Vaduz",
1411  "Europe/Vatican",
1412  "Europe/Vienna",
1413  "Europe/Vilnius",
1414  "Europe/Volgograd",
1415  "Europe/Warsaw",
1416  "Europe/Zagreb",
1417  "Europe/Zaporozhye",
1418  "Europe/Zurich",
1419  "GB-Eire",
1420  "GB",
1421  "GMT-0",
1422  "GMT",
1423  "GMT+0",
1424  "GMT0",
1425  "Greenwich",
1426  "Hongkong",
1427  "HST",
1428  "Iceland",
1429  "Indian/Antananarivo",
1430  "Indian/Chagos",
1431  "Indian/Christmas",
1432  "Indian/Cocos",
1433  "Indian/Comoro",
1434  "Indian/Kerguelen",
1435  "Indian/Mahe",
1436  "Indian/Maldives",
1437  "Indian/Mauritius",
1438  "Indian/Mayotte",
1439  "Indian/Reunion",
1440  "Iran",
1441  "Israel",
1442  "Jamaica",
1443  "Japan",
1444  "Kwajalein",
1445  "Libya",
1446  "MET",
1447  "Mexico/BajaNorte",
1448  "Mexico/BajaSur",
1449  "Mexico/General",
1450  "MST",
1451  "MST7MDT",
1452  "Navajo",
1453  "NZ-CHAT",
1454  "NZ",
1455  "Pacific/Apia",
1456  "Pacific/Auckland",
1457  "Pacific/Bougainville",
1458  "Pacific/Chatham",
1459  "Pacific/Chuuk",
1460  "Pacific/Easter",
1461  "Pacific/Efate",
1462  "Pacific/Enderbury",
1463  "Pacific/Fakaofo",
1464  "Pacific/Fiji",
1465  "Pacific/Funafuti",
1466  "Pacific/Galapagos",
1467  "Pacific/Gambier",
1468  "Pacific/Guadalcanal",
1469  "Pacific/Guam",
1470  "Pacific/Honolulu",
1471  "Pacific/Johnston",
1472  "Pacific/Kanton",
1473  "Pacific/Kiritimati",
1474  "Pacific/Kosrae",
1475  "Pacific/Kwajalein",
1476  "Pacific/Majuro",
1477  "Pacific/Marquesas",
1478  "Pacific/Midway",
1479  "Pacific/Nauru",
1480  "Pacific/Niue",
1481  "Pacific/Norfolk",
1482  "Pacific/Noumea",
1483  "Pacific/Pago_Pago",
1484  "Pacific/Palau",
1485  "Pacific/Pitcairn",
1486  "Pacific/Pohnpei",
1487  "Pacific/Ponape",
1488  "Pacific/Port_Moresby",
1489  "Pacific/Rarotonga",
1490  "Pacific/Saipan",
1491  "Pacific/Samoa",
1492  "Pacific/Tahiti",
1493  "Pacific/Tarawa",
1494  "Pacific/Tongatapu",
1495  "Pacific/Truk",
1496  "Pacific/Wake",
1497  "Pacific/Wallis",
1498  "Pacific/Yap",
1499  "Poland",
1500  "Portugal",
1501  "PRC",
1502  "PST8PDT",
1503  "ROC",
1504  "ROK",
1505  "Singapore",
1506  "Turkey",
1507  "UCT",
1508  "Universal",
1509  "US/Alaska",
1510  "US/Aleutian",
1511  "US/Arizona",
1512  "US/Central",
1513  "US/East-Indiana",
1514  "US/Eastern",
1515  "US/Hawaii",
1516  "US/Indiana-Starke",
1517  "US/Michigan",
1518  "US/Mountain",
1519  "US/Pacific",
1520  "US/Samoa",
1521  "UTC",
1522  "W-SU",
1523  "WET",
1524  "Zulu"
1525]
1526;
1527  if (jsonType) {
1528    return JSON.stringify(tzlibZoneNames);
1529  }
1530  return tzlibZoneNames;
1531}
1532
1533
1534
1535
1536
1537/*!
1538 *  @preserve
1539 *
1540 *  ++++++++++++++++++++++
1541 *  Add to Calendar Button
1542 *  ++++++++++++++++++++++
1543 *
1544 *  Creator: Jens Kuerschner (https://jenskuerschner.de)
1545 *  Project: https://github.com/add2cal/add-to-calendar-button
1546 *  License: Apache-2.0 with “Commons Clause” License Condition v1.0
1547 *  Note:    DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
1548 *
1549 */
1550const atcbVersion = '1.17.0';
1551
1552
1553
1554// CHECKING FOR SPECIFIC DEVICED AND SYSTEMS
1555// browser
1556const isBrowser = () => {
1557  if (typeof window === 'undefined') {
1558    return false;
1559  } else {
1560    return true;
1561  }
1562};
1563// iOS
1564const isiOS = isBrowser()
1565  ? () => {
1566      if (
1567        (/iPad|iPhone|iPod/i.test(navigator.userAgent || navigator.vendor || window.opera) &&
1568          !window.MSStream) ||
1569        (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
1570      ) {
1571        return true;
1572      } else {
1573        return false;
1574      }
1575    }
1576  : () => {
1577      return false;
1578    };
1579// Android
1580const isAndroid = isBrowser()
1581  ? () => {
1582      if (/android/i.test(navigator.userAgent || navigator.vendor || window.opera) && !window.MSStream) {
1583        return true;
1584      } else {
1585        return false;
1586      }
1587    }
1588  : () => {
1589      return false;
1590    };
1591// Chrome
1592const isChrome = isBrowser()
1593  ? () => {
1594      if (/chrome|chromium|crios/i.test(navigator.userAgent)) {
1595        return true;
1596      } else {
1597        return false;
1598      }
1599    }
1600  : () => {
1601      return false;
1602    };
1603// Mobile
1604const isMobile = () => {
1605  if (isAndroid() || isiOS()) {
1606    return true;
1607  } else {
1608    return false;
1609  }
1610};
1611// WebView (iOS and Android)
1612const isWebView = isBrowser()
1613  ? () => {
1614      if (
1615        /(; ?wv|(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari))/i.test(navigator.userAgent || navigator.vendor)
1616      ) {
1617        return true;
1618      } else {
1619        return false;
1620      }
1621    }
1622  : () => {
1623      return false;
1624    };
1625// checking for problematic apps
1626const isProblematicWebView = isBrowser()
1627  ? () => {
1628      if (/(Instagram)/i.test(navigator.userAgent || navigator.vendor || window.opera)) {
1629        return true;
1630      } else {
1631        return false;
1632      }
1633    }
1634  : () => {
1635      return false;
1636    };
1637
1638// DEFINE GLOBAL VARIABLES
1639let atcbConsoleInit = false;
1640const atcbDefaultTarget = isWebView() ? '_system' : '_blank';
1641const atcbOptions = ['apple', 'google', 'ical', 'ms365', 'outlookcom', 'msteams', 'yahoo'];
1642const atcbValidRecurrOptions = ['apple', 'google', 'ical'];
1643const atcbiOSInvalidOptions = ['ical'];
1644
1645// DEFINING GLOBAL ICONS
1646const atcbIcon = {
1647  trigger:
1648    '<span class="atcb-icon-trigger"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200.016"><path d="M132.829 7.699c0-4.248 4.199-7.699 9.391-7.699s9.391 3.451 9.391 7.699v33.724c0 4.248-4.199 7.699-9.391 7.699s-9.391-3.451-9.391-7.699zm-5.941 123.747c2.979 0 5.404 2.425 5.404 5.404s-2.425 5.404-5.404 5.404l-21.077-.065-.065 21.045c0 2.979-2.425 5.404-5.404 5.404s-5.404-2.425-5.404-5.404l.065-21.061-21.045-.081c-2.979 0-5.404-2.425-5.404-5.404s2.425-5.404 5.404-5.404l21.061.065.065-21.045c0-2.979 2.425-5.404 5.404-5.404s5.404 2.425 5.404 5.404l-.065 21.077 21.061.065zM48.193 7.699C48.193 3.451 52.393 0 57.585 0s9.391 3.451 9.391 7.699v33.724c0 4.248-4.199 7.699-9.391 7.699s-9.391-3.451-9.391-7.699zM10.417 73.763h179.167V34.945c0-1.302-.537-2.49-1.4-3.369-.863-.863-2.051-1.4-3.369-1.4h-17.171c-2.881 0-5.208-2.327-5.208-5.208s2.327-5.208 5.208-5.208h17.171c4.183 0 7.975 1.709 10.726 4.46S200 30.762 200 34.945v44.043 105.843c0 4.183-1.709 7.975-4.46 10.726s-6.543 4.46-10.726 4.46H15.186c-4.183 0-7.975-1.709-10.726-4.46C1.709 192.79 0 188.997 0 184.814V78.988 34.945c0-4.183 1.709-7.975 4.46-10.726s6.543-4.46 10.726-4.46h18.343c2.881 0 5.208 2.327 5.208 5.208s-2.327 5.208-5.208 5.208H15.186c-1.302 0-2.49.537-3.369 1.4-.863.863-1.4 2.051-1.4 3.369zm179.167 10.433H10.417v100.618c0 1.302.537 2.49 1.4 3.369.863.863 2.051 1.4 3.369 1.4h169.629c1.302 0 2.49-.537 3.369-1.4.863-.863 1.4-2.051 1.4-3.369zM82.08 30.176c-2.881 0-5.208-2.327-5.208-5.208s2.327-5.208 5.208-5.208h34.977c2.881 0 5.208 2.327 5.208 5.208s-2.327 5.208-5.208 5.208z"/></svg></span>',
1649  apple:
1650    '<span class="atcb-icon-apple"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 245.657"><path d="M167.084 130.514c-.308-31.099 25.364-46.022 26.511-46.761-14.429-21.107-36.91-24.008-44.921-24.335-19.13-1.931-37.323 11.27-47.042 11.27-9.692 0-24.67-10.98-40.532-10.689-20.849.308-40.07 12.126-50.818 30.799-21.661 37.581-5.54 93.281 15.572 123.754 10.313 14.923 22.612 31.688 38.764 31.089 15.549-.612 21.433-10.073 40.242-10.073s24.086 10.073 40.546 9.751c16.737-.308 27.34-15.214 37.585-30.187 11.855-17.318 16.714-34.064 17.009-34.925-.372-.168-32.635-12.525-32.962-49.68l.045-.013zm-30.917-91.287C144.735 28.832 150.524 14.402 148.942 0c-12.344.503-27.313 8.228-36.176 18.609-7.956 9.216-14.906 23.904-13.047 38.011 13.786 1.075 27.862-7.004 36.434-17.376z"/></svg></span>',
1651  google:
1652    '<span class="atcb-icon-google"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><path d="M152.637 47.363H47.363v105.273h105.273z" fill="#fff"/><path d="M152.637 200L200 152.637h-47.363z" fill="#f72a25"/><path d="M200 47.363h-47.363v105.273H200z" fill="#fbbc04"/><path d="M152.637 152.637H47.363V200h105.273z" fill="#34a853"/><path d="M0 152.637v31.576A15.788 15.788 0 0 0 15.788 200h31.576v-47.363z" fill="#188038"/><path d="M200 47.363V15.788A15.79 15.79 0 0 0 184.212 0h-31.575v47.363z" fill="#1967d2"/><path d="M15.788 0A15.79 15.79 0 0 0 0 15.788v136.849h47.363V47.363h105.274V0z" fill="#4285f4"/><path d="M68.962 129.02c-3.939-2.653-6.657-6.543-8.138-11.67l9.131-3.76c.83 3.158 2.279 5.599 4.346 7.341 2.051 1.742 4.557 2.588 7.471 2.588 2.995 0 5.55-.911 7.699-2.718 2.148-1.823 3.223-4.134 3.223-6.934 0-2.865-1.139-5.208-3.402-7.031s-5.111-2.718-8.496-2.718h-5.273v-9.033h4.736c2.913 0 5.387-.781 7.389-2.376 2.002-1.579 2.995-3.743 2.995-6.494 0-2.441-.895-4.395-2.686-5.859s-4.053-2.197-6.803-2.197c-2.686 0-4.818.716-6.396 2.148s-2.767 3.255-3.451 5.273l-9.033-3.76c1.204-3.402 3.402-6.396 6.624-8.984s7.34-3.89 12.337-3.89c3.695 0 7.031.716 9.977 2.148s5.257 3.418 6.934 5.941c1.676 2.539 2.507 5.387 2.507 8.545 0 3.223-.781 5.941-2.327 8.187-1.546 2.23-3.467 3.955-5.729 5.143v.537a17.39 17.39 0 0 1 7.34 5.729c1.904 2.572 2.865 5.632 2.865 9.212s-.911 6.771-2.718 9.57c-1.823 2.799-4.329 5.013-7.52 6.624s-6.787 2.425-10.775 2.425c-4.622 0-8.887-1.318-12.826-3.988zm56.087-45.312l-10.026 7.243-5.013-7.601 17.985-12.972h6.901v61.198h-9.847z" fill="#1a73e8"/></svg></span>',
1653  ical: '<span class="atcb-icon-ical"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200.016"><path d="M132.829 7.699c0-4.248 4.199-7.699 9.391-7.699s9.391 3.451 9.391 7.699v33.724c0 4.248-4.199 7.699-9.391 7.699s-9.391-3.451-9.391-7.699zm-25.228 161.263c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm-81.803-59.766c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.902 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.902 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.918 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zM25.798 139.079c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.902 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.902 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.918 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zM25.798 168.962c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zm40.902 0c-.553 0-.993-2.327-.993-5.208s.439-5.208.993-5.208h25.7c.553 0 .993 2.327.993 5.208s-.439 5.208-.993 5.208zM48.193 7.699C48.193 3.451 52.393 0 57.585 0s9.391 3.451 9.391 7.699v33.724c0 4.248-4.199 7.699-9.391 7.699s-9.391-3.451-9.391-7.699zM10.417 73.763h179.15V34.945c0-1.302-.537-2.49-1.4-3.369-.863-.863-2.051-1.4-3.369-1.4h-17.155c-2.881 0-5.208-2.327-5.208-5.208s2.327-5.208 5.208-5.208h17.171c4.183 0 7.975 1.709 10.726 4.46S200 30.762 200 34.945v44.043 105.843c0 4.183-1.709 7.975-4.46 10.726s-6.543 4.46-10.726 4.46H15.186c-4.183 0-7.975-1.709-10.726-4.46C1.709 192.79 0 188.997 0 184.814V78.971 34.945c0-4.183 1.709-7.975 4.46-10.726s6.543-4.46 10.726-4.46h18.343c2.881 0 5.208 2.327 5.208 5.208s-2.327 5.208-5.208 5.208H15.186c-1.302 0-2.49.537-3.369 1.4-.863.863-1.4 2.051-1.4 3.3
165369zm179.167 10.433H10.417v100.618c0 1.302.537 2.49 1.4 3.369.863.863 2.051 1.4 3.369 1.4h169.629c1.302 0 2.49-.537 3.369-1.4.863-.863 1.4-2.051 1.4-3.369zM82.08 30.176c-2.881 0-5.208-2.327-5.208-5.208s2.327-5.208 5.208-5.208h34.977c2.881 0 5.208 2.327 5.208 5.208s-2.327 5.208-5.208 5.208z"/></svg></span>',
1654  msteams:
1655    '<span class="atcb-icon-msteams"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 186.047"><path d="M195.349 39.535a20.93 20.93 0 1 1-41.86 0 20.93 20.93 0 1 1 41.86 0zm-55.847 30.233h51.66A8.84 8.84 0 0 1 200 78.605v47.056c0 17.938-14.541 32.479-32.479 32.479h0-.154c-17.938.003-32.481-14.537-32.484-32.474v-.005-51.274a4.62 4.62 0 0 1 4.619-4.619z" fill="#5059c9"/><path d="M149.614 69.767H64.34c-4.823.119-8.637 4.122-8.526 8.944v53.67c-.673 28.941 22.223 52.957 51.163 53.665 28.94-.708 51.836-24.725 51.163-53.665v-53.67c.112-4.823-3.703-8.825-8.526-8.944zm-10.079-39.535a30.233 30.233 0 0 1-60.465 0 30.233 30.233 0 0 1 60.465 0z" fill="#7b83eb"/><path opacity=".1" d="M111.628 69.767v75.209c-.023 3.449-2.113 6.547-5.302 7.86-1.015.43-2.107.651-3.209.651H59.907l-1.628-4.651c-1.628-5.337-2.459-10.885-2.465-16.465V78.698c-.112-4.815 3.697-8.811 8.512-8.93z"/><path opacity=".2" d="M106.977 69.767v79.86a8.241 8.241 0 0 1-.651 3.209c-1.313 3.189-4.412 5.279-7.86 5.302H62.093l-2.186-4.651a46.13 46.13 0 0 1-1.628-4.651 56.647 56.647 0 0 1-2.465-16.465V78.698c-.112-4.815 3.697-8.811 8.512-8.93z"/><path opacity=".2" d="M102.326 69.767v70.558a8.58 8.58 0 0 1-8.512 8.512H58.279a56.647 56.647 0 0 1-2.465-16.465V78.698c-.112-4.815 3.697-8.811 8.512-8.93z"/><path opacity=".1" d="M111.628 45.721v14.651l-2.326.093c-.791 0-1.535-.046-2.326-.093-1.57-.104-3.127-.353-4.651-.744a30.233 30.233 0 0 1-20.93-17.767 25.845 25.845 0 0 1-1.488-4.651h23.209c4.693.018 8.494 3.818 8.512 8.512z"/><use xlink:href="#B" opacity=".2" transform="scale(.08973306)"/><path d="M106.977 50.372v10c-1.57-.104-3.127-.353-4.651-.744a30.233 30.233 0 0 1-20.93-17.767h17.07c4.693.018 8.494 3.818 8.512 8.512zm0 19.395v70.558a8.58 8.58 0 0 1-8.512 8.512H58.279a56.647 56.647 0 0 1-2.465-16.465V78.698c-.112-4.815 3.697-8.811 8.512-8.93z" opacity=".2"/><path opacity=".2" d="M102.326 50.372v9.256a30.233 30.233 0 0 1-20.93-17.767h12.419c4.693.018 8.494 3.818 8.512 8.512z"/><linearGradient id="A" gradientUnits="userSpaceOnUse" x1="17.776" y1="35.199" x2="84.55" y2="150.848"><stop offset="0" stop-color="#5a62c3"/><stop offset=".5" stop-color="#4d55bd"/><stop offset="1" stop-color="#3940ab"/></linearGradient><path fill="url(#A)" d="M8.526 41.86H93.8a8.53 8.53 0 0 1 8.526 8.526v85.274a8.53 8.53 0 0 1-8.526 8.526H8.526A8.53 8.53 0 0 1 0 135.66V
165550.386a8.53 8.53 0 0 1 8.526-8.526z"/><path fill="#fff" d="M73.6 74.316H56.553v46.419h-10.86V74.316H28.726v-9.005H73.6z"/><defs><path id="B" d="M1192.167 561.355v111.442c-17.496-1.161-34.848-3.937-51.833-8.293a336.92 336.92 0 0 1-233.25-198.003h190.228c52.304.198 94.656 42.55 94.855 94.854z"/></defs></svg></span>',
1656  ms365:
1657    '<span class="atcb-icon-ms365"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 239.766"><path d="M200 219.785l-.021-.012V20.591L128.615 0 .322 48.172 0 48.234.016 192.257l43.78-17.134V57.943l84.819-20.279-.012 172.285L.088 192.257l128.515 47.456v.053l71.376-19.753v-.227z"/></svg></span>',
1658  outlookcom:
1659    '<span class="atcb-icon-outlookcom"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 175"><path d="M178.725 0H71.275A8.775 8.775 0 0 0 62.5 8.775v9.975l60.563 18.75L187.5 18.75V8.775A8.775 8.775 0 0 0 178.725 0z" fill="#0364b8"/><path d="M197.813 96.281c.915-2.878 2.187-5.855 2.187-8.781-.002-1.485-.795-2.857-1.491-3.26l-68.434-38.99a9.37 9.37 0 0 0-9.244-.519c-.312.154-.614.325-.906.512l-67.737 38.6-.025.013-.075.044a4.16 4.16 0 0 0-2.088 3.6c.541 2.971 1.272 5.904 2.188 8.781l71.825 52.532z" fill="#0a2767"/><path d="M150 18.75h-43.75L93.619 37.5l12.631 18.75L150 93.75h37.5v-37.5z" fill="#28a8ea"/><path d="M150 18.75h37.5v37.5H150z" fill="#50d9ff"/><path d="M150 93.75l-43.75-37.5H62.5v37.5l43.75 37.5 67.7 11.05z" fill="#0364b8"/><path d="M106.25 56.25v37.5H150v-37.5zM150 93.75v37.5h37.5v-37.5zm-87.5-75h43.75v37.5H62.5z" fill="#0078d4"/><path d="M62.5 93.75h43.75v37.5H62.5z" fill="#064a8c"/><path d="M126.188 145.113l-73.706-53.75 3.094-5.438 68.181 38.825a3.3 3.3 0 0 0 2.625-.075l68.331-38.937 3.1 5.431z" fill="#0a2767" opacity=".5"/><path d="M197.919 91.106l-.088.05-.019.013-67.738 38.588c-2.736 1.764-6.192 1.979-9.125.569l23.588 31.631 51.588 11.257v-.001c2.434-1.761 3.876-4.583 3.875-7.587V87.5c.001 1.488-.793 2.862-2.081 3.606z" fill="#1490df"/><path d="M200 165.625v-4.613l-62.394-35.55-7.531 4.294a9.356 9.356 0 0 1-9.125.569l23.588 31.631 51.588 11.231v.025a9.362 9.362 0 0 0 3.875-7.588z" opacity=".05"/><path d="M199.688 168.019l-68.394-38.956-1.219.688c-2.734 1.766-6.19 1.984-9.125.575l23.588 31.631 51.587 11.256v.001a9.38 9.38 0 0 0 3.562-5.187z" opacity=".1"/><path d="M51.455 90.721c-.733-.467-1.468-1.795-1.455-3.221v78.125c-.007 5.181 4.194 9.382 9.375 9.375h131.25c1.395-.015 2.614-.366 3.813-.813.638-.258 1.252-.652 1.687-.974z" fill="#28a8ea"/><path d="M112.5 141.669V39.581a8.356 8.356 0 0 0-8.331-8.331H62.687v46.6l-10.5 5.987-.031.012-.075.044A4.162 4.162 0 0 0 50 87.5v.031-.031V150h54.169a8.356 8.356 0 0 0 8.331-8.331z" opacity=".1"/><path d="M106.25 147.919V45.831a8.356 8.356 0 0 0-8.331-8.331H62.687v40.35l-10.5 5.987-.031.012-.075.044A4.162 4.162 0 0 0 50 87.5v.031-.031 68.75h47.919a8.356 8.356 0 0 0 8.331-8.331z" opacity=".2"/><path d="M106.25 135.419V45.831a8.356 8.356 0 0 0-8.331-8.331H62.687v40.35l-10.5 5.987-.031.012-.075.044A4.162 4.162 0 0 0 50 87.5v.031-.031 56.25h47.919a8.356 8.356 0 0 0 8.331-8.331z" opacity=".2"/><path d="M100 135.419V45.831a8.356 8.356 0 0 0-8.331-8.331H62.687v40.35l-10.5 5.987-.031.012-.075.044A4.162 4.162 0 0 0 50 87.5v.031-.031 56.25h41.669a8.356 8.356 0 0 0 8.331-8.331z" opacity=".2"/><path d="M8.331 37.5h83.337A8.331 8.331 0 0 1 100 45.831v83.338a8.331 8.331 0 0 1-8.331 8.331H8.331A8.331 8.331 0 0 1 0 129.169V45.831A8.331 8.331 0 0 1 8.331 37.5z" fill="#0078d4"/><path d="M24.169 71.675a26.131 26.131 0 0 1 10.263-11.337 31.031 31.031 0 0 1 16.313-4.087 28.856 28.856 0 0 1 15.081 3.875 25.875 25.875 0 0 1 9.988 10.831 34.981 34.981 0 0 1 3.5 15.938 36.881 36.881 0 0 1-3.606 16.662 26.494 26.494 0 0 1-10.281 11.213 30 30 0 0 1-15.656 3.981 29.556 29.556 0 0 1-15.425-3.919 26.275 26.275 0 0 1-10.112-10.85 34.119 34.119 0 0 1-3.544-15.744 37.844 37.844 0 0 1 3.481-16.563zm10.938 26.613a16.975 16.975 0 0 0 5.769 7.463 15.069 15.069 0 0 0 9.019 2.719 15.831 15.831 0 0 0 9.631-2.806 16.269 16.269 0 0 0 5.606-7.481 28.913 28.913 0 0 0 1.787-10.406 31.644 31.644 0 0 0-1.687-10.538 16.681 16.681 0 0 0-5.413-7.75 14.919 14.919 0 0 0-9.544-2.956 15.581 15.581 0 0 0-9.231 2.744 17.131 17.131 0 0 0-5.9 7.519 29.85 29.85 0 0 0-.044 21.5z" fill="#fff"/></svg></span>',
1660  yahoo:
1661    '<span class="atcb-icon-yahoo"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 177.803"><path d="M0 43.284h38.144l22.211 56.822 22.5-56.822h37.135L64.071 177.803H26.694l15.308-35.645L.001 43.284zm163.235 45.403H121.64L158.558 0 200 .002zm-30.699 8.488c12.762 0 23.108 10.346 23.108 23.106s-10.345 23.106-23.108 23.106a23.11 23.11 0 0 1-23.104-23.106 23.11 23.11 0 0 1 23.104-23.106z"/></svg></span>',
1662  atcb: '<svg version="1.1" viewBox="0 0 150 8.5002" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="matrix(1.3333 0 0 -1.3333 -2427.5 1757.9)"><g transform="matrix(.22189 0 0 -.22189 1822.6 1374.6)" fill="#9a9a9a" style="paint-order:stroke markers fill;shape-inside:url(#rect2441);white-space:pre" aria-label="Add-to-Calendar-PRO.com"><path d="m-1.2773 253.99h12.148l7.9688 27.5h-9.4141l-1.0547-5.2734h-7.1094l-1.1328 5.2734h-9.0234zm8.7109 17.305-2.6172-12.031-2.6953 12.031z" style="paint-order:stroke markers fill"/><path d="m29.66 261.16q2.2656 0 3.9062 0.9375t2.6562 3.1055v-10.078l8.4375-1.25v27.617h-8.4375v-3.7109q-0.9375 2.0117-2.5586 3.0273-1.6211 0.9961-4.043 0.9961-2.0898 0-3.8672-0.83985-1.7773-0.85937-3.0859-2.2852-1.2891-1.4258-2.0312-3.2812-0.74219-1.875-0.74219-3.9062 0-2.1875 0.78125-4.082 0.80078-1.8945 2.1484-3.2812 1.3477-1.3867 3.1055-2.168 1.7773-0.80078 3.7305-0.80078zm6.5625 10.176q-0.03906-0.78125-0.37109-1.4844-0.33203-0.70312-0.87891-1.2109-0.52734-0.52735-1.2305-0.82032-0.70312-0.3125-1.4648-0.3125-0.85938 0-1.6016 0.33203-0.72266 0.3125-1.25 0.85938-0.52734 0.54687-0.83984 1.2695-0.29297 0.72266-0.29297 1.5234 0 0.85937 0.3125 1.582 0.33203 0.72266 0.87891 1.2695 0.54688 0.52734 1.2695 0.83984 0.72266 0.29297 1.5234 0.29297 0.83984 0 1.5625-0.33203 0.72266-0.33203 1.25-0.8789 0.52734-0.54688 0.82031-1.2695 0.3125-0.72265 0.3125-1.5039z" style="paint-order:stroke markers fill"/><path d="m57.551 261.16q2.2656 0 3.9062 0.9375t2.6562 3.1055v-10.078l8.4375-1.25v27.617h-8.4375v-3.7109q-0.9375 2.0117-2.5586 3.0273-1.6211 0.9961-4.043 0.9961-2.0898 0-3.8672-0.83985-1.7773-0.85937-3.0859-2.2852-1.2891-1.4258-2.0312-3.2812-0.74219-1.875-0.74219-3.9062 0-2.1875 0.78125-4.082 0.80078-1.8945 2.1484-3.2812 1.3477-1.3867 3.1055-2.168 1.7773-0.80078 3.7305-0.80078zm6.5625 10.176q-0.03906-0.78125-0.37109-1.4844-0.33203-0.70312-0.87891-1.2109-0.52734-0.52735-1.2305-0.82032-0.70312-0.3125-1.4648-0.3125-0.85938 0-1.6016 0.33203-0.72266 0.3125-1.25 0.85938-0.52734 0.54687-0.83984 1.2695-0.29297 0.72266-0.29297 1.5234 0 0.85937 0.3125 1.582 0.33203 0.72266 0.87891 1.2695 0.54688 0.52734 1.2695 0.83984 0.72266 0.29297 1.5234 0.29297 0.83984 0 1.5625-0.33203 0.72266-0.33203 1.25-0.8789 0.52734-0.54688 0.82031-1.2695 0.3125-0.72265 0.3125-1.5039z" style="paint-order:stroke markers fill"/><path d="m76.496 268.8h10.742v4.7266h-10.742z" style="paint-order:stroke markers fill"/><path d="m104.8 280.44q-2.7148 1.3672-6.0156 1.3672-1.6992 0-3.0273-0.54688-1.3281-0.5664-2.2461-1.6016-0.89844-1.0547-1.3672-2.5195-0.46875-1.4844-0.46875-3.3398l0.03906-7.3828h-2.5391v-4.9609h3.1641l3.7109-7.5781h4.0625v7.5781h4.6094v4.9609h-4.6094v6.7969q0 1.0547 0.68359 1.582t1.8555 0.52734q0.91797 0 1.8359-0.46875z" style="paint-order:stroke markers fill"/><path d="m114.62 271.45q0 1.0938 0.3125 1.9141 0.33204 0.82031 0.85938 1.3672 0.52734 0.54687 1.2109 0.82031 0.68359 0.27344 1.3867 0.27344 0.70312 0 1.3672-0.27344 0.6836-0.27344 1.2109-0.82031 0.54688-0.54688 0.85938-1.3672 0.33203-0.82031 0.33203-1.9141t-0.33203-1.9141q-0.3125-0.82031-0.85938-1.3476-0.52734-0.54688-1.2109-0.82032-0.66406-0.27343-1.3672-0.27343-0.70313 0-1.3867 0.27343-0.6836 0.27344-1.2109 0.82032-0.52734 0.52734-0.85938 1.3476-0.3125 0.82032-0.3125 1.9141zm-7.8125 0q0.0977-2.5195 1.0352-4.4336 0.95703-1.9141 2.5195-3.2226 1.5625-1.3086 3.6133-1.9727 2.0703-0.66406 4.3945-0.66406 2.5391 0 4.6484 0.76172 2.1094 0.76171 3.6328 2.1289 1.5234 1.3477 2.3633 3.2422 0.83985 1.8945 0.83985 4.1602 0 1.8359-0.48828 3.3203-0.46875 1.4844-1.2891 2.6367-0.82031 1.1328-1.9336 1.9726-1.1133 0.83985-2.4023 1.3867-1.2891 0.52735-2.6758 0.78125-1.3672 0.25391-2.7344 0.25391-2.5781 0-4.707-0.74219-2.1094-0.76172-3.6328-2.1289-1.5234-1.3672-2.3633-3.2617-0.82032-1.9141-0.82032-4.2188z" style="paint-order:stroke markers fill"/><path d="m132.73 268.8h10.742v4.7266h-10.742z" style="paint-order:stroke markers fill"/><path d="m162.41 274.89q0.6836 0 1.2695-0.0586 0.58594-0.0781 1.1524-0.21484 0.58593-0.13672 1.1914-0.35156 0.60547-0.21485 1.3477-0.50782l1.0938 6.3477q-3.4766 2.0117-7.5781 2.0117-5.8008 0-9.9414-3.9062-4.3359-4.0625-4.3945-10.488 0-3.125 1.0742-5.7617 1.0742-2.6367 2.9688-4.5312 1.8945-1.9141 4.4726-2.9688 2.5781-1.0742 5.5859-1.0742 4.2773 0 7.7734 1.9922l-1.0547 6.2109q-2.8711-1.0938-5.1953-1.0938-3.2617 0-4.9609 1.8945-1.6797 1.875-1.6797 5.293 0 1.6992 0.44922 3.0469 0.46875 1.3281 1.3477 2.2656 0.8789 0.91797 2.1484 1.4062 1.2891 0.48829 2.9297 0.48829z" style="paint-order:stroke markers fill"/><path d="m186.52 277.78q-0.9375 1.9531-2.5391 2.9883-1.6016 1.0352-4.0234 1.0352-1.9531 0-3.7305-0.78125-1.7774-0.80078-3.125-2.1875-1.3477-1.4062-2.1484-3.3203-0.80078-1.9141-0.80078-4.1602 0-2.2266 0.80078-4.1016 0.82031-1.875 2.168-3.2227 1.3672-1.3672 3.125-2.1094 1.7
1662773-0.76172 3.7109-0.76172 0.9961 0 1.9531 0.27343 0.97656 0.25391 1.8359 0.83985 0.8789 0.5664 1.582 1.4453 0.72265 0.87891 1.1914 2.1094v-4.668h8.3984v20.332h-8.3984zm0-6.4453q-0.0391-0.78125-0.37109-1.4844-0.33203-0.70312-0.87891-1.2109-0.52734-0.52735-1.2305-0.82032-0.70313-0.3125-1.4648-0.3125-0.85937 0-1.6016 0.33203-0.72266 0.3125-1.2695 0.85938-0.52735 0.52734-0.83985 1.25t-0.3125 1.543q0 0.85937 0.33204 1.6016 0.33203 0.72266 0.8789 1.25 0.56641 0.52734 1.2891 0.83984 0.72266 0.29297 1.5234 0.29297 0.82031 0 1.5234-0.3125 0.70312-0.3125 1.2305-0.83984 0.52734-0.52734 0.83984-1.2109 0.3125-0.70312 0.35156-1.4648z" style="paint-order:stroke markers fill"/><path d="m212.02 280.44q-2.7148 1.3672-6.0156 1.3672-1.6992 0-3.0273-0.54688-1.3281-0.5664-2.2461-1.6016-0.89843-1.0547-1.3672-2.5195-0.46875-1.4844-0.46875-3.3398v-18.672l8.4375-1.25v19.336q0 1.0742 0.68359 1.6016 0.68359 0.50781 1.8555 0.50781 0.89844 0 1.8359-0.46875z" style="paint-order:stroke markers fill"/><path d="m225.71 276.2q2.7344 0 5.8984-1.4062l0.97656 5.3711q-3.6328 1.6406-8.1641 1.6406-2.5195 0-4.6094-0.76172-2.0703-0.78125-3.5742-2.1484-1.4844-1.3867-2.3242-3.2812-0.82031-1.8945-0.82031-4.1211 0-2.3438 0.85937-4.2383 0.87891-1.9141 2.3828-3.2617 1.5039-1.3672 3.5156-2.0898 2.0117-0.74218 4.2969-0.74218 2.3047 0 4.043 0.78125 1.7383 0.76172 2.8906 2.1094 1.1719 1.3281 1.7383 3.125 0.58594 1.7774 0.58594 3.8086 0 0.27343-0.0195 0.52734t-0.0586 0.48828l-11.992 1.5625q0.46875 1.4062 1.5625 2.0312 1.0938 0.60547 2.8125 0.60547zm1.4844-7.6758q-0.6836-2.5-3.0469-2.5-0.74218 0-1.3281 0.29297t-0.9961 0.82031q-0.39062 0.50781-0.60546 1.2305-0.21485 0.70312-0.23438 1.5234z" style="paint-order:stroke markers fill"/><path d="m236.3 261.75h8.0469v4.082q1.1719-2.3633 3.0078-3.5156 1.8555-1.1523 4.3359-1.1523 1.9922 0 3.5156 0.74218 1.5234 0.74219 2.5586 2.1094 1.0547 1.3477 1.582 3.2617 0.54687 1.8945 0.54687 4.2188v10h-8.3984v-10.82q0-0.78125-0.15625-1.4453-0.13672-0.66406-0.46875-1.1328-0.3125-0.48828-0.80078-0.74219-0.48828-0.27343-1.1914-0.27343-0.85938 0-1.582 0.42968-0.72265 0.41016-1.2695 1.0156-0.52734 0.60547-0.85937 1.2695-0.33203 0.64453-0.42969 1.1133v10.586h-8.4375z" style="paint-order:stroke markers fill"/><path d="m272.59 261.16q2.2656 0 3.9062 0.9375 1.6406 0.9375 2.6562 3.1055v-10.078l8.4375-1.25v27.617h-8.4375v-3.7109q-0.9375 2.0117-2.5586 3.0273-1.6211 0.9961-4.043 0.9961-2.0898 0-3.8672-0.83985-1.7773-0.85937-3.0859-2.2852-1.2891-1.4258-2.0312-3.2812-0.74219-1.875-0.74219-3.9062 0-2.1875 0.78125-4.082 0.80078-1.8945 2.1484-3.2812 1.3476-1.3867 3.1055-2.168 1.7774-0.80078 3.7305-0.80078zm6.5625 10.176q-0.0391-0.78125-0.37109-1.4844-0.33203-0.70312-0.87891-1.2109-0.52734-0.52735-1.2305-0.82032-0.70312-0.3125-1.4648-0.3125-0.85937 0-1.6016 0.33203-0.72266 0.3125-1.25 0.85938-0.52735 0.54687-0.83985 1.2695-0.29296 0.72266-0.29296 1.5234 0 0.85937 0.3125 1.582 0.33203 0.72266 0.8789 1.2695 0.54688 0.52734 1.2695 0.83984 0.72266 0.29297 1.5234 0.29297 0.83984 0 1.5625-0.33203t1.25-0.8789q0.52734-0.54688 0.82031-1.2695 0.3125-0.72265 0.3125-1.5039z" style="paint-order:stroke markers fill"/><path d="m307.06 277.78q-0.9375 1.9531-2.5391 2.9883-1.6016 1.0352-4.0234 1.0352-1.9531 0-3.7305-0.78125-1.7773-0.80078-3.125-2.1875-1.3477-1.4062-2.1484-3.3203-0.80078-1.9141-0.80078-4.1602 0-2.2266 0.80078-4.1016 0.82032-1.875 2.168-3.2227 1.3672-1.3672 3.125-2.1094 1.7774-0.76172 3.7109-0.76172 0.99609 0 1.9531 0.27343 0.97657 0.25391 1.8359 0.83985 0.87891 0.5664 1.582 1.4453 0.72266 0.87891 1.1914 2.1094v-4.668h8.3984v20.332h-8.3984zm0-6.4453q-0.0391-0.78125-0.37109-1.4844-0.33204-0.70312-0.87891-1.2109-0.52734-0.52735-1.2305-0.82032-0.70312-0.3125-1.4648-0.3125-0.85938 0-1.6016 0.33203-0.72265 0.3125-1.2695 0.85938-0.52734 0.52734-0.83984 1.25t-0.3125 1.543q0 0.85937 0.33203 1.6016 0.33203 0.72266 0.87891 1.25 0.5664 0.52734 1.2891 0.83984 0.72266 0.29297 1.5234 0.29297 0.82031 0 1.5234-0.3125 0.70313-0.3125 1.2305-0.83984 0.52735-0.52734 0.83985-1.2109 0.3125-0.70312 0.35156-1.4648z" style="paint-order:stroke markers fill"/><path d="m319.5 261.75h8.4375v4.082q0.95703-2.4219 2.5391-3.5352 1.6016-1.1328 3.6719-1.1328l1.1719 6.543q-3.8086 0-5.6055 0.82031-1.7773 0.800
166278-1.7773 2.5781v10.391h-8.4375z" style="paint-order:stroke markers fill"/><path d="m337.22 268.8h10.742v4.7266h-10.742z" style="paint-order:stroke markers fill"/><path d="m352.43 253.99h9.9219q3.0859 0 5.4883 0.60547t4.043 1.7773q1.6406 1.1719 2.5 2.8711 0.85937 1.6797 0.85937 3.8477 0 2.1094-0.97656 3.7695-0.97656 1.6602-2.6758 2.832-1.6992 1.1524-3.9844 1.7774-2.2656 0.60547-4.8633 0.60547h-1.6797v9.4141h-8.6328zm10.352 12.539q2.0703 0 3.2422-0.85937 1.1719-0.85938 1.1719-2.5781 0-0.83984-0.35156-1.4648-0.33204-0.625-0.9375-1.0352-0.58594-0.42969-1.3867-0.64453t-1.7383-0.21484h-1.7188v6.7188q0.27343 0.0391 0.70312 0.0586t1.0156 0.0195z" style="paint-order:stroke markers fill"/><path d="m402.92 281.41q-2.5586 0.70313-3.6914 0.70313-7.2461 0-9.1016-6.6016l-1.0938-4.4531h-1.875v10.43h-8.75v-27.5h12.227q2.3828 0 4.4726 0.54687 2.0898 0.52735 3.6328 1.5625 1.5625 1.0156 2.4414 2.5195 0.89844 1.5039 0.89844 3.457 0 1.4453-0.37109 2.5976-0.35157 1.1328-1.0352 2.0508-0.68359 0.89844-1.6797 1.6016-0.97656 0.70312-2.2266 1.25l0.17578 0.95703q0.0586 0.33203 0.11719 0.5664 0.0586 0.23438 0.0977 0.39063l0.21484 0.83984q0.23437 0.74219 0.52734 1.2695 0.3125 0.50782 0.78125 0.83985 0.46875 0.3125 1.1524 0.46875 0.70312 0.13672 1.6992 0.13672 0.15625 0 0.48828-0.0391 0.35156-0.0391 0.89843-0.11718zm-8.8476-18.945q0-0.76172-0.33203-1.25-0.3125-0.50781-0.87891-0.78125-0.56641-0.29297-1.3281-0.39062-0.76172-0.11719-1.6406-0.11719h-2.7344v5.5078h1.5234l1.2891-0.0586q0.76171-0.0586 1.4844-0.19532 0.74219-0.15625 1.3281-0.46875 0.58593-0.33203 0.9375-0.85937 0.35156-0.54688 0.35156-1.3867z" style="paint-order:stroke markers fill"/><path d="m404.76 267.78q0-5.957 3.7891-10.039 4.043-4.3555 10.938-4.3555 3.3594 0 6.0742 1.0938 2.7148 1.0742 4.6289 2.9883t2.9492 4.5703q1.0352 2.6367 1.0352 5.7422 0 1.875-0.42968 3.6719-0.42969 1.7969-1.2891 3.3984-0.83984 1.582-2.0898 2.9297-1.25 1.3281-2.8906 2.3047-1.6211 0.95703-3.6328 1.4844-1.9922 0.54688-4.3555 0.54688-2.4219 0-4.4531-0.54688-2.0117-0.54687-3.6523-1.5234-1.6406-0.97657-2.8906-2.3242-1.2305-1.3477-2.0703-2.9297-0.82031-1.6016-1.25-3.3789-0.41016-1.7773-0.41016-3.6328zm14.727 7.0703q1.543 0 2.6172-0.64453 1.0938-0.66406 1.7774-1.6797 0.70312-1.0352 1.0156-2.3047 0.33203-1.2695 0.33203-2.4805 0-1.582-0.41015-2.8906-0.39063-1.3281-1.1524-2.2852-0.74219-0.97657-1.8164-1.543-1.0547-0.56641-2.3633-0.625-1.5625 0.0391-2.6562 0.70312-1.0938 0.66407-1.7969 1.7188-0.68359 1.0352-1.0156 2.3438-0.3125 1.2891-0.3125 2.5781 0 1.5234 0.39063 2.832 0.41016 1.2891 1.1523 2.2461 0.76172 0.9375 1.8359 1.4844 1.0742 0.54687 2.4023 0.54687z" style="paint-order:stroke markers fill"/><path d="m436.18 279.2q0-0.58594 0.2474-1.0807 0.26042-0.49479 0.67708-0.84636 0.41667-0.35156 0.95053-0.54687 0.54687-0.20834 1.1198-0.20834 0.59896 0 1.1458 0.22136 0.54688 0.20833 0.95053 0.58594 0.41666 0.36458 0.65104 0.84635 0.2474 0.48177 0.2474 1.0286 0 0.61198-0.26042 1.1068-0.2474 0.49479-0.66407 0.85938-0.41666 0.35156-0.96354 0.54687-0.53385 0.19531-1.1068 0.19531-0.625 0-1.1719-0.20833-0.54687-0.20833-0.95052-0.57292-0.40365-0.36458-0.63802-0.85937-0.23438-0.49479-0.23438-1.0677z" style="paint-order:stroke markers fill"/><path d="m454.38 272.56q-1.4062-0.4427-2.6042-0.4427-0.61198 0-1.1068 0.19531-0.48177 0.19531-0.83333 0.54687-0.33855 0.35157-0.53386 0.85938-0.18229 0.49479-0.18229 1.1068t0.18229 1.1198q0.19531 0.49479 0.54688 0.85937 0.35156 0.36459 0.84635 0.5599 0.50782 0.19531 1.1328 0.19531 1.1849 0 2.5-0.44271l0.52083 3.724q-1.0026 0.52083-1.9661 0.6901-0.95053 0.16927-2.0703 0.16927-1.5755 0-2.9036-0.49479t-2.2917-1.3932-1.5104-2.1615q-0.53385-1.276-0.53385-2.8255t0.58594-2.8125q0.58593-1.276 1.5755-2.1745 1.0026-0.91146 2.3177-1.4062 1.3151-0.4948 2.7604-0.4948 0.63802 0 1.1458 0.0521 0.52084 0.0391 0.98959 0.16927 0.46875 0.11718 0.9375 0.32552 0.46875 0.20833 1.0156 0.53385z" style="paint-order:stroke markers fill"/><path d="m461.18 274.8q0 0.72917 0.20833 1.276 0.22136 0.54688 0.57292 0.91146 0.35157 0.36459 0.80729 0.54688 0.45573 0.18229 0.92449 0.18229 0.46875 0 0.91145-0.18229 0.45573-0.18229 0.8073-0.54688 0.36458-0.36458 0.57291-0.91146 0.22136-0.54687 0.22136-1.276t-0.22136-1.276q-0.20833-0.54688-0.57291-0.89844-0.35157-0.36458-0.8073-0.54688-0.4427-0.18229-0.91145-0.18229-0.46876 0-0.92449 0.18229-0.45572 0.1823-0.80729 0.54688-0.35156 0.35156-0.57292 0.89844-0.20833 0.54687-0.20833 1.276zm-5.2083 0q0.0651-1.6797 0.69011-2.9557 0.63802-1.276 1.6797-2.1484 1.0417-0.8724 2.4088-1.3151 1.3802-0.44271 2.9297-0.44271 1.6927 0 3.099 0.50782 1.4062 0.50781 2.4219 1.4193 1.0156 0.89844 1.5755 2.1615 0.55989 1.263 0.55989 2.7734 0 1.224-0.32552 2.2136-0.3125 0.98958-0.85937 1.7578-0.54688 0.75521-1.2891 1.3151-0.74219 0.55989-1.6016 0.92448-0.85938 0.35156-1.7839 0.52083-0.91146 0.16927-1.8229 0.16927-1.7188 0-3.138-0.49479-1.4062-0.50781-2.4219-1.4193t-1.5755-2.1745q-0.54688-1.276-0.54688-2.8125z" style="paint-order:stroke markers fill"/><path d="m488.16 271.26q0.54688-1.6536 1.7578-2.487 1.2109-0.83334 3.138-0.83334 1.0938 0 2.0182 0.50782 0.92448 0.50781 1.6016 1.4193 0.67708 0.91146 1.0547 2.1745 0.3776 1.263 0.3776 2.7865v6.6667h-5.625v-6.6667q0-0.55989-0.13021-1.0286-0.11718-0.48178-0.35156-0.83334-0.23437-0.35156-0.57292-0.54687-0.33854-0.19532-0.76823-0.19532-0.55989 0-0.96354 0.26042-0.40364 0.2474-0.66406 0.63802-0.26042 0.37761-0.39063 0.84636-0.11718 0.45573-0.11718 0.85937v6.6667h-5.5729v-6.6667q0-0.54687-0.13021-1.0156-0.11718-0.48178-0.36458-0.83334-0.23438-0.35156-0.58594-0.54687-0.35156-0.20834-0.79427-0.20834-0.48177 0-0.8724 0.19532-0.3776 0.19531-0.65104 0.54687-0.27344 0.33854-0.42969 0.79427-0.14323 0.45573-0.15625 0.96355v6.7708h-5.625v-13.164h5.625v2.7214q0.74219-1.6536 1.875-2.3828 1.1458-0.72917 2.6823-0.72917 0.74219 0 1.4323 0.19532 0.69011 0.18229 1.2891 0.58593 0.61198 0.40365 1.0938 1.0417 0.49479 0.625 0.82031 1.4974z" style="paint-order:stroke markers fill"/></g></g></svg>',
1663  close:
1664    '<span class="atcb-icon-close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><path d="M2.321 13.529a7.927 7.927 0 0 1 0-11.208 7.927 7.927 0 0 1 11.208 0l86.471 86.471L186.47 2.321a7.927 7.927 0 0 1 11.209 0 7.927 7.927 0 0 1 0 11.208l-86.474 86.469 86.472 86.473a7.927 7.927 0 0 1-11.209 11.208l-86.471-86.471-86.469 86.471a7.927 7.927 0 0 1-11.208-11.208l86.471-86.473z"/></svg></span>',
1665  browser:
1666    '<span class="atcb-icon-browser"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 245.657"><path d="M117.011 163.676q-6.283 6.218-13.623 12.419l6.087-1.009a80.373 80.373 0 0 0 11.524-3.255l.7 1.042 1.628 2.067h0 0a26.693 26.693 0 0 0 3.467 3.25
16665 89.992 89.992 0 0 1-15.837 4.753 95.592 95.592 0 0 1-37.159 0 87.046 87.046 0 0 1-17.253-5.322.423.423 0 0 1-.228-.114 101.077 101.077 0 0 1-15.625-8.415 88.56 88.56 0 0 1-13.672-11.214 85.761 85.761 0 0 1-11.214-13.64 97.317 97.317 0 0 1-8.545-15.658 90.806 90.806 0 0 1-5.436-17.546 95.592 95.592 0 0 1 0-37.159 86.037 86.037 0 0 1 5.339-17.253.537.537 0 0 1 .098-.228 98.212 98.212 0 0 1 8.545-15.707 87.893 87.893 0 0 1 11.214-13.656 84.947 84.947 0 0 1 13.672-11.231A97.17 97.17 0 0 1 56.43 7.259a88.739 88.739 0 0 1 17.448-5.436 95.592 95.592 0 0 1 37.159 0 87.714 87.714 0 0 1 17.253 5.322.456.456 0 0 1 .212.114 100.507 100.507 0 0 1 15.756 8.545 88.56 88.56 0 0 1 13.623 11.198 85.077 85.077 0 0 1 11.214 13.688 94.713 94.713 0 0 1 8.545 15.739 88.739 88.739 0 0 1 5.436 17.481l.195.977-8.822-2.49a76.499 76.499 0 0 0-4.232-12.744 88.251 88.251 0 0 0-4.671-9.375H138.48a106.562 106.562 0 0 1 6.836 13.819l-10.026-2.702a106.985 106.985 0 0 0-6.283-11.117H96.454v5.55l-.993.358a21.941 21.941 0 0 0-7.097 4.362V50.245H55.812q-12.484 19.385-14.03 38.152H83.4q1.628 4.02 3.402 8.138H41.7c.505 12.81 4.883 25.505 12.826 38.152h33.888v-34.49l8.138 17.904v16.553h7.748l3.727 8.138H96.503v28.5a201.567 201.567 0 0 0 17.139-15.707q1.709 4.053 3.369 8.138zm69.761-4.167a7.552 7.552 0 0 1-1.904 1.286h-.13a6.738 6.738 0 0 1-7.097-.977l-18.881-16.016-6.511 15.902a21.045 21.045 0 0 1-1.937 3.662 14.812 14.812 0 0 1-2.458 2.865 7.78 7.78 0 0 1-12.207-1.335 15.105 15.105 0 0 1-1.497-2.653c-11.231-28.467-26.465-56.805-37.859-85.289a5.062 5.062 0 0 1 5.68-6.966c27.296 5.046 62.664 16.586 90.416 23.943 8.627 2.279 10.026 9.88 3.662 15.772a19.874 19.874 0 0 1-3.255 2.474c-4.883 2.767-9.766 5.973-14.649 8.903l18.799 16.114a6.917 6.917 0 0 1 1.628 2.051v.13a6.966 6.966 0 0 1 .635 2.393h0a6.934 6.934 0 0 1-.26 2.507 7.145 7.145 0 0 1-1.172 2.262 153.894 153.894 0 0 1-11.003 12.972zm-4.883-6.25l9.099-10.677c-4.004-3.434-21.159-16.748-22.933-19.955a3.923 3.923 0 0 1 1.351-5.29c5.957-3.255 13.607-7.91 19.255-11.67a13.64 13.64 0 0 0 1.986-1.449 7.194 7.194 0 0 0 1.221-1.416l.26-.488-.505-.293a6.38 6.38 0 0 0-1.237-.423l-84.589-22.494 35.531 79.982a7.813 7.813 0 0 0 .619 1.139l.358.472.456-.326a7.341 7.341 0 0 0 1.188-1.449 12.224 12.224 0 0 0 1.107-2.165c2.653-6.511 5.68-15.414 8.789-21.436l.374-.521a3.906 3.906 0 0 1 5.485-.439l22.201 18.832zM81.594 176.095a171.814 171.814 0 0 1-31.348-33.334h-25.57A83.824 83.824 0 0 0 45.2 162.292a85.956 85.956 0 0 0 14.47 7.813.22.22 0 0 0 .179.114 79.966 79.966 0 0 0 15.69 4.883 106.008 106.008 0 0 0 6.104 1.009zm-62.241-41.44h25.733a82.359 82.359 0 0 1-11.394-38.152H8.138a90.741 90.741 0 0 0 1.628 12.923 78.566 78.566 0 0 0 4.883 15.902 88.153 88.153 0 0 0 4.655 9.375zM8.138 88.397h25.635A88.511 88.511 0 0 1 46.42 50.245H19.353a88.153 88.153 0 0 0-4.704 9.375s0 .114-.114.163A81.236 81.236 0 0 0 9.652 75.49a83.759 83.759 0 0 0-1.628 12.907zm16.488-46.241h27.003A191.606 191.606 0 0 1 82.131 8.708c-2.262.277-4.492.602-6.641 1.058a78.713 78.713 0 0 0-15.87 4.883 89.911 89.911 0 0 0-14.47 7.813 83.824 83.824 0 0 0-20.525 19.532h0zm78.127-33.448a186.577 186.577 0 0 1 30.518 33.448h27.019a79.152 79.152 0 0 0-8.138-9.375 81.073 81.073 0 0 0-12.419-10.205 86.705 86.705 0 0 0-14.405-7.829s-.098 0-.163-.098a79.999 79.999 0 0 0-15.69-4.883c-2.214-.439-4.443-.781-6.657-1.058h0zm-6.25 5.274v28.175h26.84a188.286 188.286 0 0 0-26.84-28.175zm-8.138 157.279v-28.5H60.223a171.993 171.993 0 0 0 28.24 28.5zm0-129.105V13.981a189.295 189.295 0 0 0-26.807 28.175z"/></svg></span>',
1667};
1668
1669// INITIALIZE THE SCRIPT AND FUNCTIONALITY
1670function atcb_init() {
1671  // let's get started
1672  if (!atcbConsoleInit) {
1673    console.log('Add to Calendar Button Script initialized (version ' + atcbVersion + ')');
1674    console.log('See https://github.com/add2cal/add-to-calendar-button for details');
1675    atcbConsoleInit = true;
1676  }
1677  // abort if not in a browser
1678  if (!isBrowser()) {
1679    console.error('no further initialization due to wrong environment (no browser)');
1680    return;
1681  }
1682  // get all placeholders
1683  const atcButtons = document.querySelectorAll('.atcb');
1684  // if there are some, move on
1685  if (atcButtons.length > 0) {
1686    // get the amount of already initialized ones first
1687    const atcButtonsInitialized = document.querySelectorAll('.atcb-initialized');
1688    // generate the buttons one by one
1689    for (let i = 0; i < atcButtons.length; i++) {
1690      // skip already initialized ones
1691      if (atcButtons[parseInt(i)].classList.contains('atcb-initialized')) {
1692        continue;
1693      }
1694      // get JSON from HTML block, but remove real code line breaks before parsing.
1695      // use <br> or \n explicitely in the description to create a line break.
1696      const atcbJsonInput = (function () {
1697        try {
1698          return JSON.parse(
1699            atcb_secure_content(atcButtons[parseInt(i)].innerHTML.replace(/(\r\n|\n|\r)/g, ''), false)
1700          );
1701        } catch (e) {
1702          console.error(
1703            'Add to Calendar Button generation failed: JSON content provided, but badly formatted (in doubt, try some tool like https://jsonformatter.org/ to validate).\r\nError message: ' +
1704              e
1705          );
1706          return '';
1707        }
1708      })();
1709      if (atcbJsonInput === '') {
1710        continue;
1711      }
1712      // rewrite config for backwards compatibility
1713      const atcbJsonInputPatched = atcb_patch_config(atcbJsonInput);
1714      // check, if all required data is available
1715      if (atcb_check_required(atcbJsonInputPatched)) {
1716        // Rewrite dynamic dates, standardize line breaks and transform urls in the description
1717        const atcbConfig = atcb_decorate_data(atcbJsonInputPatched);
1718        // set identifier
1719        if (atcbConfig.identifier == null || atcbConfig.identifier == '') {
1720          atcbConfig.identifier = 'atcb-btn-' + (i + atcButtonsInitialized.length + 1);
1721        }
1722        // validate the config (JSON iput) ...
1723        if (atcb_validate(atcbConfig)) {
1724          // ... and generate the button on success
1725          atcb_generate(atcButtons[parseInt(i)], atcbConfig);
1726        }
1727      }
1728    }
1729  }
1730}
1731
1732// BACKWARDS COMPATIBILITY REWRITE
1733function atcb_patch_config(configData) {
1734  // you can remove this, if you did not use this script before v1.10.0
1735  // adjusts any old schema.org structure
1736  if (configData.event != null) {
1737    Object.keys(configData.event).forEach((key) => {
1738      // move entries one level up, but skip schema types
1739      if (key.charAt(0) !== '@') {
1740        configData[`${key}`] = configData.event[`${key}`];
1741      }
1742    });
1743    delete configData.event;
1744  }
1745  // you can remove this, if you did not use this script before v1.4.0
1746  // adjust deprecated config options
1747  const keyChanges = {
1748    title: 'name',
1749    dateStart: 'startDate',
1750    dateEnd: 'endDate',
1751    timeStart: 'startTime',
1752    timeEnd: 'endTime',
1753  };
1754  Object.keys(keyChanges).forEach((key) => {
1755    if (configData[keyChanges[`${key}`]] == null && configData[`${key}`] != null) {
1756      configData[keyChanges[`${key}`]] = configData[`${key}`];
1757    }
1758  });
1759  return configData;
1760}
1761
1762// CLEAN DATA BEFORE FURTHER VALIDATION (CONSIDERING SPECIAL RULES AND SCHEMES)
1763function atcb_decorate_data(data) {
1764  const now = new Date();
1765  // format RRULE
1766  if (data.recurrence != null && data.recurrence != '') {
1767    // remove spaces and force upper case
1768    data.recurrence = data.recurrence.replace(/\s+/g, '').toUpperCase();
1769    // pre-validate
1770    if (!/^(RRULE:[\w=;,:+-/\\]+|daily|weekly|monthly|yearly)$/im.test(data.recurrence)) {
1771      data.recurrence = '!wrong rrule format!';
1772    } else {
1773      // check if RRULE already
1774      if (/^RRULE:/i.test(data.recurrence)) {
1775        // draw easy rules from RRULE if possible
1776        const rruleParts = data.recurrence.substr(6).split(';');
1777        const rruleObj = new Object();
1778        rruleParts.forEach(function (rule) {
1779          rruleObj[rule.split('=')[0]] = rule.split('=')[1];
1780        });
1781        data.recurrence_until = rruleObj.UNTIL ? rruleObj.UNTIL : '';
1782        data.recurrence_count = rruleObj.COUNT ? rruleObj.COUNT : '';
1783        data.recurrence_byDay = rruleObj.BYDAY ? rruleObj.BYDAY : '';
1784        data.recurrence_byMonth = rruleObj.BYMONTH ? rruleObj.BYMONTH : '';
1785        data.recurrence_byMonthDay = rruleObj.BYMONTHDAY ? rruleObj.BYMONTHDAY : '';
1786        data.recurrence_interval = rruleObj.INTERVAL ? rruleObj.INTERVAL : 1;
1787        data.recurrence_frequency = rruleObj.FREQ ? rruleObj.FREQ : '';
1788      } else {
1789        // set interval if not given
1790        if (data.recurrence_interval == null || data.recurrence_interval == '') {
1791          data.recurrence_interval = 1;
1792        }
1793        // set weekstart if not given
1794        if (
1795          data.recurrence_weekstart == null ||
1796          (data.recurrence_weekstart == '') | (data.recurrence_weekstart.length > 2)
1797        ) {
1798          data.recurrence_weekstart = 'MO';
1799        }
1800        // save frequency before overriding the main recurrence data
1801        data.recurrence_frequency = data.recurrence;
1802        // generate the RRULE from easy rules
1803        data.recurrence =
1804          'RRULE:FREQ=' +
1805          data.recurrence +
1806          ';WKST=' +
1807          data.recurrence_weekstart +
1808          ';INTERVAL=' +
1809          data.recurrence_interval;
1810        // TODO: If "until" is given, translate it into a "count" and remove the "until" (here and in the above block). This would be way more stable!
1811        if (data.recurrence_until != null && data.recurrence_until != '') {
1812          if (data.endTime != null && data.endTime != '') {
1813            data.recurrence =
1814              data.recurrence +
1815              ';UNTIL=' +
1816              data.recurrence_until.replace(/-/g, '').slice(0, 8) +
1817              'T' +
1818              data.endTime.replace(':', '') +
1819              '00';
1820          } else {
1821            data.recurrence =
1822              data.recurrence + ';UNTIL=' + data.recurrence_until.replace(/-/g, '').slice(0, 8);
1823          }
1824        }
1825        if (data.recurrence_count != null && data.recurrence_count != '') {
1826          data.recurrence = data.recurrence + ';COUNT=' + data.recurrence_count;
1827        }
1828        if (data.recurrence_byDay != null && data.recurrence_byDay != '') {
1829          data.recurrence = data.recurrence + ';BYDAY=' + data.recurrence_byDay;
1830        }
1831        if (data.recurrence_byMonth != null && data.recurrence_byMonth != '') {
1832          data.recurrence = data.recurrence + ';BYMONTH=' + data.recurrence_byMonth;
1833        }
1834        if (data.recurrence_byMonthDay != null && data.recurrence_byMonthDay != '') {
1835          data.recurrence = data.recurrence + ';BYMONTHDAY=' + data.recurrence_byMonthDay;
1836        }
1837      }
1838    }
1839  }
1840  // cleanup options, standardizing names and splitting off custom labels
1841  data.optionLabels = [];
1842  for (let i = 0; i < data.options.length; i++) {
1843    let cleanOption = data.options[`${i}`].split('|');
1844    data.options[`${i}`] = cleanOption[0].toLowerCase().replace('microsoft', 'ms').replace('.', '');
1845    if (cleanOption[1] != null) {
1846      data.optionLabels[`${i}`] = cleanOption[1];
1847    } else {
1848      data.optionLabels[`${i}`] = '';
1849    }
1850  }
1851  // remove unsupported options
1852  // for iOS, we remove iCal (further down) and force the Apple option (if it is not there, but iCal is)
1853  if (isiOS() && data.options.includes('ical') && !data.options.includes('apple')) {
1854    data.options.push('apple');
1855  }
1856  // next, iterrate over the options
1857  for (let i = 0; i < data.options.length; i++) {
1858    // remove iCal for iOS as mentioned above (and potentially others)
1859    if (isiOS() && atcbiOSInvalidOptions.includes(data.options[`${i}`])) {
1860      data.options.splice(i, 1);
1861      if (data.optionLabels[`${i}`] != null) {
1862        delete data.optionLabels[`${i}`];
1863      }
1864      continue;
1865    }
1866    // in the recurrence case, we strip out all options, which do not support it
1867    if (data.recurrence != null && data.recurrence != '') {
1868      if (!atcbValidRecurrOptions.includes(data.options[`${i}`])) {
1869        data.options.splice(i, 1);
1870        if (data.optionLabels[`${i}`] != null) {
1871          delete data.optionLabels[`${i}`];
1872        }
1873        continue;
1874      }
1875      // also skip Apple and iCal for rrules with "until"
1876      if (
1877        data.recurrence_until != null &&
1878        data.recurrence_until != '' &&
1879        (data.options[`${i}`] == 'apple' || data.options[`${i}`] == 'ical')
1880      ) {
1881        data.options.splice(i, 1);
1882        if (data.optionLabels[`${i}`] != null) {
1883          delete data.optionLabels[`${i}`];
1884        }
1885      }
1886    }
1887  }
1888  // cleanup different date-time formats
1889  data = atcb_date_cleanup(data);
1890  // calculate the real date values in case that there are some special rules included (e.g. adding days dynamically)
1891  data.startDate = atcb_date_calculation(data.startDate);
1892  data.endDate = atcb_date_calculation(data.endDate);
1893  // set default listStyle
1894  if (data.listStyle == null || data.listStyle == '') {
1895    data.listStyle = 'dropdown';
1896  }
1897  // force click trigger on modal style
1898  if (data.listStyle === 'modal') {
1899    data.trigger = 'click';
1900  }
1901  // set button style and force click on styles, where the dropdown is not attached to the button
1902  if (data.buttonStyle != null && data.buttonStyle != '' && data.buttonStyle != 'default') {
1903    if (data.buttonStyle == 'round' || data.buttonStyle == 'text') {
1904      data.trigger = 'click';
1905    }
1906  } else {
1907    data.buttonStyle = '';
1908  }
1909  // set size
1910  if (data.size != null && data.size != '' && data.size >= 0 && data.size < 11) {
1911    data.size = 10 + parseInt(data.size);
1912  } else {
1913    data.size = 16;
1914  }
1915  // set sequence
1916  if (data.sequence == null || data.sequence == '') {
1917    data.sequence = 0;
1918  }
1919  // set UID
1920  if (data.uid == null || data.uid == '') {
1921    data.uid = atcb_generate_uuid();
1922  }
1923  // set created date
1924  if (data.created == null || data.created == '') {
1925    data.created = atcb_format_datetime(now, 'clean', true);
1926  }
1927  // set updated date
1928  if (data.updated == null || data.updated == '') {
1929    data.updated = atcb_format_datetime(now, 'clean', true);
1930  }
1931  // set status
1932  if (data.status == null || data.status == '') {
1933    data.status = 'CONFIRMED';
1934  } else {
1935    data.status = data.status.toUpperCase();
1936  }
1937  // determine dark mode
1938  if (data.lightMode == null || data.lightMode == '') {
1939    data.lightMode = 'light';
1940  } else if (data.lightMode != null && data.lightMode != '') {
1941    const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
1942    switch (data.lightMode) {
1943      case 'system':
1944        if (prefersDarkScheme.matches) {
1945          data.lightMode = 'dark';
1946        } else {
1947          data.lightMode = 'light';
1948        }
1949        break;
1950      case 'bodyScheme':
1951      case 'dark':
1952        break;
1953      default:
1954        data.lightMode = 'light';
1955        break;
1956    }
1957  }
1958  // set language if not set
1959  if (data.language == null || data.language == '') {
1960    data.language = 'en';
1961  }
1962  // set right-to-left for relevant languages
1963  if (data.language == 'ar') {
1964    data.rtl = true;
1965  } else {
1966    data.rtl = false;
1967  }
1968  // decorate description
1969  if (data.description != null && data.description != '') {
1970    // store a clean description copy without the URL magic for iCal
1971    data.descriptionHtmlFree = atcb_rewrite_html_elements(data.description, true);
1972    // ...and transform pseudo elements for the regular one
1973    data.description = atcb_rewrite_html_elements(data.description);
1974  }
1975  return data;
1976}
1977
1978// CHECK FOR REQUIRED FIELDS
1979function atcb_check_required(data) {
1980  // check for at least 1 option
1981  if (data.options == null || data.options.length < 1) {
1982    console.error('Add to Calendar Button generation failed: no valid options set');
1983    return false;
1984  }
1985  // check for min required data (without "options")
1986  const requiredField = ['name', 'startDate'];
1987  return requiredField.every(function (field) {
1988    if (data[`${field}`] == null || data[`${field}`] == '') {
1989      console.error('Add to Calendar Button generation failed: required setting missing [' + field + ']');
1990      return false;
1991    }
1992    return true;
1993  });
1994}
1995
1996// CALCULATE AND CLEAN UP THE ACTUAL DATES
1997function atcb_date_cleanup(data) {
1998  // set endDate = startDate, if not provided
1999  if ((data.endDate == null || data.endDate == '') && data.startDate != null) {
2000    data.endDate = data.startDate;
2001  }
2002  // parse date+time format (unofficial alternative to the main implementation)
2003  const endpoints = ['start', 'end'];
2004  endpoints.forEach(function (point) {
2005    if (data[point + 'Date'] != null) {
2006      // remove any milliseconds information
2007      data[point + 'Date'] = data[point + 'Date'].replace(/\.\d{3}/, '').replace('Z', '');
2008      // identify a possible time information within the date string
2009      const tmpSplitStartDate = data[point + 'Date'].split('T');
2010      if (tmpSplitStartDate[1] != null) {
2011        data[point + 'Date'] = tmpSplitStartDate[0];
2012        data[point + 'Time'] = tmpSplitStartDate[1];
2013      }
2014    }
2015    // remove any seconds from time information
2016    if (data[point + 'Time'] != null && data[point + 'Time'].length === 8) {
2017      const timeStr = data[point + 'Time'];
2018      data[point + 'Time'] = timeStr.substring(0, timeStr.length - 3);
2019    }
2020    // update time zone, if special case set to go for the user's browser
2021    if (data.timeZone == 'currentBrowser') {
2022      data.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
2023    }
2024  });
2025  return data;
2026}
2027
2028function atcb_date_calculation(dateString) {
2029  // replace "today" with the current date first
2030  const today = new Date();
2031  const todayString = today.getUTCMonth() + 1 + '-' + today.getUTCDate() + '-' + today.getUTCFullYear();
2032  dateString = dateString.replace(/today/gi, todayString);
2033  // check for any dynamic additions and adjust
2034  const dateStringParts = dateString.split('+');
2035  const dateParts = dateStringParts[0].split('-');
2036  const newDate = (function () {
2037    // backwards compatibility for version <1.5.0
2038    if (dateParts[0].length < 4) {
2039      return new Date(dateParts[2], dateParts[0] - 1, dateParts[1]);
2040    }
2041    return new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
2042  })();
2043  if (dateStringParts[1] != null && dateStringParts[1] > 0) {
2044    newDate.setDate(newDate.getDate() + parseInt(dateStringParts[1]));
2045  }
2046  return (
2047    newDate.getFullYear() +
2048    '-' +
2049    ((newDate.getMonth() + 1 < 10 ? '0' : '') + (newDate.getMonth() + 1)) +
2050    '-' +
2051    (newDate.getDate() < 10 ? '0' : '') +
2052    newDate.getDate()
2053  );
2054}
2055
2056// VALIDATE THE INPUT DATA
2057function atcb_validate(data) {
2058  // validate prefix
2059  if (data.identifier != null && data.identifier != '') {
2060    if (!/^[\w-]+$/.test(data.identifier)) {
2061      data.identifier = '';
2062      console.error('Add to Calendar Button generation: identifier invalid - using auto numbers instead');
2063    }
2064  }
2065  const msgPrefix = 'Add to Calendar Button generation (' + data.identifier + ')';
2066  // validate explicit ics file
2067  if (data.icsFile != null && data.icsFile != '') {
2068    if (
2069      !atcb_secure_url(data.icsFile, false) ||
2070      !/\.ics$/.test(data.icsFile) ||
2071      !data.icsFile.startsWith('https://')
2072    ) {
2073      console.error(msgPrefix + ' failed: explicit ics file path not val
2073id');
2074      return false;
2075    }
2076  }
2077  // validate organizer
2078  if (data.organizer != null && data.organizer != '') {
2079    const organizerParts = data.organizer.split('|');
2080    if (
2081      organizerParts.length != 2 ||
2082      organizerParts[0].length > 50 ||
2083      organizerParts[1].length > 80 ||
2084      !atcb_validEmail(organizerParts[1])
2085    ) {
2086      console.error(
2087        msgPrefix + ' failed: organizer needs to match the schema "NAME|EMAIL" with a valid email address'
2088      );
2089      return false;
2090    }
2091  }
2092  // validate UID (must have less then 255 characters and only allowes for ; see )
2093  if (!/^(\w|-){1,254}$/.test(data.uid)) {
2094    console.error(
2095      msgPrefix +
2096        ': UID not valid. May only contain alpha, digits, and dashes; and be less than 255 characters'
2097    );
2098    return false;
2099  }
2100  // validate UID for the recommended form, which is not forced, but show throw a warning
2101  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(data.uid)) {
2102    console.warn(
2103      msgPrefix +
2104        ': UID is strictly recommended to be a hex-encoded random Universally Unique Identifier (UUID)!'
2105    );
2106    data.sequence = 0;
2107  }
2108  // validate sequence number if given and set it 0 if not
2109  if (!/^\d+$/.test(data.sequence)) {
2110    console.log(msgPrefix + ': sequence needs to be a number. Used the default 0 instead');
2111    data.sequence = 0;
2112  }
2113  // validate created and updated input
2114  if (!/^\d{8}T\d{6}Z$/.test(data.created)) {
2115    console.error(
2116      msgPrefix +
2117        ': created date format not valid. Needs to be a full ISO-8601 UTC date and time string, formatted YYYYMMDDTHHMMSSZ'
2118    );
2119    return false;
2120  }
2121  if (!/^\d{8}T\d{6}Z$/.test(data.updated)) {
2122    console.error(
2123      msgPrefix +
2124        ': updated date format not valid. Needs to be a full ISO-8601 UTC date and time string, formatted YYYYMMDDTHHMMSSZ'
2125    );
2126    return false;
2127  }
2128  // validate status
2129  if (data.status != 'TENTATIVE' && data.status != 'CONFIRMED' && data.status != 'CANCELLED') {
2130    console.error(msgPrefix + ': event status needs to be TENTATIVE, CONFIRMED, or CANCELLED');
2131    return false;
2132  }
2133  // validate options
2134  if (
2135    !data.options.every(function (option) {
2136      if (!atcbOptions.includes(option)) {
2137        console.error(msgPrefix + ' failed: invalid option [' + option + ']');
2138        return false;
2139      }
2140      return true;
2141    })
2142  ) {
2143    return false;
2144  }
2145  // validate time zone
2146  if (data.timeZone != null && data.timeZone != '') {
2147    const validTimeZones = tzlib_get_timezones();
2148    if (!validTimeZones.includes(data.timeZone)) {
2149      console.error(msgPrefix + ' failed: invalid time zone given');
2150      return false;
2151    }
2152  }
2153  // validate date
2154  const dates = ['startDate', 'endDate'];
2155  const newDate = dates;
2156  if (
2157    !dates.every(function (date) {
2158      if (data[`${date}`].length !== 10) {
2159        console.error(msgPrefix + ' failed: date misspelled [-> YYYY-MM-DD]');
2160        return false;
2161      }
2162      const dateParts = data[`${date}`].split('-');
2163      if (dateParts.length < 3 || dateParts.length > 3) {
2164        console.error(msgPrefix + ' failed: date misspelled [' + date + ': ' + data[`${date}`] + ']');
2165        return false;
2166      }
2167      newDate[`${date}`] = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
2168      return true;
2169    })
2170  ) {
2171    return false;
2172  }
2173  // validate time
2174  const times = ['startTime', 'endTime'];
2175  if (
2176    !times.every(function (time) {
2177      if (data[`${time}`] != null) {
2178        if (data[`${time}`].length !== 5) {
2179          console.error(msgPrefix + ' failed: time misspelled [-> HH:MM]');
2180          return false;
2181        }
2182        const timeParts = data[`${time}`].split(':');
2183        // validate the time parts
2184        if (timeParts.length < 2 || timeParts.length > 2) {
2185          console.error(msgPrefix + ' failed: time misspelled [' + time + ': ' + data[`${time}`] + ']');
2186          return false;
2187        }
2188        if (timeParts[0] > 23) {
2189          console.error(
2190            msgPrefix +
2191              ' failed: time misspelled - hours number too high [' +
2192              time +
2193              ': ' +
2194              timeParts[0] +
2195              ']'
2196          );
2197          return false;
2198        }
2199        if (timeParts[1] > 59) {
2200          console.error(
2201            msgPrefix +
2202              ' failed: time misspelled - minutes number too high [' +
2203              time +
2204              ': ' +
2205              timeParts[1] +
2206              ']'
2207          );
2208          return false;
2209        }
2210        // update the date with the time for further validation steps
2211        if (time == 'startTime') {
2212          newDate.startDate = new Date(
2213            newDate.startDate.getTime() + timeParts[0] * 3600000 + timeParts[1] * 60000
2214          );
2215        }
2216        if (time == 'endTime') {
2217          newDate.endDate = new Date(
2218            newDate.endDate.getTime() + timeParts[0] * 3600000 + timeParts[1] * 60000
2219          );
2220        }
2221      }
2222      return true;
2223    })
2224  ) {
2225    return false;
2226  }
2227  if ((data.startTime != null && data.endTime == null) || (data.startTime == null && data.endTime != null)) {
2228    console.error(msgPrefix + ' failed: if you set a starting time, you also need to define an end time');
2229    return false;
2230  }
2231  // validate whether end is not before start
2232  if (newDate.endDate < newDate.startDate) {
2233    console.error(msgPrefix + ' failed: end date before start date');
2234    return false;
2235  }
2236  // validate any given RRULE
2237  if (data.recurrence != null && data.recurrence != '' && !/^RRULE:[\w=;,:+-/\\]+$/i.test(data.recurrence)) {
2238    console.error(msgPrefix + ' failed: RRULE data misspelled');
2239    return false;
2240  }
2241  // also validate the more easy recurrence settings, since any error there would be also hidden in the RRULE
2242  if (
2243    data.recurrence_interval != null &&
2244    data.recurrence_interval != '' &&
2245    !/^\d+$/.test(data.recurrence_interval)
2246  ) {
2247    console.error(msgPrefix + ' failed: recurrence data (interval) misspelled');
2248    return false;
2249  }
2250  if (
2251    data.recurrence_until != null &&
2252    data.recurrence_until != '' &&
2253    !/^(\d|-|:)+$/i.test(data.recurrence_until)
2254  ) {
2255    console.error(msgPrefix + ' failed: recurrence data (until) misspelled');
2256    return false;
2257  }
2258  if (data.recurrence_count != null && data.recurrence_count != '' && !/^\d+$/.test(data.recurrence_count)) {
2259    console.error(msgPrefix + ' failed: recurrence data (interval) misspelled');
2260    return false;
2261  }
2262  if (
2263    data.recurrence_byMonth != null &&
2264    data.recurrence_byMonth != '' &&
2265    !/^(\d|,)+$/.test(data.recurrence_byMonth)
2266  ) {
2267    console.error(msgPrefix + ' failed: recurrence data (byMonth) misspelled');
2268    return false;
2269  }
2270  if (
2271    data.recurrence_byMonthDay != null &&
2272    data.recurrence_byMonthDay != '' &&
2273    !/^(\d|,)+$/.test(data.recurrence_byMonthDay)
2274  ) {
2275    console.error(msgPrefix + ' failed: recurrence data (byMonthDay) misspelled');
2276    return false;
2277  }
2278  if (
2279    data.recurrence_byDay != null &&
2280    data.recurrence_byDay != '' &&
2281    !/^(\d|-|MO|TU|WE|TH|FR|SA|SU|,)+$/im.test(data.recurrence_byDay)
2282  ) {
2283    console.error(msgPrefix + ' failed: recurrence data (byDay) misspelled');
2284    return false;
2285  }
2286  if (
2287    data.recurrence_weekstart != null &&
2288    data.recurrence_weekstart != '' &&
2289    !/^(MO|TU|WE|TH|FR|SA|SU)$/im.test(data.recurrence_weekstart)
2290  ) {
2291    console.error(msgPrefix + ' failed: recurrence data (weekstart) misspelled');
2292    return false;
2293  }
2294  // on passing the validation, return true
2295  return true;
2296}
2297
2298// GENERATE THE ACTUAL BUTTON
2299// helper function to generate the labels for the button and list options
2300function atcb_generate_label(data, parent, type, icon = false, text = '', oneOption = false) {
2301  let defaultTriggerText = atcb_translate_hook('Add to Calendar', data.language, data);
2302  // if there is only 1 option, we use the trigger text on the option label. Therefore, forcing it here
2303  if (oneOption && text == '') {
2304    text = defaultTriggerText;
2305  }
2306  switch (type) {
2307    case 'trigger':
2308    default:
2309      if (data.trigger === 'click') {
2310        parent.addEventListener('click', (event) => {
2311          event.preventDefault();
2312          atcb_toggle('auto', data, parent, false, true);
2313        });
2314      } else {
2315        parent.addEventListener('touchend', (event) => {
2316          event.preventDefault();
2317          atcb_toggle('auto', data, parent, false, true);
2318        });
2319        parent.addEventListener(
2320          'mouseenter',
2321          atcb_debounce_leading((event) => {
2322            event.preventDefault();
2323            atcb_toggle('open', data, parent, false, true);
2324          })
2325        );
2326      }
2327      parent.id = data.identifier;
2328      text = text || defaultTriggerText;
2329      break;
2330    case 'apple':
2331      parent.addEventListener(
2332        'click',
2333        atcb_debounce(() => {
2334          oneOption ? parent.blur() : atcb_toggle('close');
2335          atcb_generate_ical(data);
2336        })
2337      );
2338      parent.id = data.identifier + '-apple';
2339      text = text || 'Apple';
2340      break;
2341    case 'google':
2342      parent.addEventListener(
2343        'click',
2344        atcb_debounce(() => {
2345          oneOption ? parent.blur() : atcb_toggle('close');
2346          atcb_generate_google(data);
2347        })
2348      );
2349      parent.id = data.identifier + '-google';
2350      text = text || 'Google';
2351      break;
2352    case 'ical':
2353      parent.addEventListener(
2354        'click',
2355        atcb_debounce(() => {
2356          oneOption ? parent.blur() : atcb_toggle('close');
2357          atcb_generate_ical(data);
2358        })
2359      );
2360      parent.id = data.identifier + '-ical';
2361      text = text || atcb_translate_hook('iCal File', data.language, data);
2362      break;
2363    case 'msteams':
2364      parent.addEventListener(
2365        'click',
2366        atcb_debounce(() => {
2367          oneOption ? parent.blur() : atcb_toggle('close');
2368          atcb_generate_teams(data);
2369        })
2370      );
2371      parent.id = data.identifier + '-msteams';
2372      text = text || 'Microsoft Teams';
2373      break;
2374    case 'ms365':
2375      parent.addEventListener(
2376        'click',
2377        atcb_debounce(() => {
2378          oneOption ? parent.blur() : atcb_toggle('close');
2379          atcb_generate_microsoft(data, '365');
2380        })
2381      );
2382      parent.id = data.identifier + '-ms365';
2383      text = text || 'Microsoft 365';
2384      break;
2385    case 'outlookcom':
2386      parent.addEventListener(
2387        'click',
2388        atcb_debounce(() => {
2389          oneOption ? parent.blur() : atcb_toggle('close');
2390          atcb_generate_microsoft(data, 'outlook');
2391        })
2392      );
2393      parent.id = data.identifier + '-outlook';
2394      text = text || 'Outlook.com';
2395      break;
2396    case 'yahoo':
2397      parent.addEventListener(
2398        'click',
2399        atcb_debounce(() => {
2400          oneOption ? parent.blur() : atcb_toggle('close');
2401          atcb_generate_yahoo(data);
2402        })
2403      );
2404      parent.id = data.identifier + '-yahoo';
2405      text = text || 'Yahoo';
2406      break;
2407    case 'close':
2408      parent.addEventListener(
2409        'click',
2410        atcb_debounce(() => {
2411          oneOption ? parent.blur() : atcb_toggle('close');
2412        })
2413      );
2414      parent.addEventListener(
2415        'focus',
2416        atcb_debounce(() => atcb_close(false))
2417      );
2418      parent.id = data.identifier + '-close';
2419      text = atcb_translate_hook('Close', data.language, data);
2420      break;
2421  }
2422  // override the id for the oneOption button, since the button always needs to have the button id
2423  if (oneOption) {
2424    parent.id = data.identifier;
2425  }
2426  // support keyboard input
2427  if (!oneOption && type === 'trigger') {
2428    parent.addEventListener(
2429      'keyup',
2430      atcb_debounce_leading((event) => {
2431        if (event.key == 'Enter') {
2432          event.preventDefault();
2433          atcb_toggle('auto', data, parent, true, true);
2434        }
2435      })
2436    );
2437  } else {
2438    parent.addEventListener(
2439      'keyup',
2440      atcb_debounce_leading((event) => {
2441        if (event.key == 'Enter') {
2442          event.preventDefault();
2443          parent.click();
2444        }
2445      })
2446    );
2447  }
2448  // add icon and text label
2449  if (icon) {
2450    const iconEl = document.createElement('span');
2451    iconEl.classList.add('atcb-icon');
2452    iconEl.innerHTML = atcbIcon[`${type}`];
2453    parent.appendChild(iconEl);
2454  }
2455  const textEl = document.createElement('span');
2456  textEl.classList.add('atcb-text');
2457  textEl.textContent = text;
2458  parent.appendChild(textEl);
2459}
2460
2461// generate the triggering button
2462function atcb_generate(button, data) {
2463  // clean the placeholder
2464  button.textContent = '';
2465  // create schema.org data, if possible (https://schema.org/Event)
2466  // see https://developers.google.com/search/docs/advanced/structured-data/event for more details on how this affects Google search results
2467  if (data.name && data.location && data.startDate) {
2468    const schemaEl = document.createElement('script');
2469    schemaEl.type = 'application/ld+json';
2470    const schemaContent = [];
2471    schemaContent.push('{\r\n"event": {\r\n"@context":"https://schema.org",\r\n"@type":"Event"');
2472    if (data.status == 'CANCELLED') {
2473      schemaContent.push('"eventStatus":"https://schema.org/EventCancelled"');
2474    }
2475    schemaContent.push('"name":"' + data.name + '"');
2476    if (data.descriptionHtmlFree) {
2477      schemaContent.push('"description":"' + data.descriptionHtmlFree + '"');
2478    }
2479    const formattedDate = atcb_generate_time(data, 'delimiters', 'general', true);
2480    schemaContent.push('"startDate":"' + formattedDate.start + '"');
2481    schemaContent.push('"duration":"' + formattedDate.duration + '"');
2482    if (data.recurrence != null && data.recurrence != '') {
2483      schemaContent.push('"eventSchedule": { "@type": "Schedule"');
2484      if (data.timeZone != null && data.timeZone != '') {
2485        schemaContent.push('"scheduleTimezone":"' + data.timeZone + '"');
2486      }
2487      const repeatFrequency = 'P' + data.recurrence_interval + data.recurrence_frequency.substr(0, 1);
2488      schemaContent.push('"repeatFrequency":"' + repeatFrequency + '"');
2489      if (data.recurrence_byDay != null && data.recurrence_byDay != '') {
2490        const byDayString = (function () {
2491          if (/\d/.test(data.recurrence_byDay)) {
2492            return '"' + data.recurrence_byDay + '"';
2493          } else {
2494            const byDays = data.recurrence_byDay.split(',');
2495            const helperMap = {
2496              MO: 'https://schema.org/Monday',
2497              TU: 'https://schema.org/Tuesday',
2498              WE: 'https://schema.org/Wednesday',
2499              TH: 'https://schema.org/Thursday',
2500              FR: 'https://schema.org/Friday',
2501              SA: 'https://schema.org/Saturday',
2502              SU: 'https://schema.org/Sunday',
2503            };
2504            const output = [];
2505            for (let i = 0; i < byDays.length; i++) {
2506              output.push('"' + helperMap[byDays[`${i}`]] + '"');
2507            }
2508            return '[' + output.join(',') + ']';
2509          }
2510        })();
2511        schemaContent.push('"byDay":' + byDayString);
2512      }
2513      if (data.recurrence_byMonth != null && data.recurrence_byMonth != '') {
2514        const byMonthString = data.recurrence_byMonth.includes(',')
2515          ? '[' + data.recurrence_byMonth + ']'
2516          : data.recurrence_byMonth;
2517        schemaContent.push('"byMonth":"' + byMonthString + '"');
2518      }
2519      if (data.recurrence_byMonthDay != null && data.recurrence_byMonthDay != '') {
2520        const byMonthDayString = data.recurrence_byMonthDay.includes(',')
2521          ? '[' + data.recurrence_byMonthDay + ']'
2522          : data.recurrence_byMonthDay;
2523        schemaContent.push('"byMonthDay":"' + byMonthDayString + '"');
2524      }
2525      if (data.recurrence_count != null && data.recurrence_count != '') {
2526        schemaContent.push('"repeatCount":"' + data.recurrence_count + '"');
2527      }
2528      if (data.recurrence_until != null && data.recurrence_until != '') {
2529        schemaContent.push('"endDate":"' + data.recurrence_until + '"');
2530      }
2531      if (data.startTime != null && data.startTime != '' && data.endTime != null && data.endTime != '') {
2532        schemaContent.push('"startTime":"' + data.startTime + ':00"');
2533        schemaContent.push('"endTime":"' + data.endTime + ':00"');
2534        schemaContent.push('"duration":"' + formattedDate.duration + '"');
2535      }
2536      schemaContent.push('"startDate":"' + data.startDate + '" }');
2537    } else {
2538      schemaContent.push('"endDate":"' + formattedDate.end + '"');
2539    }
2540    schemaContent.push(
2541      data.location.startsWith('http')
2542        ? '"eventAttendanceMode":"https://schema.org/OnlineEventAttendanceMode",\r\n"location": {\r\n"@type":"VirtualLocation",\r\n"url":"' +
2543            data.location +
2544            '"\r\n}'
2545        : '"location":"' + data.location + '"'
2546    );
2547    if (data.organizer != null && data.organizer != '') {
2548      const organizerParts = data.organizer.split('|');
2549      schemaContent.push(
2550        '"organizer":{\r\n"@type":"Person",\r\n"name":"' +
2551          organizerParts[0] +
2552          '",\r\n"email":"' +
2553          organizerParts[1] +
2554          '"\r\n}'
2555      );
2556    }
2557    const imageData = [];
2558    if (data.images != null) {
2559      if (Array.isArray(data.images)) {
2560        for (let i = 0; i < data.images.length; i++) {
2561          if (atcb_secure_url(data.images[`${i}`]) && data.images[`${i}`].startsWith('http')) {
2562            imageData.push('"' + data.images[`${i}`] + '"');
2563          }
2564        }
2565      }
2566    } else {
2567      imageData.push('"https://add-to-calendar-button.com/demo_assets/img/1x1.png"');
2568      imageData.push('"https://add-to-calendar-button.com/demo_assets/img/4x3.png"');
2569      imageData.push('"https://add-to-calendar-button.com/demo_assets/img/16x9.png"');
2570    }
2571    schemaContent.push('"image":[\r\n' + imageData.join(',\r\n') + ']');
2572    schemaEl.textContent = schemaContent.join(',\r\n') + '\r\n}\r\n}';
2573    button.appendChild(schemaEl);
2574  }
2575  // generate the wrapper div
2576  const buttonTriggerWrapper = document.createElement('div');
2577  buttonTriggerWrapper.classList.add('atcb-button-wrapper');
2578  buttonTriggerWrapper.classList.add('atcb-' + data.lightMode);
2579  if (data.rtl) {
2580    buttonTriggerWrapper.classList.add('atcb-rtl');
2581  }
2582  buttonTriggerWrapper.style.fontSize = data.size + 'px';
2583  button.appendChild(buttonTriggerWrapper);
2584  // generate the button trigger div
2585  const buttonTrigger = document.createElement('button');
2586  buttonTrigger.classList.add('atcb-button');
2587  if (data.trigger === 'click') {
2588    buttonTrigger.classList.add('atcb-click');
2589  }
2590  if (data.listStyle === 'overlay') {
2591    buttonTrigger.classList.add('atcb-dropoverlay');
2592  }
2593  buttonTrigger.type = 'button';
2594  buttonTriggerWrapper.appendChild(buttonTrigger);
2595  // generate the label incl. eventListeners
2596  // if there is only 1 calendar option, we directly show this at the button, but with the trigger's label text
2597  if (data.options.length === 1) {
2598    buttonTrigger.classList.add('atcb-single');
2599    atcb_generate_label(data, buttonTrigger, data.options[0], true, data.label, true);
2600  } else {
2601    atcb_generate_label(data, buttonTrigger, 'trigger', true, data.label);
2602    // create an empty anchor div to place the dropdown, while the position can be defined via CSS
2603    const buttonDropdownAnchor = document.createElement('div');
2604    buttonDropdownAnchor.classList.add('atcb-dropdown-anchor');
2605    buttonTrigger.appendChild(buttonDropdownAnchor);
2606  }
2607  // update the placeholder class to prevent multiple initializations
2608  button.classList.remove('atcb');
2609  button.classList.add('atcb-initialized');
2610  // show the placeholder div
2611  if (data.inline) {
2612    button.style.display = 'inline-block';
2613  } else {
2614    button.style.display = 'block';
2615  }
2616  // console log
2617  console.log('Add to Calendar Button "' + data.identifier + '" created');
2618}
2619
2620// generate the dropdown list (can also appear wihtin a modal, if option is set)
2621function atcb_generate_dropdown_list(data) {
2622  const optionsList = document.createElement('div');
2623  optionsList.classList.add('atcb-list');
2624  optionsList.classList.add('atcb-' + data.lightMode);
2625  if (data.rtl) {
2626    optionsList.classList.add('atcb-rtl');
2627  }
2628  optionsList.style.fontSize = data.size + 'px';
2629  // generate the list items
2630  let listCount = 0;
2631  data.options.forEach(function (option) {
2632    const optionItem = document.createElement('div');
2633    optionItem.classList.add('atcb-list-item');
2634    optionItem.tabIndex = 0;
2635    listCount++;
2636    optionItem.dataset.optionNumber = listCount;
2637    optionsList.appendChild(optionItem);
2638    // generate the label incl. individual eventListener
2639    atcb_generate_label(data, optionItem, option, true, data.optionLabels[listCount - 1]);
2640  });
2641  // in the modal case, we also render a close option
2642  if (data.listStyle === 'modal') {
2643    const optionItem = document.createElement('div');
2644    optionItem.classList.add('atcb-list-item', 'atcb-list-item-close');
2645    optionItem.tabIndex = 0;
2646    optionsList.appendChild(optionItem);
2647    atcb_generate_label(data, optionItem, 'close', true);
2648  }
2649  return optionsList;
2650}
2651
2652// create the background overlay, which also acts as trigger to close any dropdowns
2653function atcb_generate_bg_overlay(listStyle = 'dropdown', trigger = '', lightMode = 'light', darken = true) {
2654  const bgOverlay = document.createElement('div');
2655  bgOverlay.id = 'atcb-bgoverlay';
2656  if (listStyle !== 'modal' && darken) {
2657    bgOverlay.classList.add('atcb-animate-bg');
2658  }
2659  if (!darken) {
2660    bgOverlay.classList.add('atcb-no-bg');
2661  }
2662  bgOverlay.classList.add('atcb-' + lightMode);
2663  bgOverlay.tabIndex = 0;
2664  bgOverlay.addEventListener(
2665    'click',
2666    atcb_debounce((e) => {
2667      if (e.target !== e.currentTarget) return;
2668      atcb_toggle('close');
2669    })
2670  );
2671  let fingerMoved = false;
2672  bgOverlay.addEventListener(
2673    'touchstart',
2674    atcb_debounce_leading(() => (fingerMoved = false)),
2675    { passive: true }
2676  );
2677  bgOverlay.addEventListener(
2678    'touchmove',
2679    atcb_debounce_leading(() => (fingerMoved = true)),
2680    { passive: true }
2681  );
2682  bgOverlay.addEventListener(
2683    'touchend',
2684    atcb_debounce((e) => {
2685      if (fingerMoved !== false || e.target !== e.currentTarget) return;
2686      atcb_toggle('close');
2687    }),
2688    { passive: true }
2689  );
2690  bgOverlay.addEventListener(
2691    'focus',
2692    atcb_debounce_leading((e) => {
2693      if (e.target !== e.currentTarget) return;
2694      atcb_toggle('close');
2695    })
2696  );
2697  if (trigger !== 'click') {
2698    bgOverlay.addEventListener(
2699      'mousemove',
2700      atcb_debounce_leading((e) => {
2701        if (e.target !== e.currentTarget) return;
2702        atcb_toggle('close');
2703      })
2704    );
2705  } else {
2706    // if trigger is not set to 'click', we render a close icon, when hovering over the background
2707    bgOverlay.classList.add('atcb-click');
2708  }
2709  return bgOverlay;
2710}
2711
2712// FUNCTIONS TO CONTROL THE INTERACTION
2713function atcb_toggle(action, data = '', button = '', keyboardTrigger = false, generatedButton = false) {
2714  // check for state and adjust accordingly
2715  // action can be 'open', 'close', or 'auto'
2716  if (action == 'open') {
2717    atcb_open(data, button, keyboardTrigger, generatedButton);
2718  } else if (
2719    action == 'close' ||
2720    button.classList.contains('atcb-active') ||
2721    document.querySelector('.atcb-active-modal')
2722  ) {
2723    atcb_close(keyboardTrigger);
2724  } else {
2725    atcb_open(data, button, keyboardTrigger, generatedButton);
2726  }
2727}
2728
2729// show the dropdown list + background overlay
2730function atcb_open(data, button, keyboardTrigger = false, generatedButton = false) {
2731  // abort early if an add to calendar dropdown or modal already opened
2732  if (document.querySelector('.atcb-list') || document.querySelector('.atcb-modal')) return;
2733  // generate list and prepare wrapper
2734  const list = atcb_generate_dropdown_list(data);
2735  const listWrapper = document.createElement('div');
2736  listWrapper.classList.add('atcb-list-wrapper');
2737  // set list styles, set button to atcb-active and force modal listStyle if no button is set
2738  if (button) {
2739    button.classList.add('atcb-active');
2740    if (data.listStyle === 'modal') {
2741      button.classList.add('atcb-modal-style');
2742      list.classList.add('atcb-modal');
2743    } else {
2744      listWrapper.appendChild(list);
2745      listWrapper.classList.add('atcb-dropdown');
2746      if (data.listStyle === 'overlay') {
2747        listWrapper.classList.add('atcb-dropoverlay');
2748      }
2749    }
2750    if (generatedButton) {
2751      list.classList.add('atcb-generated-button'); // if the button has been generated by the script, we add some more specifics
2752    }
2753  } else {
2754    list.classList.add('atcb-modal');
2755  }
2756  // define background overlay
2757  const bgOverlay = atcb_generate_bg_overlay(data.listStyle, data.trigger, data.lightMode, data.background);
2758  // render the items depending on the liststyle
2759  const atcbL = document.createElement('div');
2760  atcbL.id = 'add-to-calendar-button-reference';
2761  atcbL.style.width = '150px';
2762  atcbL.style.padding = '10px 0';
2763  atcbL.style.height = 'auto';
2764  atcbL.style.transform = 'translate3d(0, 0, 0)';
2765  atcbL.style.zIndex = '15000000';
2766  setTimeout(() => {
2767    atcbL.innerHTML =
2768      '<a href="https://add-to-calendar-button.com" target="_blank" rel="noopener">' +
2769      atcbIcon['atcb'] +
2770      '</a>';
2771  }, 500);
2772  if (data.listStyle === 'modal') {
2773    document.body.appendChild(bgOverlay);
2774    bgOverlay.appendChild(list);
2775    //document.body.appendChild(atcbL);
2776    atcbL.style.position = 'fixed';
2777    atcbL.style.bottom = '15px';
2778    atcbL.style.right = '30px';
2779    document.body.classList.add('atcb-modal-no-scroll');
2780  } else {
2781    atcbL.style.position = 'absolute';
2782    document.body.appendChild(listWrapper);
2783    listWrapper.appendChild(list);
2784    listWrapper.classList.add('atcb-style-' + data.buttonStyle);
2785    //document.body.appendChild(atcbL);
2786    document.body.appendChild(bgOverlay);
2787    if (data.listStyle === 'dropdown-static') {
2788      // in the dropdown-static case, we do not dynamically adjust whether we show the dropdown upwards
2789      atcb_position_list(button, listWrapper, true);
2790    } else {
2791      atcb_position_list(button, listWrapper);
2792    }
2793  }
2794  // set overlay size just to be sure
2795  atcb_set_fullsize(bgOverlay);
2796  // give keyboard focus to first item in list, if not blocked, because there is definitely no keyboard trigger
2797  if (keyboardTrigger) {
2798    list.firstChild.focus();
2799  } else {
2800    list.firstChild.focus({ preventScroll: true });
2801  }
2802  list.firstChild.blur();
2803}
2804
2805function atcb_close(keyboardTrigger = false) {
2806  // focus triggering button if available - especially relevant for keyboard navigation
2807  const newFocusEl = document.querySelector('.atcb-active, .atcb-active-modal');
2808  if (newFocusEl) {
2809    newFocusEl.focus({ preventScroll: true });
2810    if (!keyboardTrigger) {
2811      newFocusEl.blur();
2812    }
2813  }
2814  // inactivate all buttons
2815  Array.from(document.querySelectorAll('.atcb-active')).forEach((button) => {
2816    button.classList.remove('atcb-active');
2817  });
2818  Array.from(document.querySelectorAll('.atcb-active-modal')).forEach((button) => {
2819    button.classList.remove('atcb-active-modal');
2820  });
2821  // make body scrollable again
2822  document.body.classList.remove('atcb-modal-no-scroll');
2823  // remove dropdowns, modals, and bg overlays (should only be one of each at max)
2824  Array.from(document.querySelectorAll('.atcb-list-wrapper'))
2825    .concat(Array.from(document.querySelectorAll('.atcb-list')))
2826    .concat(Array.from(document.querySelectorAll('.atcb-info-modal')))
2827    .concat(Array.from(document.querySelectorAll('#add-to-calendar-button-reference')))
2828    .concat(Array.from(document.querySelectorAll('#atcb-bgoverlay')))
2829    .forEach((el) => el.remove());
2830}
2831
2832// prepare data when not using the init function
2833// eslint-disable-next-line @typescript-eslint/no-unused-vars
2834function atcb_action(data, triggerElement, keyboardTrigger = true) {
2835  if (!atcbConsoleInit) {
2836    console.log('Add to Calendar Button Script initialized (version ' + atcbVersion + ')');
2837    console.log('See https://github.com/add2cal/add-to-calendar-button for details');
2838    atcbConsoleInit = true;
2839  }
2840  data = atcb_secure_content(data);
2841  // decorate & validate data
2842  if (!atcb_check_required(data)) {
2843    throw new Error('Add to Calendar Button generation failed: required data missing; see console logs');
2844  }
2845  data = atcb_decorate_data(data);
2846  if (triggerElement) {
2847    data.identifier = triggerElement.id;
2848    // if listStyle some dropdown one, force overlay
2849    if (data.listStyle != 'modal') {
2850      data.listStyle = 'overlay';
2851    }
2852  } else {
2853    data.identifier = 'atcb-btn-custom';
2854    // if no button is defined, fallback to listStyle "modal" and "click" trigger
2855    data.listStyle = 'modal';
2856    data.trigger = 'click';
2857  }
2858  if (!atcb_validate(data)) {
2859    throw new Error(
2860      'Add to Calendar Button generation (' + data.identifier + ') failed: invali
2860d data; see console logs'
2861    );
2862  }
2863  // if all is fine, open the options list
2864  atcb_toggle('open', data, triggerElement, keyboardTrigger);
2865}
2866
2867// FUNCTION TO GENERATE THE GOOGLE URL
2868// See specs at: TODO
2869function atcb_generate_google(data) {
2870  // url parts
2871  const urlParts = [];
2872  urlParts.push('https://calendar.google.com/calendar/render?action=TEMPLATE');
2873  // generate and add date
2874  const formattedDate = atcb_generate_time(data, 'clean', 'google');
2875  urlParts.push(
2876    'dates=' + encodeURIComponent(formattedDate.start) + '%2F' + encodeURIComponent(formattedDate.end)
2877  );
2878  // setting time zone if given and not GMT +/- something, since this is not supported by Google Calendar
2879  if (data.timeZone != null && data.timeZone != '' && !/GMT[+|-]\d{1,2}/i.test(data.timeZone)) {
2880    urlParts.push('ctz=' + data.timeZone);
2881  }
2882  // add details (if set)
2883  if (data.name != null && data.name != '') {
2884    urlParts.push('text=' + encodeURIComponent(data.name));
2885  }
2886  const tmpDataDescription = [];
2887  if (data.description != null && data.description != '') {
2888    tmpDataDescription.push(data.description);
2889  }
2890  if (data.location != null && data.location != '') {
2891    urlParts.push('location=' + encodeURIComponent(data.location));
2892    // TODO: Find a better solution for the next temporary workaround.
2893    if (isiOS()) {
2894      // workaround to cover a bug, where, when using Google Calendar on an iPhone, the location is not recognized. So, for the moment, we simply add it to the description.
2895      if (tmpDataDescription.length > 0) {
2896        tmpDataDescription.push('<br><br>');
2897      }
2898      tmpDataDescription.push('&#128205;: ' + data.location);
2899    }
2900  }
2901  if (tmpDataDescription.length > 0) {
2902    urlParts.push('details=' + encodeURIComponent(tmpDataDescription.join()));
2903  }
2904  if (data.recurrence != null && data.recurrence != '') {
2905    urlParts.push('recur=' + encodeURIComponent(data.recurrence));
2906  }
2907  // We also push the UID. It has no real effect, but at least becomes part of the url that way
2908  urlParts.push('uid=' + encodeURIComponent(data.uid));
2909  const url = urlParts.join('&');
2910  if (atcb_secure_url(url)) {
2911    // eslint-disable-next-line security/detect-non-literal-fs-filename
2912    window.open(url, atcbDefaultTarget).focus();
2913  }
2914}
2915
2916// FUNCTION TO GENERATE THE YAHOO URL
2917// See specs at: TODO
2918function atcb_generate_yahoo(data) {
2919  // url parts
2920  const urlParts = [];
2921  urlParts.push('https://calendar.yahoo.com/?v=60');
2922  // generate and add date
2923  const formattedDate = atcb_generate_time(data, 'clean');
2924  urlParts.push(
2925    'st=' + encodeURIComponent(formattedDate.start) + '&et=' + encodeURIComponent(formattedDate.end)
2926  );
2927  if (formattedDate.allday) {
2928    urlParts.push('dur=allday');
2929  }
2930  // add details (if set)
2931  if (data.name != null && data.name != '') {
2932    urlParts.push('title=' + encodeURIComponent(data.name));
2933  }
2934  if (data.location != null && data.location != '') {
2935    urlParts.push('in_loc=' + encodeURIComponent(data.location));
2936  }
2937  if (data.descriptionHtmlFree != null && data.descriptionHtmlFree != '') {
2938    // using descriptionHtmlFree instead of description, since Yahoo does not support html tags in a stable way
2939    urlParts.push('desc=' + encodeURIComponent(data.descriptionHtmlFree));
2940  }
2941  // We also push the UID. It has no real effect, but at least becomes part of the url that way
2942  urlParts.push('uid=' + encodeURIComponent(data.uid));
2943  const url = urlParts.join('&');
2944  if (atcb_secure_url(url)) {
2945    // eslint-disable-next-line security/detect-non-literal-fs-filename
2946    window.open(url, atcbDefaultTarget).focus();
2947  }
2948}
2949
2950// FUNCTION TO GENERATE THE MICROSOFT 365 OR OUTLOOK WEB URL
2951// See specs at: TODO
2952function atcb_generate_microsoft(data, type = '365') {
2953  // redirect to iCal solution on mobile devices, since the Microsoft web apps are buggy on mobile devices (see https://github.com/add2cal/add-to-calendar-button/discussions/113)
2954  if (isMobile()) {
2955    atcb_generate_ical(data);
2956    return;
2957  }
2958  // url parts
2959  const urlParts = [];
2960  const basePath = '/calendar/0/deeplink/compose?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent';
2961  const baseUrl = (function () {
2962    if (type == 'outlook') {
2963      return 'https://outlook.live.com' + basePath;
2964    } else {
2965      return 'https://outlook.office.com' + basePath;
2966    }
2967  })();
2968  urlParts.push(baseUrl);
2969  // generate and add date
2970  const formattedDate = atcb_generate_time(data, 'delimiters', 'microsoft');
2971  urlParts.push('startdt=' + encodeURIComponent(formattedDate.start));
2972  urlParts.push('enddt=' + encodeURIComponent(formattedDate.end));
2973  if (formattedDate.allday) {
2974    urlParts.push('allday=true');
2975  }
2976  // add details (if set)
2977  if (data.name != null && data.name != '') {
2978    urlParts.push('subject=' + encodeURIComponent(data.name));
2979  }
2980  if (data.location != null && data.location != '') {
2981    urlParts.push('location=' + encodeURIComponent(data.location));
2982  }
2983  if (data.description != null && data.description != '') {
2984    urlParts.push('body=' + encodeURIComponent(data.description.replace(/\n/g, '<br>')));
2985  }
2986  // We also push the UID. It has no real effect, but at least becomes part of the url that way
2987  urlParts.push('uid=' + encodeURIComponent(data.uid));
2988  const url = urlParts.join('&');
2989  if (atcb_secure_url(url)) {
2990    // eslint-disable-next-line security/detect-non-literal-fs-filename
2991    window.open(url, atcbDefaultTarget).focus();
2992  }
2993}
2994
2995// FUNCTION TO GENERATE THE MICROSOFT TEAMS URL
2996// See specs at: https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/deep-links#deep-linking-to-the-scheduling-dialog
2997// Mind that this is still in development mode by Microsoft! Location, html tags and linebreaks in the description are not supported yet.
2998function atcb_generate_teams(data) {
2999  // url parts
3000  const urlParts = [];
3001  const baseUrl = 'https://teams.microsoft.com/l/meeting/new?';
3002  // generate and add date
3003  const formattedDate = atcb_generate_time(data, 'delimiters', 'microsoft');
3004  urlParts.push('startTime=' + encodeURIComponent(formattedDate.start));
3005  urlParts.push('endTime=' + encodeURIComponent(formattedDate.end));
3006  // add details (if set)
3007  if (data.name != null && data.name != '') {
3008    urlParts.push('subject=' + encodeURIComponent(data.name));
3009  }
3010  let locationString = '';
3011  if (data.location != null && data.location != '') {
3012    locationString = encodeURIComponent(data.location);
3013    urlParts.push('location=' + locationString);
3014    locationString += ' // '; // preparing the workaround putting the location into the description, since the native field is not supported yet
3015  }
3016  if (data.descriptionHtmlFree != null && data.descriptionHtmlFree != '') {
3017    // using descriptionHtmlFree instead of description, since Teams does not support html tags
3018    urlParts.push('content=' + locationString + encodeURIComponent(data.descriptionHtmlFree));
3019  }
3020  // We also push the UID. It has no real effect, but at least becomes part of the url that way
3021  urlParts.push('uid=' + encodeURIComponent(data.uid));
3022  const url = baseUrl + urlParts.join('&');
3023  if (atcb_secure_url(url)) {
3024    // eslint-disable-next-line security/detect-non-literal-fs-filename
3025    window.open(url, atcbDefaultTarget).focus();
3026  }
3027}
3028
3029// FUNCTION TO GENERATE THE iCAL FILE (also for the Apple option)
3030// See specs at: https://www.rfc-editor.org/rfc/rfc5545.html
3031function atcb_generate_ical(data) {
3032  // define the right filename
3033  let filename = 'event-to-save-in-my-calendar';
3034  if (data.iCalFileName != null && data.iCalFileName != '') {
3035    filename = data.iCalFileName;
3036  } else if (data.icsFile != null && data.icsFile != '') {
3037    const filenamePart = data.icsFile.split('/').pop().split('.')[0];
3038    if (filenamePart != '') {
3039      filename = filenamePart;
3040    }
3041  }
3042  // check for a given explicit file (not if iOS and WebView - will be catched further down)
3043  if (data.icsFile != null && data.icsFile != '' && (!isiOS() || !isWebView())) {
3044    atcb_save_file(data.icsFile, filename);
3045    return;
3046  }
3047  // otherwise, generate one on the fly
3048  const now = new Date();
3049  const formattedDate = atcb_generate_time(data, 'clean', 'ical');
3050  const ics_lines = ['BEGIN:VCALENDAR', 'VERSION:2.0'];
3051  ics_lines.push('PRODID:-// https://add-to-calendar-pro.com // button v' + atcbVersion + ' //EN');
3052  ics_lines.push('CALSCALE:GREGORIAN');
3053  // we set CANCEL, whenever the status says so
3054  if (data.status == 'CANCELLED') {
3055    ics_lines.push('METHOD:CANCEL');
3056  } else {
3057    // for all other cases, we use REQUEST for organized/hosted events
3058    if (data.organizer != null && data.organizer != '') {
3059      ics_lines.push('METHOD:REQUEST');
3060    } else {
3061      // and PUBLISH for events without a host
3062      ics_lines.push('METHOD:PUBLISH');
3063    }
3064  }
3065  // include time zone information, if set and if not allday (not necessary in that case)
3066  const timeAddon = (function () {
3067    if (formattedDate.allday) {
3068      return ';VALUE=DATE';
3069    }
3070    if (data.timeZone != null && data.timeZone != '') {
3071      const timeZoneBlock = tzlib_get_ical_block(data.timeZone);
3072      ics_lines.push(timeZoneBlock[0]);
3073      return ';' + timeZoneBlock[1];
3074    }
3075  })();
3076  ics_lines.push('BEGIN:VEVENT');
3077  ics_lines.push('UID:' + data.uid);
3078  ics_lines.push('DTSTAMP:' + atcb_format_datetime(now, 'clean', true));
3079  ics_lines.push('DTSTART' + timeAddon + ':' + formattedDate.start);
3080  ics_lines.push('DTEND' + timeAddon + ':' + formattedDate.end);
3081  ics_lines.push('SUMMARY:' + data.name.replace(/.{65}/g, '$&' + '\r\n ')); // making sure it does not exceed 75 characters per line
3082  if (data.descriptionHtmlFree != null && data.descriptionHtmlFree != '') {
3083    ics_lines.push(
3084      'DESCRIPTION:' + data.descriptionHtmlFree.replace(/\n/g, '\\n').replace(/.{60}/g, '$&' + '\r\n ') // adjusting for intended line breaks + making sure it does not exceed 75 characters per line
3085    );
3086  }
3087  if (data.description != null && data.description != '') {
3088    ics_lines.push(
3089      'X-ALT-DESC;FMTTYPE=text/html:\r\n <!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN"">\r\n <HTML><BODY>\r\n ' +
3090        data.description.replace(/\n/g, '<br>').replace(/.{60}/g, '$&' + '\r\n ') +
3091        '\r\n </BODY></HTML>'
3092    );
3093  }
3094  if (data.location != null && data.location != '') {
3095    ics_lines.push('LOCATION:' + data.location);
3096  }
3097  if (data.organizer != null && data.organizer != '') {
3098    const organizerParts = data.organizer.split('|');
3099    ics_lines.push('ORGANIZER;CN=' + organizerParts[0] + ':MAILTO:' + organizerParts[1]);
3100  }
3101  if (data.recurrence != null && data.recurrence != '') {
3102    ics_lines.push(data.recurrence);
3103  }
3104  ics_lines.push('STATUS:' + data.status);
3105  ics_lines.push('CREATED:' + data.created);
3106  ics_lines.push('LAST-MODIFIED:' + data.updated);
3107  ics_lines.push('SEQUENCE:' + data.sequence);
3108  ics_lines.push('END:VEVENT');
3109  ics_lines.push('END:VCALENDAR');
3110  const dataUrl = (function () {
3111    // if we got to this point with an explicitely given iCal file, we are on an iOS device within an in-app browser (WebView). In this case, we use this as dataUrl
3112    if (data.icsFile != null && data.icsFile != '') {
3113      return data.icsFile;
3114    }
3115    // otherwise, we generate it from the array
3116    return 'data:text/calendar;charset=utf-8,' + encodeURIComponent(ics_lines.join('\r\n'));
3117  })();
3118  // in in-app browser cases (WebView), we offer a copy option, since the on-the-fly client side generation is usually not supported
3119  // for Android, we are more specific and only go for specific apps at the moment
3120  // for Chrome on iOS we basically do the same
3121  if ((isiOS() && isChrome()) || (isWebView() && (isiOS() || (isAndroid() && isProblematicWebView())))) {
3122    // putting the download url to the clipboard
3123    const tmpInput = document.createElement('input');
3124    document.body.appendChild(tmpInput);
3125    const editable = tmpInput.contentEditable;
3126    const readOnly = tmpInput.readOnly;
3127    tmpInput.value = dataUrl;
3128    tmpInput.contentEditable = true;
3129    tmpInput.readOnly = false;
3130    if (isiOS()) {
3131      var range = document.createRange();
3132      range.selectNodeContents(tmpInput);
3133      var selection = window.getSelection();
3134      selection.removeAllRanges();
3135      selection.addRange(range);
3136      tmpInput.setSelectionRange(0, 999999);
3137    } else {
3138      // the next 2 lines are basically doing the same in different ways (just to be sure)
3139      navigator.clipboard.writeText(dataUrl);
3140      tmpInput.select();
3141    }
3142    tmpInput.contentEditable = editable;
3143    tmpInput.readOnly = readOnly;
3144    document.execCommand('copy');
3145    tmpInput.remove();
3146    // creating the modal
3147    if (isiOS() && isChrome()) {
3148      atcb_create_modal(
3149        data,
3150        'browser',
3151        atcb_translate_hook('Crios iCal headline', data.language, data),
3152        atcb_translate_hook('Crios iCal info', data.language, data) +
3153          '<br>' +
3154          atcb_translate_hook('WebView iCal solution 1', data.language, data) +
3155          '<br>' +
3156          atcb_translate_hook('Crios iCal solution 2', data.language, data)
3157      );
3158    } else {
3159      atcb_create_modal(
3160        data,
3161        'browser',
3162        atcb_translate_hook('WebView iCal headline', data.language, data),
3163        atcb_translate_hook('WebView iCal info', data.language, data) +
3164          '<br>' +
3165          atcb_translate_hook('WebView iCal solution 1', data.language, data) +
3166          '<br>' +
3167          atcb_translate_hook('WebView iCal solution 2', data.language, data)
3168      );
3169    }
3170  } else {
3171    atcb_save_file(dataUrl, filename);
3172  }
3173}
3174
3175// SHARED FUNCTION TO SAVE A FILE
3176function atcb_save_file(file, filename) {
3177  try {
3178    const save = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
3179    save.rel = 'noopener';
3180    save.href = file;
3181    // not using default target here, since this needs to happen _self on iOS (abstracted to mobile in general) and _blank at Firefox (abstracted to other setups) due to potential cross-origin restrictions
3182    if (isMobile()) {
3183      save.target = '_self';
3184    } else {
3185      save.target = '_blank';
3186    }
3187    save.download = filename + '.ics';
3188    const evt = new MouseEvent('click', {
3189      view: window,
3190      button: 0,
3191      bubbles: true,
3192      cancelable: false,
3193    });
3194    save.dispatchEvent(evt);
3195    (window.URL || window.webkitURL).revokeObjectURL(save.href);
3196  } catch (e) {
3197    console.error(e);
3198  }
3199}
3200
3201// SHARED FUNCTION TO GENERATE A TIME STRING
3202function atcb_generate_time(data, style = 'delimiters', targetCal = 'general', addTimeZoneOffset = false) {
3203  const startDate = data.startDate.split('-');
3204  const endDate = data.endDate.split('-');
3205  if (data.startTime != null && data.endTime != null) {
3206    // for the input, we assume UTC per default
3207    const newStartDate = new Date(
3208      startDate[0] + '-' + startDate[1] + '-' + startDate[2] + 'T' + data.startTime + ':00.000+00:00'
3209    );
3210    const newEndDate = new Date(
3211      endDate[0] + '-' + endDate[1] + '-' + endDate[2] + 'T' + data.endTime + ':00.000+00:00'
3212    );
3213    const durationMS = newEndDate - newStartDate;
3214    const durationHours = Math.floor(durationMS / 1000 / 60 / 60);
3215    const durationMinutes = Math.floor(((durationMS - durationHours * 60 * 60 * 1000) / 1000 / 60) % 60);
3216    const durationString = (function () {
3217      if (durationHours < 10) {
3218        return '0' + durationHours + ':' + ('0' + durationMinutes).slice(-2);
3219      }
3220      return durationHours + ':' + ('0' + durationMinutes).slice(-2);
3221    })();
3222    // if no time zone is given and we need to add the offset to the datetime string, do so directly and return
3223    if ((data.timeZone == null || (data.timeZone != null && data.timeZone == '')) && addTimeZoneOffset) {
3224      return {
3225        start: newStartDate.toISOString().replace('.000Z', '+00:00'),
3226        end: newEndDate.toISOString().replace('.000Z', '+00:00'),
3227        duration: durationString,
3228        allday: false,
3229      };
3230    }
3231    // if a time zone is given, we adjust the diverse cases
3232    // (see https://tz.add-to-calendar-technology.com/ for available TZ names)
3233    if (data.timeZone != null && data.timeZone != '') {
3234      if (targetCal == 'ical' || (targetCal == 'google' && !/GMT[+|-]\d{1,2}/i.test(data.timeZone))) {
3235        // in the iCal case, we simply return and cut off the Z. Same applies to Google, except for GMT +/- time zones, which are not supported there.
3236        // everything else will be done by injecting the VTIMEZONE block at the iCal function
3237        return {
3238          start: atcb_format_datetime(newStartDate, 'clean', true, true),
3239          end: atcb_format_datetime(newEndDate, 'clean', true, true),
3240          duration: durationString,
3241          allday: false,
3242        };
3243      }
3244      // we get the correct offset via the timeZones iCal Library
3245      const offsetStart = tzlib_get_offset(data.timeZone, data.startDate, data.startTime);
3246      const offsetEnd = tzlib_get_offset(data.timeZone, data.endDate, data.endTime);
3247      // if we need to add the offset to the datetime string, do so respectively
3248      if (addTimeZoneOffset) {
3249        const formattedOffsetStart = offsetStart.slice(0, 3) + ':' + offsetStart.slice(3);
3250        const formattedOffsetEnd = offsetEnd.slice(0, 3) + ':' + offsetEnd.slice(3);
3251        return {
3252          start: newStartDate.toISOString().replace('.000Z', formattedOffsetStart),
3253          end: newEndDate.toISOString().replace('.000Z', formattedOffsetEnd),
3254          duration: durationString,
3255          allday: false,
3256        };
3257      }
3258      // in other cases, we substract the offset from the dates
3259      // (substraction to reflect the fact that the user assumed his timezone and to convert to UTC; since calendars assume UTC and add offsets again)
3260      const calcOffsetStart =
3261        parseInt(offsetStart[0] + 1) *
3262        -1 *
3263        ((parseInt(offsetStart.substr(1, 2)) * 60 + parseInt(offsetStart.substr(3, 2))) * 60 * 1000);
3264      const calcOffsetEnd =
3265        parseInt(offsetEnd[0] + 1) *
3266        -1 *
3267        ((parseInt(offsetEnd.substr(1, 2)) * 60 + parseInt(offsetEnd.substr(3, 2))) * 60 * 1000);
3268      newStartDate.setTime(newStartDate.getTime() + calcOffsetStart);
3269      newEndDate.setTime(newEndDate.getTime() + calcOffsetEnd);
3270    }
3271    // return formatted data
3272    return {
3273      start: atcb_format_datetime(newStartDate, style),
3274      end: atcb_format_datetime(newEndDate, style),
3275      duration: durationString,
3276      allday: false,
3277    };
3278  } else {
3279    // would be an allday event then
3280    const newStartDate = new Date(Date.UTC(startDate[0], startDate[1] - 1, startDate[2]));
3281    const newEndDate = new Date(Date.UTC(endDate[0], endDate[1] - 1, endDate[2]));
3282    // increment the end day by 1 for Google Calendar, iCal and Outlook
3283    if (targetCal == 'google' || targetCal == 'microsoft' || targetCal == 'ical') {
3284      newEndDate.setDate(newEndDate.getDate() + 1);
3285    }
3286    // return formatted data
3287    return {
3288      start: atcb_format_datetime(newStartDate, style, false),
3289      end: atcb_format_datetime(newEndDate, style, false),
3290      allday: true,
3291    };
3292  }
3293}
3294
3295function atcb_format_datetime(datetime, style = 'delimiters', includeTime = true, removeZ = false) {
3296  const regex = (function () {
3297    if (includeTime) {
3298      if (style == 'clean') {
3299        return /(-|:|(\.\d{3}))/g;
3300      }
3301      return /(\.\d{3})/g;
3302    }
3303    if (style == 'clean') {
3304      return /(-|T(\d{2}:\d{2}:\d{2}\.\d{3})Z)/g;
3305    }
3306    return /T(\d{2}:\d{2}:\d{2}\.\d{3})Z/g;
3307  })();
3308  const output = removeZ
3309    ? datetime.toISOString().replace(regex, '').replace('Z', '')
3310    : datetime.toISOString().replace(regex, '');
3311  return output;
3312}
3313
3314// SHARED FUNCTION TO SECURE DATA
3315function atcb_secure_content(data, isJSON = true) {
3316  // strip HTML tags (especially since stupid Safari adds stuff) - except for <br>
3317  const toClean = isJSON ? JSON.stringify(data) : data;
3318  const cleanedUp = toClean.replace(/(<(?!br)([^>]+)>)/gi, '');
3319  if (isJSON) {
3320    return JSON.parse(cleanedUp);
3321  } else {
3322    return cleanedUp;
3323  }
3324}
3325
3326// SHARED FUNCTION TO SECURE URLS
3327function atcb_secure_url(url, throwError = true) {
3328  if (
3329    url.match(
3330      /((\.\.\/)|(\.\.\\)|(%2e%2e%2f)|(%252e%252e%252f)|(%2e%2e\/)|(%252e%252e\/)|(\.\.%2f)|(\.\.%252f)|(%2e%2e%5c)|(%252e%252e%255c)|(%2e%2e\\)|(%252e%252e\\)|(\.\.%5c)|(\.\.%255c)|(\.\.%c0%af)|(\.\.%25c0%25af)|(\.\.%c1%9c)|(\.\.%25c1%259c))/gi
3331    )
3332  ) {
3333    if (throwError) {
3334      console.error(
3335        'Seems like the generated URL includes at least one security issue and got blocked. Please check the calendar button parameters!'
3336      );
3337    }
3338    return false;
3339  } else {
3340    return true;
3341  }
3342}
3343
3344// SHARED FUNCTION TO VALIDATE EMAIL ADDRESSES
3345function atcb_validEmail(email, mx = false) {
3346  // rough format check first
3347  if (!/^.{0,70}@.{1,30}\.[\w.]{2,9}$/.test(email)) {
3348    return false;
3349  }
3350  // testing for mx records second, if activated
3351  if (mx) {
3352    // TODO: call external service to validate email address
3353  }
3354  return true;
3355}
3356
3357// SHARED FUNCTION TO REPLACE HTML PSEUDO ELEMENTS
3358function atcb_rewrite_html_elements(content, clear = false) {
3359  // standardize any line breaks
3360  content = content.replace(/<br\s*\/?>/gi, '\n');
3361  // remove any pseudo elements, if necessary
3362  if (clear) {
3363    content = content.replace(/\[(|\/)(url|br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]|((\|.*)\[\/url\])/gi, '');
3364    // and build html for the rest
3365    // supporting: br, hr, p, strong, u, i, em, li, ul, ol, h (like h1, h2, h3, ...), url (= a)
3366  } else {
3367    content = content.replace(/\[(\/|)(br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]/gi, '<$1$2>');
3368    content = content.replace(/\[url\]([\w&$+.,:;=~!*'?@^%#|\s\-()/]*)\[\/url\]/gi, function (match, p1) {
3369      const urlText = p1.split('|');
3370      const text = (function () {
3371        if (urlText.length > 1 && urlText[1] != '') {
3372          return urlText[1];
3373        } else {
3374          return urlText[0];
3375        }
3376      })();
3377      return (
3378        '<a href="' + urlText[0] + '" target="' + atcbDefaultTarget + '" rel="noopener">
3378' + text + '</a>'
3379      );
3380    });
3381  }
3382  return content;
3383}
3384
3385// SHARED FUNCTION TO CREATE INFO MODALS
3386function atcb_create_modal(data, icon = '', headline, content, buttons) {
3387  // setting the stage
3388  const bgOverlay = atcb_generate_bg_overlay('modal', 'click', data.lightMode);
3389  const infoModalWrapper = document.createElement('div');
3390  infoModalWrapper.classList.add('atcb-modal', 'atcb-info-modal');
3391  infoModalWrapper.tabIndex = 0;
3392  bgOverlay.appendChild(infoModalWrapper);
3393  document.body.appendChild(bgOverlay);
3394  document.body.classList.add('atcb-modal-no-scroll');
3395  const parentButton = document.getElementById(data.identifier);
3396  if (parentButton != null) {
3397    parentButton.classList.add('atcb-active-modal');
3398  }
3399  const infoModal = document.createElement('div');
3400  infoModal.classList.add('atcb-modal-box');
3401  infoModal.classList.add('atcb-' + data.lightMode);
3402  if (data.rtl) {
3403    infoModal.classList.add('atcb-rtl');
3404  }
3405  infoModal.style.fontSize = data.size + 'px';
3406  infoModalWrapper.appendChild(infoModal);
3407  // set overlay size just to be sure
3408  atcb_set_fullsize(bgOverlay);
3409  // adding closing button
3410  const infoModalClose = document.createElement('div');
3411  infoModalClose.classList.add('atcb-modal-close');
3412  infoModalClose.innerHTML = atcbIcon.close;
3413  infoModal.appendChild(infoModalClose);
3414  infoModalClose.addEventListener(
3415    'click',
3416    atcb_debounce(() => atcb_close())
3417  );
3418  infoModalClose.addEventListener(
3419    'keyup',
3420    atcb_debounce_leading((event) => {
3421      if (event.key == 'Enter') {
3422        event.preventDefault();
3423        atcb_toggle('close', '', '', true);
3424      }
3425    })
3426  );
3427  if (buttons == null || buttons.length == 0) {
3428    infoModalClose.tabIndex = 0;
3429    infoModalClose.focus();
3430  }
3431  // adding headline (incl. icon)
3432  const infoModalHeadline = document.createElement('div');
3433  infoModalHeadline.classList.add('atcb-modal-headline');
3434  infoModal.appendChild(infoModalHeadline);
3435  if (icon != '') {
3436    const infoModalHeadlineIcon = document.createElement('span');
3437    infoModalHeadlineIcon.classList.add('atcb-modal-headline-icon');
3438    infoModalHeadlineIcon.innerHTML = atcbIcon[`${icon}`];
3439    infoModalHeadline.appendChild(infoModalHeadlineIcon);
3440  }
3441  let infoModalHeadlineText = document.createTextNode(headline);
3442  infoModalHeadline.appendChild(infoModalHeadlineText);
3443  // and text content
3444  const infoModalContent = document.createElement('div');
3445  infoModalContent.classList.add('atcb-modal-content');
3446  infoModalContent.innerHTML = content;
3447  infoModal.appendChild(infoModalContent);
3448  // and buttons (array of objects; attributes: href, type, label, primary(boolean))
3449  if (buttons != null && buttons.length > 0) {
3450    const infoModalButtons = document.createElement('div');
3451    infoModalButtons.classList.add('atcb-modal-buttons');
3452    infoModal.appendChild(infoModalButtons);
3453    buttons.forEach((button, index) => {
3454      let infoModalButton;
3455      if (button.href != null && button.href != '') {
3456        infoModalButton = document.createElement('a');
3457        infoModalButton.setAttribute('target', atcbDefaultTarget);
3458        infoModalButton.setAttribute('href', button.href);
3459        infoModalButton.setAttribute('rel', 'noopener');
3460      } else {
3461        infoModalButton = document.createElement('button');
3462        infoModalButton.type = 'button';
3463      }
3464      infoModalButton.classList.add('atcb-modal-btn');
3465      if (button.primary) {
3466        infoModalButton.classList.add('atcb-modal-btn-primary');
3467      }
3468      if (button.label == null || button.label == '') {
3469        button.label = atcb_translate_hook('Click me', data.language, data);
3470      }
3471      infoModalButton.textContent = button.label;
3472      infoModalButtons.appendChild(infoModalButton);
3473      if (index == 0) {
3474        infoModalButton.focus();
3475      }
3476      switch (button.type) {
3477        default:
3478        case 'close':
3479          infoModalButton.addEventListener(
3480            'click',
3481            atcb_debounce(() => atcb_close())
3482          );
3483          infoModalButton.addEventListener(
3484            'keyup',
3485            atcb_debounce((event) => {
3486              if (event.key == 'Enter') {
3487                atcb_toggle('close', '', '', true);
3488              }
3489            })
3490          );
3491          break;
3492      }
3493    });
3494  }
3495}
3496
3497// SHARED FUNCTION TO CALCULATE THE POSITION OF THE DROPDOWN LIST
3498function atcb_position_list(trigger, list, blockUpwards = false, resize = false) {
3499  // check for position anchor
3500  let anchorSet = false;
3501  const originalTrigger = trigger;
3502  if (trigger.querySelector('.atcb-dropdown-anchor') !== null) {
3503    trigger = trigger.querySelector('.atcb-dropdown-anchor');
3504    anchorSet = true;
3505  }
3506  // calculate position
3507  let triggerDim = trigger.getBoundingClientRect();
3508  let listDim = list.getBoundingClientRect();
3509  const btnDim = originalTrigger.getBoundingClientRect();
3510  if (anchorSet === true && !list.classList.contains('atcb-dropoverlay')) {
3511    // in the regular case, we also check for the ideal direction
3512    // not in the !updateDirection case and not if there is not enough space above
3513    const viewportHeight = document.documentElement.clientHeight;
3514    if (
3515      (list.classList.contains('atcb-dropup') && resize) ||
3516      (!blockUpwards &&
3517        triggerDim.top + listDim.height > viewportHeight - 20 &&
3518        2 * btnDim.top + btnDim.height - triggerDim.top - listDim.height > 20)
3519    ) {
3520      originalTrigger.classList.add('atcb-dropup');
3521      list.classList.add('atcb-dropup');
3522      list.style.bottom =
3523        2 * viewportHeight -
3524        (viewportHeight + (btnDim.top + (btnDim.top + btnDim.height - triggerDim.top))) -
3525        window.scrollY +
3526        'px';
3527    } else {
3528      list.style.top = window.scrollY + triggerDim.top + 'px';
3529      if (originalTrigger.classList.contains('atcb-dropup')) {
3530        originalTrigger.classList.remove('atcb-dropup');
3531      }
3532    }
3533    // read trigger dimensions again, since after adjusting the top value of the list, something might have changed (e.g. re-adjustment due to missing scrollbars at this point in time)
3534    triggerDim = trigger.getBoundingClientRect();
3535    if (list.classList.contains('atcb-style-round') || list.classList.contains('atcb-style-text')) {
3536      list.style.minWidth = triggerDim.width + 'px';
3537    } else {
3538      list.style.width = triggerDim.width + 'px';
3539    }
3540    // read list dimensions again, since we altered the width in the step before
3541    listDim = list.getBoundingClientRect();
3542    list.style.left = triggerDim.left - (listDim.width - triggerDim.width) / 2 + 'px';
3543  } else {
3544    // when there is no anchor set (only the case with custom implementations) or the listStyle is set respectively (overlay), we render the modal centered above the trigger
3545    // make sure the trigger is not moved over it via CSS in this case!
3546    let listWidth = triggerDim.width + 20 + 'px';
3547    list.style.minWidth = listWidth;
3548    // read list dimensions again, since we altered the width in the step before
3549    listDim = list.getBoundingClientRect();
3550    list.style.top = window.scrollY + btnDim.top + btnDim.height / 2 - listDim.height / 2 + 'px';
3551    list.style.left = triggerDim.left - (listDim.width - triggerDim.width) / 2 + 'px';
3552  }
3553  const atcbL = document.getElementById('add-to-calendar-button-reference');
3554  if (atcbL) {
3555    if (originalTrigger.classList.contains('atcb-dropup')) {
3556      atcbL.style.top = window.scrollY + btnDim.top + btnDim.height + 'px';
3557      atcbL.style.left = btnDim.left + (btnDim.width - 150) / 2 + 'px';
3558    } else {
3559      listDim = list.getBoundingClientRect();
3560      if (originalTrigger.classList.contains('atcb-dropoverlay') || !anchorSet) {
3561        atcbL.style.top = window.scrollY + listDim.top + listDim.height + 'px';
3562      } else {
3563        atcbL.style.top = window.scrollY + triggerDim.top + listDim.height + 'px';
3564      }
3565      atcbL.style.left = listDim.left + (listDim.width - 150) / 2 + 'px';
3566    }
3567  }
3568}
3569
3570// SHARED FUNCTION TO DEFINE WIDTH AND HEIGHT FOR "FULLSCREEN" FULLSIZE ELEMENTS
3571function atcb_set_fullsize(el) {
3572  el.style.width = window.innerWidth + 'px';
3573  el.style.height = window.innerHeight + 100 + 'px';
3574}
3575
3576// SHARED FUNCTION TO GENERATE UUIDs
3577function atcb_generate_uuid() {
3578  //return crypto.randomUUID(); // lacking support of Safari < 15.4 and Firefox < 95, which is to important for now
3579  return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
3580    (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
3581  );
3582}
3583
3584// SHARED DEBOUNCE AND THROTTLE FUNCTIONS
3585// going for last call debounce
3586function atcb_debounce(func, timeout = 200) {
3587  let timer;
3588  return (...args) => {
3589    clearTimeout(timer);
3590    timer = setTimeout(() => {
3591      func.apply(this, args);
3592    }, timeout);
3593  };
3594}
3595// dropping subsequent calls debounce
3596function atcb_debounce_leading(func, timeout = 300) {
3597  let timer;
3598  return (...args) => {
3599    if (!timer) {
3600      func.apply(this, args);
3601    }
3602    clearTimeout(timer);
3603    timer = setTimeout(() => {
3604      timer = undefined;
3605    }, timeout);
3606  };
3607}
3608// throttle
3609function atcb_throttle(func, delay = 10) {
3610  let result;
3611  let timeout = null;
3612  let previous = 0;
3613  let later = (...args) => {
3614    previous = Date.now();
3615    timeout = null;
3616    result = func.apply(this, args);
3617  };
3618  return (...args) => {
3619    let now = Date.now();
3620    let remaining = delay - (now - previous);
3621    if (remaining <= 0 || remaining > delay) {
3622      if (timeout) {
3623        clearTimeout(timeout);
3624        timeout = null;
3625      }
3626      previous = now;
3627      result = func.apply(this, args);
3628    } else if (!timeout) {
3629      timeout = setTimeout(later, remaining);
3630    }
3631    return result;
3632  };
3633}
3634
3635// GLOBAL KEYBOARD AND DEVICE LISTENERS
3636if (isBrowser()) {
3637  // global listener to ESC key to close dropdown
3638  document.addEventListener(
3639    'keyup',
3640    atcb_debounce_leading((event) => {
3641      if (event.key === 'Escape') {
3642        atcb_toggle('close', '', '', true);
3643      }
3644    })
3645  );
3646  // global listener to arrow key optionlist navigation
3647  document.addEventListener('keydown', (event) => {
3648    if (
3649      document.querySelector('.atcb-list') &&
3650      (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Tab')
3651    ) {
3652      let targetFocus = 0;
3653      let currFocusOption = document.activeElement;
3654      const optionListCount = document.querySelectorAll('.atcb-list-item').length;
3655      if (currFocusOption.classList.contains('atcb-list-item')) {
3656        if (event.key === 'ArrowDown' && currFocusOption.dataset.optionNumber < optionListCount) {
3657          event.preventDefault();
3658          targetFocus = parseInt(currFocusOption.dataset.optionNumber) + 1;
3659        } else if (event.key === 'ArrowUp' && currFocusOption.dataset.optionNumber >= 1) {
3660          event.preventDefault();
3661          targetFocus = parseInt(currFocusOption.dataset.optionNumber) - 1;
3662        }
3663        if (targetFocus > 0) {
3664          document.querySelector('.atcb-list-item[data-option-number="' + targetFocus + '"]').focus();
3665        }
3666      } else {
3667        event.preventDefault();
3668        if (
3669          document.querySelector('.atcb-list-wrapper.atcb-dropup') &&
3670          (event.key === 'ArrowDown' || event.key === 'ArrowUp')
3671        ) {
3672          document.querySelector('.atcb-list-item[data-option-number="' + optionListCount + '"]').focus();
3673        } else {
3674          document.querySelector('.atcb-list-item[data-option-number="1"]').focus();
3675        }
3676      }
3677    }
3678  });
3679  // Global listener to any screen changes
3680  window.addEventListener(
3681    'resize',
3682    atcb_throttle(() => {
3683      const activeOverlay = document.getElementById('atcb-bgoverlay');
3684      if (activeOverlay != null) {
3685        atcb_set_fullsize(activeOverlay);
3686      }
3687      const activeButton = document.querySelector('.atcb-active');
3688      const activeList = document.querySelector('.atcb-dropdown');
3689      if (activeButton != null && activeList != null) {
3690        atcb_position_list(activeButton, activeList, false, true);
3691      }
3692    })
3693  );
3694}
3695
3696// TRANSLATIONS
3697// the database object
3698const i18nStrings = {
3699  en: {
3700    'Add to Calendar': 'Add to Calendar',
3701    'iCal File': 'iCal File',
3702    Close: 'Close',
3703    'Close Selection': 'Close Selection',
3704    'Click me': 'Click me',
3705    'WebView iCal headline': 'Open your browser',
3706    'WebView iCal info':
3707      'Unfortunately, in-app browsers have problems with the way we generate the calendar file.',
3708    'WebView iCal solution 1': "We automatically put a magical URL into your phone's clipboard.",
3709    'WebView iCal solution 2':
3710      '<ol><li><strong>Open another browser</strong> on your phone, ...</li><li><strong>Paste</strong> the clipboard content and go.</li></ol>',
3711    'Crios iCal headline': 'Open Safari',
3712    'Crios iCal info':
3713      'Unfortunately, Chrome on iOS has problems with the way we generate the calendar file.',
3714    'Crios iCal solution 2':
3715      '<ol><li><strong>Open Safari</strong>, ...</li><li><strong>Paste</strong> the clipboard content and go.</li></ol>',
3716  },
3717  de: {
3718    'Add to Calendar': 'Im Kalender speichern',
3719    'iCal File': 'iCal-Datei',
3720    Close: 'Schließen',
3721    'Close Selection': 'Auswahl schließen',
3722    'Click me': 'Klick mich',
3723    'WebView iCal headline': 'Öffne deinen Browser',
3724    'WebView iCal info':
3725      'Leider haben In-App-Browser Probleme mit der Art, wie wir Kalender-Dateien erzeugen.',
3726    'WebView iCal solution 1':
3727      'Wir haben automatisch eine magische URL in die Zwischenablage deines Smartphones kopiert.',
3728    'WebView iCal solution 2':
3729      '<ol><li><strong>Öffne einen anderen Browser</strong> auf deinem Smartphone, ...</li><li>Nutze die <strong>Einfügen</strong>-Funktion, um fortzufahren.</li></ol>',
3730    'Crios iCal headline': 'Öffne Safari',
3731    'Crios iCal info': 'Leider Chrome unter iOS Probleme mit der Art, wie wir Kalender-Dateien erzeugen.',
3732    'Crios iCal solution 2':
3733      '<ol><li><strong>Öffne Safari</strong>, ...</li><li>Nutze die <strong>Einfügen</strong>-Funktion, um fortzufahren.</li></ol>',
3734  },
3735  es: {
3736    'Add to Calendar': 'Añadir al Calendario',
3737    'iCal File': 'iCal Ficha',
3738    Close: 'Ciérralo',
3739    'Close Selection': 'Cerrar Selección',
3740    'Click me': 'Haz clic mí',
3741    'WebView iCal headline': 'Abra su browser',
3742    'WebView iCal info':
3743      'Lamentablemente, los browsers in-app tienen problemas con la forma en que generamos el archivo del calendario.',
3744    'WebView iCal solution 1':
3745      'Hemos copiado automáticamente una URL mágica en el portapapeles de tu smartphone.',
3746    'WebView iCal solution 2':
3747      '<ol><li><strong>Abre otro browser</strong> en tu smartphone, ...</li><li>Utilice la función de <strong>pegar</strong> para continuar.</li></ol>',
3748    'Crios iCal headline': 'Abrir Safari',
3749    'Crios iCal info':
3750      'Lamentablemente, Chrome en iOS tiene problemas con la forma de generar el archivo de calendario.',
3751    'Crios iCal solution 2':
3752      '<ol><li><strong>Abrir Safari</strong>, ...</li><li>Utilice la función de <strong>pegar</strong> para continuar.</li></ol>',
3753  },
3754  pt: {
3755    'Add to Calendar': 'Incluir no Calendário',
3756    'iCal File': 'Ficheiro iCal',
3757    Close: 'Fechar',
3758    'Close Selection': 'Fechar selecção',
3759    'Click me': 'Clicar-me',
3760    'WebView iCal headline': 'Abra o seu browser',
3761    'WebView iCal info':
3762      'Infelizmente, os navegadores em tampas têm problemas com a forma como geramos o ficheiro de calendário.',
3763    'WebView iCal solution 1':
3764      'Copiámos automaticamente um URL mágico para a área de transferência do seu smartphone.',
3765    'WebView iCal solution 2':
3766      '<ol><li><strong>Abrir outro browser</strong> en tu smartphone, ...</li><li>Use a função <forte>colar</strong> para continuar.</li></ol>',
3767    'Crios iCal headline': 'Safari aberto',
3768    'Crios iCal info':
3769      'Infelizmente, o cromado no iOS tem problemas com a forma como geramos o ficheiro do calendário.',
3770    'Crios iCal solution 2':
3771      '<ol><li><strong>Safari aberto</strong>, ...</li><li>Use a função <forte>colar</strong> para continuar.</li></ol>',
3772  },
3773  fr: {
3774    'Add to Calendar': 'Ajout au Calendrier',
3775    'iCal File': 'iCal Fichier',
3776    Close: 'Fermez',
3777    'Close Selection': 'Fermez la sélection',
3778    'Click me': 'Cliquez-moi',
3779    'WebView iCal headline': 'Ouvrez votre navigateur',
3780    'WebView iCal info':
3781      'Malheureusement, les navigateurs in-app ont des problèmes avec la manière dont nous créons les fichiers de calendrier.',
3782    'WebView iCal solution 1':
3783      'Nous avons automatiquement copié une URL magique dans le presse-papiers de ton smartphone.',
3784    'WebView iCal solution 2':
3785      '<ol><li><strong>Ouvre un autre navigateur</strong> sur ton smartphone, ...</li><li>Utilise la fonction <strong>insérer</strong> pour continuer.</li></ol>',
3786    'Crios iCal headline': 'Ouvre Safari',
3787    'Crios iCal info':
3788      'Malheureusement, Chrome sur iOS a des problèmes avec la façon dont nous générons le fichier du calendrier.',
3789    'Crios iCal solution 2':
3790      '<ol><li><strong>Ouvre Safari</strong>, ...</li><li>Utilise la fonction <strong>insérer</strong> pour continuer.</li></ol>',
3791  },
3792  nl: {
3793    'Add to Calendar': 'Opslaan in Kalender',
3794    'iCal File': 'iCal File',
3795    Close: 'Sluiten',
3796    'Close Selection': 'Sluit selectie',
3797    'Click me': 'Klik me',
3798    'WebView iCal headline': 'Open uw browser',
3799    'WebView iCal info':
3800      'Helaas hebben in-app browsers problemen met de manier waarop wij kalenderbestanden maken.',
3801    'WebView iCal solution 1':
3802      'We hebben automatisch een magische URL naar het klembord van uw smartphone gekopieerd.',
3803    'WebView iCal solution 2':
3804      '<ol><li><strong>Open een andere browser</strong> op uw smartphone, ...</li><li>Gebruik de <strong>insert</strong> functie om verder te gaan.</li></ol>',
3805    'Crios iCal headline': 'Open Safari',
3806    'Crios iCal info':
3807      'Helaas heeft Chrome op iOS problemen met de manier waarop we het kalenderbestand genereren.',
3808    'Crios iCal solution 2':
3809      '<ol><li><strong>Open Safari</strong>, ...</li><li>Gebruik de <strong>insert</strong> functie om verder te gaan.</li></ol>',
3810  },
3811  tr: {
3812    'Add to Calendar': 'Takvime Ekle',
3813    'iCal File': 'iCal Dosyası',
3814    Close: 'Kapat',
3815    'Close Selection': 'Seçimi kapat',
3816    'Click me': 'Beni tıklayın',
3817    'WebView iCal headline': 'Tarayıcınızı açın',
3818    'WebView iCal info':
3819      'Ne yazık ki, uygulama içi tarayıcılar takvim dosyalarını oluşturma şeklimizle ilgili sorunlar yaşıyor.',
3820    'WebView iCal solution 1': 'Akıllı telefonunuzun panosuna otomatik olarak sihirli bir URL kopyaladık.',
3821    'WebView iCal solution 2':
3822      '<ol><li><strong>Akıllı telefonunuzda başka bir tarayıcı açın</strong>, ...</li><li>Devam etmek için <strong>insert</strong> fonksiyonunu kullanın.</li></ol>',
3823    'Crios iCal headline': 'Açık Safari',
3824    'Crios iCal info':
3825      "Ne yazık ki iOS'ta Chrome'un takvim dosyası oluşturma yöntemiyle ilgili sorunları var.",
3826    'Crios iCal solution 2':
3827      '<ol><li><strong>Açık Safari</strong>, ...</li><li>Devam etmek için <strong>insert</strong> fonksiyonunu kullanın.</li></ol>',
3828  },
3829  zh: {
3830    'Add to Calendar': '添加到日历',
3831    'iCal File': 'iCal 文件',
3832    Close: '关',
3833    'Close Selection': '关闭选择',
3834    'Click me': '点我',
3835    'WebView iCal headline': '打开浏览器',
3836    'WebView iCal info': '不幸的是,应用内浏览器在我们生成日历文件的方式上存在问题.',
3837    'WebView iCal solution 1': '我们会自动将一个神奇的 URL 放入您手机的剪贴板.',
3838    'WebView iCal solution 2':
3839      '<ol><li>打开手机上的任何其他浏览器, ...</li><li>粘贴剪贴板内容并开始.</li></ol>',
3840    'Crios iCal headline': '打开 Safari',
3841    'Crios iCal info': '不幸的是,iOS 上的 Chrome 在我们生成日历文件的方式上存在问题.',
3842    'Crios iCal solution 2':
3843      '<ol><li><strong>打开 Safari</strong>, ...</li><li>粘贴剪贴板内容并开始.</li></ol>',
3844  },
3845  ar: {
3846    'Add to Calendar': 'إضافة إلى التقويم',
3847    'iCal File': 'ملف iCal',
3848    Close: 'قريب',
3849    'Close Selection': 'إغلاق التحديد',
3850    'Click me': 'انقر فوق لي',
3851    'WebView iCal headline': 'افتح المستعرض الخاص بك',
3852    'WebView iCal info': 'لسوء الحظ ، تواجه المتصفحات داخل التطبيق مشاكل في طريقة إنشاء ملف التقويم.',
3853    'WebView iCal solution 1': 'نضع تلقائيًا عنوان ويب سحريًا في حافظة هاتفك.',
3854    'WebView iCal solution 2':
3855      '<ol><li>افتح أي متصفح آخر على هاتفك الذكي, ...</li><li>الصق محتوى الحافظة واذهب.</li></ol>',
3856    'Crios iCal headline': 'افتح Safari',
3857    'Crios iCal info': 'لسوء الحظ ، يواجه Chrome على iOS مشاكل في طريقة إنشاء ملف التقويم.',
3858    'Crios iCal solution 2':
3859      '<ol><li><strong>افتح Safari</strong>, ...</li><li>الصق محتوى الحافظة واذهب.</li></ol>',
3860  },
3861  hi: {
3862    'Add to Calendar': 'कैलेंडर में जोड़ें',
3863    'iCal File': 'iCal फ़ाइल',
3864    Close: 'बंद करना',
3865    'Close Selection': 'चयन बंद करें',
3866    'Click me': 'मुझे क्लिक करें',
3867    'WebView iCal headline': 'अपना ब्राउज़र खोलें',
3868    'WebView iCal info': 'दुर्भाग्य से, इन-ऐप ब्राउज़र में कैलेंडर फ़ाइल बनाने के तरीके में समस्याएँ हैं।',
3869    'WebView iCal solution 1': 'हम स्वचालित रूप से आपके फ़ोन के क्लिपबोर्ड में एक जादुई URL डालते हैं।',
3870    'WebView iCal solution 2':
3871      '<ol><li>अपने फ़ोन पर <strong>दूसरा ब्राउज़र खोलें</strong>, ...</li><li>क्लिपबोर्ड सामग्री <strong>चिपकाएं</strong> और जाएं।</li></ol>',
3872    'Crios iCal headline': 'सफारी खोलें',
3873    'Crios iCal info':
3874      'दुर्भाग्य से, iOS पर Chrome को कैलेंडर फ़ाइल जेनरेट करने के हमारे तरीके में समस्या है।',
3875    'Crios iCal solution 2':
3876      '<ol><li><strong>सफारी खोलें</strong>, ...</li><li>क्लिपबोर्ड सामग्री <strong>चिपकाएं</strong> और जाएं।</li></ol>',
3877  },
3878  pl: {
3879    'Add to Calendar': 'Dodaj do kalendarza',
3880    'iCal File': 'Plik iCal',
3881    Close: 'Zamknij',
3882    'Close Selection': 'Zamknij wybór',
3883    'Click me': 'Kliknij mnie',
3884    'WebView iCal headline': 'Otwórz przeglądarkę',
3885    'WebView iCal info':
3886      'Niestety, przeglądarki in-app mają problemy ze sposobem, w jaki generujemy plik kalendarza.',
3887    'WebView iCal solution 1': 'Automatycznie umieszczamy magiczny adres URL w schowku telefonu.',
3888    'WebView iCal solution 2':
3889      '<ol><li><strong>Otwórz inną przeglądarkę</strong> w swoim telefonie, ...</li><li><strong>Wklej</strong> zawartość schowka i ruszaj.</li></ol>',
3890    'Crios iCal headline': 'Otwórz Safari',
3891    'Crios iCal info': 'Niestety, Chrome na iOS ma problemy ze sposobem generowania pliku kalendarza.',
3892    'Crios iCal solution 2':
3893      '<ol><li><strong>Otwórz Safari</strong>, ...</li><li><strong>Wklej</strong> zawartość schowka i ruszaj.</li></ol>',
3894  },
3895  id: {
3896    'Add to Calendar': 'Tambahkan ke Kalender',
3897    'iCal File': 'File iCal',
3898    Close: 'Tutup',
3899    'Close Selection': 'Seleksi Tutup',
3900    'Click me': 'Klik saya',
3901    'WebView iCal headline': 'Buka browser Anda',
3902    'WebView iCal info':
3903      'Sayangnya, browser dalam aplikasi memiliki masalah dengan cara kami menghasilkan file kalender.',
3904    'WebView iCal solution 1': 'Kami secara otomatis memasukkan URL ajaib ke 
3904clipboard ponsel Anda.',
3905    'WebView iCal solution 2':
3906      '<ol><li><strong>Buka peramban lain</strong> pada ponsel Anda, ...</li><li>Tempelkan konten clipboard dan pergi.</li></ol>',
3907    'Crios iCal headline': 'Buka Safari',
3908    'Crios iCal info':
3909      'Sayangnya, Chrome di iOS memiliki masalah dengan cara kami menghasilkan file kalender.',
3910    'Crios iCal solution 2':
3911      '<ol><li><strong>Buka Safari</strong>, ...</li><li>Tempelkan konten clipboard dan pergi.</li></ol>',
3912  },
3913  no: {
3914    'Add to Calendar': 'Legg til i kalenderen',
3915    'iCal File': 'iCal-fil',
3916    Close: 'Lukk',
3917    'Close Selection': 'Lukk utvalg',
3918    'Click me': 'Klikk på meg',
3919    'WebView iCal headline': 'Åpne nettleseren din',
3920    'WebView iCal info':
3921      'Dessverre har nettlesere i appen problemer med måten vi genererer kalenderfilen på.',
3922    'WebView iCal solution 1': 'Vi legger automatisk inn en magisk URL i telefonens utklippstavle.',
3923    'WebView iCal solution 2':
3924      '<ol><li><strong>Åpne en annen nettleser</strong> på telefonen, ...</li><li><strong>Lim inn</strong> innholdet på utklippstavlen og gå.</li></ol>',
3925    'Crios iCal headline': 'Åpne Safari',
3926    'Crios iCal info': 'Dessverre har Chrome på iOS problemer med måten vi genererer kalenderfilen på.',
3927    'Crios iCal solution 2':
3928      '<ol><li><strong>Åpne Safari</strong>, ...</li><li><strong>Lim inn</strong> innholdet på utklippstavlen og gå.</li></ol>',
3929  },
3930  fi: {
3931    'Add to Calendar': 'Lisää kalenteriin',
3932    'iCal File': 'iCal-tiedosto',
3933    Close: 'Sulje',
3934    'Close Selection': 'Sulje valinta',
3935    'Click me': 'Klikkaa minua',
3936    'WebView iCal headline': 'Avaa selain',
3937    'WebView iCal info':
3938      'Valitettavasti sovelluksen sisäisillä selaimilla on ongelmia kalenteritiedoston luomisessa.',
3939    'WebView iCal solution 1': 'Laitamme automaattisesti maagisen URL-osoitteen puhelimesi leikepöydälle.',
3940    'WebView iCal solution 2':
3941      '<ol><li><strong>Avaa toinen selain</strong> puhelimessasi., ...</li><li><strong>liitä</strong> leikepöydän sisältö ja lähde.</li></ol>',
3942    'Crios iCal headline': 'Avaa Safari',
3943    'Crios iCal info': 'Valitettavasti iOS:n Chromessa on ongelmia kalenteritiedoston luomisessa.',
3944    'Crios iCal solution 2':
3945      '<ol><li><strong>Avaa Safari</strong>, ...</li><li><strong>liitä</strong> leikepöydän sisältö ja lähde.</li></ol>',
3946  },
3947  sv: {
3948    'Add to Calendar': 'Lägg till i kalender',
3949    'iCal File': 'iCal-fil',
3950    Close: 'Stäng',
3951    'Close Selection': 'Stäng urvalet',
3952    'Click me': 'Klicka på mig',
3953    'WebView iCal headline': 'Öppna din webbläsare',
3954    'WebView iCal info': 'Tyvärr har webbläsare i appen problem med hur vi genererar kalenderfilen.',
3955    'WebView iCal solution 1': 'Vi lägger automatiskt in en magisk webbadress i telefonens klippbräda.',
3956    'WebView iCal solution 2':
3957      '<ol><li><strong>Öppna en annan webbläsare</strong> på telefonen, ...</li><li><strong>Insätt</strong> innehållet i klippbordet och kör.</li></ol>',
3958    'Crios iCal headline': 'Öppna Safari',
3959    'Crios iCal info': 'Tyvärr har Chrome på iOS problem med hur vi genererar kalenderfilen.',
3960    'Crios iCal solution 2':
3961      '<ol><li><strong>Öppna Safari</strong>, ...</li><li><strong>Insätt</strong> innehållet i klippbordet och kör.</li></ol>',
3962  },
3963  cs: {
3964    'Add to Calendar': 'Přidat do kalendáře',
3965    'iCal File': 'Soubor iCal',
3966    Close: 'Zavřít',
3967    'Close Selection': 'Zavřít výběr',
3968    'Click me': 'Klikněte na mě',
3969    'WebView iCal headline': 'Otevřete prohlížeč',
3970    'WebView iCal info':
3971      'Prohlížeče v aplikacích mají bohužel problémy se způsobem generování souboru kalendáře.',
3972    'WebView iCal solution 1': 'Do schránky telefonu automaticky vložíme kouzelnou adresu URL.',
3973    'WebView iCal solution 2':
3974      '<ol><li><strong>Otevření jiného prohlížeče</strong> v telefonu, ...</li><li><strong>Vložte</strong> obsah schránky a přejděte.</li></ol>',
3975    'Crios iCal headline': 'Otevřít Safari',
3976    'Crios iCal info': 'Chrome v systému iOS má bohužel problémy se způsobem generování souboru kalendáře.',
3977    'Crios iCal solution 2':
3978      '<ol><li><strong>Otevřít Safari</strong>, ...</li><li><strong>Vložte</strong>
3978 obsah schránky a přejděte.</li></ol>',
3979  },
3980  ja: {
3981    'Add to Calendar': 'カレンダーに追加',
3982    'iCal File': 'iCalファイル',
3983    Close: '閉じる',
3984    'Close Selection': 'クローズ選択',
3985    'Click me': 'クリックしてください',
3986    'WebView iCal headline': 'ブラウザを起動する',
3987    'WebView iCal info': '残念ながら、アプリ内ブラウザは、カレンダーファイルの生成方法に問題があります。',
3988    'WebView iCal solution 1': 'あなたの携帯電話のクリップボードに、魔法のようなURLを自動的に入れます。',
3989    'WebView iCal solution 2':
3990      '<ol><li>スマートフォンで別のブラウザを起動する, ...</li><li>クリップボードの内容を貼り付けて行く。</li></ol>',
3991    'Crios iCal headline': 'オープンSafari',
3992    'Crios iCal info': '残念ながら、iOS版Chromeでは、カレンダーファイルの生成方法に問題があります。',
3993    'Crios iCal solution 2':
3994      '<ol><li><strong>オープンSafari</strong>, ...</li><li>クリップボードの内容を貼り付けて行く。</li></ol>',
3995  },
3996  it: {
3997    'Add to Calendar': 'Aggiungi al calendario',
3998    'iCal File': 'File iCal',
3999    Close: 'Chiudere',
4000    'Close Selection': 'Chiudere la selezione',
4001    'Click me': 'Clicca su di me',
4002    'WebView iCal headline': 'Aprire il browser',
4003    'WebView iCal info':
4004      'Purtroppo i browser in-app hanno problemi con il modo in cui generiamo il file del calendario.',
4005    'WebView iCal solution 1': 'Inseriamo automaticamente un URL magico negli appunti del telefono.',
4006    'WebView iCal solution 2':
4007      '<ol><li><strong>Aprire un altro browser</strong> sul cellulare, ...</li><li><strong>Incollare</strong> il contenuto degli appunti e partire.</li></ol>',
4008    'Crios iCal headline': 'Aprire Safari',
4009    'Crios iCal info':
4010      'Purtroppo, Chrome su iOS ha problemi con il modo in cui generiamo il file del calendario.',
4011    'Crios iCal solution 2':
4012      '<ol><li><strong>Aprire Safari</strong>, ...</li><li><strong>Incollare</strong> il contenuto degli appunti e partire.</li></ol>',
4013  },
4014  ko: {
4015    'Add to Calendar': '캘린더에 추가',
4016    'iCal File': 'iCal 파일',
4017    Close: '닫다',
4018    'Close Selection': '선택 닫기',
4019    'Click me': '클릭 해주세요',
4020    'WebView iCal headline': '브라우저 열기',
4021    'WebView iCal info': '불행히도 인앱 브라우저는 캘린더 파일을 생성하는 방식에 문제가 있습니다.',
4022    'WebView iCal solution 1': '자동으로 마법의 URL을 휴대전화의 클립보드에 넣습니다.',
4023    'WebView iCal solution 2':
4024      '<ol><li>휴대전화에서 다른 브라우저 열기, ...</li><li>클립보드 내용을 붙여넣고 이동합니다.</li></ol>',
4025    'Crios iCal headline': 'Safari 열기',
4026    'Crios iCal info': '불행히도 iOS의 Chrome은 캘린더 파일을 생성하는 방식에 문제가 있습니다.',
4027    'Crios iCal solution 2':
4028      '<ol><li><strong>Safari 열기</strong>, ...</li><li>클립보드 내용을 붙여넣고 이동합니다.</li></ol>',
4029  },
4030  vi: {
4031    'Add to Calendar': 'Thêm vào Lịch',
4032    'iCal File': 'Tệp iCal',
4033    Close: 'Đóng',
4034    'Close Selection': 'Đóng lựa chọn',
4035    'Click me': 'Nhấp vào đây',
4036    'WebView iCal headline': 'Mở trình duyệt của bạn',
4037    'WebView iCal info':
4038      'Rất tiếc, các trình duyệt trong ứng dụng gặp sự cố với cách chúng tôi tạo tệp lịch.',
4039    'WebView iCal solution 1':
4040      'Chúng tôi tự động đặt một URL kỳ diệu vào khay nhớ tạm thời trên điện thoại của bạn.',
4041    'WebView iCal solution 2':
4042      '<ol><li><strong> Mở trình duyệt khác </strong> trên điện thoại của bạn, ...</li><li><strong> Dán </strong> nội dung khay nhớ tạm và bắt đầu.</li></ol>',
4043    'Crios iCal headline': 'Mở Safari',
4044    'Crios iCal info': 'Rất tiếc, Chrome trên iOS gặp sự cố với cách chúng tôi tạo tệp lịch.',
4045    'Crios iCal solution 2':
4046      '<ol><li><strong>Mở Safari</strong>, ...</li><li><strong> Dán </strong> nội dung khay nhớ tạm và bắt đầu.</li></ol>',
4047  },
4048};
4049
4050// hook, which can be used to override all potential "hard" strings by setting customLabel_ + the key (without spaces) as option key and the intended string as value
4051function atcb_translate_hook(identifier, language, data) {
4052  const searchKey = identifier.replace(/\s+/g, '').toLowerCase();
4053  if (
4054    data.customLabels != null &&
4055    data.customLabels[`${searchKey}`] != null &&
4056    data.customLabels[`${searchKey}`] != ''
4057  ) {
4058    return atcb_rewrite_html_elements(data.customLabels[`${searchKey}`]);
4059  } else {
4060    return atcb_translate(identifier, language);
4061  }
4062}
4063
4064function atcb_translate(identifier, language) {
4065  // set default language
4066  if (!language) {
4067    language = 'en';
4068  }
4069  // return string, if available
4070  if (i18nStrings[`${language}`][`${identifier}`]) {
4071    return i18nStrings[`${language}`][`${identifier}`];
4072  }
4073  // if nothing found, return the original identifier
4074  return identifier;
4075}
4076
4077// START INIT
4078if (isBrowser()) {
4079  if (document.readyState !== 'loading') {
4080    // if the script is loaded after the page has been loaded, run the initilization
4081    atcb_init();
4082  } else {
4083    // otherwise, init the magic as soon as the DOM has been loaded
4084    document.addEventListener('DOMContentLoaded', atcb_init, false);
4085  }
4086}
4087// END INIT

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.