2005-11-27

Magic Del.icio.us JSON feeds

Blogger really doesn't lend itself well to deeper discussion through comments. It's one thing keeping the ball rolling when maintaining cross-blog trackback style dialogues; that works, but relies on both parties keeping a blog. The Blogger comment system is too week for this purpose, in that it

  • does not thread comments.
  • does not offer RSS comment feeds.
  • does not offer any quoting aids.
  • enforces severe limitations on markup.

Of course it's possible to converse without either of the above, but it makes for a rather bad reader experience, and that is one of the things the web medium is usually particularly good at, so I'll try to avoid as much of these pains as I can and hopefully improve the reader value of this conversation. Hence this post; I'm trying out my options. In most relevant aspects, this is more of an open mail (after a lead recap) or news posting, adding one thought set to the mix to move one step ahead in a discussion; we are really only discussing things to do and good ways to do them, so far.

And for those who have not followed this discussion thread at Freshblog, where Greg Hill has posted a good tutorial on how to add Del.icio.us tag navigation to a blog (and not only Blogger blog, I might add), here is an executive summary of what has happened so far in the story:
  1. Greg Hill makes a really spiffy (and very tastefully template integrated, I might add) category navigation system for some of his blogs (Vent, Speccy). It's based on Del.icio.us tags and the Del.icio.us JSON feeds, which enable any web site anywhere to programmatically integrate public tagged bookmarks from Del.icio.us into their sites in any fashion they well please.

  2. Freshblog (which keeps the temperature on, among other things, blog tech development in general and Blogger blog tech development in particular) reports on Marc Morales having published about how he, borrowing ideas from Greg's implementation, made a Del.icio.us based category sidebar for his blog.

  3. Greg, further inspired by Marc's efforts, takes the concept a bit further still, and stops by John at Freshblog to show and explain his improvements in a comment to the prior announcement post.

  4. I stumble by, suggesting Greg would start a tech blog, and then quickly dig into Greg's code to play with a few ideas and get a feel for what I would like to do myself.

  5. Attention strays when I start researching making some base post tagging tools I miss, before being comfortable with this system. (I take some pride in affording myself to enjoy being a rather lazy person, by way of making support tools.)

  6. Meanwhile, Greg types up his tutorial (for Freshblog) on not only adding above mentioned category navigation, but also lots of other nifty side effects, such as integrating this tag navigation panel with google searches or previous tags based navigation on a referring blog, and other goodies.

  7. I play a bit with the code to end up with this blog's tag navigation, and we dive into the above mentioned comment feedback race in the tutorial post, trying to forward the ideas that invariably creep up when creative minds meet around prospects. Worth mentioning is that Stephen of Singpolyma tosses in some idea material and thoughts to the mix as well; this isn't really a dialogue, but more of an eager chattering around a good chunk of geek candy. (We thrive on this kind of thing.)

  8. This post picks up the ball where we leave off, after another round of Greg's much appreciated query firing. If you made it this far without strying off picking up bits of the story elsewhere, you don't really need to, either; I'm quoting the bits I address. Those I leave (mailing list style) are aspects I have little insight into, but which others would be very welcome to pick up on in posts of their own. I believe you could trackback to Freshblog, or just link back here or to Greg's future tech blog (here's hoping... ;-). Leaving comments is of course very welcome too, but dig in any deeper, and you will probably find it about as lacking as I do.

  9. My last post suggested splicing in entire posts by means of AJAX into a list of posts you bring forth by choosing a tag might be both somewhat difficult to achieve (albeit doable), and be less robust than relying on time tested hypertext links, in response to some discussion between Greg and Stephen about inlining more post markup than Stephen presently does.

Johan, you don't need to feel ashamed for playing around with this sort of stuff for the fun of it - that is the essence of hackery!

My concern isn't really about the hackery of it, but rather about having a stable and reader friendly environment for "idea exchange" kind of posts, and a somewhat wilder playground for showing off and testing the forefront of browser technology, pushing at the limits of what javascript and modern browsers can do at any one time. I might evolve towards something like that, over time. (But now I'm on far out on a tangent.)

