Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | espaco | 1 | /*! |
| 2 | * FullCalendar v2.4.0 Google Calendar Plugin |
||
| 3 | * Docs & License: http://fullcalendar.io/ |
||
| 4 | * (c) 2015 Adam Shaw |
||
| 5 | */ |
||
| 6 | |||
| 7 | (function(factory) { |
||
| 8 | if (typeof define === 'function' && define.amd) { |
||
| 9 | define([ 'jquery' ], factory); |
||
| 10 | } |
||
| 11 | else if (typeof exports === 'object') { // Node/CommonJS |
||
| 12 | module.exports = factory(require('jquery')); |
||
| 13 | } |
||
| 14 | else { |
||
| 15 | factory(jQuery); |
||
| 16 | } |
||
| 17 | })(function($) { |
||
| 18 | |||
| 19 | |||
| 20 | var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars'; |
||
| 21 | var fc = $.fullCalendar; |
||
| 22 | var applyAll = fc.applyAll; |
||
| 23 | |||
| 24 | |||
| 25 | fc.sourceNormalizers.push(function(sourceOptions) { |
||
| 26 | var googleCalendarId = sourceOptions.googleCalendarId; |
||
| 27 | var url = sourceOptions.url; |
||
| 28 | var match; |
||
| 29 | |||
| 30 | // if the Google Calendar ID hasn't been explicitly defined |
||
| 31 | if (!googleCalendarId && url) { |
||
| 32 | |||
| 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; |
||
| 37 | } |
||
| 38 | // try to scrape it out of a V1 or V3 API feed URL |
||
| 39 | else if ( |
||
| 40 | (match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) || |
||
| 41 | (match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url)) |
||
| 42 | ) { |
||
| 43 | googleCalendarId = decodeURIComponent(match[1]); |
||
| 44 | } |
||
| 45 | |||
| 46 | if (googleCalendarId) { |
||
| 47 | sourceOptions.googleCalendarId = googleCalendarId; |
||
| 48 | } |
||
| 49 | } |
||
| 50 | |||
| 51 | |||
| 52 | if (googleCalendarId) { // is this a Google Calendar? |
||
| 53 | |||
| 54 | // make each Google Calendar source uneditable by default |
||
| 55 | if (sourceOptions.editable == null) { |
||
| 56 | sourceOptions.editable = false; |
||
| 57 | } |
||
| 58 | |||
| 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; |
||
| 63 | } |
||
| 64 | }); |
||
| 65 | |||
| 66 | |||
| 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 |
||
| 70 | } |
||
| 71 | }); |
||
| 72 | |||
| 73 | |||
| 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; |
||
| 78 | var data; |
||
| 79 | var timezoneArg; // populated when a specific timezone. escaped to Google's liking |
||
| 80 | |||
| 81 | function reportError(message, apiErrorObjs) { |
||
| 82 | var errorObjs = apiErrorObjs || [ { message: message } ]; // to be passed into error handlers |
||
| 83 | |||
| 84 | // call error handlers |
||
| 85 | (sourceOptions.googleCalendarError || $.noop).apply(calendar, errorObjs); |
||
| 86 | (calendar.options.googleCalendarError || $.noop).apply(calendar, errorObjs); |
||
| 87 | |||
| 88 | // print error to debug console |
||
| 89 | fc.warn.apply(null, [ message ].concat(apiErrorObjs || [])); |
||
| 90 | } |
||
| 91 | |||
| 92 | if (!apiKey) { |
||
| 93 | reportError("Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/"); |
||
| 94 | return {}; // an empty source to use instead. won't fetch anything. |
||
| 95 | } |
||
| 96 | |||
| 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'); |
||
| 103 | } |
||
| 104 | if (!end.hasZone()) { |
||
| 105 | end = end.clone().utc().add(1, 'day'); |
||
| 106 | } |
||
| 107 | |||
| 108 | // when sending timezone names to Google, only accepts underscores, not spaces |
||
| 109 | if (timezone && timezone != 'local') { |
||
| 110 | timezoneArg = timezone.replace(' ', '_'); |
||
| 111 | } |
||
| 112 | |||
| 113 | data = $.extend({}, sourceOptions.data || {}, { |
||
| 114 | key: apiKey, |
||
| 115 | timeMin: start.format(), |
||
| 116 | timeMax: end.format(), |
||
| 117 | timeZone: timezoneArg, |
||
| 118 | singleEvents: true, |
||
| 119 | maxResults: 9999 |
||
| 120 | }); |
||
| 121 | |||
| 122 | return $.extend({}, sourceOptions, { |
||
| 123 | googleCalendarId: null, // prevents source-normalizing from happening again |
||
| 124 | url: url, |
||
| 125 | data: data, |
||
| 126 | startParam: false, // `false` omits this parameter. we already included it above |
||
| 127 | endParam: false, // same |
||
| 128 | timezoneParam: false, // same |
||
| 129 | success: function(data) { |
||
| 130 | var events = []; |
||
| 131 | var successArgs; |
||
| 132 | var successRes; |
||
| 133 | |||
| 134 | if (data.error) { |
||
| 135 | reportError('Google Calendar API: ' + data.error.message, data.error.errors); |
||
| 136 | } |
||
| 137 | else if (data.items) { |
||
| 138 | $.each(data.items, function(i, entry) { |
||
| 139 | var url = entry.htmlLink; |
||
| 140 | |||
| 141 | // make the URLs for each event show times in the correct timezone |
||
| 142 | if (timezoneArg) { |
||
| 143 | url = injectQsComponent(url, 'ctz=' + timezoneArg); |
||
| 144 | } |
||
| 145 | |||
| 146 | events.push({ |
||
| 147 | id: entry.id, |
||
| 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 |
||
| 151 | url: url, |
||
| 152 | location: entry.location, |
||
| 153 | description: entry.description |
||
| 154 | }); |
||
| 155 | }); |
||
| 156 | |||
| 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)) { |
||
| 161 | return successRes; |
||
| 162 | } |
||
| 163 | } |
||
| 164 | |||
| 165 | return events; |
||
| 166 | } |
||
| 167 | }); |
||
| 168 | } |
||
| 169 | |||
| 170 | |||
| 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; |
||
| 176 | }); |
||
| 177 | } |
||
| 178 | |||
| 179 | |||
| 180 | }); |