;(function($){
   $.formatDate = function(date,pattern) {
      var result = [];
      while (pattern.length > 0) {
         $.formatDate.patternParts.lastIndex = 0;
         var matched = $.formatDate.patternParts.exec(pattern);
         if (matched) {
            result.push(
               $.formatDate.patternValue[matched[0]].call(this,date)
            );
            pattern = pattern.slice(matched[0].length);
         } else {
            result.push(pattern.charAt(0));
            pattern = pattern.slice(1);
         }
      }
      return result.join('');
   };

   $.formatDate.patternParts = /^(yy(yy)?|M(M(M(M)?)?)?|d(d)?|EEE(E)?|a|H(H)?|h(h)?|m(m)?|s(s)?|S)/;
   $.formatDate.monthNames = [ 'January','February','March','April','May','June','July', 'August','September','October','November','December' ];
   $.formatDate.dayNames = [ 'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday', 'Saturday' ];
   $.formatDate.patternValue = {
      yy: function(date) {
         return $.toFixedWidth(date.getFullYear(),2);
      },
      yyyy: function(date) {
         return date.getFullYear().toString();
      },
      MMMM: function(date) {
         return $.formatDate.monthNames[date.getMonth()];
      },
      MMM: function(date) {
         return $.formatDate.monthNames[date.getMonth()].substr(0,3);
      },
      MM: function(date) {
         return $.toFixedWidth(date.getMonth() + 1,2);
      },
      M: function(date) {
         return date.getMonth()+1;
      },
      dd: function(date) {
         return $.toFixedWidth(date.getDate(),2);
      },
      d: function(date) {
         return date.getDate();
      },
      EEEE: function(date) {
         return $.formatDate.dayNames[date.getDay()];
      },
      EEE: function(date) {
         return $.formatDate.dayNames[date.getDay()].substr(0,3);
      },
      HH: function(date) {
         return $.toFixedWidth(date.getHours(),2);
      },
      H: function(date) {
         return date.getHours();
      },
      hh: function(date) {
         var hours = date.getHours();
         return $.toFixedWidth(hours > 12 ? hours - 12 : hours,2);
      },
      h: function(date) {
         return date.getHours() % 12;
      },
      mm: function(date) {
         return $.toFixedWidth(date.getMinutes(),2);
      },
      m: function(date) {
         return date.getMinutes();
      },
      ss: function(date) {
         return $.toFixedWidth(date.getSeconds(),2);
      },
      s: function(date) {
         return date.getSeconds();
      },
      S: function(date) {
         return $.toFixedWidth(date.getMilliseconds(),3);
      },
      a: function(date) {
         return date.getHours() < 12 ? 'AM' : 'PM';
      }
   };

   $.toFixedWidth = function(value,length,fill) {
      var result = value.toString();
      if (!fill) fill = '0';
      var padding = length - result.length;

      if (padding < 0) {
         result = result.substr(-padding);
      } else {

      for (var n = 0; n < padding; n++)
         result = fill + result;
      }

      return result;
   };

   $.timeAgoInWords = function(date) {
      currentDate       = new Date().getTime();
      fromDate          = date.getTime();
      oneDay            = 60*1000
      distanceInMinutes = Math.round((Math.abs(currentDate - fromDate))/oneDay);
      distanceInSeconds = Math.round(Math.abs(currentDate - fromDate));
      // return distanceInMinutes;
      
      switch(true) {
         case isInRange(distanceInMinutes, 0, 1):
            return "less than a minute"
            break;
         case isInRange(distanceInMinutes, 2, 44):
            return distanceInMinutes + ' minutes'
            break;
         case isInRange(distanceInMinutes, 45, 89):
            return 'about 1 hour'
            break;
         case isInRange(distanceInMinutes, 90, 1439):
            return Math.round(distanceInMinutes / 60) + ' hours'
            break;
         case isInRange(distanceInMinutes, 1440, 2879):
            return '1 day'
            break;
         case isInRange(distanceInMinutes, 2880, 43199):
            return Math.round(distanceInMinutes / 1440) + ' days'
            break;
         case isInRange(distanceInMinutes, 43200, 86399):
            return 'about a month'
            break;
         case isInRange(distanceInMinutes, 86400, 525599):
            return Math.round(distanceInMinutes / 43200) + ' months'
            break;
         case isInRange(distanceInMinutes, 525600, 1051199):
            return 'about a year'
            break;
         default:
            return Math.round(distanceInMinutes / 525600) + ' years'
      }
   };
})(jQuery);

function isInRange(value, min, max) {
   return value >= min && value <= max;
}