2 * FullCalendar v3.5.1 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 FC
= $.fullCalendar
;
21 var Promise
= FC
.Promise
;
22 var EventSource
= FC
.EventSource
;
23 var JsonFeedEventSource
= FC
.JsonFeedEventSource
;
24 var EventSourceParser
= FC
.EventSourceParser
;
25 var applyAll
= FC
.applyAll
;
29 var GcalEventSource
= EventSource
.extend({
31 // TODO: eventually remove "googleCalendar" prefix (API-breaking)
32 googleCalendarApiKey
: null,
33 googleCalendarId
: null,
34 googleCalendarError
: null, // optional function
38 fetch: function(start
, end
, timezone
) {
40 var url
= this.buildUrl();
41 var requestParams
= this.buildRequestParams(start
, end
, timezone
);
42 var ajaxSettings
= this.ajaxSettings
;
43 var onSuccess
= ajaxSettings
.success
;
45 if (!requestParams
) { // could have failed
46 return Promise
.reject();
49 return Promise
.construct(function(onResolve
, onReject
) {
52 JsonFeedEventSource
.AJAX_DEFAULTS
,
57 success: function(responseData
) {
61 if (responseData
.error
) {
62 _this
.reportError('Google Calendar API: ' + responseData
.error
.message
, responseData
.error
.errors
);
65 else if (responseData
.items
) {
66 rawEventDefs
= _this
.gcalItemsToRawEventDefs(
68 requestParams
.timeZone
71 successRes
= applyAll(
73 this, // forward `this`
74 // call the success handler(s) and allow it to return a new events array
75 [ rawEventDefs
].concat(Array
.prototype.slice
.call(arguments
, 1))
78 if ($.isArray(successRes
)) {
79 rawEventDefs
= successRes
;
82 onResolve(_this
.parseEventDefs(rawEventDefs
));
91 gcalItemsToRawEventDefs: function(items
, gcalTimezone
) {
94 return items
.map(function(item
) {
95 return _this
.gcalItemToRawEventDef(item
, gcalTimezone
);
100 gcalItemToRawEventDef: function(item
, gcalTimezone
) {
101 var url
= item
.htmlLink
|| null;
103 // make the URLs for each event show times in the correct timezone
104 if (url
&& gcalTimezone
) {
105 url
= injectQsComponent(url
, 'ctz=' + gcalTimezone
);
111 start
: item
.start
.dateTime
|| item
.start
.date
, // try timed. will fall back to all-day
112 end
: item
.end
.dateTime
|| item
.end
.date
, // same
114 location
: item
.location
,
115 description
: item
.description
120 buildUrl: function() {
121 return GcalEventSource
.API_BASE
+ '/' +
122 encodeURIComponent(this.googleCalendarId
) +
123 '/events?callback=?'; // jsonp
127 buildRequestParams: function(start
, end
, timezone
) {
128 var apiKey
= this.googleCalendarApiKey
|| this.calendar
.opt('googleCalendarApiKey');
132 this.reportError("Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/");
136 // The API expects an ISO8601 datetime with a time and timezone part.
137 // Since the calendar's timezone offset isn't always known, request the date in UTC and pad it by a day on each
138 // side, guaranteeing we will receive all events in the desired range, albeit a superset.
139 // .utc() will set a zone and give it a 00:00:00 time.
140 if (!start
.hasZone()) {
141 start
= start
.clone().utc().add(-1, 'day');
143 if (!end
.hasZone()) {
144 end
= end
.clone().utc().add(1, 'day');
148 this.ajaxSettings
.data
|| {},
151 timeMin
: start
.format(),
152 timeMax
: end
.format(),
158 if (timezone
&& timezone
!== 'local') {
159 // when sending timezone names to Google, only accepts underscores, not spaces
160 params
.timeZone
= timezone
.replace(' ', '_');
167 reportError: function(message
, apiErrorObjs
) {
168 var calendar
= this.calendar
;
169 var calendarOnError
= calendar
.opt('googleCalendarError');
170 var errorObjs
= apiErrorObjs
|| [ { message
: message
} ]; // to be passed into error handlers
172 if (this.googleCalendarError
) {
173 this.googleCalendarError
.apply(calendar
, errorObjs
);
176 if (calendarOnError
) {
177 calendarOnError
.apply(calendar
, errorObjs
);
180 // print error to debug console
181 FC
.warn
.apply(null, [ message
].concat(apiErrorObjs
|| []));
185 getPrimitive: function() {
186 return this.googleCalendarId
;
190 applyManualRawProps: function(rawProps
) {
191 var superSuccess
= EventSource
.prototype.applyManualRawProps
.apply(this, arguments
);
192 var googleCalendarId
= rawProps
.googleCalendarId
;
194 if (googleCalendarId
== null && rawProps
.url
) {
195 googleCalendarId
= parseGoogleCalendarId(rawProps
.url
);
198 if (googleCalendarId
!= null) {
199 this.googleCalendarId
= googleCalendarId
;
208 applyOtherRawProps: function(rawProps
) {
209 this.ajaxSettings
= rawProps
;
215 GcalEventSource
.API_BASE
= 'https://www.googleapis.com/calendar/v3/calendars';
218 GcalEventSource
.allowRawProps({
219 // manually process...
221 googleCalendarId
: false,
223 // automatically transfer...
224 googleCalendarApiKey
: true,
225 googleCalendarError
: true
229 GcalEventSource
.parse = function(rawInput
, calendar
) {
232 if (typeof rawInput
=== 'object') { // long form. might fail in applyManualRawProps
235 else if (typeof rawInput
=== 'string') { // short form
236 rawProps
= { url
: rawInput
}; // url will be parsed with parseGoogleCalendarId
240 return EventSource
.parse
.call(this, rawProps
, calendar
);
247 function parseGoogleCalendarId(url
) {
250 // detect if the ID was specified as a single string.
251 // will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars.
252 if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url
)) {
255 // try to scrape it out of a V1 or V3 API feed URL
257 (match
= /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url
)) ||
258 (match
= /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url
))
260 return decodeURIComponent(match
[1]);
265 // Injects a string like "arg=value" into the querystring of a URL
266 function injectQsComponent(url
, component
) {
267 // inject it after the querystring but before the fragment
268 return url
.replace(/(\?.*?)?(#|$)/, function(whole
, qs
, hash
) {
269 return (qs
? qs
+ '&' : '?') + component
+ hash
;
276 EventSourceParser
.registerClass(GcalEventSource
);
278 FC
.GcalEventSource
= GcalEventSource
;