Tuesday, April 14, 2009 - 19:11
Blarg.
For reasons beyond my current understanding, posting more than once a day is causing problems. And not just problems with my crazy "I'm taking control of EVERYTHING" stuff, but also of blogger built in tags (although they're a bit less broken).
What is happening is this: in my test blog, the archive script worked fine. Realize though, there were only two posts (and not on the same day) so it was a next-to-trivial test case. Regardless, I didn't see this coming. For some reason (some combination of my archive settings and potentially other unknown factors), more than once post per date is breaking the date for those posts. It's as though the blog only recognizes one post a day. On the page for a single affected post, everything appears fine. Using the blogger archive tags, the post is listed with no date (although it does appear in the correct place, relative to the other posts).
This is a problem for the script. It assumes that there will always be a post date (and there always should be, in my mind). However, due to this problem (bug?), the object is empty. And that is the end of this script working correctly for now. I'll probably try to tweak my archive settings, to see if that fixes it. If not, I'll probably try to upgrade the script to deal with mysteriously missing information.
While I'm at it, I should probably fix the labels script too. It doesn't work if there is a post with no labels, and there's no good reason it shouldn't. Moreover, I should probably make sure it works for posts without titles too.
/sigh.
Labels: blog, classic blogger, feeds, javascript, series 2
Sunday, April 12, 2009 - 19:28
WARNING
This code will fail (miserably) if there are any posts with no labels. It will simply fail to place anything at all in the labelList div. It will also generate an error in the Error Console (if you use a browser that has one).
You have been warned...
Now it's time to revisit the label list. It may seem fine as it is, but you may also notice that the label widget shows how many posts have each label. So if there are three posts with the label 'test', it might be displayed in the list as 'test (3)'. The javascript I originally dug up does not accomplish this - it has no way to know how many times a label has been used.
There is a solution. You may recall I mentioned this before. A careful investigation of the feed object reveals that it contains an array of entries, each of which contains a list of categories. With some careful sorting and counting, we can create a list of labels with the count included.
First things first, I'm still learning to decode the feed urls, but it seems that the feed used for the label list does not have the same entry list as the json feed I used for the archive. So we'll just switch over to using the other feed.
<script src="http://BLOGURL/feeds/posts/default?alt=json-in-script&callback=labels"></script>
Notice that the callback function has changed - next we'll write the labels function.
I fully admit that this is probably not the most efficient way to generate this list, but it works and doesn't cause an obvious slowdown when I load. That said, I haven't exactly got that many posts yet, so I have no idea if this might cause problems on longer blogs.
So first, we'll create a list by looping through each posts categories. Since most posts reuse categories, it should be no surprise that this will create duplicates. That's okay though - we need to calculate the number of uses of a label somehow.
function labels(json) {
var posts = json.feed.entry;
var labels = new Array();
var counts = new Array();
var baseURL = '/search/label/';
var isFTP = false;
var categoryList = new Array();
for( var i=0; i < posts.length; i++) {
// for each post
var category = posts[i].category;
for( var j=0; j < category.length; j++ ) {
categoryList[ categoryList.length ] = category[j].term;
}
}
// to be continued ...
Now that we have a list of categories including duplicates, we'll go through and add each unique one to the array 'labels'. We'll also use counts as a hash table / dictionary. When we add a category to labels, we'll use the label as a key and initialize it to 1. When a category is already in labels, we'll increment the value in the hash.
// ... continued from before
// category list contains duplicates - iron them out while counting them
for( var i=0; i < categoryList.length; i++ ) {
var inlist = false;
for( var j=0; j < labels.length; j++ ) {
if( categoryList[i] == labels[j] ) { inlist = true; break; }
}
if ( inlist ) { counts[ categoryList[i] ] = counts[ categoryList[i] ] + 1; }
else {
labels[ labels.length ] = categoryList[i];
counts[ categoryList[i] ] = 1;
}
}
// to be continued ...
All we have left to do is to add the labels to the document.
Notice that this time, the count for each label is included in the innerHTML of link.
// ... continued from before
labels.sort();
var ul = document.createElement('ul');
for( var r=0; r < labels.length; r++ ) {
var li = document.createElement('li');
var a = document.createElement('a');
a.href = baseURL + encodeURIComponent(labels[r]);
if(isFTP) { a.href = a.href + '.html'; }
a.innerHTML = labels[r] + ' ('+counts[labels[r]]+')';
li.appendChild(a);
ul.appendChild(li);
var blank = document.createTextNode('');
ul.appendChild(blank);
}
document.getElementById('labelList').appendChild(ul);
} // end of labels function
And there you have it, now we have a sorted list of labels which displays the count. Another useful note is that we could sort the labels in all sort of complicated ways - by the number of uses, by the order they were published.
One final note - I've discovered that I much prefer adding things to the document using this method, rather than just document.write. It allows several conveniences: first, all the javascript can be included in one place in the template; second, I don't have to convert the html to post it - I can just copy and paste.
Labels: blog, classic blogger, feeds, html, javascript, series 2
As I mentioned before, I like control. The next victim was the archive links. The most obvious way to do it in classic blogger is:
<ul>
<BloggerArchives>
<li><a href="<$BlogArchiveURL$>"><$BlogArchiveName$></a></li>
</BloggerArchives>
</ul>
My problem with this is simple - I can only get links to the archive pages - I can't actually have a complicated expandable list like the archive widget allows. So since my archives are set to monthly, all this gives me is a list of months. I could find no way to include the actual links in, or to add numbers indicating how many posts were in each archive. All I could do was use some basic javascript to reorder them. Ick.
My next attempt was with the <BloggerPreviousItems> tag.
<ul>
<BloggerPreviousItems>
<li><a href="<$BlogItemPermalinkURL$>"><$BlogPreviousItemTitle$><a></li>
</BloggerPreviousItems>
</ul>
Sadly, it seems to me that the best use for this is some kind of "previous post" button. It really does not work for an archive section, as the contents of the list will depend on the page. From the main page, it works fine, but once you click on a single post, you can only see posts previous to that most. Moreover, there is no corresponding "NextItems" tag.
My next course of action was to revisit the javascript for the labels. That odd bit at the end was nagging at me. Surely I could learn something from <script type="text/javascript" src="http://www.blogger.com/feeds/USERID/blogs/BLOGID?alt=json-in-script&callback=listLabels" ></script>. Some googling and I discovered blogger feeds, which is what that link is. I discovered this, which is a great introduction to blog feeds, and provides some javascript which lets you investigate the layout of the feed, so that you know some very useful things:
- a) how to get the blog feed into a javascript object
- b) how to get that object into a javascript function
- c) where to find the useful components of that object ( guessing at a javascript object is tough, so avoiding it is worthwhile, to say the least.)
Knowing this, I was suddenly armed with the ability to make my own archive section, customized to my exact desires (with a bit of work, anyway).
With some trial and error, I constructed my personal archive (it's very basic, right now - in fact it fills the role of "proof of concept" better than being a final product). First, the javascript "archive" function:
function archive(json) {
var posts = json.feed.entry; // get the posts
var sorted = new Array();
for( var i=0; i < posts.length; i++) { // for each post
if( sorted.length == 0 ) {
sorted[0] = posts[i];
}
else {
for( var j=0; j < sorted.length; j++) { // compare to each post that has already been sorted
// compare by date
var sorteddate = datenumber(sorted[j].updated['$t'] );
var postdate = datenumber( posts[i].updated['$t'] );
if( sorteddate > postdate ) {
var tmp = new Array();
for( var k=0; k < j; k++ ) { tmp[tmp.length] = sorted[k]; }
tmp[tmp.length] = posts[i]; //postdate;
for( var k=j; k < sorted.length; k++) { tmp[tmp.length] = sorted[k]; }
sorted = tmp;
break;
}
if( j == sorted.length - 1) {
sorted[sorted.length] = postdate;
}
} // end loop through sorted
} // end else (if sorted not empty )
} // end loop through posts
document.write('<ul>');
for( var i=0; i < sorted.length; i++) {
document.write('<li><a href="'+sorted[i].link[4].href+'">'+sorted[i].title['$t']+'</a></li>');
}
document.write('</ul>');
}
Notice two things: the "datenumber" function and the "document.write". The "datenumber" function I wrote because I didn't know how to directly compare the dates provided by the feed. Rather than looking it up, I hacked together a function which would compare the dates as numbers.
function datenumber(date){
var year = date.substring(0,4);
var month = date.substring(5,7);
var day = date.substring(8,10);
return parseInt(year+month+day);
}
This function is pretty simple - it just grabs the substrings of the date for the year month and day, then makes a string such as 20090302 (march 2nd, 2009). Then this string is parsed into an integer, so since 20090301 is clearly a smaller number (march 1st, 2009), it will compare as smaller than the 2nd. As it is written, the order is in the order the posts were published (march 1st appears before march 2nd in the list). It's a simple change to swap the posts to show in reverse order.
The "document.write" part is even simpler - it just writes the text directly into the document (from where the function is called from). It's a lazy shortcut for finding the correct div and changing the tree of the document with "appendChild". One critical result is that the call to this function MUST be placed where the list is intended to appear, where the label list function can be called from anywhere in the document.
Finally, to actually include the archive list, the following must be placed where the archive is intended to appear:
<script src="http://BLOGURL/feeds/posts/default?alt=json-in-script&callback=archive"></script>
Notice also that this script call uses the URL of the blog, rather than the blog id. And callback is set to call the archive function. Now, with a little more work sorting and grouping the posts in the archive function, we could have a very complex archive link list. Note also, that the posts are only sorted by date, not time. This means that two posts in the same day will appear in the order they are in the feed (the order they were posted, I believe). A better date comparison would be needed for more fine-tuned sorting.
Labels: blog, classic blogger, feeds, html, javascript, series 2
Saturday, April 11, 2009 - 14:07
After scouting around a bit, I found a post on creating labels for classic blogs. It was just what I was looking for. I snagged the code, tweaked it a bit, and had a working label list. Here is the original javascript label list:
<div id="labelList"></div> <script type="text/javascript">
//<![CDATA[
function listLabels(root){
var baseURL = '/search/label/';
var baseHeading = "Labels";
var isFTP = false;
var llDiv = document.getElementById('labelList');
var entry = root.entry;
var h2 = document.createElement('h2');
h2.className = 'sidebar-title';
var h2t = document.createTextNode(baseHeading);
h2.appendChild(h2t);
llDiv.appendChild(h2);
var ul = document.createElement('ul');
ul.id = 'label-list';
var category = entry.category;
labelSort = new Array();
for(p in category){
labelSort[labelSort.length] = [category[p].term];
}
labelSort.sort();
for (var r=0; r < labelSort.length; r++){
var li = document.createElement('li');
var a = document.createElement('a');
if(isFTP){
a.href = baseURL + encodeURIComponent(labelSort[r])+'.html';
}
else {
a.href = baseURL + encodeURIComponent(labelSort[r]);
}
a.innerHTML = labelSort[r] + ' ';
li.appendChild(a);
ul.appendChild(li);
abnk = document.createTextNode(' ');
ul.appendChild(abnk);
}
llDiv.appendChild(ul);
}
//]]>
</script>
<script type="text/javascript" src="http://www.blogger.com/feeds/USERID/blogs/BLOGID?alt=json-in-script&callback=listLabels" ></script>
The source for this code is found here. I recommend taking a look, as both the post and the comments are helpful. As I said, I made a few tweaks as I was typing the code into my blog. I already had my own label, so I skipped that part entirely. I also left out giving an id to the label list. I just didn't see a need, as my css already handled the list in a general fashion (and I was happy with the result). I also added in a <noscript> tag, to cover those that don't have javascript enabled. Other than that, I basically used it as-is. At first, I simply didn't understand the mumbo jumbo at the end, where the second script tag calls a source. Here is my revised version:
<div id="labelList"></div>
<noscript>Sorry, labels are available only with javascript enabled.</noscript>
<script type="text/javascript">
//<![CDATA[
function listLabels(root){
var baseURL = '/search/label/';
var baseHeading = 'Labels';
var isFTP = false;
var labelDiv = document.getElementById('labelList');
var entry = root.entry;
var ul = document.createElement('ul');
var category = entry.category;
labelSort = new Array();
for(p in category) {
labelSort[labelSort.length] = [category[p].term];
}
labelSort.sort();
for( var r=0; r < labelSort.length; r++){
var li = document.createElement('li');
var a = document.createElement('a');
a.href = baseURL + encodeURIComponent(labelSort[r]);
if(isFTP){
a.href = a.href + '.html';
}
a.innerHTML = labelSort[r]+'';
li.appendChild(a);
ul.appendChild(li);
blank = document.createTextNode( '' );
ul.appendChild(blank);
}
labelDiv.appendChild(ul);
}
//]]>
</script>
<script type="text/javascript" src="http://www.blogger.com/feeds/USERID/blogs/BLOGID?alt=json-in-script&callback=listLabels" ></script>
But I did mention I'm a bit picky, right? Well, it may seem as though I found satisfaction, but this script, combined with the provided tags for classic blogger soon sparked my imagination and drive for control.
Labels: blog, classic blogger, feeds, html, javascript, series 2
Tuesday, March 31, 2009 - 20:05
I'm a bit of a control freak. I don't much care for the "new blogger" or widgets - they get the basic job done, but I found it hard to get the result I was after. So I reverted my blog and spewed out some html and css. Having achieved that, I started searching for "widget" functionality for classic blogs. The most obvious failing was the labels widget. There are no tags for classic blogs to grab all the labels. A bit of digging on the net and I soon found a javascript solution.
Now, I'm not hugely fond of javascript. In fact, I consider it the bane of my web programming experience. That being said, sometimes it's just too bloody useful to avoid. So I threw in the labels javascript to my sidebar.
While browsing the blogs and webpages that informed me about the wonderful solutions to customizing blogs (most of which turned out to be "new blogger"), I made mental notes of what features I liked and what made blogs navigatable. I discovered I really wanted users to be able to sort the archives (and maybe labels) in whichever way suited them best - date, alphebetical, reversed. Although classic blog templates have a tag for archives, the only way to do it was by date (or, with a little hacking, reversed) and each result was a link to the archive page. One extra step means harder to browse. Another nice javascript addition in the widget is the expandable links. I was disappointed and frustrated by the limitations.
I studied the labels solution I had found. There I found a hint - the url used as the source. Poking around a bit more, I discovered the magic of blog feeds. Here, at last, was the solution for the control I sought! Sadly, it requires the use of javascript, but I can live with that. It's certainly been a bit tricky, and I'm only through the beginning of the learning curve, but I'm feeling pretty good about it.
I expect post more details later. Look forward to it!
Labels: blog, classic blogger, feeds, javascript, series 2, template
Leave one.