Customizable Widget Installer Tutorial

Some of you were not completely satisfied with my last tutorial
on how to create a simple widget installer. You wanted to learn more.
You wanted to learn about using the JSON-feeds, and above all you wanted
to learn how to make the widget customizable.

Well, I hope you
will like this tutorial. I will teach you how to make a customizable
Table Of Contents Widget, and how to create an installer for this
widget.

For the purpose of this tutorial it will be an extremely
simple widget. If we make it too sophisticated, the code will be more
complicated and less easy to understand. Our purpose is to take you by
the hand and make you understand everything that happens. You can work
from there to develop this widget into something useful for yourself.

What the TOC widget has to do
The
TOC-widget will get the posts-feed from your blog, with a maximum of
100 posts. It will display a list of the posttitles. You can customize 3
settings: the widget title, the number of posts to display, and whether
or not to sort the posts alphabetically.

What the Installer has to do
We
will create an Installer. The Installer is a HTML-document with a form
that contains a few fields and a few buttons and some javascript. It is
best to host this installer from a webserver where you can upload
HTML-pages to. I experienced big trouble with the Blogger page elements
trying to embed the installer directly into the sidebar: Blogger messes
everything up in a horrific way. So what we will do is add a simple
button to the blog, and this button will invoke the widget installer. A
visitor of your blog can then enter his/her data into the fields, push
the Install-button, and the widget will be installed to his/her blog.

Okay, how do I get this done?
Step 1: Create a javascript function that retrieves a JSON-feed from Blogger
Step 2: Define javascript variables for the number of posts to display and for sorting
Step 3: Create a javascript function for displaying the table of contents
Step 4: Combine steps 1 to 3 into a Table Of Contents Widget and add it to your Blog
Step 5: Check that everything works as it should
Step 6: Create an input form for the Widget Installer
Step 7: Create a javascript function that builds the widget
Step 8: Combine steps 6 and 7 into a Widget Installer
Step 9: Upload the Installer and add a button to your Blog
Step 10: Test and debug the Installer

Well, it's not as difficult as it looks, and not as easy as you hope. Somewhere in between.

Step 1: Retrieving the JSON-feed from Blogger.
To retrieve the JSON-feed from Blogger, we will use the following line of javascript:

<script src="http://YOURBLOGNAME.blogspot.com/feeds/posts/default?alt=json-in-script&max-results=100&callback=showtoc"></script>


Replace
YOURBLOGNAME with the name of your blog. This line of javascript asks
Blogger to return the feed of your posts in JSON-format. JSON means:
JavaScript Object Notation. The feed is returned as a set of Javascript
Objects, and that makes it easy to handle for us in step 3. We also tell
Blogger to return a maximum of 100 results (Blogger won't allow for
more than 100, and the default size of feeds is 25). And finally we tell
Blogger to send the JSON-feed to a Javascript function called showtoc.
We'll write this function in step 3.

Step 2: Create variables for the Table of Contents
We will create 2 Javascript variables, using the following code:
<script style="text/javascript"><br />   var numposts = 20;<br />   var sortposts = true;<br /></script>


The
variable numposts is the number of posts you want to show in the Table
of Contents. If you set it to 20, the 20 most recent posts will be
displayed.
The variable sortpost is a boolean variable, and can be
true or false. If it is true, the posttitles will be sorted. If it is
false, the posts will be in chronological order, from newest to oldest.
For your information: these variables make the widget customizable, as we will see later.

Step 3: Create a javascript function for displaying the table of contents.
Now
we have to write the javascript function showtoc that we met in step 1.
It takes a JSON-object (our feed) as input, rips the posttitles from
it, and stores them in an array. If you don't know what an array is:
it's a stack of data. We push the posttitles on this stack, and then we
can sort them and display them on the screen. Take a look at the code, I
have added comments, so even if you are new to Javascript you can get
an idea of what is happening.
<br /><script style="text/javascript><br />function showtoc(json) {<br />   // create an array to hold all posttitles<br />   var toc = new Array();<br />   // get the number of entries that are in the feed<br />   // if there are more entries in the feed than we want to show, take the number we want to show<br />   var numentries = json.feed.entry.length;<br />   if (numentries > numposts) numentries = numposts;<br />   // main loop gets all the entries from the feed<br />   for (var i = 0; i < numentries; i++) {<br />      // if current value of i is bigger than number of entries in feed, then stop the loop<br />      if (i == json.feed.entry.length) break;<br />      // get the entry from the feed<br />      var entry = json.feed.entry[i];<br />      // get the posttitle from the entry<br />      var posttitle = entry.title.$t;<br />      // add the post to the array<br />      toc.push(posttitle);<br />   }<br />   // sort the array if needed<br />   if (sortposts) toc.sort();<br />   // write all posts to the page<br />   for (var i = 0; i < numentries; i++)<br />      document.write('<br/>' + toc[i] + '<br/>');<br />}<br /></script><br />


