/*  $Id: benchmark_htmlish.js,v 1.13 2007/09/05 01:48:06 altblue Exp $
    (c) 2006-2007 Marius Feraru <altblue@n0i.net>

    This module is free software; you can redistribute it
    and/or modify it under the same terms as Perl itself.

    For details, check http://gfx.neohub.com/benchmark/
*/

Benchmark.Responders.HTMLish = {
  NAME:     'Benchmark.Responders.HTMLish',
  VERSION:  '0.3.1',
  REVISION: '$Revision: 1.13 $'.replace(/^[^ ]+ (.+?) \$$/,'$1'),
  toString: Benchmark.toString,

  onInit: function() {
    if (this.rerun) { // it's a RERUN!
      this.rerun.disable();
      this.mrerun.invoke('disable');
      return false;
    }
    this.title = this.title ? this.title.escapeHTML() : 'results';
    this.useEffects = typeof Effect !== 'undefined' && !this.noEffects;
    this.columns = $w('name rate worst avg best duration iterations nATime');
    if (this.spikes > 1)
      this.columns.push('spikes');
    this.board = $T('table', document.body, {className: 'tester sortable', summary: 'benchmark results'});

    var cap = $T('caption', this.board);
    cap.appendChild($TT(this.title + ' '));
    this.rerun = $T('input', cap, {type: 'button', value: 'redo'}).disable();
    this.rerun.observe('click', function(ev) {
      Event.stop(ev);
      this.run();
    }.bindAsEventListener(this));
    this.mrerun = [];

    var thead = $T('thead', this.board);
    var thr = $T('tr', thead);
    this.columns.each(function(n) {
      $T('th', thr, {
          innerHTML: n,
          className: n + ' sortable sortable-' + (n === 'name' ? 'Text' : 'Number')
        });
    });
    var tbody = $T('tbody', this.board);
    var idx = 0;
    for (var m in this.data) {
      var r = this.data[m];
      r.tr = $T('tr', tbody, {className: ++idx % 2 ? 'odd' : 'even'});
      r.columns = {};
      this.columns.each(function(n) {
        r.columns[n] = $T('td', r.tr, {className: n});
      });
      r.columns.name.style.paddingRight = '110px';
      var rerun = $T('input', r.columns.name,
        {type: 'button', value: 'redo'}).disable();
      rerun.observe('click', function(ev, mname) {
        Event.stop(ev);
        this.run(mname);
      }.bindAsEventListener(this, r.name));
      r.columns.name.appendChild($TT(' ' + r.name));
      this.mrerun.push(rerun);
    }
    if (typeof Sheet !== 'undefined')
      this.sheet = new Sheet(this.board);
    this.efQ = { position: 'end', scope: 'bench-' + (new Date()).getTime() };
  },
  onStart: function(m) {
    this.data[m].tr.addClassName('testing');
  },
  onPause: function(m) {
    this.data[m].tr.addClassName('paused');
    this.populateColumns(m);
  },
  onResume: function(m) {
    this.data[m].tr.removeClassName('paused');
  },
  onFinish: function(m) {
    this.data[m].tr.removeClassName('paused').removeClassName('testing');
    this.populateColumns(m);
    if (this.useEffects)
      new Effect.Highlight(this.data[m].tr, {
        duration: 0.2, queue: this.efQ });
  },
  populateColumns: function(m) {
    var r = this.data[m];
    if (!r.duration) {
      r.columns.rate.innerHTML = 'FAILED';
      r.columns.rate.addClassName('error');
      return;
    }
    r.columns.rate.removeClassName('error');

    var rp = isFinite(r.rate) && r.rate > 1 ? 0 : 2;
    r.columns.rate.innerHTML       = isFinite(r.rate) ? r.rate.toFixed(rp) + '/s' : 'Inf';
    r.columns.duration.innerHTML   = (r.duration/1000).toFixed(3) + 's';
    r.columns.iterations.innerHTML = r.iterations;
    r.columns.nATime.innerHTML     = r.nATime;
    r.columns.best.innerHTML       = r.best.toFixed(3) + 'ms';
    r.columns.worst.innerHTML      = r.worst.toFixed(3) + 'ms';
    r.columns.avg.innerHTML        = r.avg.toFixed(3) + 'ms';
    if (this.spikes > 1)
      r.columns.spikes.innerHTML     = r.ignoredIterations;
    if (r.best > 0) {
      r.columns.best.removeClassName('warning');
      if (r.worst > 10 * r.best)
        r.columns.worst.removeClassName('warning').addClassName('error');
      else if (r.worst > 3 * r.best)
        r.columns.worst.addClassName('warning').removeClassName('error');
      else
        r.columns.worst.removeClassName('warning').removeClassName('error');
    } else {
      r.columns.best.addClassName('warning');
    }
  },
  onComplete: function() {
    this.rerun.enable();
    this.mrerun.invoke('enable');
    var max = $H(this.data).map(function(r) {return r[1].rate }).max();
    var topRow;
    for (var m in this.data) {
      var r    = this.data[m],
          perc = parseInt(r.rate * 100 / max),
          clr  = perc > 89 ? '#0c0' : perc < 51 ? '#c00' : '#990';
      r.columns.name.style.borderRight  = perc + 'px solid ' + clr;
      r.columns.name.style.paddingRight = (110-perc) + 'px';
      if (!topRow && r.rate >= max) // floating numbers kludge ;-)
        topRow = r.tr;
    }
    if (topRow && this.useEffects)
      new Effect.Highlight(topRow, {
        duration:   0.4, startcolor: '#00FF00', queue: this.efQ });
  }
};
