(function($){
    $.fn.extend({
        timesince: function(options) {
            var options = $.extend(timesince_defaults, options);
            
            this.each(function() {
                var obj = $(this);
                var date = new Date(obj.attr('title'));

                var now = options.now;
                var delta, since = 0;

                // If date from title is invaild try the text of the object
                if (!date.isVaild()) {
                    date = new Date(obj.text());
                }
                // If date is still invaild bail now quietly and do nothing
                if (!date.isVaild()) {
                    return;
                }
                // Make sure now is a vaild date if not reassign to now
                if (!now.isVaild()) {
                    now = new Date();
                }
            
                obj.text([timesince(date, now, options[options.defaultNames]), options.appendText].join(' '));
                if (options.updateSeconds) {
                    setInterval(function() {
                        now = new Date();
                        obj.text([timesince(date, now, options[options.defaultNames]), options.appendText].join(' '));
                    }, options.updateSeconds * 1000)
                }
            });
        },
        timeuntil: function(options) {
            var defaults = timesince_defaults;
            defaults.appendText = 'until';
            var options = $.extend(defaults, options);
            
            this.each(function() {
                var obj = $(this);
                var date = new Date(obj.attr('title'));
                var now = options.now;
                var delta, since = 0;
                
                // If date from title is invaild try the text of the object
                if (!date.isVaild()) {
                    date = new Date(obj.text());
                }
                // If date is still invaild bail now quietly and do nothing
                if (!date.isVaild()) {
                    return;
                }
                // Make sure now is a vaild date if not reassign to now
                if (!now.isVaild()) {
                    now = new Date();
                }
                
                obj.text([timesince(now, date, options[options.defaultNames]), options.appendText].join(' '));
                if (options.updateSeconds) {
                    setInterval(function() {
                        now = new Date();
                        obj.text([timesince(now, date, options[options.defaultNames]),  options.appendText].join(' '));
                    }, options.updateSeconds * 1000)
                }
            });
        }
    });

    var timesince_defaults = {
        now: new Date(),
        friendlyNames: ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'],
        friendlyNamesShort: ['yr', 'mon', 'wk', 'day', 'hr', 'min', 'sec'],
        defaultNames: 'friendlyNames',
        appendText: 'ago',
        updateSeconds: false
    };

    // Utility functions for timesince
    function timesince(date, now, friendlyNames) {
        var chunks = [
            1000 * 60 * 60 * 24 * 365,
            1000 * 60 * 60 * 24 * 30,
            1000 * 60 * 60 * 24 * 7,
            1000 * 60 * 60 * 24,
            1000 * 60 * 60,
            1000 * 60,
            1000
        ];

        since = now - date;
        if (since <= 0) {
            // If date is in the future compared to now, stop processing
            return ['0 ', friendlyNames.slice(-1), pluralize(0)].join('');
        }

        for (i in chunks) {
            i = parseInt(i, 10);
            count = Math.floor(since / chunks[i]);
            if (count != 0) {
                break;
            }
        }
        s = [count, ' ', friendlyNames[i], pluralize(count)];
        if (i + 1 < chunks.length) {
            // Now get second time
            count2 = Math.floor((since - (chunks[i] * count)) / chunks[i + 1]);
            if (count2 != 0) {
                s = s.concat([', ', count2, ' ', friendlyNames[i + 1], pluralize(count2)]);
            }
        }
        return s.join('');
    };

    function pluralize(count) {
        if (count === 1) {
            return '';
        }
        return 's';
    };

    // Here we are prototyping the Date object to 
    // add some utility functions used by timesince
    Date.prototype.isVaild = function() {
        return (this != 'Invalid Date' && this != 'NaN');
    };

})(jQuery);

