add-to-calendar-button 1.7.1 → 1.7.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.
package/README.md CHANGED
@@ -173,17 +173,19 @@ You can use startTime and endTime in the event block, but it is recommended to r
173
173
  </div>
174
174
  ```
175
175
 
176
- ### `atcb_action` example with React
176
+ ### React examples
177
+
178
+ #### atcb_action
177
179
 
178
180
  If you can't or don't want to use `atcb_init`, you can use the `atcb_action` import with your own buttons or other elements/components. If you omit the second argument, the dropdown list will display as a modal in the middle of the viewport - in this case, add the "atcb_customTrigger" class to the submitting element for better keyboard support.
179
181
 
180
- This may work better with React and other frontend frameworks.
182
+ This may work better with React and other frontend frameworks, but it misses the Schema.org and button specific functionalities.
181
183
 
182
184
  ```js
183
185
  import React from 'react'
184
186
  import { atcb_action } from 'add-to-calendar-button'
185
187
 
186
- const atcb_action = () => {
188
+ const MyComponent = () => {
187
189
  const [name, setName] = React.useState('Some event')
188
190
  return (
189
191
  <form onSubmit={e => {
@@ -204,6 +206,8 @@ const atcb_action = () => {
204
206
  }
205
207
  ```
206
208
 
209
+ #### atcb_init
210
+
207
211
  Alternatively, you could use `atcb_init` with a `useEffect` hook:
208
212
 
209
213
  ```js
@@ -3,7 +3,7 @@
3
3
  * Add-to-Calendar Button
4
4
  * ++++++++++++++++++++++
5
5
  *
6
- * Version: 1.7.1
6
+ * Version: 1.7.4
7
7
  * Creator: Jens Kuerschner (https://jenskuerschner.de)
8
8
  * Project: https://github.com/jekuer/add-to-calendar-button
9
9
  * License: MIT with “Commons Clause” License Condition v1.0
@@ -16,6 +16,7 @@
16
16
 
17
17
  .atcb_button_wrapper {
18
18
  display: inline-block;
19
+ padding: 5px;
19
20
  position: relative;
20
21
  }
21
22
 
@@ -29,6 +30,7 @@
29
30
  color: rgb(51, 51, 51);
30
31
  cursor: pointer;
31
32
  display: flex;
33
+ font-family:Arial, Helvetica, sans-serif;
32
34
  font-size: 16px;
33
35
  font-weight: 600;
34
36
  line-height: 24px;
@@ -60,8 +62,8 @@
60
62
  border-radius: 6px 6px 3px 3px;
61
63
  -webkit-box-shadow: 1px 5px 15px 0px rgba(0,0,0,.5);
62
64
  box-shadow: 1px 5px 15px 0px rgba(0,0,0,.5);
65
+ margin: -2px -4px;
63
66
  padding: 12px 20px 13px 20px;
64
- margin-bottom: -4px;
65
67
  z-index: 90;
66
68
  }
67
69
 
@@ -81,6 +83,7 @@
81
83
  box-sizing: border-box;
82
84
  color: rgb(51, 51, 51);
83
85
  display: block;
86
+ font-family:Arial, Helvetica, sans-serif;
84
87
  max-width: 100%;
85
88
  position: absolute;
86
89
  padding: 0 3px;
@@ -1,2 +1,2 @@
1
- .atcb{display:none}.atcb_button_wrapper{display:inline-block;position:relative}.atcb_button{align-items:center;background:#f5f5f5;border:1px solid #d2d2d2;border-radius:6px;-webkit-box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);color:#333;cursor:pointer;display:flex;font-size:16px;font-weight:600;line-height:24px;max-width:300px;min-width:150px;padding:10px 16px 11px 16px;position:relative;text-align:center;touch-action:manipulation;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;width:auto}.atcb_button:focus,.atcb_button:hover{background:#fff}@media only screen and (max-width:575px){.atcb_button{font-size:14px}}.atcb_button.atcb_active{background:#fff;border-radius:6px 6px 3px 3px;-webkit-box-shadow:1px 5px 15px 0 rgba(0,0,0,.5);box-shadow:1px 5px 15px 0 rgba(0,0,0,.5);padding:12px 20px 13px 20px;margin-bottom:-4px;z-index:90}.atcb_icon{height:16px;display:inline-block;margin-bottom:4px;margin-right:10px}.atcb_icon svg{height:100%;color:#333;width:auto}.atcb_list{box-sizing:border-box;color:#333;display:block;max-width:100%;position:absolute;padding:0 3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%;min-width:10em;z-index:80}.atcb_list.atcb_modal{position:fixed;width:16em;left:50%;top:50%;transform:translateY(-50%) translateX(-50%)}.atcb_list_item{align-items:center;background:#fafafa;border:1px solid #d2d2d2;border-top:0;-webkit-box-shadow:1px 2px 8px 0 rgba(0,0,0,.3);box-shadow:1px 2px 8px 0 rgba(0,0,0,.3);box-sizing:border-box;cursor:pointer;display:flex;font-size:16px;left:50%;position:relative;padding:12px 18px;text-align:left;transform:translate(-50%);touch-action:manipulation;-webkit-tap-highlight-color:transparent}.atcb_list_item:focus,.atcb_list_item:hover{background:#fff;-webkit-box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);color:#000}@media only screen and (max-width:575px){.atcb_list_item{font-size:14px}}.atcb_list.atcb_generated_button .atcb_list_item:first-child{padding-top:20px}.atcb_list:not(.atcb_generated_button) .atcb_list_item:first-child{border-radius:6px 6px 0 0}.atcb_list_item:last-child{border-radius:0 0 6px 6px}.atcb_list_item .atcb_icon{margin-right:8px;width:18px}.atcb_bgoverlay{background:rgba(20,20,20,.2);backdrop-filter:blur(2px);height:100%;left:0;position:fixed;top:0;width:100%;z-index:70}.atcb_bgoverlay.atcb_click:hover{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath fill-rule='evenodd' d='M11.991.69a2.35 2.35 0 0 1 3.318-.009c.918.911.922 2.392.009 3.307l-4.009 4.014 4.013 4.018c.906.909.893 2.38-.027 3.287a2.35 2.35 0 0 1-3.307-.004l-3.985-3.99-3.993 3.997a2.35 2.35 0 0 1-3.318.009c-.918-.911-.922-2.392-.009-3.307l4.009-4.014L.678 3.98C-.228 3.072-.215 1.6.706.693a2.35 2.35 0 0 1 3.307.004l3.985 3.99z'/%3E%3C/svg%3E") 32 32,pointer}
1
+ .atcb{display:none}.atcb_button_wrapper{display:inline-block;padding:5px;position:relative}.atcb_button{align-items:center;background:#f5f5f5;border:1px solid #d2d2d2;border-radius:6px;-webkit-box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);color:#333;cursor:pointer;display:flex;font-family:Arial,Helvetica,sans-serif;font-size:16px;font-weight:600;line-height:24px;max-width:300px;min-width:150px;padding:10px 16px 11px 16px;position:relative;text-align:center;touch-action:manipulation;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;width:auto}.atcb_button:focus,.atcb_button:hover{background:#fff}@media only screen and (max-width:575px){.atcb_button{font-size:14px}}.atcb_button.atcb_active{background:#fff;border-radius:6px 6px 3px 3px;-webkit-box-shadow:1px 5px 15px 0 rgba(0,0,0,.5);box-shadow:1px 5px 15px 0 rgba(0,0,0,.5);margin:-2px -4px;padding:12px 20px 13px 20px;z-index:90}.atcb_icon{height:16px;display:inline-block;margin-bottom:4px;margin-right:10px}.atcb_icon svg{height:100%;color:#333;width:auto}.atcb_list{box-sizing:border-box;color:#333;display:block;font-family:Arial,Helvetica,sans-serif;max-width:100%;position:absolute;padding:0 3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%;min-width:10em;z-index:80}.atcb_list.atcb_modal{position:fixed;width:16em;left:50%;top:50%;transform:translateY(-50%) translateX(-50%)}.atcb_list_item{align-items:center;background:#fafafa;border:1px solid #d2d2d2;border-top:0;-webkit-box-shadow:1px 2px 8px 0 rgba(0,0,0,.3);box-shadow:1px 2px 8px 0 rgba(0,0,0,.3);box-sizing:border-box;cursor:pointer;display:flex;font-size:16px;left:50%;position:relative;padding:12px 18px;text-align:left;transform:translate(-50%);touch-action:manipulation;-webkit-tap-highlight-color:transparent}.atcb_list_item:focus,.atcb_list_item:hover{background:#fff;-webkit-box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);box-shadow:1px 2px 10px 0 rgba(0,0,0,.4);color:#000}@media only screen and (max-width:575px){.atcb_list_item{font-size:14px}}.atcb_list.atcb_generated_button .atcb_list_item:first-child{padding-top:20px}.atcb_list:not(.atcb_generated_button) .atcb_list_item:first-child{border-radius:6px 6px 0 0}.atcb_list_item:last-child{border-radius:0 0 6px 6px}.atcb_list_item .atcb_icon{margin-right:8px;width:18px}.atcb_bgoverlay{background:rgba(20,20,20,.2);backdrop-filter:blur(2px);height:100%;left:0;position:fixed;top:0;width:100%;z-index:70}.atcb_bgoverlay.atcb_click:hover{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath fill-rule='evenodd' d='M11.991.69a2.35 2.35 0 0 1 3.318-.009c.918.911.922 2.392.009 3.307l-4.009 4.014 4.013 4.018c.906.909.893 2.38-.027 3.287a2.35 2.35 0 0 1-3.307-.004l-3.985-3.99-3.993 3.997a2.35 2.35 0 0 1-3.318.009c-.918-.911-.922-2.392-.009-3.307l4.009-4.014L.678 3.98C-.228 3.072-.215 1.6.706.693a2.35 2.35 0 0 1 3.307.004l3.985 3.99z'/%3E%3C/svg%3E") 32 32,pointer}
2
2
  /*# sourceMappingURL=atcb.min.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["assets\\css\\atcb.css"],"names":[],"mappings":"AAYA,MACE,QAAS,KAGX,qBACE,QAAS,aACT,SAAU,SAGZ,aACE,YAAa,OACb,WAAY,QACZ,OAAQ,IAAI,MAAM,QAClB,cAAe,IACf,mBAAoB,IAAI,IAAI,KAAK,EAAI,eACrC,WAAY,IAAI,IAAI,KAAK,EAAI,eAC7B,MAAO,KACP,OAAQ,QACR,QAAS,KACT,UAAW,KACX,YAAa,IACb,YAAa,KACb,UAAW,MACX,UAAW,MACX,QAAS,KAAK,KAAK,KAAK,KACxB,SAAU,SACV,WAAY,OACZ,aAAc,aACd,oBAAqB,KACrB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KACb,4BAA6B,YAC7B,MAAO,KAET,mBACA,mBACE,WAAY,KAEd,yCACE,aACE,UAAW,MAIf,yBACE,WAAY,KACZ,cAAe,IAAI,IAAI,IAAI,IAC3B,mBAAoB,IAAI,IAAI,KAAK,EAAI,eACrC,WAAY,IAAI,IAAI,KAAK,EAAI,eAC7B,QAAS,KAAK,KAAK,KAAK,KACxB,cAAe,KACf,QAAS,GAGX,WACE,OAAQ,KACR,QAAS,aACT,cAAe,IACf,aAAc,KAEhB,eACE,OAAQ,KACR,MAAO,KACP,MAAO,KAGT,WACE,WAAY,WACZ,MAAO,KACP,QAAS,MACT,UAAW,KACX,SAAU,SACV,QAAS,EAAE,IACX,oBAAqB,KACrB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KACb,MAAO,KACP,UAAW,KACX,QAAS,GAGX,sBACE,SAAU,MACV,MAAO,KACP,KAAM,IACN,IAAK,IACL,UAAW,iBAAiB,iBAG9B,gBACE,YAAa,OACb,WAAY,QACZ,OAAQ,IAAI,MAAM,QAClB,WAAY,EACZ,mBAAoB,IAAI,IAAI,IAAI,EAAI,eACpC,WAAY,IAAI,IAAI,IAAI,EAAI,eAC5B,WAAY,WACZ,OAAQ,QACR,QAAS,KACT,UAAW,KACX,KAAM,IACN,SAAU,SACV,QAAS,KAAK,KACd,WAAY,KACZ,UAAW,gBACX,aAAc,aACd,4BAA6B,YAE/B,sBACA,sBACE,WAAY,KACZ,mBAAoB,IAAI,IAAI,KAAK,EAAI,eACrC,WAAY,IAAI,IAAI,KAAK,EAAI,eAC7B,MAAO,KAET,yCACE,gBACE,UAAW,MAIf,6DACE,YAAa,KAGf,mEACE,cAAe,IAAI,IAAI,EAAE,EAG3B,2BACE,cAAe,EAAE,EAAE,IAAI,IAIzB,2BACE,aAAc,IACd,MAAO,KAGT,gBACE,WAAY,kBACZ,gBAAiB,UACjB,OAAQ,KACR,KAAM,EACN,SAAU,MACV,IAAK,EACL,MAAO,KACP,QAAS,GAEX,iCACE,OAAQ,2cAA2c,GAAG,EAAE,CAAE"}
1
+ {"version":3,"sources":["assets\\css\\atcb.css"],"names":[],"mappings":"AAYA,MACE,QAAS,KAGX,qBACE,QAAS,aACT,QAAS,IACT,SAAU,SAGZ,aACE,YAAa,OACb,WAAY,QACZ,OAAQ,IAAI,MAAM,QAClB,cAAe,IACf,mBAAoB,IAAI,IAAI,KAAK,EAAI,eACrC,WAAY,IAAI,IAAI,KAAK,EAAI,eAC7B,MAAO,KACP,OAAQ,QACR,QAAS,KACT,YAAY,KAAK,CAAE,SAAS,CAAE,WAC9B,UAAW,KACX,YAAa,IACb,YAAa,KACb,UAAW,MACX,UAAW,MACX,QAAS,KAAK,KAAK,KAAK,KACxB,SAAU,SACV,WAAY,OACZ,aAAc,aACd,oBAAqB,KACrB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KACb,4BAA6B,YAC7B,MAAO,KAET,mBACA,mBACE,WAAY,KAEd,yCACE,aACE,UAAW,MAIf,yBACE,WAAY,KACZ,cAAe,IAAI,IAAI,IAAI,IAC3B,mBAAoB,IAAI,IAAI,KAAK,EAAI,eACrC,WAAY,IAAI,IAAI,KAAK,EAAI,eAC7B,OAAQ,KAAK,KACb,QAAS,KAAK,KAAK,KAAK,KACxB,QAAS,GAGX,WACE,OAAQ,KACR,QAAS,aACT,cAAe,IACf,aAAc,KAEhB,eACE,OAAQ,KACR,MAAO,KACP,MAAO,KAGT,WACE,WAAY,WACZ,MAAO,KACP,QAAS,MACT,YAAY,KAAK,CAAE,SAAS,CAAE,WAC9B,UAAW,KACX,SAAU,SACV,QAAS,EAAE,IACX,oBAAqB,KACrB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KACb,MAAO,KACP,UAAW,KACX,QAAS,GAGX,sBACE,SAAU,MACV,MAAO,KACP,KAAM,IACN,IAAK,IACL,UAAW,iBAAiB,iBAG9B,gBACE,YAAa,OACb,WAAY,QACZ,OAAQ,IAAI,MAAM,QAClB,WAAY,EACZ,mBAAoB,IAAI,IAAI,IAAI,EAAI,eACpC,WAAY,IAAI,IAAI,IAAI,EAAI,eAC5B,WAAY,WACZ,OAAQ,QACR,QAAS,KACT,UAAW,KACX,KAAM,IACN,SAAU,SACV,QAAS,KAAK,KACd,WAAY,KACZ,UAAW,gBACX,aAAc,aACd,4BAA6B,YAE/B,sBACA,sBACE,WAAY,KACZ,mBAAoB,IAAI,IAAI,KAAK,EAAI,eACrC,WAAY,IAAI,IAAI,KAAK,EAAI,eAC7B,MAAO,KAET,yCACE,gBACE,UAAW,MAIf,6DACE,YAAa,KAGf,mEACE,cAAe,IAAI,IAAI,EAAE,EAG3B,2BACE,cAAe,EAAE,EAAE,IAAI,IAIzB,2BACE,aAAc,IACd,MAAO,KAGT,gBACE,WAAY,kBACZ,gBAAiB,UACjB,OAAQ,KACR,KAAM,EACN,SAAU,MACV,IAAK,EACL,MAAO,KACP,QAAS,GAEX,iCACE,OAAQ,2cAA2c,GAAG,EAAE,CAAE"}
@@ -3,7 +3,7 @@
3
3
  * Add-to-Calendar Button
4
4
  * ++++++++++++++++++++++
5
5
  */
6
- const atcbVersion = '1.7.1';
6
+ const atcbVersion = '1.7.4';
7
7
  /* Creator: Jens Kuerschner (https://jenskuerschner.de)
8
8
  * Project: https://github.com/jekuer/add-to-calendar-button
9
9
  * License: MIT with “Commons Clause” License Condition v1.0
@@ -49,12 +49,9 @@ function atcb_init() {
49
49
  atcbConfig = atcb_patch_config(atcbConfig);
50
50
  // check, if all required data is available
51
51
  if (atcb_check_required(atcbConfig)) {
52
- // standardize line breaks and transform urls in the description
53
- atcbConfig = atcb_decorate_description(atcbConfig);
54
- // calculate the real date values in case that there are some special rules included (e.g. adding days dynamically)
55
- atcbConfig['startDate'] = atcb_date_calculation(atcbConfig['startDate']);
56
- atcbConfig['endDate'] = atcb_date_calculation(atcbConfig['endDate']);
57
- // validate the JSON ...
52
+ // Rewrite dynamic dates, standardize line breaks and transform urls in the description
53
+ atcbConfig = atcb_decorate_data(atcbConfig);
54
+ // validate the config (JSON iput) ...
58
55
  if (atcb_validate(atcbConfig)) {
59
56
  // ... and generate the button on success
60
57
  atcb_generate(atcButtons[i], i + atcButtonsInitialized.length, atcbConfig);
@@ -75,17 +72,6 @@ function atcb_parse_schema_json(atcbConfig) {
75
72
  atcbConfig[key] = atcbConfig['event'][key];
76
73
  }
77
74
  });
78
- // parse schema date+time format
79
- const endpoints = ['start', 'end'];
80
- endpoints.forEach(function(point) {
81
- if (atcbConfig[point + 'Date'] != null) {
82
- let tmpSplitStartDate = atcbConfig[point + 'Date'].split('T');
83
- if (tmpSplitStartDate[1] != null) {
84
- atcbConfig[point + 'Date'] = tmpSplitStartDate[0];
85
- atcbConfig[point + 'Time'] = tmpSplitStartDate[1];
86
- }
87
- }
88
- });
89
75
  // drop the event block and return
90
76
  delete atcbConfig.event;
91
77
  }
@@ -114,7 +100,16 @@ function atcb_patch_config(atcbConfig) {
114
100
  return atcbConfig;
115
101
  }
116
102
 
117
- function atcb_decorate_description(atcbConfig) {
103
+
104
+
105
+ // CLEAN DATA BEFORE FURTHER VALIDATION (CONSIDERING SPECIAL RULES AND SCHEMES)
106
+ function atcb_decorate_data(atcbConfig) {
107
+ // cleanup different date-time formats
108
+ atcbConfig = atcb_date_cleanup(atcbConfig);
109
+ // calculate the real date values in case that there are some special rules included (e.g. adding days dynamically)
110
+ atcbConfig['startDate'] = atcb_date_calculation(atcbConfig['startDate']);
111
+ atcbConfig['endDate'] = atcb_date_calculation(atcbConfig['endDate']);
112
+
118
113
  // if no description or already decorated, return early
119
114
  if (!atcbConfig.description || atcbConfig.description_iCal) return atcbConfig;
120
115
 
@@ -128,6 +123,7 @@ function atcb_decorate_description(atcbConfig) {
128
123
  }
129
124
 
130
125
 
126
+
131
127
  // CHECK FOR REQUIRED FIELDS
132
128
  function atcb_check_required(data) {
133
129
  // check for at least 1 option
@@ -149,6 +145,29 @@ function atcb_check_required(data) {
149
145
 
150
146
 
151
147
  // CALCULATE AND CLEAN UP THE ACTUAL DATES
148
+ function atcb_date_cleanup(data) {
149
+ // parse date+time format (default with Schema.org, but also an unofficial alternative to other implementation)
150
+ const endpoints = ['start', 'end'];
151
+ endpoints.forEach(function(point) {
152
+ if (data[point + 'Date'] != null) {
153
+ // remove any milliseconds information
154
+ data[point + 'Date'] = data[point + 'Date'].replace(/\..../, '').replace('Z', '');
155
+ // identify a possible time information within the date string
156
+ let tmpSplitStartDate = data[point + 'Date'].split('T');
157
+ if (tmpSplitStartDate[1] != null) {
158
+ data[point + 'Date'] = tmpSplitStartDate[0];
159
+ data[point + 'Time'] = tmpSplitStartDate[1];
160
+ }
161
+ }
162
+ // remove any seconds from time information
163
+ if (data[point + 'Time'] != null && data[point + 'Time'].length == 8) {
164
+ let timeStr = data[point + 'Time'];
165
+ data[point + 'Time'] = timeStr.substring(0, timeStr.length - 3);
166
+ }
167
+ });
168
+ return data;
169
+ }
170
+
152
171
  function atcb_date_calculation(dateString) {
153
172
  // replace "today" with the current date first
154
173
  let today = new Date();
@@ -188,6 +207,10 @@ function atcb_validate(data) {
188
207
  const dates = ['startDate', 'endDate'];
189
208
  let newDate = dates;
190
209
  if (!dates.every(function(date) {
210
+ if (data[date].length != 10) {
211
+ console.error("add-to-calendar button generation failed: date misspelled [-> YYYY-MM-DD]");
212
+ return false;
213
+ }
191
214
  const dateParts = data[date].split('-');
192
215
  if (dateParts.length < 3 || dateParts.length > 3) {
193
216
  console.error("add-to-calendar button generation failed: date misspelled [" + date + ": " + data[date] + "]");
@@ -202,18 +225,22 @@ function atcb_validate(data) {
202
225
  const times = ['startTime', 'endTime'];
203
226
  if (!times.every(function(time) {
204
227
  if (data[time] != null) {
228
+ if (data[time].length != 5) {
229
+ console.error("add-to-calendar button generation failed: time misspelled [-> HH:MM]");
230
+ return false;
231
+ }
205
232
  const timeParts = data[time].split(':');
206
233
  // validate the time parts
207
234
  if (timeParts.length < 2 || timeParts.length > 2) {
208
- console.log("add-to-calendar button generation failed: time misspelled [" + time + ": " + data[time] + "]");
235
+ console.error("add-to-calendar button generation failed: time misspelled [" + time + ": " + data[time] + "]");
209
236
  return false;
210
237
  }
211
238
  if (timeParts[0] > 23) {
212
- console.log("add-to-calendar button generation failed: time misspelled - hours number too high [" + time + ": " + timeParts[0] + "]");
239
+ console.error("add-to-calendar button generation failed: time misspelled - hours number too high [" + time + ": " + timeParts[0] + "]");
213
240
  return false;
214
241
  }
215
242
  if (timeParts[1] > 59) {
216
- console.log("add-to-calendar button generation failed: time misspelled - minutes number too high [" + time + ": " + timeParts[1] + "]");
243
+ console.error("add-to-calendar button generation failed: time misspelled - minutes number too high [" + time + ": " + timeParts[1] + "]");
217
244
  return false;
218
245
  }
219
246
  // update the date with the time for further validation steps
@@ -229,12 +256,12 @@ function atcb_validate(data) {
229
256
  return false;
230
257
  }
231
258
  if ((data['startTime'] != null && data['endTime'] == null) || (data['startTime'] == null && data['endTime'] != null)) {
232
- console.log("add-to-calendar button generation failed: if you set a starting time, you also need to define an end time");
259
+ console.error("add-to-calendar button generation failed: if you set a starting time, you also need to define an end time");
233
260
  return false;
234
261
  }
235
262
  // validate whether end is not before start
236
263
  if (newDate['endDate'] < newDate['startDate']) {
237
- console.log("add-to-calendar button generation failed: end date before start date");
264
+ console.error("add-to-calendar button generation failed: end date before start date");
238
265
  return false;
239
266
  }
240
267
  // on passing the validation, return true
@@ -265,7 +292,7 @@ function atcb_generate(button, buttonId, data) {
265
292
  buttonTrigger.addEventListener('mousedown', () => atcb_toggle(data, buttonTrigger, true, false));
266
293
  } else {
267
294
  buttonTrigger.addEventListener('touchstart', () => atcb_toggle(data, buttonTrigger, true, false), {passive: true});
268
- buttonTrigger.addEventListener('mouseenter', () => atcb_action(data, buttonTrigger, true, false));
295
+ buttonTrigger.addEventListener('mouseenter', () => atcb_open(data, buttonTrigger, true, false));
269
296
  }
270
297
  buttonTrigger.addEventListener('keydown', function(event) { // trigger click on enter as well
271
298
  if (event.key == 'Enter') {
@@ -401,21 +428,15 @@ function atcb_toggle(data, button, buttonGenerated, keyboardTrigger = true) {
401
428
  if (button.classList.contains('atcb_active')) {
402
429
  atcb_close();
403
430
  } else {
404
- atcb_action(data, button, buttonGenerated, keyboardTrigger);
431
+ atcb_open(data, button, buttonGenerated, keyboardTrigger);
405
432
  }
406
433
  }
407
434
 
408
435
  // show the dropdown list + background overlay
409
- function atcb_action(data, button, buttonGenerated = false, keyboardTrigger = true) {
436
+ function atcb_open(data, button, buttonGenerated = false, keyboardTrigger = true) {
410
437
  // abort early if an add-to-calendar dropdown already opened
411
438
  if (document.querySelector('.atcb_list')) return
412
439
 
413
- // validate & decorate data
414
- if (!atcb_check_required(data) || !atcb_validate(data)) {
415
- throw new Error("Invalid data; see logs")
416
- }
417
- data = atcb_decorate_description(data);
418
-
419
440
  // generate list
420
441
  const list = atcb_generate_dropdown_list(data, buttonGenerated);
421
442
 
@@ -426,7 +447,7 @@ function atcb_action(data, button, buttonGenerated = false, keyboardTrigger = tr
426
447
  const rect = button.getBoundingClientRect();
427
448
  list.style.width = rect.width + 'px';
428
449
  list.style.top = rect.bottom + window.scrollY + 'px';
429
- list.style.right = document.body.offsetWidth - rect.right + 'px';
450
+ list.style.left = rect.left + 'px';
430
451
  } else {
431
452
  list.classList.add('atcb_modal')
432
453
  }
@@ -444,7 +465,6 @@ function atcb_action(data, button, buttonGenerated = false, keyboardTrigger = tr
444
465
  if (keyboardTrigger) {
445
466
  list.firstChild.focus();
446
467
  }
447
-
448
468
  }
449
469
 
450
470
  function atcb_close(blockFocus = false) {
@@ -470,6 +490,19 @@ function atcb_close(blockFocus = false) {
470
490
  ).forEach(el => el.remove());
471
491
  }
472
492
 
493
+ // prepare data when not using the init function
494
+ function atcb_action(data, button) {
495
+ // validate & decorate data
496
+ if (!atcb_check_required(data)) {
497
+ throw new Error("data missing; see logs")
498
+ }
499
+ data = atcb_decorate_data(data);
500
+ if (!atcb_validate(data)) {
501
+ throw new Error("Invalid data; see logs")
502
+ }
503
+ atcb_open(data, button);
504
+ }
505
+
473
506
 
474
507
 
475
508
  // FUNCTION TO GENERATE THE GOOGLE URL
@@ -625,7 +658,7 @@ function atcb_generate_ical(data) {
625
658
  (window.URL || window.webkitURL).revokeObjectURL(save.href);
626
659
  }
627
660
  } catch(e) {
628
- console.log(e);
661
+ console.error(e);
629
662
  }
630
663
  }
631
664
 
@@ -695,14 +728,16 @@ function atcb_generate_time(data, style = 'delimiters', targetCal = 'general') {
695
728
  return returnObject;
696
729
  }
697
730
 
731
+ const isBrowser=new Function("try {return this===window;}catch(e){ return false;}");
698
732
 
699
-
700
- // Global listener to ESC key to close dropdown
701
- document.addEventListener('keydown', evt => {
702
- if (evt.key === 'Escape') {
703
- atcb_close();
704
- }
705
- });
733
+ if (isBrowser()) {
734
+ // Global listener to ESC key to close dropdown
735
+ document.addEventListener('keydown', evt => {
736
+ if (evt.key === 'Escape') {
737
+ atcb_close();
738
+ }
739
+ });
740
+ }
706
741
 
707
742
 
708
743
 
@@ -3,7 +3,7 @@
3
3
  * Add-to-Calendar Button
4
4
  * ++++++++++++++++++++++
5
5
  */
6
- const atcbVersion = '1.7.1';
6
+ const atcbVersion = '1.7.4';
7
7
  /* Creator: Jens Kuerschner (https://jenskuerschner.de)
8
8
  * Project: https://github.com/jekuer/add-to-calendar-button
9
9
  * License: MIT with “Commons Clause” License Condition v1.0
@@ -49,12 +49,9 @@ function atcb_init() {
49
49
  atcbConfig = atcb_patch_config(atcbConfig);
50
50
  // check, if all required data is available
51
51
  if (atcb_check_required(atcbConfig)) {
52
- // standardize line breaks and transform urls in the description
53
- atcbConfig = atcb_decorate_description(atcbConfig);
54
- // calculate the real date values in case that there are some special rules included (e.g. adding days dynamically)
55
- atcbConfig['startDate'] = atcb_date_calculation(atcbConfig['startDate']);
56
- atcbConfig['endDate'] = atcb_date_calculation(atcbConfig['endDate']);
57
- // validate the JSON ...
52
+ // Rewrite dynamic dates, standardize line breaks and transform urls in the description
53
+ atcbConfig = atcb_decorate_data(atcbConfig);
54
+ // validate the config (JSON iput) ...
58
55
  if (atcb_validate(atcbConfig)) {
59
56
  // ... and generate the button on success
60
57
  atcb_generate(atcButtons[i], i + atcButtonsInitialized.length, atcbConfig);
@@ -75,17 +72,6 @@ function atcb_parse_schema_json(atcbConfig) {
75
72
  atcbConfig[key] = atcbConfig['event'][key];
76
73
  }
77
74
  });
78
- // parse schema date+time format
79
- const endpoints = ['start', 'end'];
80
- endpoints.forEach(function(point) {
81
- if (atcbConfig[point + 'Date'] != null) {
82
- let tmpSplitStartDate = atcbConfig[point + 'Date'].split('T');
83
- if (tmpSplitStartDate[1] != null) {
84
- atcbConfig[point + 'Date'] = tmpSplitStartDate[0];
85
- atcbConfig[point + 'Time'] = tmpSplitStartDate[1];
86
- }
87
- }
88
- });
89
75
  // drop the event block and return
90
76
  delete atcbConfig.event;
91
77
  }
@@ -114,7 +100,16 @@ function atcb_patch_config(atcbConfig) {
114
100
  return atcbConfig;
115
101
  }
116
102
 
117
- function atcb_decorate_description(atcbConfig) {
103
+
104
+
105
+ // CLEAN DATA BEFORE FURTHER VALIDATION (CONSIDERING SPECIAL RULES AND SCHEMES)
106
+ function atcb_decorate_data(atcbConfig) {
107
+ // cleanup different date-time formats
108
+ atcbConfig = atcb_date_cleanup(atcbConfig);
109
+ // calculate the real date values in case that there are some special rules included (e.g. adding days dynamically)
110
+ atcbConfig['startDate'] = atcb_date_calculation(atcbConfig['startDate']);
111
+ atcbConfig['endDate'] = atcb_date_calculation(atcbConfig['endDate']);
112
+
118
113
  // if no description or already decorated, return early
119
114
  if (!atcbConfig.description || atcbConfig.description_iCal) return atcbConfig;
120
115
 
@@ -128,6 +123,7 @@ function atcb_decorate_description(atcbConfig) {
128
123
  }
129
124
 
130
125
 
126
+
131
127
  // CHECK FOR REQUIRED FIELDS
132
128
  function atcb_check_required(data) {
133
129
  // check for at least 1 option
@@ -149,6 +145,29 @@ function atcb_check_required(data) {
149
145
 
150
146
 
151
147
  // CALCULATE AND CLEAN UP THE ACTUAL DATES
148
+ function atcb_date_cleanup(data) {
149
+ // parse date+time format (default with Schema.org, but also an unofficial alternative to other implementation)
150
+ const endpoints = ['start', 'end'];
151
+ endpoints.forEach(function(point) {
152
+ if (data[point + 'Date'] != null) {
153
+ // remove any milliseconds information
154
+ data[point + 'Date'] = data[point + 'Date'].replace(/\..../, '').replace('Z', '');
155
+ // identify a possible time information within the date string
156
+ let tmpSplitStartDate = data[point + 'Date'].split('T');
157
+ if (tmpSplitStartDate[1] != null) {
158
+ data[point + 'Date'] = tmpSplitStartDate[0];
159
+ data[point + 'Time'] = tmpSplitStartDate[1];
160
+ }
161
+ }
162
+ // remove any seconds from time information
163
+ if (data[point + 'Time'] != null && data[point + 'Time'].length == 8) {
164
+ let timeStr = data[point + 'Time'];
165
+ data[point + 'Time'] = timeStr.substring(0, timeStr.length - 3);
166
+ }
167
+ });
168
+ return data;
169
+ }
170
+
152
171
  function atcb_date_calculation(dateString) {
153
172
  // replace "today" with the current date first
154
173
  let today = new Date();
@@ -188,6 +207,10 @@ function atcb_validate(data) {
188
207
  const dates = ['startDate', 'endDate'];
189
208
  let newDate = dates;
190
209
  if (!dates.every(function(date) {
210
+ if (data[date].length != 10) {
211
+ console.error("add-to-calendar button generation failed: date misspelled [-> YYYY-MM-DD]");
212
+ return false;
213
+ }
191
214
  const dateParts = data[date].split('-');
192
215
  if (dateParts.length < 3 || dateParts.length > 3) {
193
216
  console.error("add-to-calendar button generation failed: date misspelled [" + date + ": " + data[date] + "]");
@@ -202,18 +225,22 @@ function atcb_validate(data) {
202
225
  const times = ['startTime', 'endTime'];
203
226
  if (!times.every(function(time) {
204
227
  if (data[time] != null) {
228
+ if (data[time].length != 5) {
229
+ console.error("add-to-calendar button generation failed: time misspelled [-> HH:MM]");
230
+ return false;
231
+ }
205
232
  const timeParts = data[time].split(':');
206
233
  // validate the time parts
207
234
  if (timeParts.length < 2 || timeParts.length > 2) {
208
- console.log("add-to-calendar button generation failed: time misspelled [" + time + ": " + data[time] + "]");
235
+ console.error("add-to-calendar button generation failed: time misspelled [" + time + ": " + data[time] + "]");
209
236
  return false;
210
237
  }
211
238
  if (timeParts[0] > 23) {
212
- console.log("add-to-calendar button generation failed: time misspelled - hours number too high [" + time + ": " + timeParts[0] + "]");
239
+ console.error("add-to-calendar button generation failed: time misspelled - hours number too high [" + time + ": " + timeParts[0] + "]");
213
240
  return false;
214
241
  }
215
242
  if (timeParts[1] > 59) {
216
- console.log("add-to-calendar button generation failed: time misspelled - minutes number too high [" + time + ": " + timeParts[1] + "]");
243
+ console.error("add-to-calendar button generation failed: time misspelled - minutes number too high [" + time + ": " + timeParts[1] + "]");
217
244
  return false;
218
245
  }
219
246
  // update the date with the time for further validation steps
@@ -229,12 +256,12 @@ function atcb_validate(data) {
229
256
  return false;
230
257
  }
231
258
  if ((data['startTime'] != null && data['endTime'] == null) || (data['startTime'] == null && data['endTime'] != null)) {
232
- console.log("add-to-calendar button generation failed: if you set a starting time, you also need to define an end time");
259
+ console.error("add-to-calendar button generation failed: if you set a starting time, you also need to define an end time");
233
260
  return false;
234
261
  }
235
262
  // validate whether end is not before start
236
263
  if (newDate['endDate'] < newDate['startDate']) {
237
- console.log("add-to-calendar button generation failed: end date before start date");
264
+ console.error("add-to-calendar button generation failed: end date before start date");
238
265
  return false;
239
266
  }
240
267
  // on passing the validation, return true
@@ -265,7 +292,7 @@ function atcb_generate(button, buttonId, data) {
265
292
  buttonTrigger.addEventListener('mousedown', () => atcb_toggle(data, buttonTrigger, true, false));
266
293
  } else {
267
294
  buttonTrigger.addEventListener('touchstart', () => atcb_toggle(data, buttonTrigger, true, false), {passive: true});
268
- buttonTrigger.addEventListener('mouseenter', () => atcb_action(data, buttonTrigger, true, false));
295
+ buttonTrigger.addEventListener('mouseenter', () => atcb_open(data, buttonTrigger, true, false));
269
296
  }
270
297
  buttonTrigger.addEventListener('keydown', function(event) { // trigger click on enter as well
271
298
  if (event.key == 'Enter') {
@@ -401,21 +428,15 @@ function atcb_toggle(data, button, buttonGenerated, keyboardTrigger = true) {
401
428
  if (button.classList.contains('atcb_active')) {
402
429
  atcb_close();
403
430
  } else {
404
- atcb_action(data, button, buttonGenerated, keyboardTrigger);
431
+ atcb_open(data, button, buttonGenerated, keyboardTrigger);
405
432
  }
406
433
  }
407
434
 
408
435
  // show the dropdown list + background overlay
409
- function atcb_action(data, button, buttonGenerated = false, keyboardTrigger = true) {
436
+ function atcb_open(data, button, buttonGenerated = false, keyboardTrigger = true) {
410
437
  // abort early if an add-to-calendar dropdown already opened
411
438
  if (document.querySelector('.atcb_list')) return
412
439
 
413
- // validate & decorate data
414
- if (!atcb_check_required(data) || !atcb_validate(data)) {
415
- throw new Error("Invalid data; see logs")
416
- }
417
- data = atcb_decorate_description(data);
418
-
419
440
  // generate list
420
441
  const list = atcb_generate_dropdown_list(data, buttonGenerated);
421
442
 
@@ -426,7 +447,7 @@ function atcb_action(data, button, buttonGenerated = false, keyboardTrigger = tr
426
447
  const rect = button.getBoundingClientRect();
427
448
  list.style.width = rect.width + 'px';
428
449
  list.style.top = rect.bottom + window.scrollY + 'px';
429
- list.style.right = document.body.offsetWidth - rect.right + 'px';
450
+ list.style.left = rect.left + 'px';
430
451
  } else {
431
452
  list.classList.add('atcb_modal')
432
453
  }
@@ -444,7 +465,6 @@ function atcb_action(data, button, buttonGenerated = false, keyboardTrigger = tr
444
465
  if (keyboardTrigger) {
445
466
  list.firstChild.focus();
446
467
  }
447
-
448
468
  }
449
469
 
450
470
  function atcb_close(blockFocus = false) {
@@ -470,6 +490,19 @@ function atcb_close(blockFocus = false) {
470
490
  ).forEach(el => el.remove());
471
491
  }
472
492
 
493
+ // prepare data when not using the init function
494
+ function atcb_action(data, button) {
495
+ // validate & decorate data
496
+ if (!atcb_check_required(data)) {
497
+ throw new Error("data missing; see logs")
498
+ }
499
+ data = atcb_decorate_data(data);
500
+ if (!atcb_validate(data)) {
501
+ throw new Error("Invalid data; see logs")
502
+ }
503
+ atcb_open(data, button);
504
+ }
505
+
473
506
 
474
507
 
475
508
  // FUNCTION TO GENERATE THE GOOGLE URL
@@ -625,7 +658,7 @@ function atcb_generate_ical(data) {
625
658
  (window.URL || window.webkitURL).revokeObjectURL(save.href);
626
659
  }
627
660
  } catch(e) {
628
- console.log(e);
661
+ console.error(e);
629
662
  }
630
663
  }
631
664
 
@@ -695,14 +728,16 @@ function atcb_generate_time(data, style = 'delimiters', targetCal = 'general') {
695
728
  return returnObject;
696
729
  }
697
730
 
731
+ const isBrowser=new Function("try {return this===window;}catch(e){ return false;}");
698
732
 
699
-
700
- // Global listener to ESC key to close dropdown
701
- document.addEventListener('keydown', evt => {
702
- if (evt.key === 'Escape') {
703
- atcb_close();
704
- }
705
- });
733
+ if (isBrowser()) {
734
+ // Global listener to ESC key to close dropdown
735
+ document.addEventListener('keydown', evt => {
736
+ if (evt.key === 'Escape') {
737
+ atcb_close();
738
+ }
739
+ });
740
+ }
706
741
 
707
742
 
708
743
 
package/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "add-to-calendar-button",
3
- "version": "1.7.1",
3
+ "version": "1.7.4",
4
4
  "description": "A convenient JavaScript snippet, which lets you create beautiful buttons, where people can add events to their calendars.",
5
5
  "main": "npm_dist/cjs/index.js",
6
6
  "module": "npm_dist/mjs/index.js",
7
7
  "exports": {
8
+ "./assets/css/": "./assets/css/",
8
9
  ".": {
9
10
  "import": "./npm_dist/mjs/index.js",
10
11
  "require": "./npm_dist/cjs/index.js"
11
12
  }
12
13
  },
13
14
  "types": "index.d.ts",
14
- "style": "assets/css/atcb.css",
15
+ "style": "assets/css/atcb.min.css",
15
16
  "repository": {
16
17
  "type": "git",
17
18
  "url": "https://github.com/jekuer/add-to-calendar-button.git"
@@ -47,7 +48,7 @@
47
48
  },
48
49
  "homepage": "https://github.com/jekuer/add-to-calendar-button",
49
50
  "scripts": {
50
- "test": "echo \"Error: no test specified\" && exit 1",
51
+ "test": "node test/server-side-rendering.js",
51
52
  "build": "grunt"
52
53
  },
53
54
  "devDependencies": {
@@ -0,0 +1,2 @@
1
+ // simple import of the atcb JS, to ensure it can load in NodeJS
2
+ require('../assets/js/atcb.js')