7e895337e3dbfe02aa0c4f69e6eaab03421ab09f
2 * FullCalendar v3.2.0 Google Calendar Plugin
3 * Docs & License: https://fullcalendar.io/
8 if (typeof define
=== 'function' && define
.amd
) {
9 define([ 'jquery' ], factory
);
11 else if (typeof exports
=== 'object') { // Node/CommonJS
12 module
.exports
= factory(require('jquery'));
20 var API_BASE
= 'https://www.googleapis.com/calendar/v3/calendars';
21 var FC
= $.fullCalendar
;
22 var applyAll
= FC
.applyAll
;
25 FC
.sourceNormalizers
.push(function(sourceOptions
) {
26 var googleCalendarId
= sourceOptions
.googleCalendarId
;
27 var url
= sourceOptions
.url
;
30 // if the Google Calendar ID hasn't been explicitly defined
31 if (!googleCalendarId
&& url
) {
33 // detect if the ID was specified as a single string.
34 // will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars.
35 if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url
)) {
36 googleCalendarId
= url
;
38 // try to scrape it out of a V1 or V3 API feed URL
40 (match
= /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url
)) ||
41 (match
= /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url
))
43 googleCalendarId
= decodeURIComponent(match
[1]);
46 if (googleCalendarId
) {
47 sourceOptions
.googleCalendarId
= googleCalendarId
;
52 if (googleCalendarId
) { // is this a Google Calendar?
54 // make each Google Calendar source uneditable by default
55 if (sourceOptions
.editable
== null) {
56 sourceOptions
.editable
= false;
59 // We want removeEventSource to work, but it won't know about the googleCalendarId primitive.
60 // Shoehorn it into the url, which will function as the unique primitive. Won't cause side effects.
61 // This hack is obsolete since 2.2.3, but keep it so this plugin file is compatible with old versions.
62 sourceOptions
.url
= googleCalendarId
;
67 FC
.sourceFetchers
.push(function(sourceOptions
, start
, end
, timezone
) {
68 if (sourceOptions
.googleCalendarId
) {
69 return transformOptions(sourceOptions
, start
, end
, timezone
, this); // `this` is the calendar
74 function transformOptions(sourceOptions
, start
, end
, timezone
, calendar
) {
75 var url
= API_BASE
+ '/' + encodeURIComponent(sourceOptions
.googleCalendarId
) + '/events?callback=?'; // jsonp
76 var apiKey
= sourceOptions
.googleCalendarApiKey
|| calendar
.options
.googleCalendarApiKey
;
77 var success
= sourceOptions
.success
;
79 var timezoneArg
; // populated when a specific timezone. escaped to Google's liking
81 function reportError(message
, apiErrorObjs
) {
82 var errorObjs
= apiErrorObjs
|| [ { message
: message
} ]; // to be passed into error handlers
84 // call error handlers
85 (sourceOptions
.googleCalendarError
|| $.noop
).apply(calendar
, errorObjs
);
86 (calendar
.options
.googleCalendarError
|| $.noop
).apply(calendar
, errorObjs
);
88 // print error to debug console
89 FC
.warn
.apply(null, [ message
].concat(apiErrorObjs
|| []));
93 reportError("Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/");
94 return {}; // an empty source to use instead. won't fetch anything.
97 // The API expects an ISO8601 datetime with a time and timezone part.
98 // Since the calendar's timezone offset isn't always known, request the date in UTC and pad it by a day on each
99 // side, guaranteeing we will receive all events in the desired range, albeit a superset.
100 // .utc() will set a zone and give it a 00:00:00 time.
101 if (!start
.hasZone()) {
102 start
= start
.clone().utc().add(-1, 'day');
104 if (!end
.hasZone()) {
105 end
= end
.clone().utc().add(1, 'day');
108 // when sending timezone names to Google, only accepts underscores, not spaces
109 if (timezone
&& timezone
!= 'local') {
110 timezoneArg
= timezone
.replace(' ', '_');
113 data
= $.extend({}, sourceOptions
.data
|| {}, {
115 timeMin
: start
.format(),
116 timeMax
: end
.format(),
117 timeZone
: timezoneArg
,
122 return $.extend({}, sourceOptions
, {
123 googleCalendarId
: null, // prevents source-normalizing from happening again
126 startParam
: false, // `false` omits this parameter. we already included it above
127 endParam
: false, // same
128 timezoneParam
: false, // same
129 success: function(data
) {
135 reportError('Google Calendar API: ' + data
.error
.message
, data
.error
.errors
);
137 else if (data
.items
) {
138 $.each(data
.items
, function(i
, entry
) {
139 var url
= entry
.htmlLink
|| null;
141 // make the URLs for each event show times in the correct timezone
142 if (timezoneArg
&& url
!== null) {
143 url
= injectQsComponent(url
, 'ctz=' + timezoneArg
);
148 title
: entry
.summary
,
149 start
: entry
.start
.dateTime
|| entry
.start
.date
, // try timed. will fall back to all-day
150 end
: entry
.end
.dateTime
|| entry
.end
.date
, // same
152 location
: entry
.location
,
153 description
: entry
.description
157 // call the success handler(s) and allow it to return a new events array
158 successArgs
= [ events
].concat(Array
.prototype.slice
.call(arguments
, 1)); // forward other jq args
159 successRes
= applyAll(success
, this, successArgs
);
160 if ($.isArray(successRes
)) {
171 // Injects a string like "arg=value" into the querystring of a URL
172 function injectQsComponent(url
, component
) {
173 // inject it after the querystring but before the fragment
174 return url
.replace(/(\?.*?)?(#|$)/, function(whole
, qs
, hash
) {
175 return (qs
? qs
+ '&' : '?') + component
+ hash
;