add-to-calendar-button 1.3.1 → 1.4.0

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
@@ -87,9 +87,9 @@ Mind that with Angular, you might need to escape the { with `{{ '{' }}` and } wi
87
87
  ```html
88
88
  <div class="atcb" style="display:none;">
89
89
  {
90
- "title":"Add the title of your event",
91
- "dateStart":"02-21-2022",
92
- "dateEnd":"03-24-2022",
90
+ "name":"Add the title of your event",
91
+ "startDate":"02-21-2022",
92
+ "endDate":"03-24-2022",
93
93
  "options":[
94
94
  "Google"
95
95
  ]
@@ -97,19 +97,19 @@ Mind that with Angular, you might need to escape the { with `{{ '{' }}` and } wi
97
97
  </div>
98
98
  ```
99
99
 
100
- ### Full structure
100
+ ### Full structure (without schema.org markup)
101
101
 
102
102
  ```html
103
103
  <div class="atcb" style="display:none;">
104
104
  {
105
- "label":"Add to Calendar",
106
- "title":"Add the title of your event",
105
+ "name":"Add the title of your event",
107
106
  "description":"A nice description does not hurt",
108
- "dateStart":"02-21-2022",
109
- "dateEnd":"03-24-2022",
110
- "timeStart":"10:13",
111
- "timeEnd":"17:57",
107
+ "startDate":"02-21-2022",
108
+ "endDate":"03-24-2022",
109
+ "startTime":"10:13",
110
+ "endTime":"17:57",
112
111
  "location":"Somewhere over the rainbow",
112
+ "label":"Add to Calendar",
113
113
  "options":[
114
114
  "Apple",
115
115
  "Google",
@@ -125,6 +125,41 @@ Mind that with Angular, you might need to escape the { with `{{ '{' }}` and } wi
125
125
  }
126
126
  </div>
127
127
  ```
128
+
129
+ ### Full structure (with schema.org markup)
130
+ You can save on the `style="display:none;"`, but mind that you should not use dynamic dates (e.g. "today" or "+1") here!
131
+ You can use startTime and endTime in the event block, but it is recommended to rather add it to startDate and endDate with "T" as delimiter here.
132
+
133
+ ```html
134
+ <div class="atcb">
135
+ <script type="application/ld+json">
136
+ {
137
+ "event": {
138
+ "@context":"https://schema.org",
139
+ "@type":"Event",
140
+ "name":"Add the title of your event",
141
+ "description":"A nice description does not hurt",
142
+ "startDate":"02-21-2022T10:13",
143
+ "endDate":"03-24-2022T17:57",
144
+ "location":"Somewhere over the rainbow"
145
+ },
146
+ "label":"Add to Calendar",
147
+ "options":[
148
+ "Apple",
149
+ "Google",
150
+ "iCal",
151
+ "Microsoft365",
152
+ "Outlook.com",
153
+ "Yahoo"
154
+ ],
155
+ "timeZone":"Europe/Berlin",
156
+ "timeZoneOffset":"+01:00",
157
+ "trigger":"click",
158
+ "iCalFileName":"Reminder-Event"
159
+ }
160
+ </script>
161
+ </div>
162
+ ```
128
163
 
129
164
 
130
165
  ### Important information and hidden features
@@ -164,10 +199,11 @@ The code is available under the [GPU 3.0 license](LICENSE.txt).
164
199
 
165
200
  ## Changelog (without bug fixes)
166
201
 
167
- * v1.3.0 : new license (MIT with “Commons Clause”)
168
- * v1.2.0 : inline and line break support
169
- * v1.1.0 : npm functionality
170
- * v1.0.0 : initial release
202
+ * v1.4 : schema.org support (also changed some keys in the JSON!)
203
+ * v1.3 : new license (MIT with “Commons Clause”)
204
+ * v1.2 : inline and line break support
205
+ * v1.1 : npm functionality
206
+ * v1.0 : initial release
171
207
 
172
208
 
173
209
  ## Kudos go to
@@ -3,7 +3,7 @@
3
3
  * Add-to-Calendar Button
4
4
  * ++++++++++++++++++++++
5
5
  *
6
- * Version: 1.3.1
6
+ * Version: 1.4.0
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
@@ -3,7 +3,7 @@
3
3
  * Add-to-Calendar Button
4
4
  * ++++++++++++++++++++++
5
5
  */
