text: 'use strict';
  
  var has = Object.prototype.hasOwnProperty
    , ms = require('millisecond');
  
  /**
   * Timer instance.
   *
   * @constructor
   * @param {Object} timer New timer instance.
   * @param {Function} clear Clears the timer instance.
   * @param {Function} fn The functions that need to be executed.
   * @api private
   */
  function Timer(timer, clear, fn) {
    this.clear = clear;
    this.timer = timer;
    this.fns = [fn];
  }
  
  /**
   * Custom wrappers for the various of clear{whatever} functions. We cannot
   * invoke them directly as this will cause thrown errors in Google Chrome with
   * an Illegal Invocation Error
   *
   * @see #2
   * @type {Function}
   * @api private
   */
  function unsetTimeout(id) { clearTimeout(id); }
  function unsetInterval(id) { clearInterval(id); }
  function unsetImmediate(id) { clearImmediate(id); }
  
  /**
   * Simple timer management.
   *
   * @constructor
   * @param {Mixed} context Context of the callbacks that we execute.
   * @api public
   */
  function Tick(context) {
    if (!(this instanceof Tick)) return new Tick(context);
  
    this.timers = {};
    this.context = context || this;
  }
  
  /**
   * Return a function which will just iterate over all assigned callbacks and
   * optionally clear the timers from memory if needed.
   *
   * @param {String} name Name of the timer we need to execute.
   * @param {Boolean} clear Also clear from memory.
   * @returns {Function}
   * @api private
   */
  Tick.prototype.tock = function ticktock(name, clear) {
    var tock = this;
  
    return function tickedtock() {
      if (!(name in tock.timers)) return;
  
      var timer = tock.timers[name]
        , fns = timer.fns.slice()
        , l = fns.length
        , i = 0;
  
      if (clear) tock.clear(name);
  
      for (; i < l; i++) {
        fns[i].call(tock.context);
      }
    };
  };
  
  /**
   * Add a new timeout.
   *
   * @param {String} name Name of the timer.
   * @param {Function} fn Completion callback.
   * @param {Mixed} time Duration of the timer.
   * @returns {Tick}
   * @api public
   */
  Tick.prototype.setTimeout = function timeout(name, fn, time) {
    var tick = this;
  
    if (tick.timers[name]) {
      tick.timers[name].fns.push(fn);
      return tick;
    }
  
    tick.timers[name] = new Timer(
      setTimeout(tick.tock(name, true), ms(time)),
      unsetTimeout,
      fn
    );
  
    return tick;
  };
  
  /**
   * Add a new interval.
   *
   * @param {String} name Name of the timer.
   * @param {Function} fn Completion callback.
   * @param {Mixed} time Interval of the timer.
   * @returns {Tick}
   * @api public
   */
  Tick.prototype.setInterval = function interval(name, fn, time) {
    var tick = this;
  
    if (tick.timers[name]) {
      tick.timers[name].fns.push(fn);
      return tick;
    }
  
    tick.timers[name] = new Timer(
      setInterval(tick.tock(name), ms(time)),
      unsetInterval,
      fn
    );
  
    return tick;
  };
  
  /**
   * Add a new setImmediate.
   *
   * @param {String} name Name of the timer.
   * @param {Function} fn Completion callback.
   * @returns {Tick}
   * @api public
   */
  Tick.prototype.setImmediate = function immediate(name, fn) {
    var tick = this;
  
    if ('function' !== typeof setImmediate) return tick.setTimeout(name, fn, 0);
  
    if (tick.timers[name]) {
      tick.timers[name].fns.push(fn);
      return tick;
    }
  
    tick.timers[name] = new Timer(
      setImmediate(tick.tock(name, true)),
      unsetImmediate,
      fn
    );
  
    return tick;
  };
  
  /**
   * Check if we have a timer set.
   *
   * @param {String} name
   * @returns {Boolean}
   * @api public
   */
  Tick.prototype.active = function active(name) {
    return name in this.timers;
  };
  
  /**
   * Properly clean up all timeout references. If no arguments are supplied we
   * will attempt to clear every single timer that is present.
   *
   * @param {Arguments} ..args.. The names of the timeouts we need to clear
   * @returns {Tick}
   * @api public
   */
  Tick.prototype.clear = function clear() {
    var args = arguments.length ? arguments : []
      , tick = this
      , timer, i, l;
  
    if (args.length === 1 && 'string' === typeof args[0]) {
      args = args[0].split(/[, ]+/);
    }
  
    if (!args.length) {
      for (timer in tick.timers) {
        if (has.call(tick.timers, timer)) args.push(timer);
      }
    }
  
    for (i = 0, l = args.length; i < l; i++) {
      timer = tick.timers[args[i]];
  
      if (!timer) continue;
      timer.clear(timer.timer);
  
      timer.fns = timer.timer = timer.clear = null;
      delete tick.timers[args[i]];
    }
  
    return tick;
  };
  
  /**
   * We will no longer use this module, prepare your self for global cleanups.
   *
   * @returns {Boolean}
   * @api public
   */
  Tick.prototype.end = Tick.prototype.destroy = function end() {
    if (!this.context) return false;
  
    this.clear();
    this.context = this.timers = null;
  
    return true;
  };
  
  /**
   * Adjust a timeout or interval to a new duration.
   *
   * @returns {Tick}
   * @api public
   */
  Tick.prototype.adjust = function adjust(name, time) {
    var interval
      , tick = this
      , timer = tick.timers[name];
  
    if (!timer) return tick;
  
    interval = timer.clear === unsetInterval;
    timer.clear(timer.timer);
    timer.timer = (interval ? setInterval : setTimeout)(tick.tock(name, !interval), ms(time));
  
    return tick;
  };
  
  //
  // Expose the timer factory.
  //
  module.exports = Tick;
  