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 {
0 commit comments