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 +7 -3
- package/assets/css/atcb.css +5 -2
- package/assets/css/atcb.min.css +1 -1
- package/assets/css/atcb.min.css.map +1 -1
- package/npm_dist/cjs/index.js +78 -43
- package/npm_dist/mjs/index.js +78 -43
- package/package.json +4 -3
- package/test/server-side-rendering.js +2 -0
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
|
-
###
|
|
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
|
|
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
|
package/assets/css/atcb.css
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Add-to-Calendar Button
|
|
4
4
|
* ++++++++++++++++++++++
|
|
5
5
|
*
|
|
6
|
-
* Version: 1.7.
|
|
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;
|
package/assets/css/atcb.min.css
CHANGED
|
@@ -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;
|
|
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,
|
|
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"}
|
package/npm_dist/cjs/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Add-to-Calendar Button
|
|
4
4
|
* ++++++++++++++++++++++
|
|
5
5
|
*/
|
|
6
|
-
const atcbVersion = '1.7.
|
|
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 =
|
|
54
|
-
//
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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', () =>
|
|
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
|
-
|
|
431
|
+
atcb_open(data, button, buttonGenerated, keyboardTrigger);
|
|
405
432
|
}
|
|
406
433
|
}
|
|
407
434
|
|
|
408
435
|
// show the dropdown list + background overlay
|
|
409
|
-
function
|
|
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.
|
|
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.
|
|
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
|
-
|
|
703
|
-
|
|
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/npm_dist/mjs/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Add-to-Calendar Button
|
|
4
4
|
* ++++++++++++++++++++++
|
|
5
5
|
*/
|
|
6
|
-
const atcbVersion = '1.7.
|
|
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 =
|
|
54
|
-
//
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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', () =>
|
|
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
|
-
|
|
431
|
+
atcb_open(data, button, buttonGenerated, keyboardTrigger);
|
|
405
432
|
}
|
|
406
433
|
}
|
|
407
434
|
|
|
408
435
|
// show the dropdown list + background overlay
|
|
409
|
-
function
|
|
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.
|
|
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.
|
|
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
|
-
|
|
703
|
-
|
|
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.
|
|
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": "
|
|
51
|
+
"test": "node test/server-side-rendering.js",
|
|
51
52
|
"build": "grunt"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|