Merging multiple Google calendar feeds into one JSON object in javascript

Posted by Jeramy on Stack Overflow See other posts from Stack Overflow or by Jeramy
Published on 2012-11-28T19:58:16Z Indexed on 2012/12/03 5:04 UTC
Read the original article Hit count: 283

Filed under:
|
|

I am trying to bring in the JSON feeds from multiple Google calendars so that I can sort the upcoming events and display the next X number in an "Upcoming Events" list.

I have this working with Yahoo! pipes but I want to get away from using a 3rd party to aggregate. I think I am close, but I cannot get the JSON objects created correctly. I am getting the data into the array but not in JSON format, so I can't manipulate it.

I have tried var myJsonString = JSON.stringify(JSONData); using https://github.com/douglascrockford/JSON-js but that just threw errors. I suspect because my variable is in the wrong starting format. I have tried just calling the feed like: $.getJSON(url); and creating a function concant1() to do the JSONData=JSONData.concat(data);, but it doesn't fire and I think it would produce the same end result anyway. I have also tried several other methods of getting the end result I want with varying degrees of doom. Here is the closest I have come so far:

var JSONData = new Array();
var urllist = ["https://www.google.com/calendar/feeds/dg61asqgqg4pust2l20obgdl64%40group.calendar.google.com/public/full?orderby=starttime&max-results=3&sortorder=ascending&futureevents=true&ctz=America/New_York&singleevents=true&alt=json&callback=concant1","https://www.google.com/calendar/feeds/5oc3kvp7lnu5rd4krg2skcu2ng%40group.calendar.google.com/public/full?orderby=starttime&max-results=3&sortorder=ascending&futureevents=true&ctz=America/New_York&singleevents=true&alt=json&callback=concant1","http://www.google.com/calendar/feeds/rine4umu96kl6t46v4fartnho8%40group.calendar.google.com/public/full?orderby=starttime&max-results=3&sortorder=ascending&futureevents=true&ctz=America/New_York&singleevents=true&alt=json&callback=concant1"];

urllist.forEach(function addFeed(url){
    alert("The URL being used: "+ url);
    if (void 0 != JSONData){JSONData=JSONData.concat($.getJSON(url));}
    else{JSONData = $.getJSON(url);}
    alert("The count from concantonated JSONData: "+JSONData.length); 
});

document.write("The final count from JSONData: "+JSONData.length+"<p>");
console.log(JSONData)

UPDATE: Now with full working source!! :) If anyone would like to make suggestions on how to improve the code's efficiency it would be gratefully accepted. I hope others find this useful.:

// GCal MFA - Google Calendar Multiple Feed Aggregator
// Useage: GCalMFA(CIDs,n); 
// Where 'CIDs' is a list of comma seperated Google calendar IDs in the format: [email protected], and 'n' is the number of results to display.
// While the contained console.log(); outputs are really handy for testing, you will probably waant to remove them for regular usage
// Author: Jeramy Kruser - http://jeramy.kruser.me

//onerror=function (d, f, g){alert (d+ "\n"+ f+ "\n");}
if (!window.console) {console = {log: function() {}};}
document.body.className += ' js-enabled';

// Global variables
var urllist = [];
var maxResults = 3; // The default is 3 results unless a value is sent
var JSONData = {};
var eventCount = 0;
var errorLog = "";

JSONData = { count: 0,
    value : {
    description: "Aggregates multiple Google calendar feeds into a single sorted list",
    generator: "StackOverflow communal coding - Thanks for the assist Patrick M",
    website: "http://jeramy.kruser.me",
    author: "Jeramy & Kasey Kruser",
    items: []
}};

// For putting dates from feed into a format that can be read by the Date function for calculating event length.
function parse (str) {
        // validate year as 4 digits, month as 01-12, and day as 01-31 
        str = str.match (/^(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])$/);
        if (str) {
           // make a date
           str[0] = new Date ( + str[1], + str[2] - 1, + str[3]);
           // check if month stayed the same (ie that day number is valid)
           if (str[0].getMonth () === + str[2] - 1) {
              return str[0];
              }
        }
        return undefined;
 }

