function twitterCallback2(twitters) {
    var statusHTML = [];
    for (var i = 0; i < twitters.length; i++) {
      var username = twitters[i].user.screen_name;
      var status = twitters[i].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
        return '<a href="' + url + '">' + url + '</a>';
      }).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
        return  reply.charAt(0) + '<a href="http://twitter.com/' + reply.substring(1) + '">' + reply.substring(1) + '</a>';
      });
      statusHTML.push('<li><span>' + status + '</span> <a style="font-size:85%" href="http://twitter.com/' + username + '/statuses/' + twitters[i].id + '">' + relative_time(twitters[i].created_at) + '</a></li>');
    }
    document.getElementById('twitter_update_list').innerHTML = statusHTML.join('');
  }

  function relative_time(time_value) {
    var values = time_value.split(" ");
    time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
    var parsed_date = Date.parse(time_value);
    var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
    var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
    delta = delta + (relative_to.getTimezoneOffset() * 60);

    if (delta < 60) {
      return 'less than a minute ago';
    } else if (delta < 120) {
      return 'about a minute ago';
    } else if (delta < (60 * 60)) {
      return (parseInt(delta / 60)).toString() + ' minutes ago';
    } else if (delta < (120 * 60)) {
      return 'about an hour ago';
    } else if (delta < (24 * 60 * 60)) {
      return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
    } else if (delta < (48 * 60 * 60)) {
      return '1 day ago';
    } else {
      return (parseInt(delta / 86400)).toString() + ' days ago';
    }
  }
  

(function($) {

  $.fn.twitterFeed = function(options) {
    var opts = $.extend({}, $.fn.twitterFeed.defaults, options);
    $(this).append('<ul id="twitter_update_list"><li></li></ul>');

    var pl = $('<p id="' + opts.preloaderId + '">' + opts.loaderText + '</p>');
    $("#twitter_update_list").append(pl);
    $.getScript("http://twitter.com/statuses/user_timeline/" + opts.userName + ".json?callback=twitterCallback2&count=" + opts.numTweets, function() {
      $(pl).remove();
			$('#twitter_update_list li:odd').addClass('odd');
			$('#twitter_update_list li').filter(':last').addClass('last');
      var el = $("ul#twitter_update_list");
      stopTicker(el);
      el.items = $("li", el);
      el.items.not('\':lt('+ opts.numTweetsPerSlide +')\'').hide().end();
      el.currentitem = 0;
      startTicker(el);

    });

    startTicker = function(el) {
      el.tickfn = setInterval(function() { doTick(el) }, opts.delay)
    };
    stopTicker = function(el) {
      clearInterval(el.tickfn);
    };
    doTick = function(el) {
      if (el.pause) return;
      el.pause = true;
      var toHide = $(el.items).not(':hidden').filter('\':lt('+ opts.numTweetsPerSlide +')\'');
      $(toHide).each(function() {
        var toShow = $(this).nextAll().not(':visible').filter('\':lt('+ opts.numTweetsPerSlide +')\'');
        if (toShow.length == 0) {toShow = $(el.items).filter('\':lt('+ opts.numTweetsPerSlide +')\'')}
        $(this).fadeOut("slow", function() {
          $(toShow).each(function() {$(this).fadeIn("slow")});
        });
      });
      el.pause = false;
    };

    this.each(
      function() {
        if (this.nodeName.toLowerCase() != "ul") return;
      }).addClass("twitterTicker")
  };
  $.fn.twitterFeed.defaults = {
    userName: null,
    numTweets: 5,
    delay: 4000,
    preloaderId: "preloader",
    loaderText: "Loading tweets...",
    slideIn: false,
    numTweetsPerSlide: 2
  };
})(jQuery);