RE: AJAX. I'm only dimly aware of this, but I think anything that makes for a better user experience is good. But I do think there's a lot of value to be had in getting the most out of URLs, referrers etc (and also seeing them in your logs), so it's probably a trade-off between convenience for users and anticipating what they want.

Improving the user experience is always a good thing. Maybe this is just one of these things that needs playing with before one can tell what constitutes improvement, and what just adds additional network traffic and surprising (generally risky business in user interface design) results. Worth considering is also bookmark semantics, and how the user perceives what constitutes the present page. Click a permalink, and the page you get to is good for bookmarking. Unfold an article or a few, and the same might not hold.

Much can be done by pouring in work to address issues like this, if one sets one's mind to it, but it's also always worth questioning your reasoning first. Doing it for fun? Go ahead. If we do it for the user, though, we might actually end up making it harder for real world users even though we meet the demands of our perceived user.

Re: prompt. The earlier version of this did have a "select category" or similar default statement. Maybe that should return in the next release (as a parameter).

Re: Fade Anything Technique. I like! It certainly emphasises that a change has been made. Again, a parameter to select the highlight colour should be passed.

Request for Advice: when doing this kind of scripting, am I better off passing parameters to the js functions (like the height/width info), or is it better code to use global variables (like del_user and anchor)?

I figure that the number of parameters/options will grow, and I'd like some guidelines on making them global variables or passed parameters or something else.

When dealing with a multitude of parameters, all optional, I tend to toss them up into one big parameter object, which is good because you can read the function call and see what each parameter does, without having to peek at the docs for or code of the function itself, unless you want to add something not already there. An example:
write_tags( { header:"Pick a category:", add_link:false,
onclick_cb:goto, tag_name_cb:expand_camelcase } );

function goto( delitag, option )
{
if( delitag && delitag.u )
location = delitag.u;
}

function expand_camelcase( tagname, option )
{
tagname = tagname.replace( /([A-Z0-9])([^A-Z0-9]+)/g, split_words );
return tagname.substr( 1 ).toLowerCase();
}

function split_words( match )
{
return ' ' + match[0];
}


My thinking is that the above would write out the normal search field, not write the blogger category link afterward (it might be something the user would want to control herself using some callback invoked when the select box changes focus, for instamce), and that tags would be listed differently in the select box than their names ("BlogTech" being shown as "blog tech", for instance).

Passing the relevant Delicios.tags and the <option> node to these callbacks, when given, might not be an ideal API, but it seems useful. I suppose I ought to peek through your present code, and toy with it as I did with the other one. It was fun too, after all. :-)

You might want to add the option of removing the anchor tag from the list, too; I find it a bit useless, at least in my own blog.

Lastly, here's a thought I had about category integration across blogs: "tag piggybacking". Ie the publisher can select another delicious account (in addition to their own) to augment their tagspace (and hence posts).

Why? Well, when you're just starting out, it might communicate to your readers how you're positioning your content, before you've had a chance to build it up.

Or maybe you'd want to include the tags (or just a subset via anchor tags) of a prominent blogger, work colleague or even a group/shared blog of which you're a member.

It could also be used transiently ie you could have "guest tags" on your blog for a week, to help cross-promote a friend or collaborator. (Perhaps on exchange.)

Certainly. You don't really need to limit the scope to Del.icio.us links being used as category navigation of blogs, even though this serves your above mentioned purposes rather nicely. Instead, I would suggest thinking of this as of syndication in a more generic sense. Consider a JSON bookmark feed another flavour of RSS feed, and the menu opens up additional possibilities.

I'm planning on eventually picking up my feed of CommentBlogging bookmarks with the anchor tags , to which I (try to) add every blog comment I write (mycomments) in blogs across the world, which I would not mind also publishing in some panel on my blog (public) and are written in English (eng). I also have a tag for comments I write in Swedish, my native language, which might end up published on my "real life" blog, in said language. For posts I make in response to incoming feedback to my own blog posts, I only occasionally tag a Del.icio.us bookmark, when I feel that the comment would be worth reading for its contents and by a wide public, then using the tag .