6
- const atcbVersion = '1.3.1';
6
+ const atcbVersion = '1.4.0';
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
@@ -29,13 +29,29 @@ function atcb_init() {
29
29
  if (atcButtons[i].classList.contains('atcb_initialized')) {
30
30
  continue;
31
31
  }
32
+ let atcbConfig;
33
+ // check if schema.org markup is present
34
+ let schema = atcButtons[i].querySelector('script');
32
35
  // get their JSON content first
33
- const atcbConfig = JSON.parse(atcButtons[i].innerHTML);
36
+ if (schema && schema.innerHTML) {
37
+ // get schema.org event markup and flatten the event block
38
+ atcbConfig = JSON.parse(schema.innerHTML);
39
+ atcbConfig = atcb_clean_schema_json(atcbConfig);
40
+ // set flag to not delete HTML content later
41
+ atcbConfig['deleteJSON'] = false;
42
+ } else {
43
+ // get JSON from HTML block
44
+ atcbConfig = JSON.parse(atcButtons[i].innerHTML);
45
+ // set flag to delete HTML content later
46
+ atcbConfig['deleteJSON'] = true;
47
+ }
48
+ // rewrite config for backwards compatibility - you can remove this, if you did not use this script before v1.4.0.
49
+ atcbConfig = atcb_rewrite_config(atcbConfig);
34
50
  // check, if all required data is available
35
51
  if (atcb_check_required(atcbConfig)) {
36
52
  // calculate the real date values in case that there are some special rules included (e.g. adding days dynamically)
37
- atcbConfig['dateStart'] = atcb_date_calculation(atcbConfig['dateStart']);
38
- atcbConfig['dateEnd'] = atcb_date_calculation(atcbConfig['dateEnd']);
53
+ atcbConfig['startDate'] = atcb_date_calculation(atcbConfig['startDate']);
54
+ atcbConfig['endDate'] = atcb_date_calculation(atcbConfig['endDate']);
39
55
  // validate the JSON ...
40
56
  if (atcb_validate(atcbConfig)) {
41
57
  // ... and generate the button on success
@@ -48,6 +64,51 @@ function atcb_init() {
48
64
 
49
65
 
50
66
 
67
+ // CLEAN/NORMALIZE JSON FROM SCHEMA.ORG MARKUP
68
+ function atcb_clean_schema_json(atcbConfig) {
69
+ Object.keys(atcbConfig['event']).forEach(key => {
70
+ // move entries one level up, but skip schema types
71
+ if (key.charAt(0) !== '@') {
72
+ atcbConfig[key] = atcbConfig['event'][key];
73
+ }
74
+ });
75
+ // clean schema date+time format
76
+ const endpoints = ['start', 'end'];
77
+ endpoints.forEach(function(point) {
78
+ if (atcbConfig[point + 'Date'] != null) {
79
+ let tmpSplitStartDate = atcbConfig[point + 'Date'].split('T');
80
+ if (tmpSplitStartDate[1] != null) {
81
+ atcbConfig[point + 'Date'] = tmpSplitStartDate[0];
82
+ atcbConfig[point + 'Time'] = tmpSplitStartDate[1];
83
+ }
84
+ }
85
+ });
86
+ // drop the event block and return
87
+ delete atcbConfig.event;
88
+ return atcbConfig;
89
+ }
90
+
91
+
92
+
93
+ // BACKWARDS COMPATIBILITY REWRITE - you can remove this, if you did not use this script before v1.4.0.
94
+ function atcb_rewrite_config(atcbConfig) {
95
+ const keyChanges = {
96
+ 'title': 'name',
97
+ 'dateStart': 'startDate',
98
+ 'dateEnd': 'endDate',
99
+ 'timeStart': 'startTime',
100
+ 'timeEnd': 'endTime',
101
+ };
102
+ Object.keys(keyChanges).forEach(key => {
103
+ if (atcbConfig[keyChanges[key]] == null && atcbConfig[key] != null) {
104
+ atcbConfig[keyChanges[key]] = atcbConfig[key];
105
+ }
106
+ });
107
+ return atcbConfig;
108
+ }
109
+
110
+
111
+
51
112
  // CHECK FOR REQUIRED FIELDS
52
113
  function atcb_check_required(data) {
53
114
  // check for at least 1 option
@@ -56,7 +117,7 @@ function atcb_check_required(data) {
56
117
  return false;
57
118
  }
58
119
  // check for min required data (without "options")
59
- const requiredField = ['title', 'dateStart', 'dateEnd']
120
+ const requiredField = ['name', 'startDate', 'endDate']
60
121
  return requiredField.every(function(field) {
61
122
  if (data[field] == null || data[field] == "") {
62
123
  console.log("add-to-calendar button generation failed: required setting missing [" + field + "]");
@@ -101,7 +162,7 @@ function atcb_validate(data) {
101
162
  return false;
102
163
  }
103
164
  // validate date
104
- const dates = ['dateStart', 'dateEnd'];
165
+ const dates = ['startDate', 'endDate'];
105
166
  let newDate = dates;
106
167
  if (!dates.every(function(date) {
107
168
  const dateParts = data[date].split('-');
@@ -115,7 +176,7 @@ function atcb_validate(data) {
115
176
  return false;
116
177
  }
117
178
  // validate time
118
- const times = ['timeStart', 'timeEnd'];
179
+ const times = ['startTime', 'endTime'];
119
180
  if (!times.every(function(time) {
120
181
  if (data[time] != null) {
121
182
  const timeParts = data[time].split(':');
@@ -133,23 +194,23 @@ function atcb_validate(data) {
133
194
  return false;
134
195
  }
135
196
  // update the date with the time for further validation steps
136
- if (time == 'timeStart') {
137
- newDate['dateStart'] = new Date(newDate['dateStart'].getTime() + (timeParts[0] * 3600000) + (timeParts[1] * 60000))
197
+ if (time == 'startTime') {
198
+ newDate['startDate'] = new Date(newDate['startDate'].getTime() + (timeParts[0] * 3600000) + (timeParts[1] * 60000))
138
199
  }
139
- if (time == 'timeEnd') {
140
- newDate['dateEnd'] = new Date(newDate['dateEnd'].getTime() + (timeParts[0] * 3600000) + (timeParts[1] * 60000))
200
+ if (time == 'endTime') {
201
+ newDate['endDate'] = new Date(newDate['endDate'].getTime() + (timeParts[0] * 3600000) + (timeParts[1] * 60000))
141
202
  }
142
203
  }
143
204
  return true;
144
205
  })) {
145
206
  return false;
146
207
  }
147
- if ((data['timeStart'] != null && data['timeEnd'] == null) || (data['timeStart'] == null && data['timeEnd'] != null)) {
208
+ if ((data['startTime'] != null && data['endTime'] == null) || (data['startTime'] == null && data['endTime'] != null)) {
148
209
  console.log("add-to-calendar button generation failed: if you set a starting time, you also need to define an end time");
149
210
  return false;
150
211
  }
151
212
  // validate whether end is not before start
152
- if (newDate['dateEnd'] < newDate['dateStart']) {
213
+ if (newDate['endDate'] < newDate['startDate']) {
153
214
  console.log("add-to-calendar button generation failed: end date before start date");
154
215
  return false;
155
216
  }
@@ -161,8 +222,10 @@ function atcb_validate(data) {
161
222
 
162
223
  // GENERATE THE ACTUAL BUTTON
163
224
  function atcb_generate(button, buttonId, data) {
164
- // clean the placeholder
165
- button.innerHTML = '';
225
+ // clean the placeholder, if flagged that way
226
+ if (data['deleteJSON']) {
227
+ button.innerHTML = '';
228
+ }
166
229
  // generate the wrapper div
167
230
  let buttonTriggerWrapper = document.createElement('div');
168
231
  buttonTriggerWrapper.classList.add('atcb_button_wrapper');
@@ -335,8 +398,8 @@ function atcb_generate_google(data) {
335
398
  if (data['location'] != null && data['location'] != '') {
336
399
  url += '&location=' + encodeURIComponent(data['location']);
337
400
  }
338
- if (data['title'] != null && data['title'] != '') {
339
- url += '&text=' + encodeURIComponent(data['title']);
401
+ if (data['name'] != null && data['name'] != '') {
402
+ url += '&text=' + encodeURIComponent(data['name']);
340
403
  }
341
404
  window.open(url, '_blank').focus();
342
405
  }
@@ -360,8 +423,8 @@ function atcb_generate_yahoo(data) {
360
423
  if (data['location'] != null && data['location'] != '') {
361
424
  url += '&in_loc=' + encodeURIComponent(data['location']);
362
425
  }
363
- if (data['title'] != null && data['title'] != '') {
364
- url += '&title=' + encodeURIComponent(data['title']);
426
+ if (data['name'] != null && data['name'] != '') {
427
+ url += '&title=' + encodeURIComponent(data['name']);
365
428
  }
366
429
  window.open(url, '_blank').focus();
367
430
  }
@@ -391,8 +454,8 @@ function atcb_generate_microsoft(data, type = '365') {
391
454
  if (data['location'] != null && data['location'] != '') {
392
455
  url += '&location=' + encodeURIComponent(data['location']);
393
456
  }
394
- if (data['title'] != null && data['title'] != '') {
395
- url += '&subject=' + encodeURIComponent(data['title']);
457
+ if (data['name'] != null && data['name'] != '') {
458
+ url += '&subject=' + encodeURIComponent(data['name']);
396
459
  }
397
460
  window.open(url, '_blank').focus();
398
461
  }
@@ -417,7 +480,7 @@ function atcb_generate_ical(data) {
417
480
  "DTSTART" + timeslot + ":" + formattedDate['start'],
418
481
  "DTEND" + timeslot + ":" + formattedDate['end'],
419
482
  "DESCRIPTION:" + data['description'].replace(/\n/g, '\\n'),
420
- "SUMMARY:" + data['title'],
483
+ "SUMMARY:" + data['name'],
421
484
  "LOCATION:" + data['location'],
422
485
  "STATUS:CONFIRMED",
423
486
  "LAST-MODIFIED:" + now,
@@ -449,17 +512,17 @@ function atcb_generate_ical(data) {
449
512
 
450
513
  // SHARED FUNCTION TO GENERATE A TIME STRING
451
514
  function atcb_generate_time(data, style = 'delimiters', targetCal = 'general') {
452
- let dateStart = data['dateStart'].split('-');
453
- let dateEnd = data['dateEnd'].split('-');
515
+ let startDate = data['startDate'].split('-');
516
+ let endDate = data['endDate'].split('-');
454
517
  let start = '';
455
518
  let end = '';
456
519
  let allday = false;
457
- if (data['timeStart'] != null && data['timeEnd'] != null) {
520
+ if (data['startTime'] != null && data['endTime'] != null) {
458
521
  // Adjust for timezone, if set (see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for either the TZ name or the offset)
459
522
  if (data['timeZoneOffset'] != null && data['timeZoneOffset'] != '') {
460
523
  // if we have a timezone offset given, consider it
461
- start = new Date( dateStart[2] + '-' + dateStart[0] + '-' + dateStart[1] + 'T' + data['timeStart'] + ':00.000' + data['timeZoneOffset'] );
462
- end = new Date( dateEnd[2] + '-' + dateEnd[0] + '-' + dateEnd[1] + 'T' + data['timeEnd'] + ':00.000' + data['timeZoneOffset'] );
524
+ start = new Date( startDate[2] + '-' + startDate[0] + '-' + startDate[1] + 'T' + data['startTime'] + ':00.000' + data['timeZoneOffset'] );
525
+ end = new Date( endDate[2] + '-' + endDate[0] + '-' + endDate[1] + 'T' + data['endTime'] + ':00.000' + data['timeZoneOffset'] );
463
526
  start = start.toISOString().replace('.000', '');
464
527
  end = end.toISOString().replace('.000', '');
465
528
  if (style == 'clean') {
@@ -468,8 +531,8 @@ function atcb_generate_time(data, style = 'delimiters', targetCal = 'general') {
468
531
  }
469
532
  } else {
470
533
  // if there is no offset, we prepare the time, assuming it is UTC formatted
471
- start = new Date( dateStart[2] + '-' + dateStart[0] + '-' + dateStart[1] + 'T' + data['timeStart'] + ':00.000+00:00' );
472
- end = new Date( dateEnd[2] + '-' + dateEnd[0] + '-' + dateEnd[1] + 'T' + data['timeEnd'] + ':00.000+00:00' );
534
+ start = new Date( startDate[2] + '-' + startDate[0] + '-' + startDate[1] + 'T' + data['startTime'] + ':00.000+00:00' );
535
+ end = new Date( endDate[2] + '-' + endDate[0] + '-' + endDate[1] + 'T' + data['endTime'] + ':00.000+00:00' );
473
536
  if (data['timeZone'] != null && data['timeZone'] != '') {
474
537
  // if a timezone is given, we adjust dynamically with the modern toLocaleString function
475
538
  let utcDate = new Date(start.toLocaleString('en-US', { timeZone: "UTC" }));
@@ -487,10 +550,10 @@ function atcb_generate_time(data, style = 'delimiters', targetCal = 'general') {
487
550
  }
488
551
  } else { // would be an allday event then
489
552
  allday = true;
490
- start = new Date( dateStart[2], dateStart[0] - 1, dateStart[1]);
553
+ start = new Date( startDate[2], startDate[0] - 1, startDate[1]);
491
554
  start.setDate(start.getDate() + 1); // increment the day by 1
492
555
  let breakStart = start.toISOString().split('T');
493
- end = new Date( dateEnd[2], dateEnd[0] - 1, dateEnd[1]);
556
+ end = new Date( endDate[2], endDate[0] - 1, endDate[1]);
494
557
  if (targetCal == 'google' || targetCal == 'microsoft' || targetCal == 'ical') {
495
558
  end.setDate(end.getDate() + 2); // increment the day by 2 for Google Calendar, iCal and Outlook
496
559
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-to-calendar-button",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
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/atcb_npm.js",
6
6
  "types": "index.d.ts",