2016-04-15 12:18:59 +02:00
/ * M a g i c M i r r o r
* Node Helper : Calendar - CalendarFetcher
*
* By Michael Teeuw http : //michaelteeuw.nl
* MIT Licensed .
* /
2016-04-20 11:32:48 +02:00
var ical = require ( "./vendor/ical.js" ) ;
2016-04-15 12:18:59 +02:00
var moment = require ( "moment" ) ;
var CalendarFetcher = function ( url , reloadInterval , maximumEntries , maximumNumberOfDays ) {
var self = this ;
var reloadTimer = null ;
var events = [ ] ;
var fetchFailedCallback = function ( ) { } ;
var eventsReceivedCallback = function ( ) { } ;
/ * f e t c h C a l e n d a r ( )
* Initiates calendar fetch .
* /
var fetchCalendar = function ( ) {
clearTimeout ( reloadTimer ) ;
reloadTimer = null ;
2016-06-04 20:32:55 -06:00
var opts = {
headers : {
'User-Agent' : 'Mozilla/5.0 (Node.js 6.0.0) MagicMirror/v2 (https://github.com/MichMich/MagicMirror/)'
}
}
ical . fromURL ( url , opts , function ( err , data ) {
2016-04-15 12:18:59 +02:00
if ( err ) {
fetchFailedCallback ( self , err ) ;
scheduleTimer ( ) ;
return ;
}
//console.log(data);
newEvents = [ ] ;
var limitFunction = function ( date , i ) { return i < maximumEntries ; } ;
for ( var e in data ) {
var event = data [ e ] ;
var now = new Date ( ) ;
var today = moment ( ) . startOf ( "day" ) . toDate ( ) ;
2016-04-19 16:45:57 +02:00
var future = moment ( ) . startOf ( "day" ) . add ( maximumNumberOfDays , "days" ) . subtract ( 1 , "seconds" ) . toDate ( ) ; // Subtract 1 second so that events that start on the middle of the night will not repeat.
2016-04-15 12:18:59 +02:00
// FIXME:
// Ugly fix to solve the facebook birthday issue.
// Otherwise, the recurring events only show the birthday for next year.
var isFacebookBirthday = false ;
if ( typeof event . uid !== "undefined" ) {
if ( event . uid . indexOf ( "@facebook.com" ) !== - 1 ) {
isFacebookBirthday = true ;
}
}
if ( event . type === "VEVENT" ) {
var startDate = ( event . start . length === 8 ) ? moment ( event . start , "YYYYMMDD" ) : moment ( new Date ( event . start ) ) ;
2016-04-15 22:09:59 +02:00
var endDate ;
if ( typeof event . end !== "undefined" ) {
endDate = ( event . end . length === 8 ) ? moment ( event . end , "YYYYMMDD" ) : moment ( new Date ( event . end ) ) ;
} else {
2016-05-03 11:56:24 +02:00
if ( ! isFacebookBirthday ) {
endDate = startDate ;
} else {
endDate = moment ( startDate ) . add ( 1 , 'days' ) ;
}
2016-04-15 22:09:59 +02:00
}
2016-04-20 15:19:36 +02:00
// calculate the duration f the event for use with recurring events.
var duration = parseInt ( endDate . format ( "x" ) ) - parseInt ( startDate . format ( "x" ) ) ;
2016-04-15 12:18:59 +02:00
if ( event . start . length === 8 ) {
startDate = startDate . startOf ( "day" ) ;
}
2016-05-10 12:24:49 +02:00
var title = "Event" ;
if ( event . summary ) {
title = ( typeof event . summary . val !== "undefined" ) ? event . summary . val : event . summary ;
} else if ( event . description ) {
title = event . description ;
}
2016-04-15 12:18:59 +02:00
if ( typeof event . rrule != "undefined" && ! isFacebookBirthday ) {
var rule = event . rrule ;
var dates = rule . between ( today , future , true , limitFunction ) ;
2016-04-20 11:32:48 +02:00
2016-04-15 12:18:59 +02:00
for ( var d in dates ) {
startDate = moment ( new Date ( dates [ d ] ) ) ;
2016-04-20 15:19:36 +02:00
endDate = moment ( parseInt ( startDate . format ( "x" ) ) + duration , 'x' ) ;
if ( endDate . format ( "x" ) > now ) {
newEvents . push ( {
2016-05-10 12:24:49 +02:00
title : title ,
2016-04-20 15:19:36 +02:00
startDate : startDate . format ( "x" ) ,
endDate : endDate . format ( "x" ) ,
2016-04-26 22:34:12 +02:00
fullDayEvent : isFullDayEvent ( event ) ,
firstYear : event . start . getFullYear ( )
2016-04-20 15:19:36 +02:00
} ) ;
}
2016-04-15 12:18:59 +02:00
}
} else {
// console.log("Single event ...");
// Single event.
2016-05-03 11:56:24 +02:00
var fullDayEvent = ( isFacebookBirthday ) ? true : isFullDayEvent ( event ) ;
2016-04-15 12:50:34 +02:00
2016-04-15 13:13:06 +02:00
if ( ! fullDayEvent && endDate < new Date ( ) ) {
2016-04-15 13:16:02 +02:00
//console.log("It's not a fullday event, and it is in the past. So skip: " + title);
2016-04-15 12:50:34 +02:00
continue ;
2016-04-15 12:18:59 +02:00
}
2016-04-15 12:50:34 +02:00
2016-04-23 17:40:44 +02:00
if ( fullDayEvent && endDate <= today ) {
2016-04-15 13:16:02 +02:00
//console.log("It's a fullday event, and it is before today. So skip: " + title);
2016-04-15 12:50:34 +02:00
continue ;
}
if ( startDate > future ) {
2016-04-15 13:16:02 +02:00
//console.log("It exceeds the maximumNumberOfDays limit. So skip: " + title);
2016-04-15 12:50:34 +02:00
continue ;
}
// Every thing is good. Add it to the list.
newEvents . push ( {
title : title ,
startDate : startDate . format ( "x" ) ,
2016-04-15 13:13:06 +02:00
endDate : endDate . format ( "x" ) ,
2016-04-15 12:50:34 +02:00
fullDayEvent : fullDayEvent
} ) ;
2016-04-15 12:18:59 +02:00
}
}
}
newEvents . sort ( function ( a , b ) {
return a . startDate - b . startDate ;
} ) ;
2016-04-20 15:19:36 +02:00
//console.log(newEvents);
2016-04-15 12:18:59 +02:00
events = newEvents . slice ( 0 , maximumEntries ) ;
self . broadcastEvents ( ) ;
scheduleTimer ( ) ;
} ) ;
} ;
/ * s c h e d u l e T i m e r ( )
* Schedule the timer for the next update .
* /
var scheduleTimer = function ( ) {
//console.log('Schedule update timer.');
clearTimeout ( reloadTimer ) ;
reloadTimer = setTimeout ( function ( ) {
fetchCalendar ( ) ;
} , reloadInterval ) ;
} ;
2016-04-19 10:34:14 +02:00
/ * i s F u l l D a y E v e n t ( e v e n t )
* Checks if an event is a fullday event .
*
* argument event obejct - The event object to check .
*
* return bool - The event is a fullday event .
* /
var isFullDayEvent = function ( event ) {
if ( event . start . length === 8 ) {
return true ;
}
var start = event . start || 0 ;
var startDate = new Date ( start ) ;
var end = event . end || 0 ;
if ( end - start === 24 * 60 * 60 * 1000 && startDate . getHours ( ) === 0 && startDate . getMinutes ( ) === 0 ) {
// Is 24 hours, and starts on the middle of the night.
return true ;
}
return false ;
} ;
2016-04-15 12:18:59 +02:00
/* public methods */
/ * s t a r t F e t c h ( )
* Initiate fetchCalendar ( ) ;
* /
this . startFetch = function ( ) {
fetchCalendar ( ) ;
} ;
/ * b r o a d c a s t I t e m s ( )
2016-06-04 20:32:55 -06:00
* Broadcast the existing events .
2016-04-15 12:18:59 +02:00
* /
this . broadcastEvents = function ( ) {
//console.log('Broadcasting ' + events.length + ' events.');
eventsReceivedCallback ( self ) ;
} ;
/ * o n R e c e i v e ( c a l l b a c k )
* Sets the on success callback
*
* argument callback function - The on success callback .
* /
this . onReceive = function ( callback ) {
eventsReceivedCallback = callback ;
} ;
/ * o n E r r o r ( c a l l b a c k )
* Sets the on error callback
*
* argument callback function - The on error callback .
* /
this . onError = function ( callback ) {
fetchFailedCallback = callback ;
} ;
/ * u r l ( )
* Returns the url of this fetcher .
*
* return string - The url of this fetcher .
* /
this . url = function ( ) {
return url ;
} ;
/ * e v e n t s ( )
* Returns current available events for this fetcher .
*
* return array - The current available events for this fetcher .
* /
this . events = function ( ) {
return events ;
} ;
} ;
module . exports = CalendarFetcher ;