This is scheduled to happen when I have figured out how I want it to look. :-) (Things often stall there for me; I don't do much web design per se, even though I take much pleasure from tweaking something half there to become better or more to my taste.)

I guess this would come in two flavours: fully integrated (their tags are entirely rolled in with yours, producing a virtual "super user"); and demarcated (separate lists, counts, labels etc).

While it may drive traffic away from your site, it could also make your blog richer and more useful hence gain you traffic. And, much like linking/blogrolls etc, what comes around goes around.

If it adds value to your blog and is relevant to your reader base, it's of course a Good (possibly Great) Thing; I won't challenge that. For blogs using the Del.icio.us for categories and/or other navigation, this could even be used to make really good interactively foldable blog rolls, where you could catch a quick glimpse on what goes on in your list of recommended reading without even leaving your own site.

If that was vague, let me clarify, picking up on other plans of mine for this site. I will eventually join the crowd and add some kind of peer links, suggesting other good reading on subjects I address, linking Freshblog, Singpolyma, Browservulsel and a few others too for the mutual benefit of tying in relevant content, knitting a tighter community, and all the other beneficial things about useful blog rolls. In linking to Freshblog, which uses Del.icio.us tags, I could add a little "unfold +" before the name, which might pop up a tree of a few recent post titles, show a tag cloud for Freshblogs' ten or twenty most frequently used tags, or similar, to convey an instant feel of whether the reader would find it worth her while going there for a browse, or perhaps subscription, while at it.

Another thing we can do with both our own and the tags from our syndicated peers, is to provide (link only, or abridged version at best, so far) RSS feeds by category. People coming to my own blog only because they are interested in what development I do on Book Burro could subscribe to the Del.icio.us provided RSS feed covering my bookmark category ecmanaut+BookBurro, which could be made available with a quick sleight of hand directly from the tags browser, similar to the Del.icio.us link we already have.

We have only started tapping the possibilities opened up by JSON feeds yet; there is so much that can be done, and Del.icio.us leverages this even further than any other, more conventional site would. We have much work and play to do here. :-)

(Oh, did I already suggest your starting your own tech blog, by the way, Greg? ;-)

2005-11-26

Simplicity rules. Trust matters.

Technology does not mandate complexity. Many really complex problems have fairly simple solutions, when applying technology to solve them. Most don't, however, and as there is no upper bounds on the complexity of conceivable problems to address, there is also no upper bound on the complexity of technology. Users of technology do not scale along the same principles, though, and we need to recognize this when designing tools made to solve a problem. If the tool seems too complex to grasp, the tool fails, even if it could have solved problem, given a better user.

The beauty of simplicity, an article in the November issue of Fast Company Magazine, addresses this problem, relating it to the success of Google, and changes Philips did to make usability one of the company's strengths on the market. Doing just enough, and no more, is driven as the recipe for success, and I'm willing to agree.

The Google front page minimalism keeps things appearing simple. It does not strain our limited supply of attention by introducing ads (a sure longevity mark of quality, by the way) nor any visible tools, except those used frequently enough to mandate being there. Additions do not enter lightly. While Google is a massive Swiss Army Knife under the hood, the front page does not expose every last tool to pick out the eyes of the majority of visitors who came by for something else. This is the "mostly closed" approach to the army knife, as compared to the "mostly open" approach offered by many competitors not keeping their front porch quite as tidy as Google's.

I find this a very compelling trade-off, but what makes me even happier is when there are hidden entrances everywhere to the tools underneath, for those of us mastering a larger subset of the knife than the parts visibly exposed. Without compromising with the needs and comfort of the many. I'm talking about things like query languages that allow me to look for pages across the web, where "ecmanaut" is part of the page URL, by prefixing inurl: to the query. Or orthogonal approaches, such as moving in through different doors entirely, becoming part of the browser itself, as an even leaner search field at the top of my window, next to the browser menus. More power with a close to zero clutter footprint.

There is always a tradeoff between visual apparrency and hidden functionality, and in the above example I'm tapping into features of the Google advanced search page a mere mortal would rarely look for. The use case isn't large enough to warrant the clutter on the front page, or indeed even showing up on the advanced search page prior to a closer study and clicking-through of that page's select boxes, for that matter. This is good design, when you offer a wide feature set.

