The amount of users who rely on Google Calendars to organize their personal and professional lives is staggering. Seeing as most clients are comfortable and proficient with the technology, there is little reason to point them elsewhere when they ask for a custom widget to display upcoming events on their site. [caption id=“attachment_966” align=“aligncenter” width=“307” caption=“Google Calendars are easy and everywhere, with a robust API we can leverage”][/caption] In fact, the only trouble is that Google’s provided widget layouts are all - er, well they are all quite lame, and likely won’t match your current theme. No worries! We can easily leverage Google’s calendar API and Javascript to create a fully customized Calendar widget showing the next N upcoming events in chronological order. Let’s start by looking at the provided Google widgets.. [caption id=“attachment_967” align=“aligncenter” width=“300” caption=“Default “Agenda” layout for Google Calendar”][/caption] Wow, that would look great in a Google webpage somewhere. But it probably doesn’t meet the layout you were looking for. So we’ll use some basic HTML and JS to manipulate an Atom feed from Google. Getting the base URL for your Calendar
Alright, so we have a URL for an RSS feed. This RSS format has a few critical downsides.
But by exploring Google’s API documentation we learn of a better format, their custom Atom feed!
So here is the provided URL from the last section:
We need to modify the “basic” projection, and replace it with “full”:
But since we don’t care about attendees in this case we shrink the payload a little bit:
That looks much better! Now we get custom fields like gd:when and gd:where
… 2011-08-08T14:16:34.000Z 2011-08-08T14:16:34.000Z Sangria & Tapas Tasting BLAH BLAH BLAH
Calendar Name
Now that all the pieces we need have their own little pockets, we can start parsing into a presentable format.
First we start with a simple block of HTML that will house the populated calendar.
Before we get into the JS I must credit a source I stumbled across while researching this. The objective is quite common, so it may not be the original source. http://blog.csdn.net/runupwind/article/details/1655837
Just edit the backend URL here, unless you want to completely change the layout of things. You should be able to CSS alone to get the style you want. Same Origin Policy applies, so you will need to call a backend script on the same domain that relays to Google.com
<!--
// the max number of evewnts to show
maxEvents = 7;
var RSSRequestObject = false; // XMLHttpRequest Object
var Backend = '/wp-content/uploads/2011/08/demo/backend.php'; // Backend call to same domain proxy (prevents 'is not allowed by Access-Control-Allow-Origin.')
window.setInterval("update_timer()", 1200000); // update the data every 20 mins
// DO NOT EDIT BELOW
if (window.XMLHttpRequest) // try to create XMLHttpRequest
RSSRequestObject = new XMLHttpRequest();
if (window.ActiveXObject) // if ActiveXObject use the Microsoft.XMLHTTP
RSSRequestObject = new ActiveXObject("Microsoft.XMLHTTP");
/*
* onreadystatechange function
*/
function ReqChange() {
// If data received correctly
if (RSSRequestObject.readyState==4) {
// if data is valid
if (RSSRequestObject.responseText.indexOf('invalid') == -1)
{
// Parsing Feeds
var node = RSSRequestObject.responseXML.documentElement;
// Get the calendar title
var title = node.getElementsByTagName('title').item(0).firstChild.data;
content = '<div class="channeltitle">'+title+'</div>';
// Browse events
var items = node.getElementsByTagName('entry');
if (items.length == 0) {
content += '<ul><li><div class=error>No events</div></li></ul>';
} else {
content += '<ul>';
if(maxEvents > items.length) maxEvents = items.length;
for (var n=0; n <= maxEvents-1; n++)
{
var itemTitle = items[n].getElementsByTagName('title').item(0).firstChild.data;
// may have empty content if no event details were added
try{
var Summary = items[n].getElementsByTagName('content').item(0).firstChild.data;
}catch(e){
var Summary = '';
}
var eventId="";
var baseUrl=items[n].getElementsByTagName('link').item(0).attributes.getNamedItem("href").value;
//alert(calId);
var itemLink = baseUrl;
console.log(items[n].getElementsByTagName('when'));
var roughStartDate=items[n].getElementsByTagName('when').item(0).attributes.getNamedItem("startTime").value;
try
{ var mydate=new Date(roughStartDate);
var readAs="" + (mydate.getMonth()+1) + "/" + mydate.getDate() + "/" + mydate.getFullYear();
var itemPubDate = '<span class="event-date">['+ readAs+']</span> ';
}
catch (e)
{
var itemPubDate = '';
}
content += '<li>'+itemPubDate+'<a href="'+itemLink+'"><span class="event-summary">'+itemTitle+'</span></a></li>';
}
content += '</ul>';
}
// Display the result
document.getElementById("calendarFeed").innerHTML = content;
// Tell the reader the everything is done
document.getElementById("status").innerHTML = "Done.";
}
else {
// Tell the reader that there was error requesting data
document.getElementById("status").innerHTML = "<div class=error>Error requesting data.<div>";
}
HideShow('status');
}
}
/*
* Main AJAX RSS reader request
*/
function RSSRequest() {
// change the status to requesting data
HideShow('status');
document.getElementById("status").innerHTML = "Requesting data ...";
// Prepare the request
RSSRequestObject.open("GET", Backend , true);
// Set the onreadystatechange function
RSSRequestObject.onreadystatechange = ReqChange;
// Send
RSSRequestObject.send(null);
}
/*
* Timer
*/
function update_timer() {
RSSRequest();
}
function HideShow(id){
var el = GetObject(id);
if(el.style.display=="none")
el.style.display='';
else
el.style.display='none';
}
function GetObject(id){
var el = document.getElementById(id);
return(el);
}
RSSRequest();
//-->
Refer to wikipedia for the “what is same origin policy” But to us it means that we can’t pull data from google and operate on it unless we call a script on the same host as our domain first. This script (PHP, ASP, python, whatever) has to pass the data to our front-end javascript. My demo uses PHP, which is easy and readily available. But by no means the only approach.
Alright, so we have a decent XML feed with the info we need. We have a JS snippet to walk the DOM and pull out our info. And we are inserting the formatted HTML into our page. Let’s have a look! Iframe containing Demo 1 - Demo 1 Yes, quite ugly without any styling, but there is a bigger issue, the dates are all wrong. No order, past and future.. no Good!
In order to get the future looking, chronological calendar we expect, we’ll need to tell Google Calendar to refine the results a bit.. Again, consulting the Google’s API documentation we learn of 4 critical modifiers:
This will tell Google Calendar we only want future events, sorted by the start time, ascending. We also ask that recurring events be specified individually, and not as one event.
Iframe containing Demo 2 - Demo 2
That looks much better!
The choir is just to add the appropriate CSS styling to format the feed as you wish. I’m no designer, and your site is much different then mine. SO I issue a challenge to some of my CSS savvy readers. **Submit your best styling for this widget and I will include the winner’s css for everyone to awe over C’mon! I wanna see rounded corners, pretty gradients, clear divisions and nice eye candy. **