function handleEvent(obj, event, func) {
    try {
        obj.addEventListener(event, func, false);
    } catch (e) {
        if (typeof eval("obj.on"+event) == "function") {
            var existing = obj['on'+event];
            obj['on'+event] = function () { existing(); func(); };
        } else {
            obj['on'+event] = func;                        
        }
    }
}   

/* PerfectTime */
    function PerfectTime() {
        /* 
            Original implimentation by Why The Lucky Stiff
            <http://whytheluckystiff.net/>, described at:
            
            http://redhanded.hobix.com/inspect/showingPerfectTime.html
            
            Modified to fit in a single, unobtrusive javascript
            class by Mike West <http://mikewest.org/>
            
            I'm not sure what the original license chosen for this
            code was.  I'm assuming it's liberal enough, and this 
            class is released under the same license, whatever that
            turns out to be.
        */
            
        var self = this;
        
        self.defaultFormat = '<nobr>%d %b %Y</nobr>'; //' at <nobr>%H:%M</nobr>';
        self.format = (arguments[0])?arguments[0]:self.defaultFormat;
        self.isoRegEx = /(\d{4})(-?(\d{2})(-?(\d{2})(T(\d{2}):?(\d{2})(:?(\d{2})([.]?(\d+))?)?(Z|(([+-])(\d{2}):?(\d{2}))?)?)?)?)?/;
        
        self.parseISO = function (isoString) {
            // Parse ISO 8601 type times (e.g. hCalendar)
            //     based on Paul Sowden's method, tweaked to match up 
            //     with 'real world' hCalendar usage:
            //
            //         http://delete.me.uk/2005/03/iso8601.html
            //

            
            var d       = isoString.match(self.isoRegEx);
            
            var theDate = new Date(d[1], 0, 1);
            
            // <month> - 1:  Because JS months are 0-11
            if (d[ 3]) { theDate.setMonth(  d[ 3] - 1); }
            if (d[ 5]) { theDate.setDate(   d[ 5]); }
            if (d[ 7]) { theDate.setHours(  d[ 7]); }
            if (d[ 8]) { theDate.setMinutes(d[ 8]); }
            if (d[10]) { theDate.setSeconds(d[10]); }
            // Must be between 0 and 999), using Paul Sowden's method: http://delete.me.uk/2005/03/iso8601.html
            if (d[12]) { theDate.setMilliseconds(Number("0." + d[12]) * 1000); }
            var offset = 0;
            if (d[15]) {
                var offset = (Number(d[16])*60 + Number(d[17])) * 60;
                if (d[15] == "+") { offset *= -1; }
            }
            
            offset -= theDate.getTimezoneOffset() * 60;
            theDate.setTime(Number(theDate) + (offset * 1000));
            return theDate;
        }
        
        self.strftime_funks = {
            zeropad: 
                    function( n ){ return n>9 ? n : '0'+n; },
            a:      function(t) { return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][t.getDay()] },
            A:      function(t) { return ['Sunday','Monday','Tuedsay','Wednesday','Thursday','Friday','Saturday'][t.getDay()] },
            b:      function(t) { return ['Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'][t.getMonth()] },
            B:      function(t) { return ['January','February','March','April','May','June', 'July','August', 'September','October','November','December'][t.getMonth()] },
            c:      function(t) { return t.toString() },
            d:      function(t) { return this.zeropad(t.getDate()) },
            H:      function(t) { return this.zeropad(t.getHours()) },
            I:      function(t) { return this.zeropad((t.getHours() + 12) % 12) },
            m:      function(t) { return this.zeropad(t.getMonth()+1) }, // month-1
            M:      function(t) { return this.zeropad(t.getMinutes()) },
            p:      function(t) { return this.H(t) < 12 ? 'AM' : 'PM'; },
            S:      function(t) { return this.zeropad(t.getSeconds()) },
            w:      function(t) { return t.getDay() }, // 0..6 == sun..sat
            y:      function(t) { return this.zeropad(this.Y(t) % 100); },
            Y:      function(t) { return t.getFullYear() },
            Z:      function(t) { 
                        if (t.getTimezoneOffset() > 0) {
                            return "-" + this.zeropad(t.getTimezoneOffset()/60) + "00";
                        } else {
                            return "+" + this.zeropad(Math.abs(t.getTimezoneOffset())/60) + "00";
                        }
                    },    
            '%':    function(t) { return '%' }
        }
        self.strftime = function (theDate, format) {
            if(format == 'time_ago_in_words') {
              format = null;
              var words = self.time_ago_in_words(theDate);
              if(words) return words;
            }

            var fmt = format ? format : self.format;
            for (var s in self.strftime_funks) {
                if (s.length == 1) {
                    fmt = fmt.replace('%' + s, self.strftime_funks[s](theDate));
                }
            }
            return fmt;
        }
        
        
        self.instantiate = function () {
            // Spans by old method
            var spans = document.getElementsByTagName('span');
            for (i=0, numSpans=spans.length; i < numSpans; i++) {
                if (spans[i].className.match(/PerfectTime/)) {
                    self.processSpan(spans[i]);
                }
            }
            
            // ABBRs by new method
            var abbrs = document.getElementsByTagName('abbr');
            for (i=0, numAbbrs=abbrs.length; i < numAbbrs; i++) {
                if (abbrs[i].className.match(/PerfectTime/)) {
                    self.processAbbr(abbrs[i]);
                }
            }
        }
        
        self.processSpan = function (theSpan) {
            var GMT = parseInt(theSpan.getAttribute('gmt_time')) * 1000;
            var fmt = theSpan.getAttribute('format');
            var newDate = new Date(GMT);
            theSpan.innerHTML = self.strftime(newDate, fmt);
        }
        
        self.processAbbr = function (theAbbr) {
            var ISOtime = theAbbr.getAttribute('title');
            var newDate = self.parseISO(ISOtime);
            theAbbr.innerHTML = self.strftime(newDate);
        }

        self.time_ago_in_words = function (from) {
         return self.distance_of_time_in_words(new Date().getTime(), from) 
        }

        self.distance_of_time_in_words = function (to, from) {
          seconds_ago = ((to  - from) / 1000);
          minutes_ago = Math.floor(seconds_ago / 60)

          if(minutes_ago == 0) { return "less than a minute ago";}
          if(minutes_ago == 1) { return "a minute ago";}
          if(minutes_ago < 45) { return minutes_ago + " minutes ago";}
          if(minutes_ago < 90) { return " about 1 hour ago";}
          hours_ago  = Math.round(minutes_ago / 60);
          if(minutes_ago < 1440) { return "about " + hours_ago + " hours ago";}
          if(minutes_ago < 2880) { return "1 day ago";}
          days_ago  = Math.round(minutes_ago / 1440);
          if(minutes_ago < 43200) { return days_ago + " days ago";}
          return false
//          if(minutes_ago < 86400) { return "about 1 month ago";}
//          months_ago  = Math.round(minutes_ago / 43200);
//          if(minutes_ago < 525960) { return months_ago + " months";}
//          if(minutes_ago < 1051920) { return "about 1 year";}
//          years_ago  = Math.round(minutes_ago / 525960);
//          return "over " + years_ago + " years" 
        }
        handleEvent(window, 'load', self.instantiate);
    }
    var timeThing = new PerfectTime(); //'%Y-%m-%d %H:%M.%S %Z');