text: (function(root) {
      'use strict';
  
      /**
       * Constructs a new FuzzyTime instance.
       * @global
       * @class FuzzyTime
       * @author Robert Bost <bostrt at gmail dot com>
       * @license MIT
       */
      var FuzzyTime = function(){
          this.ats = [];
          this.befores = [];
          this.afters = [];
      };
  
      /**
       * Enumeration of time units supported by FuzzyTimeJS.
       * @memberOf FuzzyTime
       * @enum {number}
       * @readonly
       */
      FuzzyTime.Unit = {
          YEAR : 6,
          MONTH: 5,
          WEEK: 4,
          DAY: 3,
          HOUR: 2,
          MINUTE: 1,
          SECOND: 0
      };
  
      /**
       * Table storing conversion between IS0 8601 abbreviations, {@link FuzzyTime.Unit}s,
       * string format variables, and the time period/unit in seconds.
       * @private
       * @ignore
       */
      FuzzyTime.conversions = [
          {argument : '%y', multiplier: 31536000, unit: FuzzyTime.Unit.YEAR, abbrev: 'Y'},
          {argument : '%M', multiplier: 2592000, unit: FuzzyTime.Unit.MONTH, abbrev: 'M'},
          {argument : '%w', multiplier: 604800, unit: FuzzyTime.Unit.WEEK, abbrev: 'W'},
          {argument : '%d', multiplier: 86400, unit: FuzzyTime.Unit.DAY, abbrev: 'D'},
          {argument : '%h', multiplier: 3600, unit: FuzzyTime.Unit.HOUR, abbrev: 'h'},
          {argument : '%m', multiplier: 60, unit: FuzzyTime.Unit.MINUTE, abbrev: 'm'},
          {argument : '%s', multiplier: 1, unit: FuzzyTime.Unit.SECOND, abbrev: 's'}
      ];
  
      /**
       * Get the days in the month of the given date.
       * @param {Date} date The date that you want to count the days in the month of.
       * @returns {number} days in the month in the given date
       * @private
       */
      FuzzyTime.getDaysInMonth = function(date) {
          switch(date.getMonth()) {
              case 0:
              case 2:
              case 4:
              case 6:
              case 7:
              case 9:
              case 11:
                  return 31;
              case 3:
              case 5:
              case 8:
              case 10:
                  return 30;
              case 1:
                  return isLeapYear(date) ? 29 : 28;
          }
      };
  
      /**
       * Check if the given date is a leap year.
       * @private
       * @param {Date} date The date that you want to check for a leap year.
       * @returns {boolean} True if date is a leap year. False otherwise.
       */
       FuzzyTime.isLeapYear = function(date) {
          var year = date.getFullYear();
          if (year % 400 === 0) {
              return true;
          } else if (year % 100 === 0) {
              return false;
          } else if (year % 4 === 0) {
              return true;
          } else {
              return false;
          }
      };
  
      /**
       * Get the {@link FuzzyTime.Unit} enum of a given ISO 8601 duration unit.
       * @memberOf FuzzyTime
       * @private
       * @param {string} string The ISO 8601 Duration unit (e.g. "Y", "M", "S", etc)
       * {@link FuzzyTime.Unit} of.
       * @returns {FuzzyTime.Unit} time unit enum
       */
      FuzzyTime.findFuzzyTimeUnit = function(string) {
          for (var i in FuzzyTime.conversions) {
              if (FuzzyTime.conversions[i].abbrev === string) {
                  return FuzzyTime.conversions[i].unit;
              }
          }
      };
  
      /**
       * Convert an IS0 8601 Duration into something the FuzzyTimeJS library can work
       * with a little easier.
       *
       * @memberOf FuzzyTime
       * @private
       * @param {string} delta An IS0 8601 Duration
       * @returns {list} The duration in an array of
       * maps like:
       * <pre>[
       *   {time: 1, unit: FuzzyTime.Unit.MINUTE},
       *   {time: 30, unit: FuzzyTime.Unit.SECOND},
       *   ...
       * ]</pre>
       */
      FuzzyTime.parseDuration = function(delta) {
          var lowerDelta = delta.toLowerCase().trim();
          var period, time, value = "";
          delta = [];
          time = lowerDelta.split("t")[1];
          period = lowerDelta.split("t")[0].split("p").join("");
  
          if (period != null && period.length > 0) {
              for (var i = 0; i < period.length; i++) {
                  if (/[a-z]/.test(period[i])) {
                      // We've hit the Unit part
                      delta.push({time: parseFloat(value), unit: FuzzyTime.findFuzzyTimeUnit(period[i].toUpperCase())});
                      value = "";
                  } else {
                      value += period[i];
                  }
              }
          }
          
          if (time != null && time.length > 0) {
              for (var i = 0; i < time.length; i++) {
                  if (/[a-z]/.test(time[i])) {
                      // We've hit the Unit part
                      delta.push({time: parseFloat(value), unit: FuzzyTime.findFuzzyTimeUnit(time[i])});
                      value = "";
                  } else {
                      value += time[i];
                  }
              }
          }
          
          return delta;
      };
  
  
      /**
       * Adds the given time value to the given date. The time value's unit is
       * specified by the fuzzyTimeUnit parameter.
       * @private
       * @memberOf FuzzyTime
       * @param {Date} date - A date you want to add time to
       * @param {float} timeValue - The time value you want to add to the given date
       * @param {FuzzyTime.Unit} fuzzyTimeUnit - The unit of time you want to add to given date
       * @returns {Date} A date with all given time added to it.
       */
      FuzzyTime.addTime = function(date, timeValue, fuzzyTimeUnit) {
          var dateCopy = new Date(date);
          var timeValueInt = Math.floor(timeValue);
          var decimal = 0;
  
          //console.log("addTimeString ( " + date + ", " + timeValue + ", " + fuzzyTimeUnit);
  
          if (timeValueInt < timeValue) {
              // The Math.floor call we did earlier caused the decimal place to be dropped...making
              // the timeValueInt variable be smaller than original timeValue.
              // Add the remaining decimal part of timestring.
              decimal = timeValue - timeValueInt;
          }
  
          switch(fuzzyTimeUnit) {
              case FuzzyTime.Unit.YEAR:
                  dateCopy.setYear(dateCopy.getFullYear() + timeValueInt);
                  if (decimal > 0) {
                      // Take the new current year and add some days!
                      var daysInYear = FuzzyTime.isLeapYear(dateCopy) ? 366 : 365;
                      decimal = Math.round(decimal*1000)/1000;
                      dateCopy = FuzzyTime.addTime(dateCopy, decimal * daysInYear, FuzzyTime.Unit.DAY);
                  }
                  break;
              case FuzzyTime.Unit.MONTH:
                  dateCopy.setMonth(dateCopy.getMonth() + timeValueInt);
                  if (decimal > 0) {
                      // Add some dayzzeee
                      var daysInMonth = FuzzyTime.getDaysInMonth(dateCopy);
                      dateCopy = FuzzyTime.addTime(dateCopy, decimal * daysInMonth, FuzzyTime.Unit.DAY);
                  }
                  break;
              case FuzzyTime.Unit.WEEK:
                  dateCopy.setDate(dateCopy.getDate() + (timeValueInt * 7));
                  if (decimal > 0) {
                      // Add some days.
                      dateCopy = FuzzyTime.addTime(dateCopy, decimal * 7, FuzzyTime.Unit.DAY);
                  }
                  break;
              case FuzzyTime.Unit.DAY:
                  dateCopy.setDate(dateCopy.getDate() + timeValueInt);
                  if (decimal > 0) {
                      // Add some hours
                      decimal = Math.round(decimal*1000)/1000;
                      dateCopy = FuzzyTime.addTime(dateCopy, decimal * 24, FuzzyTime.Unit.HOUR);
                  }
                  break;
              case FuzzyTime.Unit.HOUR:
                  dateCopy.setHours(dateCopy.getHours() + timeValueInt);
                  if (decimal > 0) {
                      // Add some minutes
                      decimal = Math.round(decimal*1000)/1000;
                      dateCopy = FuzzyTime.addTime(dateCopy, decimal * 60, FuzzyTime.Unit.MINUTE);
                  }
                  break;
              case FuzzyTime.Unit.MINUTE:
                  dateCopy.setMinutes(dateCopy.getMinutes() + timeValueInt);
                  if (decimal > 0) {
                      // Add some minutes
                      decimal = Math.round(decimal*1000)/1000;
                      dateCopy = FuzzyTime.addTime(dateCopy, decimal * 60, FuzzyTime.Unit.SECOND);
                  }
                  break;
              case FuzzyTime.Unit.SECOND:
                  dateCopy.setSeconds(dateCopy.getSeconds() + Math.round(timeValueInt));
                  break;
          }
  
          return dateCopy;
      };
  
      /**
       * Configure this FuzzyTime to return the given string format (with values plugged
       * in) when {@link FuzzyTime#build} is called and the given time delta has been
       * reached.
       * @param {string} format The string format
       * @param {(string|list)} delta The time delta at which to display. This can be either
       * an ISO 8601 Duration (string) or a list of maps like:
       * <pre>[
       *   {time: 1, unit: FuzzyTime.Unit.MINUTE},
       *   {time: 30, unit: FuzzyTime.Unit.SECOND},
       *   ...
       * ]</pre>
       * @instance
       * @memberOf FuzzyTime
       */
      FuzzyTime.prototype.at = function(format, delta) {
          if (typeof delta === 'string') {
              // Parse string as ISO 8601 duration
              this.ats.push({format: format, delta: FuzzyTime.parseDuration(delta)});
          } else {
              this.ats.push({format: format, delta: delta});
          }
      };
  
      /**
       * Configures this FuzzyTime instance to return the given string format (with
       * values plugged in) any time {@link FuzzyTime#build} is called and
       * <b>before</b> the given time delta has passed.
       * @param {string} format The string format
       * @param {(string|list)} delta The time delta at which to display. This can be either
       * an ISO 8601 Duration (string) or a list of maps like:
       * <pre>[
       *   {time: 1, unit: FuzzyTime.Unit.MINUTE},
       *   {time: 30, unit: FuzzyTime.Unit.SECOND},
       *   ...
       * ]</pre>
       * @instance
       * @memberOf FuzzyTime
       */
      FuzzyTime.prototype.before = function(format, delta) {
          if (typeof delta === 'string') {
              // Parse string as ISO 8601 duration
              this.befores.push({format: format, delta: FuzzyTime.parseDuration(delta)});
          } else {
              this.befores.push({format: format, delta: delta});
          }
      };
  
      /**
       * Configures this FuzzyTime instance to return the given string format (with
       * values plugged in) any time that {@link FuzzyTime#build} is called and
       * <b>after</b> the given time delta has passed
       *
       * @param {string} format The string format
       * @param {(string|list)} delta The time delta at which to display. This can be either
       * an ISO 8601 Duration (string) or a list of maps like:
       * <pre>[
       *   {time: 1, unit: FuzzyTime.Unit.MINUTE},
       *   {time: 30, unit: FuzzyTime.Unit.SECOND},
       *   ...
       * ]</pre>
       * @memberOf FuzzyTime
       * @instance
       */
      FuzzyTime.prototype.after = function(format, delta) {
          if (typeof delta === 'string') {
              // Parse string as ISO 8601 duration
              this.afters.push({format: format, delta: FuzzyTime.parseDuration(delta)});
          } else {
              this.afters.push({format: format, delta: delta});
          }
      };
  
      /**
       * Populates the variables in a string template/format.
       * @memberOf FuzzyTime
       * @protected
       * @ignore
       * @param {number} seconds - The time delta in seconds
       * @param {string} format - The format that needs to have variables populated
       */
      FuzzyTime.prototype.buildString = function(seconds, format) {
          // Loop over all possible template arguments
          for (var idx in FuzzyTime.conversions) {
              var curr = FuzzyTime.conversions[idx];
              // Check if the string format contains the current argument we are
              // looping over
              if (format.search(curr.argument) >= 0) {
                  // The format contains the argument we're looping over.
                  // Convert the remaining seconds to the argument's unit (year, min, etc.)
                  var argVal = Math.floor(seconds / curr.multiplier);
                  seconds = seconds - argVal * curr.multiplier;
                  format = format.replace(curr.argument, argVal);
                  // Recurse, need to continue replacing argument.
                  return this.buildString(seconds, format);
              }
          }
  
          return format;
      };
  
      /**
       * Build a "fuzzy" interpretation of the difference between the provided dates
       * based on previous configurations (using {@link FuzzyTime#at}, {@link FuzzyTime#before}, and
       * {@link FuzzyTime#after}).
       * @param {date} date - The starting date
       * @param {date=} referenceDate - The ending date. If nothing is provided
       * for this parameter, it defaults to <b>now</b> (i.e. <tt>new Date()</tt>).
       * @memberOf FuzzyTime
       * @instance
       * @returns {string} The "fuzzy" timestamp for the differene between the two
       * provided dates (if only one date is provided, <i>now</i> is used for 
       * second date). If there is no "at", "before", or "after" configuration
       * that applies to the difference between the dates provided, the locale
       * string of the <b>date</b> parameter is provided.
       */
      FuzzyTime.prototype.build = function(date, referenceDate) {
          if (typeof referenceDate === 'undefined' || referenceDate === null) {
              referenceDate = new Date();
          }
          
          var delta = Math.round((referenceDate - date)/1000);
  
          // Check "at"
          for (var atIdx in this.ats) {
              var xDate = new Date(date);
              var at = this.ats[atIdx];
              
              for (var i in at.delta) {
                  xDate = FuzzyTime.addTime(xDate, at.delta[i].time, at.delta[i].unit);
              }
  
              // Is the difference between the two provided dates equal
              // to the "at" configuration we're looping on?
              if (xDate.toUTCString() === referenceDate.toUTCString()) {
                  return this.buildString(delta, at.format);
              }
          }
  
          // FIXME: The `date < refreferenceDate` does not allow for future date deltas.
          if (delta > 0 && date < referenceDate) {
              // Check "before"
              
              var returnVar = null,
                      xDateDelta = null;
              for (var beforeIdx in this.befores) {
                  var xDate = new Date(date);
                  var before = this.befores[beforeIdx];
  
                  for (var i in before.delta) {
                      xDate = FuzzyTime.addTime(xDate, before.delta[i].time, before.delta[i].unit);
                  }
  
                  // Is the difference between the two provided dates less than
                  // the "before" configuration we're looping on?
                  if (!(date > referenceDate) && xDate >= referenceDate) {
                      if (xDateDelta === null) {
                          xDateDelta = xDate - referenceDate;
                      }
  
                      if (xDateDelta >= (xDate - referenceDate)) {
                          xDateDelta = xDate - referenceDate;
                          returnVar = this.buildString(delta, before.format);
                      }
                  }
              }
  
              if (returnVar !== null) {
                  return returnVar;
              }
  
              xDateDelta = null;
              returnVar = null;
              
              // Check "after"
              for (var afterIdx in this.afters) {
                  var xDate = new Date(date);
                  var after = this.afters[afterIdx];
  
                  for (var i in after.delta) {
                      xDate = FuzzyTime.addTime(date, after.delta[i].time, after.delta[i].unit);
                  }
  
                  if (xDate <= referenceDate) {
                      if (xDateDelta === null) {
                          xDateDelta = xDate - referenceDate;
                      }
  
                      if (xDateDelta >= (xDate - referenceDate)) {
                          xDateDelta = xDate - referenceDate;
                          returnVar = this.buildString(delta, after.format);
                      }
                  }
              }
  
              if (returnVar !== null) {
                  return returnVar;
              }
          }
  
          // There were not any "at", "before", or "after" configurations
          // that apply to the date difference. Return the exact date...
          return date.toLocaleString();
      };
  
      // Export the FuzzyTime class to the world
      if (typeof define !== 'undefined' && define.amd) {
          define(FuzzyTime);
      } else if (typeof exports === 'object') {
          module.exports = FuzzyTime;
      } else {
          root.FuzzyTime = FuzzyTime;
      }
      
  })(this);