Saturday, February 13, 2010

Formatting dates in JavaScript

In most ways JavaScript is a pretty bare-bones language/development environment. Sure there are libraries like JQuery, MooTools, Dojo Toolkit etc., but there's nothing like Java's or C#'s accompanying standard libraries. So what's a programmer to do?

When you are starting out using JavaScript, probably one of the first areas where you'll feel this lack of standard libraries is in formatting dates. Every time you want to format dates into a presentable string, you have to roll your own solution. Well, given below is my solution to the problem (use the horizontal scrollbar to see the full code). JA_DateFormat(...) lets you format Date objects just by specifying a simple string. I saw the basic idea of using regular expressions to do global substitutions somewhere on the 'Net (couldn't find the reference), so I can't claim full credit for this solution:

function JA_FormatDate(dateFormat, date) {
    if (arguments.length < 1) throw new Error(1, "At least one argument required");
    if (arguments.length < 2) date = new Date(); //Default behaviour: format current time
    return dateFormat.replace(/(yyyy|yy|mmmm|mmm|mm|dddd|ddd|dd|hh|h|nn|n|ss|s|a\/?p)/gi,
        function($1) {
            switch ($1.toLowerCase()) {
                case 'yyyy': return date.getFullYear();
                case 'yy': return JA_PadNumber(date.getFullYear() % 100);
                case 'mmmm': return JA_MONTH_NAMES[date.getMonth()];
                case 'mmm': return JA_MONTH_NAMES[date.getMonth()].substr(0, 3);
                case 'mm': return JA_PadNumber(date.getMonth() + 1);
                case 'dddd': return JA_DAY_NAMES[date.getDay()];
                case 'ddd': return JA_DAY_NAMES[date.getDay()].substr(0, 3);
                case 'dd': return JA_PadNumber(date.getDate());
                case 'n': return date.getMinutes();
                case 'nn': return JA_PadNumber(date.getMinutes());
                case 's': return date.getSeconds();
                case 'ss': return JA_PadNumber(date.getSeconds());
                case 'ap':
                case 'a/p': return date.getHours() < 12 ? 'am' : 'pm';
            }
            switch ($1) {
                case 'HH': return JA_PadNumber(date.getHours());
                case 'H': return date.getHours();
                case 'hh':   return JA_PadNumber((h = date.getHours() % 12) ? h : 12);
                case 'h': return (h = date.getHours() % 12) ? h : 12;
            }
        }
    );
}

function JA_PadNumber(number, padLength, padWith) {
    var padsRequired;
    var retVal = "";
    if (arguments.length < 2) padLength = 2; //default
    if (arguments.length < 3) padWith = "0"; //default
    with (Math) {
        if (0 == number) padsRequired = padLength - 1;
        else padsRequired = padLength - floor(log(number) / log(10)) - 1;
    }
    for (var i = 0; i < padsRequired; ++i) retVal += padWith;
    return retVal += number;
}
The code (once you see it written down) is very simple, so just play with it to get a feel for it. Note that you can either call JA_FormatDate(...) with a Date object, or if you call it with just one argument it will create and use the current date. For example, calling JA_FormatDate("dd mmm yyy HH:mm:ss") will return something like 09 Feb 2010 14:35:20. Function JA_PadNumber(...) above is an assisting function that is used to 0-pad numbers (it can be use for padding with spaces or other characters too).
Bookmark and Share

Wednesday, January 27, 2010

Could it Really Be That Easy?

Hmm... my ISP's service is really bad. The connection quality sucks: it's not just that the connection is slow (expected), but that too many packets get dropped. Watching streaming video is out of the question. Downloading files larger than (say) 1 MB is possible only with wget (wget rules!) or DownThemAll (Firefox extension). Manually pausing and restarting downloads sometimes works, but who wants to do that. Both Firefox and Chrome exhibit similar behaviour. Without wget (or DownThemAll!), downloads just stall: no timeouts, no nothing. Extremely frustrating.

Since a lot of tech presentations (& fun stuff) are videos on YouTube, I wrote a small program a while back, which, given a YouTube video URL, could generate a link to a downloadable video file, on which wget goes to work. Problem solved, eh?

Then, in classical Top Gear fashion, I began wondering: How hard can it be (to write a robust downloading component)?

So Monday night I began writing some exploratory code, was traveling Tuesday, so got no work done, today I finished my proof-of-concept (POC). Turns out, if you set a small-ish value for your read timeout (30-45 seconds), and resume from where you left off (gotta know your HTTP!), its not that hard to write a reliable downloading component!

Which brings me to the question: why do so many downloading components in such major pieces of software as Firefox, Chrome, Eclipse (3.4) etc. find it so hard to download reliably in the face of a bad connection?

Of course, my HttpDownloader class is nowhere near robust enough for widespread use (yet), but the POC does show that basic reliability can be had in less than a day's work. There must be more to this than I've found out so far —there must be some very good reasons why reliable downloaders are so few and far between. It can't be that easy.

Keep checking this space (or follow me on Twitter): I will update when I know more. With the POC complete, this does go on the back-burner though.


Bookmark and Share

Wednesday, August 26, 2009

Avoid Those Ugly <a href="#"></a> Links

Usually when web developers want to use JavaScript to open a link in a new window with specific attributes (height, width etc.), they take recourse to this kind of ugly looking <a href> tags:

<a href="#" onclick="doSomething();">Do Something!</a>

There is much to dislike about this way of writing code:
  1. When you hover your mouse over the link, the result is both ugly and uninformative—all you see is a "#" in the status bar, rather than the URL to which the link will take you.
  2. When the link works, the location bar in the main window now shows a "#" appended to the parent page's link. Essentially, this has changed the apparent URL of the current (i.e., parent) page.
    • This may not seem like a big deal at first sight, but it fools the bookmarking feature (for example) into thinking that the current (i.e., the parent) page is not bookmarked (assuming it was bookmarked to begin with).
    • Similarly, the Read It Later Firefox extension gets fooled into showing that the current page is no longer marked for reading later (assuming it had been so marked to being with).
  3. If the user's browser has JavaScript disabled, the link simply won't work. There is no graceful failure of the code in such a case.
The following code solves all the aforementioned problems:
<a href="http://random-outpourings.blogspot.com/"
onclick="return doSomething(this);"
target="_blank">Do Something!</a>

In the doSomething() method, make sure you return a false as your last executed statement. For example:
function doSomething(link) {
  var newWin = window.open(link.href,'win1',
    'width=800,height=600,scrollbars=yes,resizable=yes');
  return false;
}

Take care that both the return false; at the end of the JavaScript function and the return in front of the call to doSomething(this) are present (highlighted above in red).

The benefits of opening new windows this way:
  1. When the user hovers his/her mouse over the link, the destination URL is visible in the status bar.
  2. When JavaScript is enabled in the user's browser and the user clicks on the link, it opens in a new window as desired.
  3. When JavaScript is disabled in the user's browser and the user clicks on the link, it will still work (the link will open in a new tab or window). The code fails gracefully!

Here, test this link: read something!

Hope this neat little trick helps other web developers!