add-to-calendar-button 2.2.3 → 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,15 +7,15 @@ const tzlibActions = require('timezones-ical-library');
7
7
  * Add to Calendar Button
8
8
  * ++++++++++++++++++++++
9
9
  *
10
- * Version: 2.2.3
10
+ * Version: 2.2.4
11
11
  * Creator: Jens Kuerschner (https://jenskuerschner.de)
12
12
  * Project: https://github.com/add2cal/add-to-calendar-button
13
13
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
14
14
  * Note: DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
15
15
  *
16
16
  */
17
- const atcbVersion = '2.2.3';
18
- const atcbCssTemplate = {
17
+ const atcbVersion = '2.2.4';
18
+ const atcbCssTemplate = {
19
19
  if (typeof window === 'undefined') {
20
20
  return false;
21
21
  } else {
@@ -440,11 +440,15 @@ function atcb_decorate_data_meta(data) {
440
440
  }
441
441
  function atcb_decorate_data_description(data, i) {
442
442
  if (data.dates[`${i}`].description != null && data.dates[`${i}`].description != '') {
443
+ data.dates[`${i}`].description = data.dates[`${i}`].description.replace(/(\\r\\n|\\n|\\r|<br(\s|\s\/|\/|)>)/g, '');
443
444
  data.dates[`${i}`].descriptionHtmlFree = atcb_rewrite_html_elements(data.dates[`${i}`].description, true);
445
+ data.dates[`${i}`].descriptionHtmlFreeICal = atcb_rewrite_html_elements(data.dates[`${i}`].description, true, true);
444
446
  data.dates[`${i}`].description = atcb_rewrite_html_elements(data.dates[`${i}`].description);
445
447
  } else {
446
448
  if (data.dates[`${i}`].description == null && data.description != null && data.description != '') {
449
+ data.description = data.description.replace(/(\\r\\n|\\n|\\r|<br(\s|\s\/|\/|)>)/g, '');
447
450
  data.dates[`${i}`].descriptionHtmlFree = atcb_rewrite_html_elements(data.description, true);
451
+ data.dates[`${i}`].descriptionHtmlFreeICal = atcb_rewrite_html_elements(data.description, true, true);
448
452
  data.dates[`${i}`].description = atcb_rewrite_html_elements(data.description);
449
453
  } else {
450
454
  data.dates[`${i}`].descriptionHtmlFree = data.dates[`${i}`].description = '';
@@ -1886,25 +1890,28 @@ function atcb_generate_rich_data_recurrence(data, formattedDate) {
1886
1890
 
1887
1891
 
1888
1892
  function atcb_generate_links(host, type, data, subEvent = 'all', keyboardTrigger = false, multiDateModal = false) {
1893
+ let linkType = type;
1894
+ if (type == 'apple') {
1895
+ linkType = 'ical';
1896
+ }
1897
+ if (isMobile() && (type == 'msteams' || type == 'ms365' || type == 'outlookcom')) {
1898
+ linkType = 'ical';
1899
+ }
1889
1900
  if (subEvent != 'all') {
1890
1901
  subEvent = parseInt(subEvent) - 1;
1891
1902
  } else if (data.dates.length == 1) {
1892
1903
  subEvent = 0;
1893
1904
  }
1894
1905
  if (data.subscribe) {
1895
- atcb_generate_subscribe_links(host, type, data, keyboardTrigger);
1906
+ atcb_generate_subscribe_links(host, linkType, data, keyboardTrigger);
1896
1907
  return;
1897
1908
  }
1898
- if (isMobile() && (type == 'msteams' || type == 'ms365' || type == 'outlookcom')) {
1899
- type = 'ical';
1900
- }
1901
1909
  if (subEvent != 'all') {
1902
- if (data.dates[`${subEvent}`].status == 'CANCELLED' && type != 'apple' && type != 'ical') {
1910
+ if (data.dates[`${subEvent}`].status == 'CANCELLED' && linkType != 'ical') {
1903
1911
  atcb_create_modal(host, data, 'warning', atcb_translate_hook('date.status.cancelled', data), atcb_translate_hook('date.status.cancelled.cta', data), [], [], keyboardTrigger);
1904
1912
  } else {
1905
- switch (type) {
1906
- case 'apple':
1907
- case 'ical':
1913
+ switch (linkType) {
1914
+ case 'ical': // also for apple (see above)
1908
1915
  atcb_generate_ical(host, data, subEvent, keyboardTrigger);
1909
1916
  break;
1910
1917
  case 'google':
@@ -1940,12 +1947,11 @@ function atcb_generate_links(host, type, data, subEvent = 'all', keyboardTrigger
1940
1947
  }
1941
1948
  return;
1942
1949
  }
1943
- atcb_generate_multidate_links(host, type, data, keyboardTrigger, multiDateModal);
1950
+ atcb_generate_multidate_links(host, type, linkType, data, keyboardTrigger, multiDateModal);
1944
1951
  }
1945
- function atcb_generate_multidate_links(host, type, data, keyboardTrigger, multiDateModal) {
1952
+ function atcb_generate_multidate_links(host, type, linkType, data, keyboardTrigger, multiDateModal) {
1946
1953
  if (
1947
- (type == 'ical' || type == 'apple') &&
1948
- data.dates.every(function (theSubEvent) {
1954
+ (linkType == 'ical') && data.dates.every(function (theSubEvent) {
1949
1955
  if (theSubEvent.status == 'CANCELLED' || (theSubEvent.organizer != null && theSubEvent.organizer != '')) {
1950
1956
  return false;
1951
1957
  }
@@ -1967,11 +1973,10 @@ function atcb_generate_multidate_links(host, type, data, keyboardTrigger, multiD
1967
1973
  atcb_create_modal(host, data, type, atcb_translate_hook('modal.multidate.h', data), atcb_translate_hook('modal.multidate.text', data), [], individualButtons, keyboardTrigger);
1968
1974
  }
1969
1975
  }
1970
- function atcb_generate_subscribe_links(host, type, data, keyboardTrigger) {
1976
+ function atcb_generate_subscribe_links(host, linkType, data, keyboardTrigger) {
1971
1977
  const adjustedFileUrl = data.icsFile.replace('https://', 'webcal://');
1972
- switch (type) {
1973
- case 'apple':
1974
- case 'ical':
1978
+ switch (linkType) {
1979
+ case 'ical': // also for apple (see above)
1975
1980
  atcb_subscribe_ical(adjustedFileUrl);
1976
1981
  break;
1977
1982
  case 'google':
@@ -2148,7 +2153,7 @@ function atcb_generate_microsoft(data, type = '365') {
2148
2153
  urlParts.push('location=' + encodeURIComponent(data.location));
2149
2154
  }
2150
2155
  if (data.description != null && data.description != '') {
2151
- urlParts.push('body=' + encodeURIComponent(data.description.replace(/\n/g, '<br>')));
2156
+ urlParts.push('body=' + encodeURIComponent(data.description));
2152
2157
  }
2153
2158
  urlParts.push('uid=' + encodeURIComponent(data.uid));
2154
2159
  atcb_open_cal_url(urlParts.join('&'));
@@ -2251,25 +2256,25 @@ function atcb_generate_ical(host, data, subEvent = 'all', keyboardTrigger = fals
2251
2256
  ics_lines.push('DTSTAMP:' + atcb_format_datetime(now, 'clean', true));
2252
2257
  ics_lines.push('DTSTART' + timeAddon + ':' + formattedDate.start);
2253
2258
  ics_lines.push('DTEND' + timeAddon + ':' + formattedDate.end);
2254
- ics_lines.push('SUMMARY:' + data.dates[`${i}`].name.replace(/.{65}/g, '$&' + '\r\n '));
2255
- if (data.dates[`${i}`].descriptionHtmlFree != null && data.dates[`${i}`].descriptionHtmlFree != '') {
2259
+ ics_lines.push('SUMMARY:' + atcb_rewrite_ical_text(data.dates[`${i}`].name, true));
2260
+ if (data.dates[`${i}`].descriptionHtmlFreeICal != null && data.dates[`${i}`].descriptionHtmlFreeICal != '') {
2256
2261
  ics_lines.push(
2257
- 'DESCRIPTION:' + data.dates[`${i}`].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
2262
+ 'DESCRIPTION:' + atcb_rewrite_ical_text(data.dates[`${i}`].descriptionHtmlFreeICal, true)
2258
2263
  );
2259
2264
  }
2260
2265
  if (data.dates[`${i}`].description != null && data.dates[`${i}`].description != '') {
2261
- ics_lines.push('X-ALT-DESC;FMTTYPE=text/html:\r\n <!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN"">\r\n <HTML><BODY>\r\n ' + data.dates[`${i}`].description.replace(/\n/g, '<br>').replace(/.{60}/g, '$&' + '\r\n ') + '\r\n </BODY></HTML>');
2266
+ ics_lines.push('X-ALT-DESC;FMTTYPE=text/html:\r\n <!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN"">\r\n <HTML><BODY>\r\n ' + atcb_rewrite_ical_text(data.dates[`${i}`].description, true) + '\r\n </BODY></HTML>');
2262
2267
  }
2263
2268
  if (data.dates[`${i}`].location != null && data.dates[`${i}`].location != '') {
2264
- ics_lines.push('LOCATION:' + data.dates[`${i}`].location);
2269
+ ics_lines.push('LOCATION:' + atcb_rewrite_ical_text(data.dates[`${i}`].location, true));
2265
2270
  }
2266
2271
  if (data.dates[`${i}`].organizer != null && data.dates[`${i}`].organizer != '') {
2267
2272
  const organizerParts = data.dates[`${i}`].organizer.split('|');
2268
- ics_lines.push('ORGANIZER;CN=' + organizerParts[0] + ':MAILTO:' + organizerParts[1]);
2273
+ ics_lines.push('ORGANIZER;CN="' + atcb_rewrite_ical_text(organizerParts[0], false, true) + '":MAILTO:' + organizerParts[1]);
2269
2274
  }
2270
2275
  if (data.dates[`${i}`].attendee != null && data.dates[`${i}`].attendee != '') {
2271
2276
  const attendeeParts = data.dates[`${i}`].attendee.split('|');
2272
- ics_lines.push('ATTENDEE;ROLE=REQ-PARTICIPANT;CN=' + attendeeParts[0] + ':MAILTO:' + attendeeParts[1]);
2277
+ ics_lines.push('ATTENDEE;ROLE=REQ-PARTICIPANT;CN="' + atcb_rewrite_ical_text(attendeeParts[0], false, true) + '":MAILTO:' + attendeeParts[1]);
2273
2278
  }
2274
2279
  if (data.recurrence != null && data.recurrence != '') {
2275
2280
  ics_lines.push(data.recurrence);
@@ -2471,11 +2476,16 @@ function atcb_validEmail(email, mx = false) {
2471
2476
  }
2472
2477
  return true;
2473
2478
  }
2474
- function atcb_rewrite_html_elements(content, clear = false) {
2475
- content = content.replace(/<br\s*\/?>/gi, '\n');
2479
+ function atcb_rewrite_html_elements(content, clear = false, iCalBreaks = false) {
2476
2480
  if (clear) {
2477
- content = content.replace(/\[(|\/)(url|br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]|((\|.*)\[\/url\])/gi, '');
2478
- content = content.replace(/\{(|\/)(url|br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\}|((\|.*)\{\/url\})/gi, '');
2481
+ content = content.replace(/\[(|\/)(url|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]|((\|.*)\[\/url\])/gi, '');
2482
+ content = content.replace(/\{(|\/)(url|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\}|((\|.*)\{\/url\})/gi, '');
2483
+ if (iCalBreaks) {
2484
+ content = content.replace(/(\[br\]|\{br\})/gi, '\\n');
2485
+ } else {
2486
+ content = content.replace(/(\[br\]|\{br\})/gi, ' ');
2487
+ }
2488
+ content = content.replace(/&[#a-zA-Z0-9]{1,9};/gi, '');
2479
2489
  } else {
2480
2490
  content = content.replace(/\[(\/|)(br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]/gi, '<$1$2>');
2481
2491
  content = content.replace(/\{(\/|)(br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\}/gi, '<$1$2>');
@@ -2499,6 +2509,17 @@ function atcb_parse_url_code(input) {
2499
2509
  })();
2500
2510
  return '<a href="' + urlText[0] + '" target="' + atcbDefaultTarget + '" rel="noopener">' + text + '</a>';
2501
2511
  }
2512
+ function atcb_rewrite_ical_text(content, truncate = true, inQuotes = false) {
2513
+ if (inQuotes) {
2514
+ content = content.replace(/"/g, '');
2515
+ } else {
2516
+ content = content.replace(/\\/g, '\\\\').replace(/(,|;)/g, '\\$1');
2517
+ }
2518
+ if (truncate) {
2519
+ content = content.replace(/.{60}/g, '$&' + '\r\n ');
2520
+ }
2521
+ return content;
2522
+ }
2502
2523
  function atcb_position_list(host, trigger, list, blockUpwards = false, resize = false) {
2503
2524
  let anchorSet = false;
2504
2525
  const originalTrigger = trigger;
@@ -3279,7 +3300,7 @@ function atcb_read_attributes(el) {
3279
3300
  for (let i = 0; i < atcbWcParams.length; i++) {
3280
3301
  let attr = atcbWcParams[`${i}`];
3281
3302
  if (el.hasAttribute(`${attr}`)) {
3282
- let inputVal = atcb_secure_content(el.getAttribute(`${attr}`).replace(/(\r\n|\n|\r)/g, ''), false);
3303
+ let inputVal = atcb_secure_content(el.getAttribute(`${attr}`).replace(/(\\r\\n|\\n|\\r)/g, ''), false);
3283
3304
  let val;
3284
3305
  if (atcbWcBooleanParams.includes(attr)) {
3285
3306
  if (inputVal == '') {
@@ -3324,7 +3345,7 @@ function atcb_read_attributes(el) {
3324
3345
  }
3325
3346
  const identifierAttr = el.getAttribute('identifier');
3326
3347
  if (identifierAttr != null && identifierAttr != '') {
3327
- data['identifier'] = atcb_secure_content(identifierAttr.replace(/(\r\n|\n|\r)/g, ''), false);
3348
+ data['identifier'] = atcb_secure_content(identifierAttr.replace(/(\\r\\n|\\n|\\r)/g, ''), false);
3328
3349
  }
3329
3350
  }
3330
3351
  if (!atcb_check_required(data)) {
@@ -3332,7 +3353,7 @@ function atcb_read_attributes(el) {
3332
3353
  const atcbJsonInput = (function () {
3333
3354
  if (slotInput != '') {
3334
3355
  try {
3335
- return JSON.parse(atcb_secure_content(slotInput.replace(/(\r\n|\n|\r)/g, ''), false));
3356
+ return JSON.parse(atcb_secure_content(slotInput.replace(/(\\r\\n|\\n|\\r)/g, ''), false));
3336
3357
  } catch (e) {
3337
3358
  throw new Error('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: ' + e);
3338
3359
  }
@@ -3424,14 +3445,14 @@ function atcb_load_css(host, rootObj = null, style = '', inline = false, buttons
3424
3445
  const cssContent = document.createElement('style');
3425
3446
  const overrideDefaultCss = (function () {
3426
3447
  if (host.host.hasAttribute('styleLight')) {
3427
- const output = ':host { ' + atcb_secure_content(host.host.getAttribute('styleLight').replace(/(\r\n|\n|\r)/g, ''), false) + ' }';
3448
+ const output = ':host { ' + atcb_secure_content(host.host.getAttribute('styleLight').replace(/(\\r\\n|\\n|\\r)/g, ''), false) + ' }';
3428
3449
  return output;
3429
3450
  }
3430
3451
  return '';
3431
3452
  })();
3432
3453
  const overrideDarkCss = (function () {
3433
3454
  if (host.host.hasAttribute('styleDark')) {
3434
- const output = ':host(.atcb-dark), :host-context(html.atcb-dark):host(.atcb-bodyScheme), :host-context(body.atcb-dark):host(.atcb-bodyScheme) { ' + atcb_secure_content(host.host.getAttribute('styleDark').replace(/(\r\n|\n|\r)/g, ''), false) + ' }';
3455
+ const output = ':host(.atcb-dark), :host-context(html.atcb-dark):host(.atcb-bodyScheme), :host-context(body.atcb-dark):host(.atcb-bodyScheme) { ' + atcb_secure_content(host.host.getAttribute('styleDark').replace(/(\\r\\n|\\n|\\r)/g, ''), false) + ' }';
3435
3456
  return output;
3436
3457
  }
3437
3458
  return '';
@@ -6,15 +6,15 @@ import { tzlib_get_ical_block, tzlib_get_offset, tzlib_get_timezones } from 'tim
6
6
  * Add to Calendar Button
7
7
  * ++++++++++++++++++++++
8
8
  *
9
- * Version: 2.2.3
9
+ * Version: 2.2.4
10
10
  * Creator: Jens Kuerschner (https://jenskuerschner.de)
11
11
  * Project: https://github.com/add2cal/add-to-calendar-button
12
12
  * License: Elastic License 2.0 (ELv2) (https://github.com/add2cal/add-to-calendar-button/blob/main/LICENSE.txt)
13
13
  * Note: DO NOT REMOVE THE COPYRIGHT NOTICE ABOVE!
14
14
  *
15
15
  */
16
- const atcbVersion = '2.2.3';
17
- const atcbCssTemplate = {
16
+ const atcbVersion = '2.2.4';
17
+ const atcbCssTemplate = {
18
18
  if (typeof window === 'undefined') {
19
19
  return false;
20
20
  } else {
@@ -439,11 +439,15 @@ function atcb_decorate_data_meta(data) {
439
439
  }
440
440
  function atcb_decorate_data_description(data, i) {
441
441
  if (data.dates[`${i}`].description != null && data.dates[`${i}`].description != '') {
442
+ data.dates[`${i}`].description = data.dates[`${i}`].description.replace(/(\\r\\n|\\n|\\r|<br(\s|\s\/|\/|)>)/g, '');
442
443
  data.dates[`${i}`].descriptionHtmlFree = atcb_rewrite_html_elements(data.dates[`${i}`].description, true);
444
+ data.dates[`${i}`].descriptionHtmlFreeICal = atcb_rewrite_html_elements(data.dates[`${i}`].description, true, true);
443
445
  data.dates[`${i}`].description = atcb_rewrite_html_elements(data.dates[`${i}`].description);
444
446
  } else {
445
447
  if (data.dates[`${i}`].description == null && data.description != null && data.description != '') {
448
+ data.description = data.description.replace(/(\\r\\n|\\n|\\r|<br(\s|\s\/|\/|)>)/g, '');
446
449
  data.dates[`${i}`].descriptionHtmlFree = atcb_rewrite_html_elements(data.description, true);
450
+ data.dates[`${i}`].descriptionHtmlFreeICal = atcb_rewrite_html_elements(data.description, true, true);
447
451
  data.dates[`${i}`].description = atcb_rewrite_html_elements(data.description);
448
452
  } else {
449
453
  data.dates[`${i}`].descriptionHtmlFree = data.dates[`${i}`].description = '';
@@ -1885,25 +1889,28 @@ function atcb_generate_rich_data_recurrence(data, formattedDate) {
1885
1889
 
1886
1890
 
1887
1891
  function atcb_generate_links(host, type, data, subEvent = 'all', keyboardTrigger = false, multiDateModal = false) {
1892
+ let linkType = type;
1893
+ if (type == 'apple') {
1894
+ linkType = 'ical';
1895
+ }
1896
+ if (isMobile() && (type == 'msteams' || type == 'ms365' || type == 'outlookcom')) {
1897
+ linkType = 'ical';
1898
+ }
1888
1899
  if (subEvent != 'all') {
1889
1900
  subEvent = parseInt(subEvent) - 1;
1890
1901
  } else if (data.dates.length == 1) {
1891
1902
  subEvent = 0;
1892
1903
  }
1893
1904
  if (data.subscribe) {
1894
- atcb_generate_subscribe_links(host, type, data, keyboardTrigger);
1905
+ atcb_generate_subscribe_links(host, linkType, data, keyboardTrigger);
1895
1906
  return;
1896
1907
  }
1897
- if (isMobile() && (type == 'msteams' || type == 'ms365' || type == 'outlookcom')) {
1898
- type = 'ical';
1899
- }
1900
1908
  if (subEvent != 'all') {
1901
- if (data.dates[`${subEvent}`].status == 'CANCELLED' && type != 'apple' && type != 'ical') {
1909
+ if (data.dates[`${subEvent}`].status == 'CANCELLED' && linkType != 'ical') {
1902
1910
  atcb_create_modal(host, data, 'warning', atcb_translate_hook('date.status.cancelled', data), atcb_translate_hook('date.status.cancelled.cta', data), [], [], keyboardTrigger);
1903
1911
  } else {
1904
- switch (type) {
1905
- case 'apple':
1906
- case 'ical':
1912
+ switch (linkType) {
1913
+ case 'ical': // also for apple (see above)
1907
1914
  atcb_generate_ical(host, data, subEvent, keyboardTrigger);
1908
1915
  break;
1909
1916
  case 'google':
@@ -1939,12 +1946,11 @@ function atcb_generate_links(host, type, data, subEvent = 'all', keyboardTrigger
1939
1946
  }
1940
1947
  return;
1941
1948
  }
1942
- atcb_generate_multidate_links(host, type, data, keyboardTrigger, multiDateModal);
1949
+ atcb_generate_multidate_links(host, type, linkType, data, keyboardTrigger, multiDateModal);
1943
1950
  }
1944
- function atcb_generate_multidate_links(host, type, data, keyboardTrigger, multiDateModal) {
1951
+ function atcb_generate_multidate_links(host, type, linkType, data, keyboardTrigger, multiDateModal) {
1945
1952
  if (
1946
- (type == 'ical' || type == 'apple') &&
1947
- data.dates.every(function (theSubEvent) {
1953
+ (linkType == 'ical') && data.dates.every(function (theSubEvent) {
1948
1954
  if (theSubEvent.status == 'CANCELLED' || (theSubEvent.organizer != null && theSubEvent.organizer != '')) {
1949
1955
  return false;
1950
1956
  }
@@ -1966,11 +1972,10 @@ function atcb_generate_multidate_links(host, type, data, keyboardTrigger, multiD
1966
1972
  atcb_create_modal(host, data, type, atcb_translate_hook('modal.multidate.h', data), atcb_translate_hook('modal.multidate.text', data), [], individualButtons, keyboardTrigger);
1967
1973
  }
1968
1974
  }
1969
- function atcb_generate_subscribe_links(host, type, data, keyboardTrigger) {
1975
+ function atcb_generate_subscribe_links(host, linkType, data, keyboardTrigger) {
1970
1976
  const adjustedFileUrl = data.icsFile.replace('https://', 'webcal://');
1971
- switch (type) {
1972
- case 'apple':
1973
- case 'ical':
1977
+ switch (linkType) {
1978
+ case 'ical': // also for apple (see above)
1974
1979
  atcb_subscribe_ical(adjustedFileUrl);
1975
1980
  break;
1976
1981
  case 'google':
@@ -2147,7 +2152,7 @@ function atcb_generate_microsoft(data, type = '365') {
2147
2152
  urlParts.push('location=' + encodeURIComponent(data.location));
2148
2153
  }
2149
2154
  if (data.description != null && data.description != '') {
2150
- urlParts.push('body=' + encodeURIComponent(data.description.replace(/\n/g, '<br>')));
2155
+ urlParts.push('body=' + encodeURIComponent(data.description));
2151
2156
  }
2152
2157
  urlParts.push('uid=' + encodeURIComponent(data.uid));
2153
2158
  atcb_open_cal_url(urlParts.join('&'));
@@ -2250,25 +2255,25 @@ function atcb_generate_ical(host, data, subEvent = 'all', keyboardTrigger = fals
2250
2255
  ics_lines.push('DTSTAMP:' + atcb_format_datetime(now, 'clean', true));
2251
2256
  ics_lines.push('DTSTART' + timeAddon + ':' + formattedDate.start);
2252
2257
  ics_lines.push('DTEND' + timeAddon + ':' + formattedDate.end);
2253
- ics_lines.push('SUMMARY:' + data.dates[`${i}`].name.replace(/.{65}/g, '$&' + '\r\n '));
2254
- if (data.dates[`${i}`].descriptionHtmlFree != null && data.dates[`${i}`].descriptionHtmlFree != '') {
2258
+ ics_lines.push('SUMMARY:' + atcb_rewrite_ical_text(data.dates[`${i}`].name, true));
2259
+ if (data.dates[`${i}`].descriptionHtmlFreeICal != null && data.dates[`${i}`].descriptionHtmlFreeICal != '') {
2255
2260
  ics_lines.push(
2256
- 'DESCRIPTION:' + data.dates[`${i}`].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
2261
+ 'DESCRIPTION:' + atcb_rewrite_ical_text(data.dates[`${i}`].descriptionHtmlFreeICal, true)
2257
2262
  );
2258
2263
  }
2259
2264
  if (data.dates[`${i}`].description != null && data.dates[`${i}`].description != '') {
2260
- ics_lines.push('X-ALT-DESC;FMTTYPE=text/html:\r\n <!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN"">\r\n <HTML><BODY>\r\n ' + data.dates[`${i}`].description.replace(/\n/g, '<br>').replace(/.{60}/g, '$&' + '\r\n ') + '\r\n </BODY></HTML>');
2265
+ ics_lines.push('X-ALT-DESC;FMTTYPE=text/html:\r\n <!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN"">\r\n <HTML><BODY>\r\n ' + atcb_rewrite_ical_text(data.dates[`${i}`].description, true) + '\r\n </BODY></HTML>');
2261
2266
  }
2262
2267
  if (data.dates[`${i}`].location != null && data.dates[`${i}`].location != '') {
2263
- ics_lines.push('LOCATION:' + data.dates[`${i}`].location);
2268
+ ics_lines.push('LOCATION:' + atcb_rewrite_ical_text(data.dates[`${i}`].location, true));
2264
2269
  }
2265
2270
  if (data.dates[`${i}`].organizer != null && data.dates[`${i}`].organizer != '') {
2266
2271
  const organizerParts = data.dates[`${i}`].organizer.split('|');
2267
- ics_lines.push('ORGANIZER;CN=' + organizerParts[0] + ':MAILTO:' + organizerParts[1]);
2272
+ ics_lines.push('ORGANIZER;CN="' + atcb_rewrite_ical_text(organizerParts[0], false, true) + '":MAILTO:' + organizerParts[1]);
2268
2273
  }
2269
2274
  if (data.dates[`${i}`].attendee != null && data.dates[`${i}`].attendee != '') {
2270
2275
  const attendeeParts = data.dates[`${i}`].attendee.split('|');
2271
- ics_lines.push('ATTENDEE;ROLE=REQ-PARTICIPANT;CN=' + attendeeParts[0] + ':MAILTO:' + attendeeParts[1]);
2276
+ ics_lines.push('ATTENDEE;ROLE=REQ-PARTICIPANT;CN="' + atcb_rewrite_ical_text(attendeeParts[0], false, true) + '":MAILTO:' + attendeeParts[1]);
2272
2277
  }
2273
2278
  if (data.recurrence != null && data.recurrence != '') {
2274
2279
  ics_lines.push(data.recurrence);
@@ -2470,11 +2475,16 @@ function atcb_validEmail(email, mx = false) {
2470
2475
  }
2471
2476
  return true;
2472
2477
  }
2473
- function atcb_rewrite_html_elements(content, clear = false) {
2474
- content = content.replace(/<br\s*\/?>/gi, '\n');
2478
+ function atcb_rewrite_html_elements(content, clear = false, iCalBreaks = false) {
2475
2479
  if (clear) {
2476
- content = content.replace(/\[(|\/)(url|br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]|((\|.*)\[\/url\])/gi, '');
2477
- content = content.replace(/\{(|\/)(url|br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\}|((\|.*)\{\/url\})/gi, '');
2480
+ content = content.replace(/\[(|\/)(url|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]|((\|.*)\[\/url\])/gi, '');
2481
+ content = content.replace(/\{(|\/)(url|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\}|((\|.*)\{\/url\})/gi, '');
2482
+ if (iCalBreaks) {
2483
+ content = content.replace(/(\[br\]|\{br\})/gi, '\\n');
2484
+ } else {
2485
+ content = content.replace(/(\[br\]|\{br\})/gi, ' ');
2486
+ }
2487
+ content = content.replace(/&[#a-zA-Z0-9]{1,9};/gi, '');
2478
2488
  } else {
2479
2489
  content = content.replace(/\[(\/|)(br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\]/gi, '<$1$2>');
2480
2490
  content = content.replace(/\{(\/|)(br|hr|p|b|strong|u|i|em|li|ul|ol|h\d)\}/gi, '<$1$2>');
@@ -2498,6 +2508,17 @@ function atcb_parse_url_code(input) {
2498
2508
  })();
2499
2509
  return '<a href="' + urlText[0] + '" target="' + atcbDefaultTarget + '" rel="noopener">' + text + '</a>';
2500
2510
  }
2511
+ function atcb_rewrite_ical_text(content, truncate = true, inQuotes = false) {
2512
+ if (inQuotes) {
2513
+ content = content.replace(/"/g, '');
2514
+ } else {
2515
+ content = content.replace(/\\/g, '\\\\').replace(/(,|;)/g, '\\$1');
2516
+ }
2517
+ if (truncate) {
2518
+ content = content.replace(/.{60}/g, '$&' + '\r\n ');
2519
+ }
2520
+ return content;
2521
+ }
2501
2522
  function atcb_position_list(host, trigger, list, blockUpwards = false, resize = false) {
2502
2523
  let anchorSet = false;
2503
2524
  const originalTrigger = trigger;
@@ -3278,7 +3299,7 @@ function atcb_read_attributes(el) {
3278
3299
  for (let i = 0; i < atcbWcParams.length; i++) {
3279
3300
  let attr = atcbWcParams[`${i}`];
3280
3301
  if (el.hasAttribute(`${attr}`)) {
3281
- let inputVal = atcb_secure_content(el.getAttribute(`${attr}`).replace(/(\r\n|\n|\r)/g, ''), false);
3302
+ let inputVal = atcb_secure_content(el.getAttribute(`${attr}`).replace(/(\\r\\n|\\n|\\r)/g, ''), false);
3282
3303
  let val;
3283
3304
  if (atcbWcBooleanParams.includes(attr)) {
3284
3305
  if (inputVal == '') {
@@ -3323,7 +3344,7 @@ function atcb_read_attributes(el) {
3323
3344
  }
3324
3345
  const identifierAttr = el.getAttribute('identifier');
3325
3346
  if (identifierAttr != null && identifierAttr != '') {
3326
- data['identifier'] = atcb_secure_content(identifierAttr.replace(/(\r\n|\n|\r)/g, ''), false);
3347
+ data['identifier'] = atcb_secure_content(identifierAttr.replace(/(\\r\\n|\\n|\\r)/g, ''), false);
3327
3348
  }
3328
3349
  }
3329
3350
  if (!atcb_check_required(data)) {
@@ -3331,7 +3352,7 @@ function atcb_read_attributes(el) {
3331
3352
  const atcbJsonInput = (function () {
3332
3353
  if (slotInput != '') {
3333
3354
  try {
3334
- return JSON.parse(atcb_secure_content(slotInput.replace(/(\r\n|\n|\r)/g, ''), false));
3355
+ return JSON.parse(atcb_secure_content(slotInput.replace(/(\\r\\n|\\n|\\r)/g, ''), false));
3335
3356
  } catch (e) {
3336
3357
  throw new Error('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: ' + e);
3337
3358
  }
@@ -3423,14 +3444,14 @@ function atcb_load_css(host, rootObj = null, style = '', inline = false, buttons
3423
3444
  const cssContent = document.createElement('style');
3424
3445
  const overrideDefaultCss = (function () {
3425
3446
  if (host.host.hasAttribute('styleLight')) {
3426
- const output = ':host { ' + atcb_secure_content(host.host.getAttribute('styleLight').replace(/(\r\n|\n|\r)/g, ''), false) + ' }';
3447
+ const output = ':host { ' + atcb_secure_content(host.host.getAttribute('styleLight').replace(/(\\r\\n|\\n|\\r)/g, ''), false) + ' }';
3427
3448
  return output;
3428
3449
  }
3429
3450
  return '';
3430
3451
  })();
3431
3452
  const overrideDarkCss = (function () {
3432
3453
  if (host.host.hasAttribute('styleDark')) {
3433
- const output = ':host(.atcb-dark), :host-context(html.atcb-dark):host(.atcb-bodyScheme), :host-context(body.atcb-dark):host(.atcb-bodyScheme) { ' + atcb_secure_content(host.host.getAttribute('styleDark').replace(/(\r\n|\n|\r)/g, ''), false) + ' }';
3454
+ const output = ':host(.atcb-dark), :host-context(html.atcb-dark):host(.atcb-bodyScheme), :host-context(body.atcb-dark):host(.atcb-bodyScheme) { ' + atcb_secure_content(host.host.getAttribute('styleDark').replace(/(\\r\\n|\\n|\\r)/g, ''), false) + ' }';
3434
3455
  return output;
3435
3456
  }
3436
3457
  return '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-to-calendar-button",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "engines": {
5
5
  "node": ">=16.18.1",
6
6
  "npm": ">=8.19.2"
@@ -79,11 +79,11 @@
79
79
  },
80
80
  "devDependencies": {
81
81
  "@open-wc/testing": "^3.1.7",
82
- "@typescript-eslint/eslint-plugin": "^5.53.0",
83
- "@typescript-eslint/parser": "^5.53.0",
84
- "@web/test-runner": "^0.15.0",
85
- "eslint": "^8.34.0",
86
- "eslint-config-prettier": "^8.6.0",
82
+ "@typescript-eslint/eslint-plugin": "^5.55.0",
83
+ "@typescript-eslint/parser": "^5.55.0",
84
+ "@web/test-runner": "^0.15.1",
85
+ "eslint": "^8.36.0",
86
+ "eslint-config-prettier": "^8.7.0",
87
87
  "eslint-plugin-commonjs": "^1.0.2",
88
88
  "eslint-plugin-security": "^1.7.1",
89
89
  "grunt": ">=1.6.1",
@@ -94,9 +94,8 @@
94
94
  "grunt-file-creator": "^0.1.3",
95
95
  "grunt-version": "^3.0.0",
96
96
  "prettier": "2.8.4",
97
- "prettier-plugin-tailwindcss": "^0.2.3",
98
- "stylelint": "^14.16.1",
99
- "stylelint-config-prettier": "^9.0.5",
100
- "stylelint-config-standard": "^29.0.0"
97
+ "prettier-plugin-tailwindcss": "^0.2.4",
98
+ "stylelint": "^15.2.0",
99
+ "stylelint-config-standard": "^30.0.1"
101
100
  }
102
101
  }