Sunday, January 25, 2009

Blog Flower Code

This article is the second in a two part series on the Blog Flower Gadget. In the previous post I described how I created the artwork using InkScape. In this post I will describe the programming behind the gadget.

There are two parts to the gadget. The first part which is the gadget itself is contained in a JavaScript file, the second part contains all of the 'Add to Blogger' submission functionality, it is a combination of an html form and another JavaScript file.

The gadget is built using the Google Data API, which provides the content of a blog as either an XML feed or a JSON feed. I primarily work on a Windows Forms application using the .NET Framework, so this was the first time I had even heard of JSON. It is a data format somewhat like XML, except that it is much more compact, and is built specifically for JavaScript. In fact the Google API returns it as an already parsed collection of JavaScript obejcts, which couldn't be easier to work with.

Here is the first JavaScript file, BlogFlower.js which is the gadget.

BlogFlower.js

// (C)2009 by Peter Noyes
function blogFlower(root)
{
var feed = root.feed;
var entries = feed.entry;
if (entries.length == 0)
return;

var entry = entries[0];
var ds = entry.published.$t;
var d = new Date(ds.substring(0,4), ds.substring(5,7) - 1, ds.substring(8,10));
var days = Math.floor((new Date() - d) / 86400000);
document.write('<div class="blogFlower">');

var flower;
if (days < 5)
flower = 'http://www.geeklounge.org/script/Flower1.png';
else if (days < 10)
flower = 'http://www.geeklounge.org/script/Flower2.png';
else
flower = 'http://www.geeklounge.org/script/Flower3.png';

document.write('<img src="' + flower + '">');
document.write('<br>');

switch (days)
{
case 0:
document.write('updated today');
break;
case 1:
document.write('updated yesterday');
break;
default:
document.write('updated ' + days + ' days ago');
}

document.write('</div>');
}


The blogFlower function is called by the Google API, and the root variable is the JSON object that the API constructs. The blog post is retrieved from root.feed.entry, which is an array of all the entries. Google has a good description of this here. The only real trouble I had with JSON was compatibility with the date format. I did find a Google Date API that may have helped, but it added a tremendous amount of JavaScript to this project, which seemed overkill. In the end I used substrings to pluck the year, month, and day from the JSON date. To find the number of days since the last posting, I subtracted the parsed date from the current date, which returns the time span in milliseconds. The conversion factor of 86,400,000 is built up by 1000ms * 60s * 60m * 24h. The corresponding image and text is then written to the document as HTML.

The submission code uses the syntax described here, in order to add the gadget to Blogger. It also sets up the API call to the Google Data API and provides the callback to the blogFlower function.

First is the submission HTML that I placed in the blog post, it is followed by SubmitBlogFlower.js which is referenced by the HTML. I first wanted the submission code to be in the blog post as well, however the syntax checker in the blog post editor barfed on the JavaScript and wouldn't let me post it.

HTML in Blog Post

<script src="http://www.geeklounge.org/script/SubmitBlogFlower.js"></script>
<form name="blogFlowerForm" method="post" action="http://www.blogger.com/add-widget">
<span>http://<input type="text" name="blogName" onkeypress="return noEnter()">.blogspot.com</span><br><a href="javascript: submitForm()"><img src="http://www.blogger.com/img/add/add2blogger_sm_w.gif" style="border:0px"/></a>
<input type="hidden" name="infoUrl" value="http://peternoyes.blogspot.com"/>
<input type="hidden" name="widget.title" value="Blog Flower"/>
<textarea name="widget.content" style="display:none;"></textarea>
</form>


SubmitBlogFlower.js

// (C)2009 by Peter Noyes
function submitForm()
{
var content1 = '\<script type="text/javascript" src="http://www.geeklounge.org/script/BlogFlower.js"\>\</script\>';
var content2 = '\<script type="text/javascript" src="http://';
var content3 = document.blogFlowerForm.blogName.value;
var content4 = '.blogspot.com/feeds/posts/default?orderby=published&max-results=1&alt=json-in-script&callback=blogFlower"\>/</script\>';
document.blogFlowerForm['widget.content'].value = content1 + content2 + content3 + content4;
document.blogFlowerForm.submit();
}

function noEnter(evt)
{
var evt = (evt) ? evt : ((event) ? event : null);
var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
if ((evt.keyCode == 13) && (node.type=="text")) {return false;}
}

document.onkeypress = noEnter;


The gadget is added to Blogger using an html form. I used all hidden form fields except for a visible text field for the user to enter the name of their blog. When the user clicks the submit button it calls the function submitForm, which dynamically constructs code for the widget.content field. This code first references the BlogFlower.js, and then it references the Google Data API. It builds up the feed URL using the blogName text field. I send the following parameters to recover the JSON feed:

  • orderby=published, This ensures that I will be returned the latest posts.

  • max-results=1, This instructs Google to only send back a single post. When doing research I came across a popular Blogger gadget that shows a list of recent posts. Even though they only consumed a few posts, they omitted this parameter which causes Google to return a feed containing every single blog post. This is a complete waste of bandwidth.

  • alt=json-in-script, This instructs Google to return the feed as JSON instead of XML, and to use a callback.

  • callback=blogFlower, This is the callback function implemented in BlogFlower.js


The noEnter function is used to prevent the user from being able to submit the form by pressing 'Enter' in the blogName text field, because doing so would submit the gadget incorrectly.

To get this to all to work took a decent amount of research. At first I kept stumbling across the Widget Tags for Layouts which is the syntax used to create a Blogger Layout. Some gadgets are written using this API, however I couldn't find a way to create the Blog Flower this way. Once I found the Google Data API the implementation went pretty quickly. Creating the artwork actually took up most of the time.

1 comment:

Pixelstar said...

thanks for the useful post.. The blog flower was just what i was looking for.