text: 
  /**
   * Module dependencies.
   */
  
  var Base = require('./base')
    , utils = require('../utils')
    , Progress = require('../browser/progress')
    , escape = utils.escape;
  
  /**
   * Expose `Doc`.
   */
  
  exports = module.exports = HTML;
  
  /**
   * Stats template.
   */
  
  var statsTemplate = '<ul id="stats">'
    + '<li class="progress"><canvas width="40" height="40"></canvas></li>'
    + '<li class="passes">passes: <em>0</em></li>'
    + '<li class="failures">failures: <em>0</em></li>'
    + '<li class="duration">duration: <em>0</em>s</li>'
    + '</ul>';
  
  /**
   * Initialize a new `Doc` reporter.
   *
   * @param {Runner} runner
   * @api public
   */
  
  function HTML(runner) {
    Base.call(this, runner);
  
    var self = this
      , stats = this.stats
      , total = runner.total
      , root = document.getElementById('mocha')
      , stat = fragment(statsTemplate)
      , items = stat.getElementsByTagName('li')
      , passes = items[1].getElementsByTagName('em')[0]
      , failures = items[2].getElementsByTagName('em')[0]
      , duration = items[3].getElementsByTagName('em')[0]
      , canvas = stat.getElementsByTagName('canvas')[0]
      , stack = [root]
      , progress
      , ctx
  
    if (canvas.getContext) {
      ctx = canvas.getContext('2d');
      progress = new Progress;
    }
  
    if (!root) return error('#mocha div missing, add it to your document');
  
    root.appendChild(stat);
  
    if (progress) progress.size(40);
  
    runner.on('suite', function(suite){
      if (suite.root) return;
  
      // suite
      var el = fragment('<div class="suite"><h1>%s</h1></div>', suite.title);
  
      // container
      stack[0].appendChild(el);
      stack.unshift(document.createElement('div'));
      el.appendChild(stack[0]);
    });
  
    runner.on('suite end', function(suite){
      if (suite.root) return;
      stack.shift();
    });
  
    runner.on('fail', function(test, err){
      if (err.uncaught) runner.emit('test end', test);
    });
  
    runner.on('test end', function(test){
      // TODO: add to stats
      var percent = stats.tests / total * 100 | 0;
      if (progress) progress.update(percent).draw(ctx);
  
      // update stats
      var ms = new Date - stats.start;
      text(passes, stats.passes);
      text(failures, stats.failures);
      text(duration, (ms / 1000).toFixed(2));
  
      // test
      if ('passed' == test.state) {
        var el = fragment('<div class="test pass"><h2>%e</h2></div>', test.title);
      } else if (test.pending) {
        var el = fragment('<div class="test pass pending"><h2>%e</h2></div>', test.title);
      } else {
        var el = fragment('<div class="test fail"><h2>%e</h2></div>', test.title);
        var str = test.err.stack || test.err;
  
        // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
        // check for the result of the stringifying.
        if ('[object Error]' == str) str = test.err.message;
  
        el.appendChild(fragment('<pre class="error">%e</pre>', str));
      }
  
      // toggle code
      var h2 = el.getElementsByTagName('h2')[0];
  
      on(h2, 'click', function(){
        pre.style.display = 'none' == pre.style.display
          ? 'block'
          : 'none';
      });
  
      // code
      // TODO: defer
      if (!test.pending) {
        var pre = fragment('<pre><code>%e</code></pre>', clean(test.fn.toString()));
        el.appendChild(pre);
        pre.style.display = 'none';
      }
  
      stack[0].appendChild(el);
    });
  }
  
  /**
   * Display error `msg`.
   */
  
  function error(msg) {
    document.body.appendChild(fragment('<div id="error">%s</div>', msg));
  }
  
  /**
   * Return a DOM fragment from `html`.
   */
  
  function fragment(html) {
    var args = arguments
      , div = document.createElement('div')
      , i = 1;
  
    div.innerHTML = html.replace(/%([se])/g, function(_, type){
      switch (type) {
        case 's': return String(args[i++]);
        case 'e': return escape(args[i++]);
      }
    });
  
    return div.firstChild;
  }
  
  /**
   * Set `el` text to `str`.
   */
  
  function text(el, str) {
    if (el.textContent) {
      el.textContent = str;
    } else {
      el.innerText = str;
    }
  }
  
  /**
   * Listen on `event` with callback `fn`.
   */
  
  function on(el, event, fn) {
    if (el.addEventListener) {
      el.addEventListener(event, fn, false);
    } else {
      el.attachEvent('on' + event, fn);
    }
  }
  
  /**
   * Strip the function definition from `str`,
   * and re-indent for pre whitespace.
   */
  
  function clean(str) {
    str = str
      .replace(/^function *\(.*\) *{/, '')
      .replace(/\s+\}$/, '');
  
    var spaces = str.match(/^\n?( *)/)[1].length
      , re = new RegExp('^ {' + spaces + '}', 'gm');
  
    str = str
      .replace(re, '')
      .replace(/^\s+/, '');
  
    return str;
  }
  