//For outputting to HTML
function output() {
    var months, day_in_ms, summary, i, item, eventlink, title, calendar, where,dtstart, dtend, endyear, endmonth, endday, startyear, startmonth, startday, endmonthdayyear, eventlinktitle, startmonthday, length, curtextval, k;
    // Array of month names from numbers for page display.
    months = {'0':'January', '1':'February', '2':'March', '3':'April', '4':'May', '5':'June', '6':'July', '7':'August', '8':'September', '9':'October', '10':'November', '11':'December'};
    // For use in calculating event length.
    day_in_ms = 24 * 60 * 60 * 1000;
    // Instantiate HTML Arrays.
    summary = [];
    for (i = 0; i < maxResults; i+=1 ) {
        //console.log("i: "+i+" < "+"maxResults: "+ maxResults);
        if (!(JSONData.value.items[i] === undefined)) {     
            item = JSONData.value.items[i];
            // Grabbing data for each event in the feed.
            eventlink = item.link[0]; 
            title = item.title.$t; 
            // Only display the calendar title if there is more than one 
            calendar = "";
            if (urllist.length > 1) {
                calendar = '<br />Calendar: <a href="https://www.google.com/calendar/embed?src=' + item.gd$who[0].email + '&ctz=America/New_York">' + item.author[0].name.$t + '<\/a> (<a href="https://www.google.com/calendar/ical/' + item.gd$who[0].email + '/public/basic.ics">iCal<\/a>)'; 
            }
            // Grabbing event location, if entered.
            if ( item.gd$where[0].valueString !== "" ) {
                where = '<br />' + (item.gd$where[0].valueString); 
                }
            else {
                where = (""); 
                }
            // Grabbing start date and putting in form YYYYmmdd. Subtracting one day from dtend to fix Google's habit of ending an all-day event at midnight on the following day.
            dtstart = new Date(parse(((item.gd$when[0].startTime).substring(0,10)).replace(/-/g,"")));
            dtend = new Date(parse(((item.gd$when[0].endTime).substring(0,10)).replace(/-/g,"")) - day_in_ms);
            // Put dates in pretty form for display.
            endyear = dtend.getFullYear();  
            endmonth = months[dtend.getMonth()];
            endday = dtend.getDate();
            startyear = dtstart.getFullYear();  
            startmonth = months[dtstart.getMonth()];
            startday = dtstart.getDate();
            //consolidate some much-used variables for HTML output.
            endmonthdayyear = endmonth + ' ' + endday + ', ' + endyear;
            eventlinktitle = '<a href="' + eventlink + '">' + title + '<\/a>';
            startmonthday = startmonth + ' ' + startday;
            // Calculates the number of days between each event's start and end dates. 
            length = ((dtend - dtstart) / day_in_ms);
            // HTML for each event, depending on which div is available on the page (different HTML applies). Only one div can exist on any one page. 
            if (document.getElementById("homeCalendar") !== null ) {
                // If the length of the event is greater than 0 days, show start and end dates. 
                if ( length > 0 && startmonth !== endmonth && startday === endday ) {       
                        summary[i] = ('<h3>' + eventlink + '">' + startmonthday + ', ' + startyear + ' - ' + endmonthdayyear + '<\/a><\/h3><p>' + title + '<\/p>'); }
                    // If the length of the event is greater than 0 and begins and ends within the same month, shorten the date display.
                    else if ( length > 0 && startmonth === endmonth && startyear === endyear ) {        
                        summary[i] = ('<h3><a href="' + eventlink + '">' + startmonthday + '-' + endday + ', ' + endyear + '<\/a><\/h3><p>' + title + '<\/p>'); }
                    // If the length of the event is greater than 0 and begins and ends within different months of the same year, shorten the date display.
                    else if ( length > 0 && startmonth !== endmonth && startyear === endyear ) {        
                        summary[i] = ('<h3><a href="' + eventlink + '">' + startmonthday + ' - ' + endmonthdayyear + '<\/a><\/h3><p>' + title + '<\/p>'); }
                    // If the length of the event is less than one day (length < = 0), show only the start date.
                    else { 
                        summary[i] = ('<h3><a href="' + eventlink + '">' + startmonthday + ', ' + startyear + '<\/a><\/h3><p>' + title + '<\/p>'); }
            }        
            else if (document.getElementById("allCalendar") !== null ) {
                // If the length of the event is greater than 0 days, show start and end dates. 
                 if ( length > 0 && startmonth !== endmonth && startday === endday ) {           
                           summary[i] = ('<li>' + eventlinktitle + '<br />' + startmonthday + ', ' + startyear + ' - ' + endmonthdayyear + where + calendar + '<br />&#160;<\/li>'); } 
                      // If the length of the event is greater than 0 and begins and ends within the same month, shorten the date display. 
                      else if ( length > 0 && startmonth === endmonth && startyear === endyear ) {           
                           summary[i] = ('<li>' + eventlinktitle + '<br />' + startmonthday + '-' + endday + ', ' + endyear + where + calendar + '<br />&#160;<\/li>'); } 
                      // If the length of the event is greater than 0 and begins and ends within different months of the same year, shorten the date display. 
                      else if ( length > 0 && startmonth !== endmonth && startyear === endyear ) {           
                           summary[i] = ('<li>' + eventlinktitle + '<br />' + startmonthday + ' - ' + endmonthdayyear + where + calendar + '<br />&#160;<\/li>'); } 
                      // If the length of the event is less than one day (length < = 0), show only the start date. 
                      else {  
                           summary[i] = ('<li>' + eventlinktitle + '<br />' + startmonthday + ', ' + startyear + where + calendar + '<br />&#160;<\/li>'); }
            } 
        }
        if (summary[i] === undefined) { summary[i] = "";}
        //console.log(summary[i]);
    }
    console.log(JSONData);
    // Puts the HTML into the div with the appropriate id. Each page can have only one.
    if (document.getElementById("homeCalendar") !== null ) {
        curtextval = document.getElementById("homeCalendar");
        console.log("homeCalendar: "+curtextval);
        }
    else if (document.getElementById("oneCalendar") !== null ) {
        curtextval = document.getElementById("oneCalendar");
        console.log("oneCalendar: "+curtextval);
         }
    else if (document.getElementById("allCalendar") !== null ) {
        curtextval = document.getElementById("allCalendar");
        console.log("allCalendar: "+curtextval);
         }
    if (curtextval.innerHTML.length < 100) {
        errorLog += '<div id="noEvents">No events found.</div>';
        }
    for (k = 0; k<maxResults; k+=1 ) { curtextval.innerHTML = curtextval.innerHTML + summary[k]; }
    if (eventCount === 0) {
        errorLog += '<div id="noEvents">No events found.</div>';
    }
    if (document.getElementById("homeCalendar") === null ) {
        curtextval.innerHTML = '<ul>' + curtextval.innerHTML + '<\/ul>'; 
        }
    if (errorLog !== "") {
        curtextval.innerHTML += errorLog;
        }
}

