add-to-calendar-button 2.3.2 → 2.3.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,22 +7,22 @@ const tzlibActions = require('timezones-ical-library');
7
7
  * Add to Calendar Button
8
8
  * ++++++++++++++++++++++
9
9
  *
10
- * Version: 2.3.2
10
+ * Version: 2.3.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.3.2';
18
- const atcbCssTemplate = {
17
+ const atcbVersion = '2.3.4';
18
+ const atcbCssTemplate = {
19
19
  if (typeof window === 'undefined') {
20
20
  return false;
21
21
  } else {
22
22
  return true;
23
23
  }
24
24
  };
25
- const isiOS = isBrowser()
25
+ const atcbIsiOS = atcbIsBrowser()
26
26
  ? () => {
27
27
  if ((/iPad|iPhone|iPod/i.test(navigator.userAgent || navigator.vendor || window.opera) && !window.MSStream) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
28
28
  return true;
@@ -33,7 +33,7 @@ const isiOS = isBrowser()
33
33
  : () => {
34
34
  return false;
35
35
  };
36
- const isAndroid = isBrowser()
36
+ const atcbIsAndroid = atcbIsBrowser()
37
37
  ? () => {
38
38
  if (/android/i.test(navigator.userAgent || navigator.vendor || window.opera) && !window.MSStream) {
39
39
  return true;
@@ -44,7 +44,7 @@ const isAndroid = isBrowser()
44
44
  : () => {
45
45
  return false;
46
46
  };
47
- /*const isChrome = isBrowser()
47
+ /*const isChrome = atcbIsBrowser()
48
48
  ? () => {
49
49
  if (/chrome|chromium|crios|google inc/i.test(navigator.userAgent || navigator.vendor)) {
50
50
  return true;
@@ -55,7 +55,7 @@ const isAndroid = isBrowser()
55
55
  : () => {
56
56
  return false;
57
57
  };*/
58
- const isSafari = isBrowser()
58
+ const atcbIsSafari = atcbIsBrowser()
59
59
  ? () => {
60
60
  if (/^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent || navigator.vendor)) {
61
61
  return true;
@@ -66,14 +66,14 @@ const isSafari = isBrowser()
66
66
  : () => {
67
67
  return false;
68
68
  };
69
- const isMobile = () => {
70
- if (isAndroid() || isiOS()) {
69
+ const atcbIsMobile = () => {
70
+ if (atcbIsAndroid() || atcbIsiOS()) {
71
71
  return true;
72
72
  } else {
73
73
  return false;
74
74
  }
75
75
  };
76
- const isWebView = isBrowser()
76
+ const atcbIsWebView = atcbIsBrowser()
77
77
  ? () => {
78
78
  if (/(; ?wv|(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari))/i.test(navigator.userAgent || navigator.vendor)) {
79
79
  return true;
@@ -84,7 +84,7 @@ const isWebView = isBrowser()
84
84
  : () => {
85
85
  return false;
86
86
  };
87
- const isProblematicWebView = isBrowser()
87
+ const atcbIsProblematicWebView = atcbIsBrowser()
88
88
  ? () => {
89
89
  if (/(Instagram)/i.test(navigator.userAgent || navigator.vendor || window.opera)) {
90
90
  return true;
@@ -95,7 +95,7 @@ const isProblematicWebView = isBrowser()
95
95
  : () => {
96
96
  return false;
97
97
  };
98
- const atcbDefaultTarget = isWebView() ? '_system' : '_blank';
98
+ const atcbDefaultTarget = atcbIsWebView() ? '_system' : '_blank';
99
99
  const atcbOptions = ['apple', 'google', 'ical', 'ms365', 'outlookcom', 'msteams', 'yahoo'];
100
100
  const atcbValidRecurrOptions = ['apple', 'google', 'ical'];
101
101
  const atcbInvalidSubscribeOptions = ['msteams'];
@@ -103,6 +103,7 @@ const atcbiOSInvalidOptions = ['ical'];
103
103
  const atcbStates = [];
104
104
  const atcbWcParams = [
105
105
  'debug',
106
+ 'cspnonce',
106
107
  'name',
107
108
  'dates',
108
109
  'description',
@@ -324,8 +325,8 @@ function atcb_decorate_data_options(data) {
324
325
  iCalGiven = true;
325
326
  }
326
327
  if (
327
- (isiOS() && atcbiOSInvalidOptions.includes(optionName)) ||
328
- (data.recurrence != null && data.recurrence != '' && (!atcbValidRecurrOptions.includes(optionName) || (data.recurrence_until != null && data.recurrence_until != '' && (optionName === 'apple' || optionName === 'ical')) || (isiOS() && optionName === 'google'))) ||
328
+ (atcbIsiOS() && atcbiOSInvalidOptions.includes(optionName)) ||
329
+ (data.recurrence != null && data.recurrence != '' && (!atcbValidRecurrOptions.includes(optionName) || (data.recurrence_until != null && data.recurrence_until != '' && (optionName === 'apple' || optionName === 'ical')) || (atcbIsiOS() && optionName === 'google'))) ||
329
330
  (data.subscribe && atcbInvalidSubscribeOptions.includes(optionName))
330
331
  ) {
331
332
  continue;
@@ -334,13 +335,13 @@ function atcb_decorate_data_options(data) {
334
335
  data.optionLabels.push(optionLabel);
335
336
  }
336
337
  if (newOptions.length === 0) {
337
- if (!isiOS()) {
338
+ if (!atcbIsiOS()) {
338
339
  newOptions.push('ical');
339
340
  data.optionLabels.push('');
340
341
  }
341
342
  iCalGiven = true;
342
343
  }
343
- if (isiOS() && iCalGiven && !appleGiven) {
344
+ if (atcbIsiOS() && iCalGiven && !appleGiven) {
344
345
  newOptions.push('apple');
345
346
  data.optionLabels.push('');
346
347
  }
@@ -405,7 +406,7 @@ function atcb_decorate_sizes(size) {
405
406
  return sizes;
406
407
  }
407
408
  function atcb_decorate_light_mode(lightMode = '') {
408
- if (lightMode == 'system' && isBrowser()) {
409
+ if (lightMode == 'system' && atcbIsBrowser()) {
409
410
  const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
410
411
  return prefersDarkScheme.matches ? 'dark' : 'light';
411
412
  }
@@ -1964,6 +1965,13 @@ function atcb_generate_modal_host(host, data, reset = true) {
1964
1965
 
1965
1966
  function atcb_generate_rich_data(data, parent) {
1966
1967
  const schemaEl = document.createElement('script');
1968
+ if (parent.hasAttribute('cspnonce')) {
1969
+ const cspnonceRegex = /[`'"()[\]{}<>\s]/;
1970
+ if (cspnonceRegex.test(parent.getAttribute('cspnonce'))) {
1971
+ throw new Error("cspnonce input contains forbidden characters.");
1972
+ }
1973
+ schemaEl.setAttribute('nonce', parent.getAttribute('cspnonce'));
1974
+ }
1967
1975
  schemaEl.type = 'application/ld+json';
1968
1976
  const schemaContentMulti = [];
1969
1977
  if (data.dates.length > 1) {
@@ -2170,7 +2178,7 @@ function atcb_generate_subscribe_links(host, linkType, data, keyboardTrigger) {
2170
2178
  const adjustedFileUrl = data.icsFile.replace('https://', 'webcal://');
2171
2179
  switch (linkType) {
2172
2180
  case 'ical': // also for apple (see above)
2173
- if (isMobile()) {
2181
+ if (atcbIsMobile()) {
2174
2182
  atcb_subscribe_ical(data.icsFile);
2175
2183
  break;
2176
2184
  }
@@ -2283,7 +2291,7 @@ function atcb_generate_google(data) {
2283
2291
  }
2284
2292
  if (data.location != null && data.location != '') {
2285
2293
  urlParts.push('location=' + encodeURIComponent(data.location));
2286
- if (isiOS()) {
2294
+ if (atcbIsiOS()) {
2287
2295
  if (tmpDataDescription.length > 0) {
2288
2296
  tmpDataDescription.push('<br><br>');
2289
2297
  }
@@ -2329,7 +2337,7 @@ function atcb_generate_yahoo(data) {
2329
2337
  function atcb_generate_microsoft(data, type = '365') {
2330
2338
  const urlParts = [];
2331
2339
  const basePath = (function () {
2332
- if (isMobile()) {
2340
+ if (atcbIsMobile()) {
2333
2341
  return '/calendar/0/deeplink/compose?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent';
2334
2342
  }
2335
2343
  return '/calendar/action/compose?rru=addevent';
@@ -2363,7 +2371,7 @@ function atcb_generate_msteams(data) {
2363
2371
  const urlParts = [];
2364
2372
  const baseUrl = 'https://teams.microsoft.com/l/meeting/new?';
2365
2373
  const formattedDate = atcb_generate_time(data, 'delimiters', 'msteams', true);
2366
- if (!formattedDate.allday || isMobile()) {
2374
+ if (!formattedDate.allday || atcbIsMobile()) {
2367
2375
  urlParts.push('startTime=' + encodeURIComponent(formattedDate.start));
2368
2376
  urlParts.push('endTime=' + encodeURIComponent(formattedDate.end));
2369
2377
  } else {
@@ -2410,7 +2418,7 @@ function atcb_generate_ical(host, data, subEvent = 'all', keyboardTrigger = fals
2410
2418
  }
2411
2419
  return '';
2412
2420
  })();
2413
- if (givenIcsFile != '' && (!isiOS() || (isiOS() && isWebView() && data.bypassWebViewCheck == true))) {
2421
+ if (givenIcsFile != '' && (!atcbIsiOS() || (atcbIsiOS() && atcbIsWebView() && data.bypassWebViewCheck == true))) {
2414
2422
  atcb_save_file(givenIcsFile, filename);
2415
2423
  return;
2416
2424
  }
@@ -2507,7 +2515,7 @@ function atcb_generate_ical(host, data, subEvent = 'all', keyboardTrigger = fals
2507
2515
  }
2508
2516
  return 'data:text/calendar;charset=utf-8,' + encodeURIComponent(ics_lines.join('\r\n'));
2509
2517
  })();
2510
- if ((isiOS() && !isSafari()) || (isWebView() && (isiOS() || (isAndroid() && isProblematicWebView())))) {
2518
+ if ((atcbIsiOS() && !atcbIsSafari()) || (atcbIsWebView() && (atcbIsiOS() || (atcbIsAndroid() && atcbIsProblematicWebView())))) {
2511
2519
  atcb_ical_copy_note(host, dataUrl, data, keyboardTrigger);
2512
2520
  return;
2513
2521
  }
@@ -2533,7 +2541,7 @@ function atcb_determine_ical_filename(data, subEvent) {
2533
2541
  }
2534
2542
  function atcb_ical_copy_note(host, dataUrl, data, keyboardTrigger) {
2535
2543
  atcb_copy_to_clipboard(dataUrl);
2536
- if (isiOS() && !isSafari()) {
2544
+ if (atcbIsiOS() && !atcbIsSafari()) {
2537
2545
  atcb_create_modal(
2538
2546
  host,
2539
2547
  data,
@@ -2561,7 +2569,7 @@ function atcb_save_file(file, filename) {
2561
2569
  const save = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
2562
2570
  save.rel = 'noopener';
2563
2571
  save.href = file;
2564
- if (isMobile()) {
2572
+ if (atcbIsMobile()) {
2565
2573
  save.target = '_self';
2566
2574
  } else {
2567
2575
  save.target = '_blank';
@@ -2627,11 +2635,11 @@ function atcb_generate_time(data, style = 'delimiters', targetCal = 'general', a
2627
2635
  const endDate = data.endDate.split('-');
2628
2636
  const newStartDate = new Date(Date.UTC(startDate[0], startDate[1] - 1, startDate[2], 12, 0, 0));
2629
2637
  const newEndDate = new Date(Date.UTC(endDate[0], endDate[1] - 1, endDate[2], 12, 0, 0));
2630
- if (targetCal === 'google' || (targetCal === 'microsoft' && !isMobile()) || targetCal === 'msteams' || targetCal === 'ical') {
2638
+ if (targetCal === 'google' || (targetCal === 'microsoft' && !atcbIsMobile()) || targetCal === 'msteams' || targetCal === 'ical') {
2631
2639
  newEndDate.setDate(newEndDate.getDate() + 1);
2632
2640
  }
2633
2641
  if (targetCal === 'msteams') {
2634
- if (isMobile()) {
2642
+ if (atcbIsMobile()) {
2635
2643
  const offset = newStartDate.getTimezoneOffset();
2636
2644
  const formattedOffset = (function () {
2637
2645
  if (offset < 0) {
@@ -2695,7 +2703,7 @@ function atcb_secure_url(url, throwError = true) {
2695
2703
  }
2696
2704
  }
2697
2705
  function atcb_validEmail(email) {
2698
- if (!/^.{0,70}@.{1,30}\.[\w.]{2,9}$/.test(email)) {
2706
+ if (!/^.{0,70}@.{1,30}\.[a-zA-Z]{2,9}$/.test(email)) {
2699
2707
  return false;
2700
2708
  }
2701
2709
  return true;
@@ -2840,7 +2848,7 @@ function atcb_copy_to_clipboard(dataString) {
2840
2848
  tmpInput.contentEditable = true;
2841
2849
  tmpInput.readOnly = false;
2842
2850
  tmpInput.value = dataString;
2843
- if (isiOS()) {
2851
+ if (atcbIsiOS()) {
2844
2852
  var range = document.createRange();
2845
2853
  range.selectNodeContents(tmpInput);
2846
2854
  var selection = window.getSelection();
@@ -2889,7 +2897,7 @@ function atcb_log_event(event, trigger, identifier) {
2889
2897
  if (parentEl) {
2890
2898
  parentEl.setAttribute('atcb-last-event', event + ':' + trigger);
2891
2899
  }
2892
- if (isBrowser()) {
2900
+ if (atcbIsBrowser()) {
2893
2901
  atcb_push_to_data_layer(event, trigger);
2894
2902
  }
2895
2903
  }
@@ -3466,7 +3474,7 @@ let atcbInitialGlobalInit = false;
3466
3474
  let atcbBtnCount = 0;
3467
3475
  const lightModeMutationObserver = [];
3468
3476
  const template = `<div class="atcb-initialized" style="display:none;position:relative;width:fit-content;"></div>`;
3469
- if (isBrowser()) {
3477
+ if (atcbIsBrowser()) {
3470
3478
  class AddToCalendarButton extends HTMLElement {
3471
3479
  constructor() {
3472
3480
  super();
@@ -3707,11 +3715,18 @@ function atcb_set_light_mode(shadowRoot, data) {
3707
3715
  shadowRoot.host.classList.add('atcb-' + hostLightMode);
3708
3716
  }
3709
3717
  function atcb_load_css(host, rootObj = null, style = '', inline = false, buttonsList = false, customCss = '') {
3718
+ const cspnonceRegex = /[`'"()[\]{}<>\s]/;
3710
3719
  if (!document.getElementById('atcb-global-style')) {
3711
3720
  const cssGlobalContent = document.createElement('style');
3712
3721
  cssGlobalContent.id = 'atcb-global-style';
3713
3722
  const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
3714
3723
  cssGlobalContent.innerText = '.atcb-modal-no-scroll { overflow-y: hidden !important; -webkit-overflow-scrolling: touch; } body.atcb-modal-no-scroll { padding-right: ' + scrollBarWidth + 'px; }';
3724
+ if (host.host.hasAttribute('cspnonce')) {
3725
+ if (cspnonceRegex.test(host.host.getAttribute('cspnonce'))) {
3726
+ throw new Error("cspnonce input contains forbidden characters.");
3727
+ }
3728
+ cssGlobalContent.setAttribute('nonce', host.host.getAttribute('cspnonce'));
3729
+ }
3715
3730
  document.head.append(cssGlobalContent);
3716
3731
  }
3717
3732
  if (customCss != '' && style == 'custom') {
@@ -3719,6 +3734,12 @@ function atcb_load_css(host, rootObj = null, style = '', inline = false, buttons
3719
3734
  cssFile.setAttribute('rel', 'stylesheet');
3720
3735
  cssFile.setAttribute('type', 'text/css');
3721
3736
  cssFile.setAttribute('href', customCss);
3737
+ if (host.host.hasAttribute('cspnonce')) {
3738
+ if (cspnonceRegex.test(host.host.getAttribute('cspnonce'))) {
3739
+ throw new Error("cspnonce input contains forbidden characters.");
3740
+ }
3741
+ cssFile.setAttribute('nonce', host.host.getAttribute('cspnonce'));
3742
+ }
3722
3743
  if (rootObj == null) {
3723
3744
  host.host.style.display = 'none';
3724
3745
  loadExternalCssAsynch(cssFile, host, host.host);
@@ -3732,6 +3753,12 @@ function atcb_load_css(host, rootObj = null, style = '', inline = false, buttons
3732
3753
  }
3733
3754
  if (style != 'none' && atcbCssTemplate[`${style}`] != null) {
3734
3755
  const cssContent = document.createElement('style');
3756
+ if (host.host.hasAttribute('cspnonce')) {
3757
+ if (cspnonceRegex.test(host.host.getAttribute('cspnonce'))) {
3758
+ throw new Error("cspnonce input contains forbidden characters.");
3759
+ }
3760
+ cssContent.setAttribute('nonce', host.host.getAttribute('cspnonce'));
3761
+ }
3735
3762
  const overrideDefaultCss = (function () {
3736
3763
  if (host.host.hasAttribute('styleLight')) {
3737
3764
  const output = ':host { ' + atcb_secure_content(host.host.getAttribute('styleLight').replace(/(\\r\\n|\\n|\\r)/g, ''), false) + ' }';
@@ -3793,7 +3820,7 @@ function atcb_render_debug_msg(host, error) {
3793
3820
  }
3794
3821
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
3795
3822
  function atcb_action(data, triggerElement, keyboardTrigger = false) {
3796
- if (!isBrowser()) {
3823
+ if (!atcbIsBrowser()) {
3797
3824
  return;
3798
3825
  }
3799
3826
  data = atcb_secure_content(data);
@@ -3849,7 +3876,7 @@ function atcb_action(data, triggerElement, keyboardTrigger = false) {
3849
3876
  atcb_log_event('initialization', data.identifier, data.identifier);
3850
3877
  if (!data.blockInteraction) {
3851
3878
  let host = null;
3852
- if (!oneOption || (data.options[0] !== 'apple' && data.options[0] !== 'ical') || (data.dates && data.dates.length > 1 && data.dates.organizer) || (isMobile())) {
3879
+ if (!oneOption || (data.options[0] !== 'apple' && data.options[0] !== 'ical') || (data.dates && data.dates.length > 1 && data.dates.organizer) || (atcbIsMobile())) {
3853
3880
  host = document.createElement('div');
3854
3881
  host.id = 'atcb-customTrigger-' + data.identifier + '-host';
3855
3882
  if (root == document.body) {
@@ -3928,7 +3955,7 @@ function atcb_get_pro_data(licenseKey) {
3928
3955
  return data;
3929
3956
  }
3930
3957
  function atcb_set_global_event_listener(host, data) {
3931
- if (!isBrowser()) {
3958
+ if (!atcbIsBrowser()) {
3932
3959
  return;
3933
3960
  }
3934
3961
  if (data.lightMode == 'bodyScheme') {
@@ -6,22 +6,22 @@ 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.3.2
9
+ * Version: 2.3.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.3.2';
17
- const atcbCssTemplate = {
16
+ const atcbVersion = '2.3.4';
17
+ const atcbCssTemplate = {
18
18
  if (typeof window === 'undefined') {
19
19
  return false;
20
20
  } else {
21
21
  return true;
22
22
  }
23
23
  };
24
- const isiOS = isBrowser()
24
+ const atcbIsiOS = atcbIsBrowser()
25
25
  ? () => {
26
26
  if ((/iPad|iPhone|iPod/i.test(navigator.userAgent || navigator.vendor || window.opera) && !window.MSStream) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
27
27
  return true;
@@ -32,7 +32,7 @@ const isiOS = isBrowser()
32
32
  : () => {
33
33
  return false;
34
34
  };
35
- const isAndroid = isBrowser()
35
+ const atcbIsAndroid = atcbIsBrowser()
36
36
  ? () => {
37
37
  if (/android/i.test(navigator.userAgent || navigator.vendor || window.opera) && !window.MSStream) {
38
38
  return true;
@@ -43,7 +43,7 @@ const isAndroid = isBrowser()
43
43
  : () => {
44
44
  return false;
45
45
  };
46
- /*const isChrome = isBrowser()
46
+ /*const isChrome = atcbIsBrowser()
47
47
  ? () => {
48
48
  if (/chrome|chromium|crios|google inc/i.test(navigator.userAgent || navigator.vendor)) {
49
49
  return true;
@@ -54,7 +54,7 @@ const isAndroid = isBrowser()
54
54
  : () => {
55
55
  return false;
56
56
  };*/
57
- const isSafari = isBrowser()
57
+ const atcbIsSafari = atcbIsBrowser()
58
58
  ? () => {
59
59
  if (/^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent || navigator.vendor)) {
60
60
  return true;
@@ -65,14 +65,14 @@ const isSafari = isBrowser()
65
65
  : () => {
66
66
  return false;
67
67
  };
68
- const isMobile = () => {
69
- if (isAndroid() || isiOS()) {
68
+ const atcbIsMobile = () => {
69
+ if (atcbIsAndroid() || atcbIsiOS()) {
70
70
  return true;
71
71
  } else {
72
72
  return false;
73
73
  }
74
74
  };
75
- const isWebView = isBrowser()
75
+ const atcbIsWebView = atcbIsBrowser()
76
76
  ? () => {
77
77
  if (/(; ?wv|(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari))/i.test(navigator.userAgent || navigator.vendor)) {
78
78
  return true;
@@ -83,7 +83,7 @@ const isWebView = isBrowser()
83
83
  : () => {
84
84
  return false;
85
85
  };
86
- const isProblematicWebView = isBrowser()
86
+ const atcbIsProblematicWebView = atcbIsBrowser()
87
87
  ? () => {
88
88
  if (/(Instagram)/i.test(navigator.userAgent || navigator.vendor || window.opera)) {
89
89
  return true;
@@ -94,7 +94,7 @@ const isProblematicWebView = isBrowser()
94
94
  : () => {
95
95
  return false;
96
96
  };
97
- const atcbDefaultTarget = isWebView() ? '_system' : '_blank';
97
+ const atcbDefaultTarget = atcbIsWebView() ? '_system' : '_blank';
98
98
  const atcbOptions = ['apple', 'google', 'ical', 'ms365', 'outlookcom', 'msteams', 'yahoo'];
99
99
  const atcbValidRecurrOptions = ['apple', 'google', 'ical'];
100
100
  const atcbInvalidSubscribeOptions = ['msteams'];
@@ -102,6 +102,7 @@ const atcbiOSInvalidOptions = ['ical'];
102
102
  const atcbStates = [];
103
103
  const atcbWcParams = [
104
104
  'debug',
105
+ 'cspnonce',
105
106
  'name',
106
107
  'dates',
107
108
  'description',
@@ -323,8 +324,8 @@ function atcb_decorate_data_options(data) {
323
324
  iCalGiven = true;
324
325
  }
325
326
  if (
326
- (isiOS() && atcbiOSInvalidOptions.includes(optionName)) ||
327
- (data.recurrence != null && data.recurrence != '' && (!atcbValidRecurrOptions.includes(optionName) || (data.recurrence_until != null && data.recurrence_until != '' && (optionName === 'apple' || optionName === 'ical')) || (isiOS() && optionName === 'google'))) ||
327
+ (atcbIsiOS() && atcbiOSInvalidOptions.includes(optionName)) ||
328
+ (data.recurrence != null && data.recurrence != '' && (!atcbValidRecurrOptions.includes(optionName) || (data.recurrence_until != null && data.recurrence_until != '' && (optionName === 'apple' || optionName === 'ical')) || (atcbIsiOS() && optionName === 'google'))) ||
328
329
  (data.subscribe && atcbInvalidSubscribeOptions.includes(optionName))
329
330
  ) {
330
331
  continue;
@@ -333,13 +334,13 @@ function atcb_decorate_data_options(data) {
333
334
  data.optionLabels.push(optionLabel);
334
335
  }
335
336
  if (newOptions.length === 0) {
336
- if (!isiOS()) {
337
+ if (!atcbIsiOS()) {
337
338
  newOptions.push('ical');
338
339
  data.optionLabels.push('');
339
340
  }
340
341
  iCalGiven = true;
341
342
  }
342
- if (isiOS() && iCalGiven && !appleGiven) {
343
+ if (atcbIsiOS() && iCalGiven && !appleGiven) {
343
344
  newOptions.push('apple');
344
345
  data.optionLabels.push('');
345
346
  }
@@ -404,7 +405,7 @@ function atcb_decorate_sizes(size) {
404
405
  return sizes;
405
406
  }
406
407
  function atcb_decorate_light_mode(lightMode = '') {
407
- if (lightMode == 'system' && isBrowser()) {
408
+ if (lightMode == 'system' && atcbIsBrowser()) {
408
409
  const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
409
410
  return prefersDarkScheme.matches ? 'dark' : 'light';
410
411
  }
@@ -1963,6 +1964,13 @@ function atcb_generate_modal_host(host, data, reset = true) {
1963
1964
 
1964
1965
  function atcb_generate_rich_data(data, parent) {
1965
1966
  const schemaEl = document.createElement('script');
1967
+ if (parent.hasAttribute('cspnonce')) {
1968
+ const cspnonceRegex = /[`'"()[\]{}<>\s]/;
1969
+ if (cspnonceRegex.test(parent.getAttribute('cspnonce'))) {
1970
+ throw new Error("cspnonce input contains forbidden characters.");
1971
+ }
1972
+ schemaEl.setAttribute('nonce', parent.getAttribute('cspnonce'));
1973
+ }
1966
1974
  schemaEl.type = 'application/ld+json';
1967
1975
  const schemaContentMulti = [];
1968
1976
  if (data.dates.length > 1) {
@@ -2169,7 +2177,7 @@ function atcb_generate_subscribe_links(host, linkType, data, keyboardTrigger) {
2169
2177
  const adjustedFileUrl = data.icsFile.replace('https://', 'webcal://');
2170
2178
  switch (linkType) {
2171
2179
  case 'ical': // also for apple (see above)
2172
- if (isMobile()) {
2180
+ if (atcbIsMobile()) {
2173
2181
  atcb_subscribe_ical(data.icsFile);
2174
2182
  break;
2175
2183
  }
@@ -2282,7 +2290,7 @@ function atcb_generate_google(data) {
2282
2290
  }
2283
2291
  if (data.location != null && data.location != '') {
2284
2292
  urlParts.push('location=' + encodeURIComponent(data.location));
2285
- if (isiOS()) {
2293
+ if (atcbIsiOS()) {
2286
2294
  if (tmpDataDescription.length > 0) {
2287
2295
  tmpDataDescription.push('<br><br>');
2288
2296
  }
@@ -2328,7 +2336,7 @@ function atcb_generate_yahoo(data) {
2328
2336
  function atcb_generate_microsoft(data, type = '365') {
2329
2337
  const urlParts = [];
2330
2338
  const basePath = (function () {
2331
- if (isMobile()) {
2339
+ if (atcbIsMobile()) {
2332
2340
  return '/calendar/0/deeplink/compose?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent';
2333
2341
  }
2334
2342
  return '/calendar/action/compose?rru=addevent';
@@ -2362,7 +2370,7 @@ function atcb_generate_msteams(data) {
2362
2370
  const urlParts = [];
2363
2371
  const baseUrl = 'https://teams.microsoft.com/l/meeting/new?';
2364
2372
  const formattedDate = atcb_generate_time(data, 'delimiters', 'msteams', true);
2365
- if (!formattedDate.allday || isMobile()) {
2373
+ if (!formattedDate.allday || atcbIsMobile()) {
2366
2374
  urlParts.push('startTime=' + encodeURIComponent(formattedDate.start));
2367
2375
  urlParts.push('endTime=' + encodeURIComponent(formattedDate.end));
2368
2376
  } else {
@@ -2409,7 +2417,7 @@ function atcb_generate_ical(host, data, subEvent = 'all', keyboardTrigger = fals
2409
2417
  }
2410
2418
  return '';
2411
2419
  })();
2412
- if (givenIcsFile != '' && (!isiOS() || (isiOS() && isWebView() && data.bypassWebViewCheck == true))) {
2420
+ if (givenIcsFile != '' && (!atcbIsiOS() || (atcbIsiOS() && atcbIsWebView() && data.bypassWebViewCheck == true))) {
2413
2421
  atcb_save_file(givenIcsFile, filename);
2414
2422
  return;
2415
2423
  }
@@ -2506,7 +2514,7 @@ function atcb_generate_ical(host, data, subEvent = 'all', keyboardTrigger = fals
2506
2514
  }
2507
2515
  return 'data:text/calendar;charset=utf-8,' + encodeURIComponent(ics_lines.join('\r\n'));
2508
2516
  })();
2509
- if ((isiOS() && !isSafari()) || (isWebView() && (isiOS() || (isAndroid() && isProblematicWebView())))) {
2517
+ if ((atcbIsiOS() && !atcbIsSafari()) || (atcbIsWebView() && (atcbIsiOS() || (atcbIsAndroid() && atcbIsProblematicWebView())))) {
2510
2518
  atcb_ical_copy_note(host, dataUrl, data, keyboardTrigger);
2511
2519
  return;
2512
2520
  }
@@ -2532,7 +2540,7 @@ function atcb_determine_ical_filename(data, subEvent) {
2532
2540
  }
2533
2541
  function atcb_ical_copy_note(host, dataUrl, data, keyboardTrigger) {
2534
2542
  atcb_copy_to_clipboard(dataUrl);
2535
- if (isiOS() && !isSafari()) {
2543
+ if (atcbIsiOS() && !atcbIsSafari()) {
2536
2544
  atcb_create_modal(
2537
2545
  host,
2538
2546
  data,
@@ -2560,7 +2568,7 @@ function atcb_save_file(file, filename) {
2560
2568
  const save = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
2561
2569
  save.rel = 'noopener';
2562
2570
  save.href = file;
2563
- if (isMobile()) {
2571
+ if (atcbIsMobile()) {
2564
2572
  save.target = '_self';
2565
2573
  } else {
2566
2574
  save.target = '_blank';
@@ -2626,11 +2634,11 @@ function atcb_generate_time(data, style = 'delimiters', targetCal = 'general', a
2626
2634
  const endDate = data.endDate.split('-');
2627
2635
  const newStartDate = new Date(Date.UTC(startDate[0], startDate[1] - 1, startDate[2], 12, 0, 0));
2628
2636
  const newEndDate = new Date(Date.UTC(endDate[0], endDate[1] - 1, endDate[2], 12, 0, 0));
2629
- if (targetCal === 'google' || (targetCal === 'microsoft' && !isMobile()) || targetCal === 'msteams' || targetCal === 'ical') {
2637
+ if (targetCal === 'google' || (targetCal === 'microsoft' && !atcbIsMobile()) || targetCal === 'msteams' || targetCal === 'ical') {
2630
2638
  newEndDate.setDate(newEndDate.getDate() + 1);
2631
2639
  }
2632
2640
  if (targetCal === 'msteams') {
2633
- if (isMobile()) {
2641
+ if (atcbIsMobile()) {
2634
2642
  const offset = newStartDate.getTimezoneOffset();
2635
2643
  const formattedOffset = (function () {
2636
2644
  if (offset < 0) {
@@ -2694,7 +2702,7 @@ function atcb_secure_url(url, throwError = true) {
2694
2702
  }
2695
2703
  }
2696
2704
  function atcb_validEmail(email) {
2697
- if (!/^.{0,70}@.{1,30}\.[\w.]{2,9}$/.test(email)) {
2705
+ if (!/^.{0,70}@.{1,30}\.[a-zA-Z]{2,9}$/.test(email)) {
2698
2706
  return false;
2699
2707
  }
2700
2708
  return true;
@@ -2839,7 +2847,7 @@ function atcb_copy_to_clipboard(dataString) {
2839
2847
  tmpInput.contentEditable = true;
2840
2848
  tmpInput.readOnly = false;
2841
2849
  tmpInput.value = dataString;
2842
- if (isiOS()) {
2850
+ if (atcbIsiOS()) {
2843
2851
  var range = document.createRange();
2844
2852
  range.selectNodeContents(tmpInput);
2845
2853
  var selection = window.getSelection();
@@ -2888,7 +2896,7 @@ function atcb_log_event(event, trigger, identifier) {
2888
2896
  if (parentEl) {
2889
2897
  parentEl.setAttribute('atcb-last-event', event + ':' + trigger);
2890
2898
  }
2891
- if (isBrowser()) {
2899
+ if (atcbIsBrowser()) {
2892
2900
  atcb_push_to_data_layer(event, trigger);
2893
2901
  }
2894
2902
  }
@@ -3465,7 +3473,7 @@ let atcbInitialGlobalInit = false;
3465
3473
  let atcbBtnCount = 0;
3466
3474
  const lightModeMutationObserver = [];
3467
3475
  const template = `<div class="atcb-initialized" style="display:none;position:relative;width:fit-content;"></div>`;
3468
- if (isBrowser()) {
3476
+ if (atcbIsBrowser()) {
3469
3477
  class AddToCalendarButton extends HTMLElement {
3470
3478
  constructor() {
3471
3479
  super();
@@ -3706,11 +3714,18 @@ function atcb_set_light_mode(shadowRoot, data) {
3706
3714
  shadowRoot.host.classList.add('atcb-' + hostLightMode);
3707
3715
  }
3708
3716
  function atcb_load_css(host, rootObj = null, style = '', inline = false, buttonsList = false, customCss = '') {
3717
+ const cspnonceRegex = /[`'"()[\]{}<>\s]/;
3709
3718
  if (!document.getElementById('atcb-global-style')) {
3710
3719
  const cssGlobalContent = document.createElement('style');
3711
3720
  cssGlobalContent.id = 'atcb-global-style';
3712
3721
  const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
3713
3722
  cssGlobalContent.innerText = '.atcb-modal-no-scroll { overflow-y: hidden !important; -webkit-overflow-scrolling: touch; } body.atcb-modal-no-scroll { padding-right: ' + scrollBarWidth + 'px; }';
3723
+ if (host.host.hasAttribute('cspnonce')) {
3724
+ if (cspnonceRegex.test(host.host.getAttribute('cspnonce'))) {
3725
+ throw new Error("cspnonce input contains forbidden characters.");
3726
+ }
3727
+ cssGlobalContent.setAttribute('nonce', host.host.getAttribute('cspnonce'));
3728
+ }
3714
3729
  document.head.append(cssGlobalContent);
3715
3730
  }
3716
3731
  if (customCss != '' && style == 'custom') {
@@ -3718,6 +3733,12 @@ function atcb_load_css(host, rootObj = null, style = '', inline = false, buttons
3718
3733
  cssFile.setAttribute('rel', 'stylesheet');
3719
3734
  cssFile.setAttribute('type', 'text/css');
3720
3735
  cssFile.setAttribute('href', customCss);
3736
+ if (host.host.hasAttribute('cspnonce')) {
3737
+ if (cspnonceRegex.test(host.host.getAttribute('cspnonce'))) {
3738
+ throw new Error("cspnonce input contains forbidden characters.");
3739
+ }
3740
+ cssFile.setAttribute('nonce', host.host.getAttribute('cspnonce'));
3741
+ }
3721
3742
  if (rootObj == null) {
3722
3743
  host.host.style.display = 'none';
3723
3744
  loadExternalCssAsynch(cssFile, host, host.host);
@@ -3731,6 +3752,12 @@ function atcb_load_css(host, rootObj = null, style = '', inline = false, buttons
3731
3752
  }
3732
3753
  if (style != 'none' && atcbCssTemplate[`${style}`] != null) {
3733
3754
  const cssContent = document.createElement('style');
3755
+ if (host.host.hasAttribute('cspnonce')) {
3756
+ if (cspnonceRegex.test(host.host.getAttribute('cspnonce'))) {
3757
+ throw new Error("cspnonce input contains forbidden characters.");
3758
+ }
3759
+ cssContent.setAttribute('nonce', host.host.getAttribute('cspnonce'));
3760
+ }
3734
3761
  const overrideDefaultCss = (function () {
3735
3762
  if (host.host.hasAttribute('styleLight')) {
3736
3763
  const output = ':host { ' + atcb_secure_content(host.host.getAttribute('styleLight').replace(/(\\r\\n|\\n|\\r)/g, ''), false) + ' }';
@@ -3792,7 +3819,7 @@ function atcb_render_debug_msg(host, error) {
3792
3819
  }
3793
3820
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
3794
3821
  function atcb_action(data, triggerElement, keyboardTrigger = false) {
3795
- if (!isBrowser()) {
3822
+ if (!atcbIsBrowser()) {
3796
3823
  return;
3797
3824
  }
3798
3825
  data = atcb_secure_content(data);
@@ -3848,7 +3875,7 @@ function atcb_action(data, triggerElement, keyboardTrigger = false) {
3848
3875
  atcb_log_event('initialization', data.identifier, data.identifier);
3849
3876
  if (!data.blockInteraction) {
3850
3877
  let host = null;
3851
- if (!oneOption || (data.options[0] !== 'apple' && data.options[0] !== 'ical') || (data.dates && data.dates.length > 1 && data.dates.organizer) || (isMobile())) {
3878
+ if (!oneOption || (data.options[0] !== 'apple' && data.options[0] !== 'ical') || (data.dates && data.dates.length > 1 && data.dates.organizer) || (atcbIsMobile())) {
3852
3879
  host = document.createElement('div');
3853
3880
  host.id = 'atcb-customTrigger-' + data.identifier + '-host';
3854
3881
  if (root == document.body) {
@@ -3927,7 +3954,7 @@ function atcb_get_pro_data(licenseKey) {
3927
3954
  return data;
3928
3955
  }
3929
3956
  function atcb_set_global_event_listener(host, data) {
3930
- if (!isBrowser()) {
3957
+ if (!atcbIsBrowser()) {
3931
3958
  return;
3932
3959
  }
3933
3960
  if (data.lightMode == 'bodyScheme') {
package/index.d.ts CHANGED
@@ -73,6 +73,7 @@ declare module 'add-to-calendar-button' {
73
73
  rsvp?: object | string;
74
74
  bypassWebViewCheck?: boolean | string;
75
75
  debug?: boolean | string;
76
+ nonce?: string;
76
77
  blockInteraction?: boolean | string;
77
78
  styleLight?: string;
78
79
  styleDark?: string;