text: // AMD support (Thanks to @FagnerMartinsBrack)
  ;(function(factory) {
      'use strict';
  
      if (typeof define === 'function' && define.amd) {
          define(['jquery'], factory);
      } else {
          factory(jQuery);
      }
  })(function($){
      'use strict';
  
      var instances = [],
          matchers  = [],
          defaultOptions  = {
              precision: 100, // 0.1 seconds, used to update the DOM
              elapse: false,
              defer: false
          };
      // Miliseconds
      matchers.push(/^[0-9]*$/.source);
      // Month/Day/Year [hours:minutes:seconds]
      matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/
          .source);
      // Year/Day/Month [hours:minutes:seconds] and
      // Year-Day-Month [hours:minutes:seconds]
      matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/
          .source);
      // Cast the matchers to a regular expression object
      matchers = new RegExp(matchers.join('|'));
      // Parse a Date formatted has String to a native object
      function parseDateString(dateString) {
          // Pass through when a native object is sent
          if(dateString instanceof Date) {
              return dateString;
          }
          // Caste string to date object
          if(String(dateString).match(matchers)) {
              // If looks like a milisecond value cast to number before
              // final casting (Thanks to @msigley)
              if(String(dateString).match(/^[0-9]*$/)) {
                  dateString = Number(dateString);
              }
              // Replace dashes to slashes
              if(String(dateString).match(/\-/)) {
                  dateString = String(dateString).replace(/\-/g, '/');
              }
              return new Date(dateString);
          } else {
              throw new Error('Couldn\'t cast `' + dateString +
                  '` to a date object.');
          }
      }
      // Map to convert from a directive to offset object property
      var DIRECTIVE_KEY_MAP = {
          'Y': 'years',
          'm': 'months',
          'n': 'daysToMonth',
          'd': 'daysToWeek',
          'w': 'weeks',
          'W': 'weeksToMonth',
          'H': 'hours',
          'M': 'minutes',
          'S': 'seconds',
          'D': 'totalDays',
          'I': 'totalHours',
          'N': 'totalMinutes',
          'T': 'totalSeconds'
      };
      // Returns an escaped regexp from the string
      function escapedRegExp(str) {
          var sanitize = str.toString().replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
          return new RegExp(sanitize);
      }
      // Time string formatter
      function strftime(offsetObject) {
          return function(format) {
              var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
              if(directives) {
                  for(var i = 0, len = directives.length; i < len; ++i) {
                      var directive   = directives[i]
                              .match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/),
                          regexp    = escapedRegExp(directive[0]),
                          modifier  = directive[1] || '',
                          plural    = directive[3] || '',
                          value     = null;
                      // Get the key
                      directive = directive[2];
                      // Swap shot-versions directives
                      if(DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
                          value = DIRECTIVE_KEY_MAP[directive];
                          value = Number(offsetObject[value]);
                      }
                      if(value !== null) {
                          // Pluralize
                          if(modifier === '!') {
                              value = pluralize(plural, value);
                          }
                          // Add zero-padding
                          if(modifier === '') {
                              if(value < 10) {
                                  value = '0' + value.toString();
                              }
                          }
                          // Replace the directive
                          format = format.replace(regexp, value.toString());
                      }
                  }
              }
              format = format.replace(/%%/, '%');
              return format;
          };
      }
      // Pluralize
      function pluralize(format, count) {
          var plural = 's', singular = '';
          if(format) {
              format = format.replace(/(:|;|\s)/gi, '').split(/\,/);
              if(format.length === 1) {
                  plural = format[0];
              } else {
                  singular = format[0];
                  plural = format[1];
              }
          }
          // Fix #187
          if(Math.abs(count) > 1) {
              return plural;
          } else {
              return singular;
          }
      }
      // The Final Countdown
      var Countdown = function(el, finalDate, options) {
          this.el       = el;
          this.$el      = $(el);
          this.interval = null;
          this.offset   = {};
          this.options  = $.extend({}, defaultOptions);
          // console.log(this.options);
          // This helper variable is necessary to mimick the previous check for an
          // event listener on this.$el. Because of the event loop there might not
          // be a registered event listener during the first tick. In order to work
          // as expected a second tick is necessary, so that the events can be fired
          // and handled properly.
          this.firstTick = true;
          // Register this instance
          this.instanceNumber = instances.length;
          instances.push(this);
          // Save the reference
          this.$el.data('countdown-instance', this.instanceNumber);
          // Handle options or callback
          if (options) {
              // Register the callbacks when supplied
              if(typeof options === 'function') {
                  this.$el.on('update.countdown', options);
                  this.$el.on('stoped.countdown', options);
                  this.$el.on('finish.countdown', options);
              } else {
                  this.options = $.extend({}, defaultOptions, options);
              }
          }
          // Set the final date and start
          this.setFinalDate(finalDate);
          // Starts the countdown automatically unless it's defered,
          // Issue #198
          if (this.options.defer === false) {
              this.start();
          }
      };
      $.extend(Countdown.prototype, {
          start: function() {
              if(this.interval !== null) {
                  clearInterval(this.interval);
              }
              var self = this;
              this.update();
              this.interval = setInterval(function() {
                  self.update.call(self);
              }, this.options.precision);
          },
          stop: function() {
              clearInterval(this.interval);
              this.interval = null;
              this.dispatchEvent('stoped');
          },
          toggle: function() {
              if (this.interval) {
                  this.stop();
              } else {
                  this.start();
              }
          },
          pause: function() {
              this.stop();
          },
          resume: function() {
              this.start();
          },
          remove: function() {
              this.stop.call(this);
              instances[this.instanceNumber] = null;
              // Reset the countdown instance under data attr (Thanks to @assiotis)
              delete this.$el.data().countdownInstance;
          },
          setFinalDate: function(value) {
              this.finalDate = parseDateString(value); // Cast the given date
          },
          update: function() {
              // Stop if dom is not in the html (Thanks to @dleavitt)
              if(this.$el.closest('html').length === 0) {
                  this.remove();
                  return;
              }
              var now = new Date(),
                  newTotalSecsLeft;
              // Create an offset date object
              newTotalSecsLeft = this.finalDate.getTime() - now.getTime(); // Millisecs
              // Calculate the remaining time
              newTotalSecsLeft = Math.ceil(newTotalSecsLeft / 1000); // Secs
              // If is not have to elapse set the finish
              newTotalSecsLeft = !this.options.elapse && newTotalSecsLeft < 0 ? 0 :
                  Math.abs(newTotalSecsLeft);
              // Do not proceed to calculation if the seconds have not changed or
              // during the first tick
              if (this.totalSecsLeft === newTotalSecsLeft || this.firstTick) {
                  this.firstTick = false;
                  return;
              } else {
                  this.totalSecsLeft = newTotalSecsLeft;
              }
              // Check if the countdown has elapsed
              this.elapsed = (now >= this.finalDate);
              // Calculate the offsets
              this.offset = {
                  seconds     : this.totalSecsLeft % 60,
                  minutes     : Math.floor(this.totalSecsLeft / 60) % 60,
                  hours       : Math.floor(this.totalSecsLeft / 60 / 60) % 24,
                  days        : Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
                  daysToWeek  : Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
                  daysToMonth : Math.floor(this.totalSecsLeft / 60 / 60 / 24 % 30.4368),
                  weeks       : Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
                  weeksToMonth: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7) % 4,
                  months      : Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30.4368),
                  years       : Math.abs(this.finalDate.getFullYear()-now.getFullYear()),
                  totalDays   : Math.floor(this.totalSecsLeft / 60 / 60 / 24),
                  totalHours  : Math.floor(this.totalSecsLeft / 60 / 60),
                  totalMinutes: Math.floor(this.totalSecsLeft / 60),
                  totalSeconds: this.totalSecsLeft
              };
              // Dispatch an event
              if(!this.options.elapse && this.totalSecsLeft === 0) {
                  this.stop();
                  this.dispatchEvent('finish');
              } else {
                  this.dispatchEvent('update');
              }
          },
          dispatchEvent: function(eventName) {
              var event = $.Event(eventName + '.countdown');
              event.finalDate     = this.finalDate;
              event.elapsed       = this.elapsed;
              event.offset        = $.extend({}, this.offset);
              event.strftime      = strftime(this.offset);
              this.$el.trigger(event);
          }
      });
      // Register the jQuery selector actions
      $.fn.countdown = function() {
          var argumentsArray = Array.prototype.slice.call(arguments, 0);
          return this.each(function() {
              // If no data was set, jQuery.data returns undefined
              var instanceNumber = $(this).data('countdown-instance');
              // Verify if we already have a countdown for this node ...
              // Fix issue #22 (Thanks to @romanbsd)
              if (instanceNumber !== undefined) {
                  var instance = instances[instanceNumber],
                      method = argumentsArray[0];
                  // If method exists in the prototype execute
                  if(Countdown.prototype.hasOwnProperty(method)) {
                      instance[method].apply(instance, argumentsArray.slice(1));
                      // If method look like a date try to set a new final date
                  } else if(String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
                      instance.setFinalDate.call(instance, method);
                      // Allow plugin to restart after finished
                      // Fix issue #38 (thanks to @yaoazhen)
                      instance.start();
                  } else {
                      $.error('Method %s does not exist on jQuery.countdown'
                          .replace(/\%s/gi, method));
                  }
              } else {
                  // ... if not we create an instance
                  new Countdown(this, argumentsArray[0], argumentsArray[1]);
              }
          });
      };
  });