Wednesday, July 1, 2009

Reinventing the Wheel

There were a couple of web-related things I recently puzzled through before finding out somebody had already solved the problem, but I'm kind of proud of them anyway.

The first one was a Perl script to convert from LiveJournal's export format to the Atom format for import into Blogger. I wrote it so my wife could transfer her old LiveJournal blog. After some finagling, I even got it to import the comments correctly and get the timestamps right.

I later found out some other guy had actually written a much more full-featured application for managing and converting LiveJournal posts. The punchline, though, is that he also wrote it for his wife. Ba-dump!

The second cool thing I figured out you can see by clicking on the link below.

Woo hoo! Now, I know everybody's blog already has this, but I ended up doing my own homebrew solution without peeking. It was doubly satisfying because I didn't know any Javascript prior to this, and I ended up with a JS-heavy solution.

Blogger's help tells you how to do a rudimentary fold by defining a style called "fullpost" that doesn't do anything if you are on a post page, but causes the text not to be displayed if you are anywhere else (e.g. like on the main page). So then you just wrap everything below the fold in a <span class="fullpost"> tag and that makes it appear in posts but disappear on the main page.

The problem is, they then have you put a fixed "Continue reading..." link in the HTML that appears at the bottom of every single post on the main page. So either you have a fold in every post (blech!) or else you deal with having a "Continue reading..." link that doesn't actually give you any more content (double-blech!). They say that fixing this is left "as an exercise for the reader".

My first solution did not involve any Javascript, but instead used a conditional based on Blogger's custom tags. I had it scan the labels for each post, and if it found the label "more", then it inserted the "Continue reading..." link. Note that it used no standard scripting language, only the custom tags provided by Blogger, meaning that all of this was executed on the server-side. The two big disadvantages of this approach is that a) I have to remember to add the label to every post where I use the "fullpost" span, and b) the label "more" was showing up all over the place. I partially solved the second problem by modifying the code for listing the labels so as to forcibly exclude "more", but this occasionally messed up the formatting of the labels, and also caused some weird bug in the tag cloud code that I ganked.

So on to Javascript! First, I changed the HTML for each post entry on the main page to wrap the entire post in a span tag with an id that matched the post ID. Then, I added a script that executes at the bottom of each page. The script grabs the HTML DOM element with the id matching the post ID, then searches that element for span tags, and compares the class of each to "fullpost". When it finds a match, it inserts the "Continue reading..." link. Voila! Now, any time I use the "fullpost" span, your browser figures out I did it and adds the link. Nice.

The finishing touch I added today. In many blogs, the "Continue reading..." link will not only take you to the post page, but will jump to an anchor marked right where you left off. I wanted it to do this, but not have to actually add the anchor in myself, i.e. I wanted it to somehow put the anchor in for me wherever I put the "fullpost" tag in.

It would be nice if CSS allowed you to put macros in for certain classes, i.e. if I could just tell it the text I wanted to appear whenever I had something of class "fullpost". But no dice.

So instead, since I had code to find the DOM element corresponding to "fullpost" span tag anyway, I decided to see if I could just modify the DOM tree to insert the anchor. I thought I might have to do something fancy, like instantiate an anchor element and insert it into the linked list. But I forgot that Javascript is, well, a scripting language and not a programming language, so they try to make it intuitive. And sure enough:

spanElem.innerHTML = "<a name='more'/>" + spanElem.innerHTML;

Yep, that was it. Just access the damn HTML and prepend a string to it. Too simple!

After I wrote that, it occurred to me that I wasn't sure if the Javascript would execute before or after the browser searched for the anchor tag, but it seems to work in Chrome. Ping me if it fails in any other browsers.

The only thing that worries me is that it's doing a lot of important formatting on the client side... it seems like putting anchors and links in place ought to be done server side, or at least that's what it feels like to a non-web developer like me. The pages still seem to load pretty fast, though, and it's not like you can view Blogger without Javascript support anyway, so I think it's okay. Hmm, maybe when I said "ping me if it fails in any other browsers," I should have mandated that they be, you know, modern browsers. I just know somebody is going to be like, "It doesn't work in Links!"


  1. i came over looking for a post about that scary thing you found in Orac's basement and all i get is perl scripts for wives livejournal blogs!