import { createPlugin , EventSourceDef , addDays , DateEnv , requestJson , Dictionary } from '@fullcalendar/common' ; import { OPTION_REFINERS } from './options-refiners' ; import './options-declare' ; import { EVENT_SOURCE_REFINERS } from './event-source-refiners' ; import './event-source-declare' ; const API_BASE = 'https://www.googleapis.com/calendar/v3/calendars' ; interface GCalMeta { googleCalendarId : string ; googleCalendarApiKey ? : string ; googleCalendarApiBase ? : string ; extraParams ? : Dictionary | ( ( ) => Dictionary ) ; } let eventSourceDef = { parseMeta ( refined ) { let { googleCalendarId } = refined ; if ( ! googleCalendarId && refined . url ) { googleCalendarId = parseGoogleCalendarId ( refined . url ) ; } if ( googleCalendarId ) { return { googleCalendarId , googleCalendarApiKey : refined . googleCalendarApiKey , googleCalendarApiBase : refined . googleCalendarApiBase , extraParams : refined . extraParams , } ; } return null ; } , fetch ( arg , onSuccess , onFailure ) { let { dateEnv , options } = arg . context ; let meta = arg . eventSource . meta ; let apiKey = meta . googleCalendarApiKey || options . googleCalendarApiKey ; if ( ! apiKey ) { onFailure ( { message : 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/' , } ) ; } else { let url = buildUrl ( meta ) ; let { extraParams } = meta ; let extraParamsObj = typeof extraParams === 'function' ? extraParams ( ) : extraParams ; let requestParams = buildRequestParams ( arg . range , apiKey , extraParamsObj , dateEnv ) ; requestJson ( 'GET' , url , requestParams , ( body , xhr ) => { if ( body . error ) { onFailure ( { message : 'Google Calendar API: ' + body . error . message , errors : body . error . errors , xhr , } ) ; } else { onSuccess ( { rawEvents : gcalItemsToRawEventDefs ( body . items , requestParams . timeZone ) , xhr , } ) ; } } , ( message , xhr ) => { onFailure ( { message , xhr } ) ; } ) ; } } , } ; function parseGoogleCalendarId ( url ) { let match ; if ( /^[^/]+@([^/.]+\.)*(google|googlemail|gmail)\.com$/ . test ( url ) ) { return url ; } if ( ( match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^/]*)/ . exec ( url ) ) || ( match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^/]*)/ . exec ( url ) ) ) { return decodeURIComponent ( match [ 1 ] ) ; } return null ; } function buildUrl ( meta ) { let apiBase = meta . googleCalendarApiBase ; if ( ! apiBase ) { apiBase = API_BASE ; } return apiBase + '/' + encodeURIComponent ( meta . googleCalendarId ) + '/events' ; } function buildRequestParams ( range , apiKey , extraParams , dateEnv ) { let params ; let startStr ; let endStr ; if ( dateEnv . canComputeOffset ) { startStr = dateEnv . formatIso ( range . start ) ; endStr = dateEnv . formatIso ( range . end ) ; } else { startStr = addDays ( range . start , - 1 ) . toISOString ( ) ; endStr = addDays ( range . end , 1 ) . toISOString ( ) ; } params = { ... ( extraParams || { } ) , key : apiKey , timeMin : startStr , timeMax : endStr , singleEvents : true , maxResults : 9999 , } ; if ( dateEnv . timeZone !== 'local' ) { params . timeZone = dateEnv . timeZone ; } return params ; } function gcalItemsToRawEventDefs ( items , gcalTimezone ) { return items . map ( ( item ) => gcalItemToRawEventDef ( item , gcalTimezone ) ) ; } function gcalItemToRawEventDef ( item , gcalTimezone ) { let url = item . htmlLink || null ; if ( url && gcalTimezone ) { url = injectQsComponent ( url , 'ctz=' + gcalTimezone ) ; } return { id : item . id , title : item . summary , start : item . start . dateTime || item . start . date , end : item . end . dateTime || item . end . date , url , location : item . location , description : item . description , attachments : item . attachments || [ ] , extendedProps : ( item . extendedProperties || { } ) . shared || { } , } ; } function injectQsComponent ( url , component ) { return url . replace ( /(\?.*?)?(#|$)/ , ( whole , qs , hash ) => ( qs ? qs + '&' : '?' ) + component + hash ) ; } export default createPlugin ( { eventSourceDefs : [ eventSourceDef ] , optionRefiners : OPTION_REFINERS , eventSourceRefiners : EVENT_SOURCE_REFINERS , } ) ;