What is often even better, is offering a really small feature set, as does the iPod, for instance. It plays music. But in cases where the interface burden is zero or close enough to it, even the iPod goes beyond the trivial wide masses use case, and lets you store other random files on it when you plug it into your computer, as if it was your run of the mill memory stick.

It's important to get a good eye for when it is right and when it is wrong to add a new feature or whole feature set to a tool, product or application. We always walk a thin line between overladen user interfaces and not evolving to match the needs of niches and use cases we could address, when looking forward as visionaries to doing more than we do today.

There are many ways of evading the sharp edges that stick out when we add one more feature to the concoction; I'm quite fond of the self configuring Greasemonkey user script addition I recently did for my publish pinger tool, for instance. It adds buttons for sites you use to send notifications to feed readers and aggregators, but not any others. Silently growing added functionality in only the places where the user is mentally prepared for it, and would obviously benefit from it. And at no configuration cost, either.

This is exactly what makes Google move into the browser toolbar business with such decisive force -- the conventional web adds barriers between sites, and will only let you adapt to user net-wide behaviour through the small looking glass of seeing which page she came from to you, whereas a browser extension, or Greasemonkey user script, sees a larger picture, and thus is able to better leverage functionality fit for the specific needs of the user. Without spilling in to the browsing experience or comfort of any other user.

Living in concert with the browser is both a very large opportunity to do good things and an equally large responsibility not to do bad things. You need lots of trust from your user base to pull it off, and you have to earn that trust. Microsoft tried taking some first steps towards tapping into the possibilities that open up when you start looking at the web as being open to invasive composition, in introducing the concept of Smart Tags with the coming of Windows XP. This would have enabled them or third parties tying into the system to add content or functionality to any page on the web (such as adding context menus to certain links or words), much as the opt-in browser extension Greasemonkey does in Mozilla today. It received some really horrible press, and the public uproar made them abandon the project.

Google have so far not made any visible moves toward this, but has eagerly started to move into as many different fields as can possibly be fit under their corporate mission and vision statements (which is a lot); there is fortunately much on offer that could stand better organization and ease of access. They are wise to be cautious and equally wise to have and value their corporate motto of not being evil. Microsoft did not pull this through, I believe, mostly attributed to their lack of user base trust. I trust Microsoft to do what Microsoft believes they would gain wealth from, but I do not trust Microsoft to value my integrity above their own moneymaking opportunities.

Of course, Google is also a company interested in turning a profit, but they have done a much better job of maintaining at least the image of trustworthiness, when it comes to being trusted to value your interests. Not all feel the way I do about that, of course; there are many who will not touch Google Mail, for not wanting to place their pricacy in Google's hands. The same probably applies to your browsing, and running Google's toolbars, though there has not been nearly as much publicity on that topic yet. This would most likely change, in the event of Google's starting to visibly tap on the added powers of the browser extension.

Greasemonkey is a very interesting middle ground, here, in being a grassroots sort of movement, where any javascript programmer with wits can make customized scripts to do mostly anything to any page or pages on the web. It can tie together information not only from one site from within the browser (as the common web is sandboxed to), but also opens up the possibility of pulling in related content from anywhere else. Book burro does this, showing book prices from other stores when visiting one store (Amazon, for instance) to let you compare and pick a more favourable price elsewhere, without having to do the detective work yourself.

The great strength in Greasemonkey is not only in the technology it offers, but also in the way that it does it -- each of these custom scripts requires the user to opt in, a procedure that is also very easy to do, once the extension is installed. You right click a user script, and pick "Install User Script", which is the first option of the menu for only this particular type of script. No annoying clutter footprint anywhere else, the gate to a whole different layer of web on top of the common web, and an immense opportunity to do new interesting applications that completely dwarf today's "web site mashups".

Greasemonkey is still in its cradle, as a phenomenon. It's very stable and rather mature development wise, and the user interface is getting better with every new release. You are still considered exceptionally front line if you have more than, say, thirty user scripts installed, though; we have about the same situation with User Scripts today as we did with browser bookmarks when the first Internet Explorer was designed, and the Favourites menu was deemed a suitable way of organizing them. (It works marvels for a handful, but loses usability steeply as you aproce dozens.)

