Monday, October 15, 2007

JavaScript Date Formatter

We offten need to display date in accordance with some pattern. There is my implementaion of this task for javascript:

SimpleDateFormatter = function() {};

SimpleDateFormatter.zeroize = function (value, length) {
    if (!length) length = 2;
    value = String(value);
    for (var i = 0, zeros = '';
         i < (length - value.length); i++) {
        zeros += '0';
    }
    return zeros + value;
};
SimpleDateFormatter.getTimezome = function (date) {
    if (!date)
        return "";
    var dateStr = date + "";
    var timezoneIndex = dateStr.indexOf("GMT");
    if (timezoneIndex == -1)
        timezoneIndex = dateStr.indexOf("UTC");
    if (timezoneIndex > 0) {
        return dateStr.substring(timezoneIndex, dateStr.length);
    }
    return "";
}
SimpleDateFormatter.getFromCache = function (pattern){
    if (SimpleDateFormatter.cache && SimpleDateFormatter.cache[pattern])
        return SimpleDateFormatter.cache[pattern];
    return null;
}
SimpleDateFormatter.putToCache = function (pattern, func) {
    if (SimpleDateFormatter.cache)
        SimpleDateFormatter.cache[pattern] = func;
}
SimpleDateFormatter.formatter = function(pattern)
{
    //remove quortes to avoid function compilation problems
    pattern = pattern.replace(/'/g, "");
    pattern = pattern.replace(/\n/g,"\\n");
    var cached = SimpleDateFormatter.getFromCache(pattern);
    if (cached)
        return cached;

    pattern = "'"+pattern+"'";
    var funcSrc = pattern.replace(/(yyyy|MMMM|MMM|MM|M|EEEE|EEE|dd|d|hh|h|kk|k|mm|m|ss|s|S|z)/g,
        function($1)
        {
            switch ($1)
            {
            case 'yyyy': return "'+date.getFullYear()+'";
            case 'MMMM': return "'+SimpleDateFormatter.monthNames[date.getMonth()]+'";
            case 'MMM':  return "'+SimpleDateFormatter.monthNames[date.getMonth()].substr(0, 3)+'";
            case 'MM':   return "'+SimpleDateFormatter.zeroize(date.getMonth() + 1)+'";
            case 'M':   return "'+(date.getMonth() + 1)+'";
            case 'EEEE': return "'+SimpleDateFormatter.dayNames[date.getDay()]+'";
            case 'EEE':  return "'+SimpleDateFormatter.dayNames[date.getDay()].substr(0, 3)+'";
            case 'dd':   return "'+SimpleDateFormatter.zeroize(date.getDate())+'";
            case 'd':   return "'+date.getDate()+'";
            case 'h':   return "'+date.getHours()+'";
            case 'hh':   return "'+SimpleDateFormatter.zeroize(date.getHours())+'";
            case 'k':   return "'+((h = date.getHours() % 12) ? h : '12')+(date.getHours() < 12 ? 'am' : 'pm')+'";
            case 'kk':   return "'+SimpleDateFormatter.zeroize((h = date.getHours() % 12) ? h : '12')+(date.getHours() < 12 ? 'am' : 'pm')+'";
            case 'mm':   return "'+SimpleDateFormatter.zeroize(date.getMinutes())+'";
            case 'm':   return "'+date.getMinutes()+'";
            case 'ss':   return "'+SimpleDateFormatter.zeroize(date.getSeconds())+'";
            case 's':   return "'+date.getSeconds()+'";
            case 'S':   return "'+date.getMilliseconds()+'";
            case 'z':   return "'+SimpleDateFormatter.getTimezome(date)+'";
            }
        }
        );

    funcSrc = "return "+funcSrc;
    var func = new Function("date", funcSrc);
    SimpleDateFormatter.putToCache(pattern, func);
    return func;
}

// a global month names array
SimpleDateFormatter.monthNames = new Array(
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
        );

// a global day names array
SimpleDateFormatter.dayNames = new Array(
        'Sunday',
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday'
        );

//cache: pattern->format function
SimpleDateFormatter.cache = {};
Pattern letters
y Year 1996; 96
M Month in year July; Jul; 07
d Day in month 10
E Day in week Tuesday; Tue
H Hour in day (0-23) 0
K Hour in am/pm (0-11) 0
m Minute in hour (0-59) 30
s Second in minute (0-59) 55
S Millisecond (0-999) 978
z Time zone GMT+04:00

Usage Now you may use it:
var formatter = SimpleDateFormatter.formatter("yyyy/MMM/EEE");//formatter is a function
alert(formatter(new Date()));
We also use cache of formatter, so you could use SimpleDateFormatter.formatter("yyyy/MMM/EEE")(date) everywhere, and formatter function will be created only single time.
Year(yyyy)Month(M)Day(d)Hour(h)Minutes(m)Seconds(s)Milliseconds(S)
Pattern

Friday, August 31, 2007

How get HTML element real position

I have spent a lot of time trying get the real position of DIV element. And I have done it, but only at Firefox. It is a complex issue to find real top and left of the element, because of IE and FF returns different offsetLeft and offsetTop and this result is depends on style.position value of element.
MochiKit helped me. There is an undocumented function MochiKit.Position.positionedOffset(element) that returns what I need. This function is not documented yet, because development still working. But as they says:"If it is denied but very, very need, than it is aloowed" :)
How I use it:

divForEntries.style.left =
    MochiKit.Position.positionedOffset(this.htmlView).x +
    this.htmlView.offsetWidth -5;
divForEntries.style.top = 
    MochiKit.Position.positionedOffset(this.htmlView).y;

Thursday, August 30, 2007

MochiKit - powerfull JavaScript framework



Many of us often have a deal with implementing base functions for JavaScript. At Java we have JDK with a lot of helpfull classes. But at the case of JavaScript developers often have to invent a wheel.
But I found a great framework - MochiKit. It is a set of util classes. I will not tell about all thier capabilities, only which I have used.

1. DIV slide up and slide down.

There is the function:
function slide(elementId){
  var element = document.getElementById(elementId);
  if (element){
    if (element.slideDown){
      MochiKit.Visual.slideDown(element);
      element.slideDown = false;
    }else{
      MochiKit.Visual.slideUp(element);
      element.slideDown = true;
    }
  }
}