text: /**
   * Module dependencies.
   */
  
  var fs = require('fs')
    , path = require('path')
    , join = path.join
    , debug = require('debug')('mocha:watch');
  
  /**
   * Ignored directories.
   */
  
  var ignore = ['node_modules', '.git'];
  
  /**
   * Escape special characters in the given string of html.
   *
   * @param  {String} html
   * @return {String}
   * @api private
   */
  
  exports.escape = function(html){
    return String(html)
      .replace(/&/g, '&amp;')
      .replace(/"/g, '&quot;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');
  };
  
  /**
   * Array#forEach (<=IE8)
   *
   * @param {Array} array
   * @param {Function} fn
   * @param {Object} scope
   * @api private
   */
  
  exports.forEach = function(arr, fn, scope){
    for (var i = 0, l = arr.length; i < l; i++)
      fn.call(scope, arr[i], i);
  };
  
  /**
   * Array#indexOf (<=IE8)
   *
   * @parma {Array} arr
   * @param {Object} obj to find index of
   * @param {Number} start
   * @api private
   */
  
  exports.indexOf = function(arr, obj, start){
    for (var i = start || 0, l = arr.length; i < l; i++) {
      if (arr[i] === obj)
        return i;
    }
    return -1;
  };
  
  /**
   * Array#reduce (<=IE8)
   *
   * @param {Array} array
   * @param {Function} fn
   * @param {Object} initial value
   * @api private
   */
  
  exports.reduce = function(arr, fn, val){
    var rval = val;
  
    for (var i = 0, l = arr.length; i < l; i++) {
      rval = fn(rval, arr[i], i, arr);
    }
  
    return rval;
  };
  
  /**
   * Array#filter (<=IE8)
   *
   * @param {Array} array
   * @param {Function} fn
   * @api private
   */
  
  exports.filter = function(arr, fn){
    var ret = [];
  
    for (var i = 0, l = arr.length; i < l; i++) {
      var val = arr[i];
      if (fn(val, i, arr)) ret.push(val);
    }
  
    return ret;
  };
  
  /**
   * Object.keys (<=IE8)
   *
   * @param {Object} obj
   * @return {Array} keys
   * @api private
   */
  
  exports.keys = Object.keys || function(obj) {
    var keys = []
      , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
  
    for (var key in obj) {
      if (has.call(obj, key)) {
        keys.push(key);
      }
    }
  
    return keys;
  };
  
  /**
   * Watch the given `files` for changes
   * and invoke `fn(file)` on modification.
   *
   * @param {Array} files
   * @param {Function} fn
   * @api private
   */
  
  exports.watch = function(files, fn){
    var options = { interval: 100 };
    files.forEach(function(file){
      debug('file %s', file);
      fs.watchFile(file, options, function(curr, prev){
        if (prev.mtime < curr.mtime) fn(file);
      });
    });
  };
  
  /**
   * Ignored files.
   */
  
  function ignored(path){
    return !~ignore.indexOf(path);
  }
  
  /**
   * Lookup files in the given `dir`.
   *
   * @return {Array}
   * @api private
   */
  
  exports.files = function(dir, ret){
    ret = ret || [];
  
    fs.readdirSync(dir)
    .filter(ignored)
    .forEach(function(path){
      path = join(dir, path);
      if (fs.statSync(path).isDirectory()) {
        exports.files(path, ret);
      } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) {
        ret.push(path);
      }
    });
  
    return ret;
  };
  
  /**
   * Compute a slug from the given `str`.
   *
   * @param {String} str
   * @return {String}
   * @api private
   */
  
  exports.slug = function(str){
    return str
      .toLowerCase()
      .replace(/ +/g, '-')
      .replace(/[^-\w]/g, '');
  };
  
  /**
   * Strip the function definition from `str`,
   * and re-indent for pre whitespace.
   */
  
  exports.clean = function(str) {
    str = str
      .replace(/^function *\(.*\) *{/, '')
      .replace(/\s+\}$/, '');
  
    var whitespace = str.match(/^\n?(\s*)/)[1]
      , re = new RegExp('^' + whitespace, 'gm');
  
    str = str.replace(re, '');
  
    return exports.trim(str);
  };
  
  /**
   * Escape regular expression characters in `str`.
   *
   * @param {String} str
   * @return {String}
   * @api private
   */
  
  exports.escapeRegexp = function(str){
    return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
  };
  
  /**
   * Trim the given `str`.
   *
   * @param {String} str
   * @return {String}
   * @api private
   */
  
  exports.trim = function(str){
    return str.replace(/^\s+|\s+$/g, '');
  };
  
  /**
   * Parse the given `qs`.
   *
   * @param {String} qs
   * @return {Object}
   * @api private
   */
  
  exports.parseQuery = function(qs){
    return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
      var i = pair.indexOf('=')
        , key = pair.slice(0, i)
        , val = pair.slice(++i);
  
      obj[key] = decodeURIComponent(val);
      return obj;
    }, {});
  };
  
  /**
   * Highlight the given string of `js`.
   *
   * @param {String} js
   * @return {String}
   * @api private
   */
  
  function highlight(js) {
    return js
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
      .replace(/('.*?')/gm, '<span class="string">$1</span>')
      .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
      .replace(/(\d+)/gm, '<span class="number">$1</span>')
      .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
      .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
  }
  
  /**
   * Highlight the contents of tag `name`.
   *
   * @param {String} name
   * @api private
   */
  
  exports.highlightTags = function(name) {
    var code = document.getElementsByTagName(name);
    for (var i = 0, len = code.length; i < len; ++i) {
      code[i].innerHTML = highlight(code[i].innerHTML);
    }
  };
  