Greasemonkey doesn't require you to invoke the scripts you install when visiting pages, though, so while its interface still has tricks to learn and sharp corners to shave down, the impact of scaling up the user interface is still quite low. I'm doing my share to help overcome these issues just by pointing at them, and the criticism is very well received. Development in the open when it works at its best.

On my own front, I'm eagerly pondering the possibilities of the Greasemonkey web-on-top-of-the-web, and have already done my own fair share of further Book Burro development to start the idea flow.

I hold a firm belief in the interconnectedness of all things. Greasemonkey, and other tools like it (Reify Turnabout adds the same kind of functionality to Internet Explorer, and the Opera browser has user scripts natively, though without the ease of installation Greasemonkey offers), empowers us to further this growth exponentially. Anybody anywhere can write the next big thing in site interoperability with no budget to start off from, and without the explicit help of the sites and parties involved. It can be shared with the rest of the world, just as easily and at the same lack of distribution cost -- it's just the matter of spreading the word. Publishing it at userscripts.org does much of that too, in a user scripts community that still has not grown very large.

Just never forget that to do and be any good, you must remember these two aspects: simplicity and trust. Toss either, and you will never take off, nor deserve to. Pay attention to both and strike a chord, finding just the right application, and you will most likely also strike gold. Today I do this for the sheer fun of it; I can not anticipate where this will lead to in a year, or two, or ten. It will probably be an interesting journey, though. And all who want to have front row tickets, too.

2005-11-20

Blogger previous and next date links

It's time for my much sought after post on setting up a Blogger blog with Previous and Next date links. This is one way of doing it, it is not exactly the way my blog does it. (This code is much cleaner, and only does this particular thing. Hence you won't need huge racks of code that probably just break browsers I have not done enough testing with.)

Caveats

Before we begin, note that the present state of the Blogger tags are rather limiting and don't yet make it possible to actually solve this problem all out for post pages, without additional labour on your part (such as manualy maintaining the Previos and Next links in each post's text body -- doable though painful) -- the solution given below gives previous and next date links for your archive pages, not Previous / Next post pages. (They can of course be put on item pages too, but on a busy blogging day with three items posted, both the Previous and Next links will skip over a note or two, jumping straight to the nearest date in either direction.) There, you have been warned, now let's get to it!

Prerequisites