Step 4: Create the Widget
Open
Notepad or any texteditor, and cut and paste the code from steps 3, 2
and 1 (in that order) to Notepad, and save the file to your desktop
(better be safe than sorry). The Widget code is this:
<br /><script style="text/javascript><br />function showtoc(json) {<br />   // create an array to hold all posttitles<br />   var toc = new Array();<br />   // get the number of entries that are in the feed<br />   // if there are more entries in the feed than we want to show, take the number we want to show<br />   var numentries = json.feed.entry.length;<br />   if (numentries > numposts) numentries = numposts;<br />   // main loop gets all the entries from the feed<br />   for (var i = 0; i < numentries; i++) {<br />      // if current value of i is bigger than number of entries in feed, then stop the loop<br />      if (i == json.feed.entry.length) break;<br />      // get the entry from the feed<br />      var entry = json.feed.entry[i];<br />      // get the posttitle from the entry<br />      var posttitle = entry.title.$t;<br />      // add the post to the array<br />      toc.push(posttitle);<br />   }<br />   // sort the array if needed<br />   if (sortposts) toc.sort();<br />   // write all posts to the page<br />   for (var i = 0; i < numentries; i++)<br />      document.write('<br/>' + toc[i] + '<br/>');<br />}<br /></script><br /><script style="text/javascript"><br />   var numposts = 20;<br />   var sortposts = true;<br /></script><br /><script src="http://YOURBLOGNAME.blogspot.com/feeds/posts/default?alt=json-in-script&max-results=100&callback=showtoc"></script><br />


Now,
go to your Blog Layout and open the Page Elements tab, click Add a Page
Element and select a HTML/Javascript Element. Copy the Widget code from
Notepad and paste it into the Page Element, and Save.
Probably
Blogger will mess things up: it did so when I pasted the code into the
Page Element. So you probably have to correct the code manually. If you
have done so, save again and view your Blog. You will see the 20 most
recent posttitles sorted alphabetically. Your Widget works!

Step 5: Check that everything works as it should.
Now
go and play with the widget. Set numposts to 10, or to 50 if you like
and see what happens. Also switch sortposts from true to false and back
again. Just make sure that you understand how the widget works.
If you reached this step, your widget should work fine. I tested it on my testblog, and it executes fine.

Step 6: Create an input form for the widget installer.
Now
that our widget is ready, we will start with our widget installer. For
this installer we need a HTML-form with several fields and buttons. We
need an input field for the Widget Title, so that the user can customize
his/her own widget title. The Widget Title is a required field for
adding the widget to Blogger. We'll also need an input field for the
user's blogname. This is needed so that the user will get his/her own
feed, and not yours. We also need an input field for the number of posts
that the user wants to display. And a checkbox that has to be checked
if the user wants his/her table of contents to be sorted. Finally there
are the buttons: Reset to clear all fields, Customize to create the
customized widgetcode, and Install to install the customized widget to
the user's blog.
There is also a textarea in the form, that is hidden
from view. This textarea will be used for storing the customized
widgetcode. This textarea is required for passing the widget code to
Blogger.
Here we go with the code:
<br /><form method="POST" action="http://beta.blogger.com/add-widget" target="_blank"><br /><span style="width:130px;">Widget Title</span>: <input type="text" size=30 name="widget.title" value="Table of Contents"/><br/><br /><span style="width:130px;">Blog url</span>: http://<input type="text" size=10 name="iblogurl" value="yourblogname"/>.blogspot.com<br/><br /><span style="width:130px;">Number of posts</span>: <input type="text" size=3 name="inumposts" value="20"/><br/><br /><span style="width:130px;">Sort posts</span>: <input type="checkbox" name="isortposts" checked/><br/><br /><br/><br /><input type="button" value="Customize" onclick="javascript:customize();"><br /><input type="button" value="Reset" onclick="javascript:defaultvalues();"><br /><input type="submit" disabled name="go" value="Install Widget"/><br /><br/><br /><textarea name="widget.content" style="display:none"></textarea><br /></form><br />