// For taking in each feed, breaking out the events and sorting them into the object by date
function sortFeed(event) {  
    var tempEntry, i;
    tempEntry = event;
    i = 0;
    console.log("*** New incoming event object #"+eventCount+" ***");
    console.log(event.title.$t);
    console.log(event);
    //console.log("i = " + i + " and maxResults " + maxResults);
    while(i<maxResults) {
        console.log("i = " + i + " < maxResults " + maxResults);
        console.log("Sorting event = " + event.title.$t + " by date of " + event.gd$when[0].startTime.substring(0,10).replace(/-/g,""));
        if (JSONData.value.items[i]) {
            console.log("JSONData.value.items[" + i + "] exists and has a startTime of " + JSONData.value.items[i].gd$when[0].startTime.substring(0,10).replace(/-/g,""));
            if (event.gd$when[0].startTime.substring(0,10).replace(/-/g,"")<JSONData.value.items[i].gd$when[0].startTime.substring(0,10).replace(/-/g,"")) {
                console.log("The incoming event value of " + event.gd$when[0].startTime.substring(0,10).replace(/-/g,"") + " is < " + JSONData.value.items[i].gd$when[0].startTime.substring(0,10).replace(/-/g,""));
                tempEntry = JSONData.value.items[i];
                console.log("Existing JSONData.value.items[" + i + "] value " + JSONData.value.items[i].gd$when[0].startTime.substring(0,10).replace(/-/g,"") + " stored in tempEntry");
                JSONData.value.items[i] = event;
                console.log("Position JSONData.value.items[" + i + "] set to new value: " + event.gd$when[0].startTime.substring(0,10).replace(/-/g,""));
                event = tempEntry;
                console.log("Now sorting event = " + event.title.$t + " by date of " + event.gd$when[0].startTime.substring(0,10).replace(/-/g,""));
            }
            else {
                console.log("The incoming event value of " + event.gd$when[0].startTime.substring(0,10).replace(/-/g,"") + " is > " + JSONData.value.items[i].gd$when[0].startTime.substring(0,10).replace(/-/g,"") + " moving on...");
            }
        }
        else {
            JSONData.value.items[i] = event;
            console.log("JSONData.value.items[" + i + "] does not exist so it was set to the Incoming value of " + event.gd$when[0].startTime.substring(0,10).replace(/-/g,""));
            i = maxResults;
        }
        i += 1;
    }
}

// For completing the aggregation
function complete(result) {
    var str, j, item;
    // Track the number of calls completed back, we're not done until all URLs have processed
    if( complete.count === undefined ){
        complete.count = urllist.length;        
    }
    console.log("complete.count = "+complete.count);
    console.log(result.feed);
    if(result.feed.entry){
        JSONData.count = maxResults;
        // Check each incoming item against JSONData.value.items
        console.log("*** Begin Sorting " + result.feed.entry.length + " Events ***");
        //console.log(result.feed.entry);
        result.feed.entry.forEach(
            function(event){
                eventCount += 1;
                sortFeed(event);                
            }
        );
    }
    if( (complete.count-=1)<1 ) {
        console.log("*** Done Sorting ***");
        output();
    }
}

// This is the main function. It takes in the list of Calendar IDs and the number of results to display
function GCalMFA(list,results){
        var i, calPreProperties, calPostProperties1, calPostProperties2;
        calPreProperties = "https://www.google.com/calendar/feeds/";
        calPostProperties1 = "/public/full?max-results=";
        calPostProperties2 = "&orderby=starttime&sortorder=ascending&futureevents=true&ctz=America/New_York&singleevents=true&alt=json&callback=?";

        if (list) {
            if (results) {
                maxResults = results;
            }
            urllist = list.split(',');
            for (i = 0; i < urllist.length; i+=1 ){
                if (urllist[i] === 0){ urllist.splice(i,1);}
                else{
                urllist[i] = calPreProperties + urllist[i] + calPostProperties1+maxResults+calPostProperties2;}
            }
            console.log("There are " + urllist.length + " URLs");
            urllist.forEach(function addFeed(url){
                $.getJSON(url, complete);   
            });
        }
        else {
            errorLog += '<div id="noURLs">No calendars have been selected.</div>';
            output();
        }
}

© Stack Overflow or respective owner

Related posts about JavaScript

Related posts about JSON