You must make sure your Blogger settings are right, since this method requires you to archive by day. Also, you have to republish the entire blog every time you add a post on a new date, or there will not be any "Next date" links for the days running up to the last post. This is because this solution is based on scanning the Archives secion of your blog for links, comparing the dates between the links available there and the dates of the post visited, to pick out the next and previous dates for the two navigation links. Crude, yet effective. This requires reposting the entire blog, because Blogger would not otherwise add today's archive page link to yesterday's archives section. (And it's either update all posts or just today's. Too bad for us; we'll just have to wait for the extra needless processing of our blog history.) One more thing: by implication, to manage to parse these dates, both your date header and archive date formats must contain all of year, month and date. "Sunday", or "November 20" is not enough.

Anyway, find the Archive settings, and set the Archive Frequency to "Daily":

Settings -> Archive -> Archive frequency: Daily


Then it's time to muck about with the template. Pick the Template tab in the view above, mark all contents, copy them and save them in a file somewhere safe. There is no undo button anywhere in sight, so you want to provide you the option, in case you're not satisfied with the results, or something goes very wrong. (I won't be able to help you, so you had better help yourself. I'm only going to say this once.)

Now seek out the <BlogDateHeader> tag; it should be somewhere in the <body> tag, and you might have to scroll through lots and lots of stylesheets before you find it. Mine looked like this before editing:

<BlogDateHeader>
<div class="DateHeader"><$BlogDateHeaderDate$></div>
</BlogDateHeader>

and we will change it a bit to toss in some additions, marked in bold face below:

<BlogDateHeader>
<div style="float:left; visibility:hidden; padding:0.5em;"
id="prev-date"><a accesskey="P">« Previous Date «</a>
</div>
<div style="float:right; visibility:hidden; padding:0.5em;"
id="next-date"><a accesskey="N">» Next Date »</a>
</div>

<div id="seen-date" class="DateHeader"><$BlogDateHeaderDate$></div>
</BlogDateHeader>

The style attributes and order of these tags isn't important; it's just a sketchy way of making them show up somewhat naturally in the page flow; your own styling should hopefully look a little better, to blend in better with the rest of your blog template. If you so prefer, you may skip the added divs and tuck the id and style attributes on to the <a> tags themselves, instead. The accesskey attributes give nice shortcuts to these links; Alt + P/N in most environments, Shift + Escape followed by P or N in Opera. In other Mac browsers, it's Control + P/N.

This adds the basis for where the navigation will end up. As you see, they will be hidden by default -- the mentioned script code will turn them on, when there is something to be clicked for each link. If you always want both links visible, even when unclickable, delete the "visibility:hidden;" part. Should you want just the key bindings, but not the visible links, substitute with "display:none;" instead.

Next, we must make sure the archive section is adressable by the script. Seek out (or add anew, if there is none) the <BloggerArchives> tag. Mine looked like this:

<BloggerArchives>
<a href="<$BlogArchiveURL$>"><$BlogArchiveName$></a> /
</BloggerArchives>

and after our modifications, it will look like this:

<div id="archive"><BloggerArchives>
<a href="<$BlogArchiveURL$>"><$BlogArchiveName$></a> /
</BloggerArchives></div>

The only bit that matters about it is that there is the <a> tag above somewhere in it and that it is surrounded by a tag with the id="archive" attribute. Below this we will add the big portion of code that does the links. Optionally, you may stow all but the leading makePrevNextLinks() line away somewhere up in your <head> tag, if you prefer. Just make sure that the makePrevNextLinks() line appears in the page flow below the above sections, and things will run just fine.

The final blow is the large chunk of code below. Tune it to use your date format (you may want to open up the Settings / Formatting page in a different browser window to find out what date format settings you use, in case you forgot. The two select boxes below need to use the same format (though they won't tell the same date, unless you are one of the few readers who happen to catch this note today, the same day I wrote it), or the code will not work. When you pick a date format, the first two lines of the code below changes accordingly; should you later feel you want to use some other date format, come back here and readjust them to see what values to put there.

Add the huge <script> block -- if you just want it on the archive pages where it always works reliably, encase it in an <ArchivePage>...</ArchivePage> container, and it won't appear on the main page and item pages. If your choice of Date Language isn't English and either of your date formats includes a month name, also be sure to edit the monthNames variable to read exactly (case matters!) how month names are listed in your language of choice, separated by the vertical bar character. Once you are done, save the template and republish your blog, and voilà -- Previous and Next buttons!

Settings -> Formatting -> Date formats



<script type="text/javascript">
var monthNames = 'January|February|March|April|May|June|July|' +
'August|September|October|November|December';
var dateHeaderParser = getDateHeaderParser( 1 );
var archiveParser = getArchiveParser( 9 );
makePrevNextLinks(); // Don't change anything from here onwards:

function makePrevNextLinks()
{
var prevNode = document.getElementById( 'prev-date' );
var seenNode = document.getElementById( 'seen-date' ), seen;
var nextNode = document.getElementById( 'next-date' );
if( !seenNode ) return alert( 'No tag with id="seen-date" found!' );
if( !(seen = dateHeaderParser( seenNode.innerHTML )) )
return alert( 'Failed to parse date "'+ seenNode.innerHTML +
'"; did you really set the right format?' );
seen = seen.getTime();
var all = getDates(), prev, next;
for( next in all )
if( next == seen )
{
next = null;
continue;
}
else if( next > seen )
break;
else if( next < seen )
{
prev = next;
next = null;
}
if( prev && prevNode )
{
if( prevNode.noveName != 'A' )
prevNode = prevNode.getElementsByTagName( 'a' ).item( 0 );
prevNode.href = all[prev];
prevNode.style.visibility = 'visible';
}
if( next && nextNode )
{
if( nextNode.noveName != 'A' )
nextNode = nextNode.getElementsByTagName( 'a' ).item( 0 );
nextNode.href = all[next];
nextNode.style.visibility = 'visible';
}
}

function getDates()
{
var ar = document.getElementById( 'archive' );
if( ar )
{
var all = ar.getElementsByTagName( 'a' ), dates = {}, i;
for( i=0; i<all.length; i++ )
{
var link = all[i];
var date = archiveParser( link.innerHTML );
if( date ) dates[date.getTime()] = link.href;
}
return dates;
}
}

function getArchiveParser( formatNo )
{
var fmt = { 0:'MonthName Day, Year', 1:'MonthNo/Day/Year',
2:'MonthNo\\.Day\\.Year', 3:'YearMonthNoDay',
4:'Day\\.MonthNo\\.Y2', 5:'Year-MonthNo-Day',
/*6:'MonthName Day',*/ 7:'MonthName Day, Year',
8:'Year/MonthNo/Day', 9:'MonthNo/Day/Y2',
10:'Y2_MonthNo_Day', 11:'Day MonthName Year',
12:'Day MonthName', 13:'Day/MonthNo/Year',
14:'Day/MonthNo/Y2' }[ formatNo ];
return fmt ? getDateParser( fmt ) : alert( 'Archive date format type ' +
(formatNo ? formatNo + ' not supported! (Year needed?)' : ' not given!') );
}

function getDateHeaderParser( formatNo )
{
var fmt = { 1:'MonthName Day, Year', 2:'MonthNo/Day/Year',
3:'MonthNo\\.Day\\.Year', 4:'YearMonthNoDay',
14:'Year/MonthNo/Day', 6:'Year-MonthNo-Day',
5:'Day\\.MonthNo\\.Y2', 7:'MonthNo\\.Day\\.Year',
/*8:'Weekday', 10:'MonthName Day',*/
12:'MonthName Day, Year', 18:'Day MonthName Year',
23:'Day MonthName, Year' }[ formatNo ];

return fmt ? getDateParser( fmt ) : alert( 'Date header format type ' +
(formatNo ? formatNo + ' not supported! (Year needed?)' : ' not given!') );
}

function sortNumeric( a, b )
{
return parseInt( a ) - parseInt( b );
}

function findInArray( array, value )
{
for( var i=0; i<array.length; i++ )
if( array[i] == value ) return i;
return -1;
}

function getDateParser( format )
{
var what = { Year:'\\d{4}', Y2:'\\d{2}', MonthName:monthNames,
MonthNo:'\\d{1,2}', Day:'\\d{1,2}' };
var re = format, monthNo = {}, where = {}, order = [], tmp, i, type;
for( i=0, tmp = what.MonthName.split( '|' ); i<tmp.length; i++ )
monthNo[tmp[i]] = i;
for( type in what ) // for each match type,
if( (tmp = format.indexOf( type )) >= 0 ) // if used in this format,
{
where[tmp] = type; // store away its match position, to find out
order[order.length] = parseInt( tmp ); // which paren matched what
re = re.replace( type, '('+ what[type] +')' ); // and fix the regexp
}
for( i=0, order = order.sort( sortNumeric ); i<order.length; i++ )
order[i] = where[order[i]]; // map back to mnemonics, again
var getYear = function( match )
{
var where = findInArray( order, 'Year' ) + 1;
if( where ) return parseInt( match[where], 10 );
where = findInArray( order, 'Y2' ) + 1;
if( !where ) return (new Date).getFullYear();
var year = parseInt( match[where], 10 ) + 1900;
if( year < 1990 ) year += 100;
return year;
};
var getMonth = function( match )
{
var where = findInArray( order, 'MonthName' ) + 1;
if( where ) return monthNo[ match[where] ];
where = findInArray( order, 'MonthNo' ) + 1;
if( where ) return parseInt( match[where], 10 ) - 1;
return (new Date).getMonth();
};
var getDate = function( match )
{
var where = findInArray( order, 'Day' ) + 1;
if( where ) return parseInt( match[where], 10 );
return (new Date).getDate();
};
re = new RegExp( '\\b'+ re +'\\b', 'i' ); // make it a real RegExp
return function( raw )
{
var match = re.exec( raw );
if( match )
return new Date( getYear(match), getMonth(match), getDate(match) );
};
}
</script>