Now
just for the fun of it: wrap this form inside a HTML-document (using
Notepad), save it to your desktop, and view it in your browser. You will
have a nice (but not-working) preview of your widget installer. You can
enter data into the fields, you can click buttons, but nothing will
happen - yet. Wrapping it inside HTML? Oh, yes, that goes like this:
<br /><html><br /><body><br /><form method="POST" action="http://beta.blogger.com/add-widget" target="_blank"><br /><span style="width:130px;">Widget Title</span>: <input type="text" size=30 name="widget.title" value="Table of Contents"/><br/><br /><span style="width:130px;">Blog url</span>: http://<input type="text" size=10 name="iblogurl" value="yourblogname"/>.blogspot.com<br/><br /><span style="width:130px;">Number of posts</span>: <input type="text" size=3 name="inumposts" value="20"/><br/><br /><span style="width:130px;">Sort posts</span>: <input type="checkbox" name="isortposts" checked/><br/><br /><br/><br /><input type="button" value="Customize" onclick="javascript:customize();"><br /><input type="button" value="Reset" onclick="javascript:defaultvalues();"><br /><input type="submit" disabled name="go" value="Install Widget"/><br /><br/><br /><textarea name="widget.content" style="display:none"></textarea><br /></form><br /></body><br /></html><br />


Now let's move on to step 7.

Step 7: Creating javascript functions to create the customized widget.
We
have to write 2 javascript functions that respond to buttons being
pressed. One to reset the form to its default values, and one to create
the widget code. Let's start with a function for resetting the form
field. The function defaultvalues() will set the form to its default
values when the Reset button is clicked. Here is the code:

<br />function defaultvalues() {<br />   document.getElementsByName("widget.title")[0].value = "Table of Contents";<br />   document.getElementsByName("iblogurl")[0].value = "yourblogname";<br />   document.getElementsByName("inumposts")[0].value = "20";<br />   document.getElementsByName("isortposts")[0].checked = true;<br />   document.getElementsByName("go")[0].disabled = true;<br />}<br />


This
function looks up the formfields by there name, and sets their
contents. The last line greys out the Install-button, so that we can
only click it if we have customized the widget settings.
The second
function we will create is the function customize(), that is invoked
when the user clicks the Customize-button. The code of this function is:

<br />function customize() {<br />   // Get input values<br />   var blogurl = document.getElementsByName("iblogurl")[0].value;<br />   var numposts = document.getElementsByName("inumposts")[0].value;<br />   var sortposts = document.getElementsByName("isortposts")[0].checked;<br />   // Generate widgetcode<br />      var txtarea = document.getElementsByTagName("textarea")[0];<br />      txtarea.value = "\<script style='text/javascript'\>";<br />      txtarea.value = txtarea.value + "function showtoc(json) { ";<br />      txtarea.value = txtarea.value + "var toc = new Array(); ";<br />      txtarea.value = txtarea.value + "var numentries = json.feed.entry.length; ";<br />      txtarea.value = txtarea.value + "if (numentries \> numposts) numentries = numposts; ";<br />      txtarea.value = txtarea.value + "for (var i = 0; i \< numentries; i++) { ";<br />      txtarea.value = txtarea.value + "if (i == json.feed.entry.length) break; ";<br />      txtarea.value = txtarea.value + "var entry = json.feed.entry[i]; ";<br />      txtarea.value = txtarea.value + "var posttitle = entry.title.$t; ";<br />      txtarea.value = txtarea.value + "toc.push(posttitle); } ";<br />      txtarea.value = txtarea.value + "if (sortposts) toc.sort(); ";<br />      txtarea.value = txtarea.value + "for (var i = 0; i \< numentries; i++) ";<br />      txtarea.value = txtarea.value + "document.write('\<br/\>' + toc[i] + '\<br/\>'); } ";<br />      txtarea.value = txtarea.value + "\</script\>";<br />      txtarea.value = txtarea.value + "\<script style='text/javascript'\>";<br />      txtarea.value = txtarea.value + "var numposts = " + numposts + "; ";<br />      if (sortposts) { txtarea.value = txtarea.value + "var sortposts = true; "; }<br />         else { txtarea.value = txtarea.value + "var sortposts = false; "; };<br />      txtarea.value = txtarea.value + "\</script\>";<br />      txtarea.value = txtarea.value + "\<script src='http://" + blogurl + ".blogspot.com/feeds/posts/default?alt=json-in-script&max-results=100&callback=showtoc'\>\</script\>";<br />   // Show add button<br />      var addbutton = document.getElementsByName("go")[0];<br />      addbutton.disabled = false;<br />}<br />


What
happens here - you won't believe it - is that this javascript-function
writes the javascript-code for the widget! Now that is kind of Baron of
Munchhaussen, if you are familiar with this German nobleman who pulled
himself out of the water by his own neck. The entire widget-code
(without the comments) is written into the textarea of the form and the
Install-button is enabled.

Step 8: Create the Widget Installer
From steps 6 and 7 we can now create the widget installer. Open Notepad, and create a textfile with the following content:
<br /><html><br /><script style="text/javascript"><br />function customize() {<br />   // Get input values<br />   var blogurl = document.getElementsByName("iblogurl")[0].value;<br />   var numposts = document.getElementsByName("inumposts")[0].value;<br />   var sortposts = document.getElementsByName("isortposts")[0].checked;<br />   // Generate widgetcode<br />      var txtarea = document.getElementsByTagName("textarea")[0];<br />      txtarea.value = "\<script style='text/javascript'\>";<br />      txtarea.value = txtarea.value + "function showtoc(json) { ";<br />      txtarea.value = txtarea.value + "var toc = new Array(); ";<br />      txtarea.value = txtarea.value + "var numentries = json.feed.entry.length; ";<br />      txtarea.value = txtarea.value + "if (numentries \> numposts) numentries = numposts; ";<br />      txtarea.value = txtarea.value + "for (var i = 0; i \< numentries; i++) { ";<br />      txtarea.value = txtarea.value + "if (i == json.feed.entry.length) break; ";<br />      txtarea.value = txtarea.value + "var entry = json.feed.entry[i]; ";<br />      txtarea.value = txtarea.value + "var posttitle = entry.title.$t; ";<br />      txtarea.value = txtarea.value + "toc.push(posttitle); } ";<br />      txtarea.value = txtarea.value + "if (sortposts) toc.sort(); ";<br />      txtarea.value = txtarea.value + "for (var i = 0; i \< numentries; i++) ";<br />      txtarea.value = txtarea.value + "document.write('\<br/\>' + toc[i] + '\<br/\>'); } ";<br />      txtarea.value = txtarea.value + "\</script\>";<br />      txtarea.value = txtarea.value + "\<script style='text/javascript'\>";<br />      txtarea.value = txtarea.value + "var numposts = " + numposts + "; ";<br />      if (sortposts) { txtarea.value = txtarea.value + "var sortposts = true; "; }<br />         else { txtarea.value = txtarea.value + "var sortposts = false; "; };<br />      txtarea.value = txtarea.value + "\</script\>";<br />      txtarea.value = txtarea.value + "\<script src='http://" + blogurl + ".blogspot.com/feeds/posts/default?alt=json-in-script&max-results=100&callback=showtoc'\>\</script\>";<br />   // Show add button<br />      var addbutton = document.getElementsByName("go")[0];<br />      addbutton.disabled = false;<br />}<br /><br />function defaultvalues() {<br />   document.getElementsByName("widget.title")[0].value = "Table of Contents";<br />   document.getElementsByName("iblogurl")[0].value = "yourblogname";<br />   document.getElementsByName("inumposts")[0].value = "20";<br />   document.getElementsByName("isortposts")[0].checked = true;<br />   document.getElementsByName("go")[0].disabled = true;<br />}<br /><body><br /><form method="POST" action="http://beta.blogger.com/add-widget" target="_blank"><br /><span style="width:130px;">Widget Title</span>: <input type="text" size=30 name="widget.title" value="Table of Contents"/><br/><br /><span style="width:130px;">Blog url</span>: http://<input type="text" size=10 name="iblogurl" value="yourblogname"/>.blogspot.com<br/><br /><span style="width:130px;">Number of posts</span>: <input type="text" size=3 name="inumposts" value="20"/><br/><br /><span style="width:130px;">Sort posts</span>: <input type="checkbox" name="isortposts" checked/><br/><br /><br/><br /><input type="button" value="Customize" onclick="javascript:customize();"><br /><input type="button" value="Reset" onclick="javascript:defaultvalues();"><br /><input type="submit" disabled name="go" value="Install Widget"/><br /><br/><br /><textarea name="widget.content" style="display:none"></textarea><br /></form><br /></body><br /></html><br />


Save
this file as widgetinstaller.html to your desktop, and launch it in
your browser. You have to allow for scripts and active content, but then
you will see that your installer really works!

Step 9: Upload it and create a button to launch the installer
Upload the widgetinstaller.html file to one of your folders with your ISP webhost.
Now add a HTML-page element to your blog, give it an appropriate title, and add some text to describe the widget.
Then add a button using the following code:

<br /><form action="_self"><input type="button" value="Add Table of Contents to Blog" onclick="window.open('http://YOURHOST/widgetinstaller.html','popupwindow','width=500, height=500'); return false" /></form><br />


Save the page element and view your Blog.

Step 10: Test and debug.
Now play around to check if everything works as it should.
Congratulations! You are done!

Beyond this point you are completely at your own risk!

If you want to improve this widget, here are some ideas:
- Add the post url to the posttitle
- Add the date to the posttitle
- Add an option to display a postsummary
- Add CSS-styling to the widget
- Add CSS-styling to the widget-installer
- Add a preview-pane to the widget-installer

Resources:
- JSON-documentation
- Delving into JSON-feeds on Beautiful Beta
Related Posts Plugin for WordPress, Blogger...