/**
 * TestCase - JavaScript testing framework, version 2.0.2
 * Copyright (C) 2007-2008 Nikolay V. Nemshilov aka St. <nemshilov-gmail-com>
 *
 * The library is freely distributable under the terms of an MIT-style license
 * For details, see the TestCase web site: http://testcase.rubyforge.net
 */


var TestCaseUtil = {
  create_class: function() {
    var superklass = null, params = this.to_a(arguments);
    if (typeof params[0] == 'function')
      superklass = params.shift();
    var klass = function() {
      return this.initialize ? this.initialize.apply(this, arguments) : this;
    }
    if (superklass) {
      var subclass = function() { };
      subclass.prototype = superklass.prototype;
      klass.prototype = new subclass;
      klass.superclass = superklass;
    }
    klass.prototype.constructor = klass;
    if (params[0])
      this.extend(klass.prototype, params[0]);
    return klass;
  },
  extend: function(object, properties, dont_overwrite) {
    for (key in properties)
      if (!(dont_overwrite && typeof object[key] != 'undefined'))
        object[key] = properties[key];
    return object;
  },
  extend_with_camelized_aliases: function(object, mixin) {
    var camelized_mixin = {};
    for (key in mixin) {
      var parts = key.split('_'), camelized = parts[0];
      for (var i=1; i < parts.length; i++)
        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
      camelized_mixin[camelized] = mixin[key];
    }
    return TestCaseUtil.extend(object, camelized_mixin);
  },
  extend_fully_object_and_prototype: function(object, mixin) {
    TestCaseUtil.extend(object, mixin);
    TestCaseUtil.extend(object.prototype, mixin);
    TestCaseUtil.extend_with_camelized_aliases(object, mixin);
    return TestCaseUtil.extend_with_camelized_aliases(object.prototype, mixin);
  },
  equal: function(obj1, obj2) {
    return TestCaseUtil.to_s(obj1) == TestCaseUtil.to_s(obj2);
  },
  to_a: function(it) {
    var a = [];
    for (var i=0; i < it.length; i++)
      a.push(it[i]);
    return a;
  },
  to_s: function(object, one_level) {
    var value = this._try_to_s(object);
    if (value !== object) return value;
    var entries = this._get_object_stringified_entries(
      one_level ? this._one_level_to_s : this.to_s, object
    );
    return object instanceof Array ? '['+ entries.join(', ')+ ']' : '{' + entries.join(', ') + '}';
  },
  debug: function(object) {
    return this.to_s(object, true);
  },
  MAX_OBJECT_STRING_LENGTH: 80,
  to_pretty_s: function(object, offset) {
    if (object == null) { return 'null'; }
    else if (typeof object != 'object') {
      return this.to_s(object);
    } else {
      var offset = offset || 0;
      var object_s = this.to_s(object);
      if (object_s.length > this.MAX_OBJECT_STRING_LENGTH) {
        if (object instanceof Array) {
          var len = object.length;
        } else {
          var len = 0;
          for (var key in object) {
            len++;
          }
        }
        if (len > 1) {
          var entries = this._get_object_stringified_entries(
            this.to_pretty_s, object, offset + 1
          );
          var offset_s = '';
          for (var i=0; i < offset; i++) {
            offset_s += '  ';
          }
          object_s = object instanceof Array ? 
            "[" + entries.join(",\n "+ offset_s) + "\n"+offset_s+"]" : 
            "{" + entries.join(",\n "+ offset_s) + "\n"+offset_s+"}" ;
        }
      }
      return object_s;
    }
  },
  _get_object_stringified_entries: function(method, object, param) {
    var entries = [];
    if (object instanceof Array) {
      for (var i=0; i < object.length; i++) {
        entries.push(method.apply(this, [object[i], param]));
      }
    } else {
      for (var property in object) {
        entries.push(this.to_s(property) + ': ' + method.apply(this, [object[property], param]));
      }
    }
    return entries;
  },
  _one_level_to_s: function(object) {
    var value = this._try_to_s(object);
    if (value !== object) {
      return value;
    } else if (object instanceof Array) {
      return '[...]';
    } else {
      return '{...}';
    }
  },
  _try_to_s: function(object) {
    switch (typeof object) {
      case 'undefined':
      case 'unknown': return 'undefined';
      case 'string': return '"'+object+'"';
      case 'number': return ''+object;
      case 'function':
      case 'boolean': return object.toString();
    }
    if (object === null) return 'null';
    if (object.nodeType == 1)
      return '<'+object.tagName+
          (object.id ? ' id="'+object.id+'"':'')+
          (object.className ? ' class="'+object.className+'"':'')+
          (object.title ? ' title="'+object.title+'"':'')+
          (object.type ? ' type="'+object.type+'"':'')+
        '>'+object.innerHTML+'</'+object.tagName+'>';
    return object;
  },
  get_object_class_name: function(object) {
    if (object === null) { return 'Null'; }
    switch (typeof object) {
      case 'string':   return 'String';
      case 'boolean':  return 'Boolean';
      case 'number':   return 'Number';
      case 'function': return 'Function';
      case 'object':
        if (typeof object.constructor != 'undefined') {
          var match = object.constructor.toString().match(/function\s*(\w+)/);
          if (match && match.length == 2) {
            return match[1];
          }
          for (var key in self) {
            try {
              if (object instanceof self[key])
                return key;
            } catch (e) {}
          }
          if (object instanceof Array) {
            return 'Array';
          } else {
            return 'Object';
          }
        }
    }
    return 'Unknown';
  },
  get_class_name: function(klass) {
    if (klass === null) { return 'Null'; }
    switch (klass) {
      case String:   return 'String';
      case Boolean:  return 'Boolean';
      case Number:   return 'Number';
      case Function: return 'Function';
      case Array:    return 'Array';
      case Object:   return 'Object';
      default:
        for (var key in self) {
          if (klass == self[key])
            return key;
        }
    }
    return 'Unknown';
  },
  Browser: {
    IE:     !!(window.attachEvent && !window.opera),
    Opera:  !!window.opera,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/),
    Konqueror: navigator.userAgent.indexOf('Konqueror') != -1
  },
  $: function(element) {
    return typeof element == 'string' ? document.getElementById(element) : element;
  },
  $$: function(css_rule) {
    return this.Element.select(document, css_rule);
  }
};
/* ------------------------------------------------------------------- */

TestCaseUtil.Array = {
  include: function(list, item) {
    for (var i=0; i < list.length; i++)
      if (list[i] == item)
        return true;
    return false;
  },
  merge: function(list1, list2) {
    for (var i=0; i < list2.length; i++) {
      if (!this.include(list1, list2[i]))
        list1.push(list2[i]);
    }
    return list1;
  },
  without: function(list, item) {
    var new_list = [];
    for (var i=0; i < list.length; i++)
      if (list[i] != item)
        new_list.push(list[i]);
    return new_list;
  },
  uniq: function(list) {
    var new_list = [];
    for (var i=0; i < list.length; i++)
      if (!this.include(new_list, list[i]))
        new_list.push(list[i]);
    return new_list;
  },
  find: function(list, callback) {
    for (var i=0; i < list.length; i++)
      if (callback(list[i]))
        return list[i];
    return null;
  },
  each: function(list, callback) {
    for (var i=0; i < list.length; i++)
      callback(list[i]);
    return list;
  }
};
/* ------------------------------------------------------------------- */

TestCaseUtil.Event = {
  KEY_UP: 38, 
  observe: function(element, name, callback) {
    var element = TestCaseUtil.$(element);
    if (element.addEventListener) {
      element.addEventListener(name, callback, false);
    } else {
      element.attachEvent("on" + name, callback);
    }
    return element;
  },
  stop: function(event) {
    if (event.stopPropagation) {
      event.stopPropagation();
      event.preventDefault();
    } else {
      event.cancelBubble = true;
      event.returnValue = false;
    }
  },
  element: function(event) {
    var target = event.srcElement ? event.srcElement : event.target;
    return (!target || target.nodeType == 1) ? target : target.parentNode;
  },
  pointerX: function(event) {
    return this.pointer(event).x;
  },
  pointerY: function(event) {
    return this.pointer(event).y;
  },
  pointer: function(event) {
    return {
      x: event.pageX || (event.clientX +
        (document.documentElement.scrollLeft || document.body.scrollLeft)),
      y: event.pageY || (event.clientY +
        (document.documentElement.scrollTop || document.body.scrollTop))
    };
  }
};
/* ------------------------------------------------------------------- */

TestCaseUtil.Element = {
  visible: function(element) {
    return TestCaseUtil.$(element).style.display != 'none';
  },
  show: function(element) {
    TestCaseUtil.$(element).style.display = '';
  },
  hide: function(element) {
    TestCaseUtil.$(element).style.display = 'none';
  },
  toggle: function(element) {
    if (this.visible(element))
      this.hide(element);
    else
      this.show(element);
  },
  up: function(element, css_rule) {
    return this.Selector.match_atom(element, css_rule) ? element :
      (element.parentNode ? this.up(element.parentNode, css_rule) : null);
  },
  down: function(element, css_rule) {
    return this.Selector.find_first(element, css_rule);
  },
  select: function(element, css_rule) {
    return this.Selector.find_all(element, css_rule);
  },
  has_class_names: function(element, class_names) {
    for (var i=0; i < class_names.length; i++)
      if (!this.has_class_name(element, class_names[i]))
        return false;
    return true;
  },
  has_class_name: function(element, class_name) {
    var elementClassName = element.className;
    return (elementClassName && elementClassName.length > 0 && (elementClassName == class_name ||
      new RegExp("(^|\\s)" + class_name + "(\\s|$)").test(elementClassName)));
  },
  add_class_name: function(element, class_name) {
    if (!this.has_class_name(element, class_name))
      element.className += (element.className ? ' ' : '') + class_name;
    return element;
  },
  remove_class_name: function(element, class_name) {
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + class_name + "(\\s+|$)"), ' ').replace(/^\s+|\s+$/g, '');
    return element;
  },
  has_attributes: function(element, attrs) {
    for (key in attrs)
      if (attrs[key] != null && element[key] != attrs[key])
        return false;
    return true;
  }
};
/* ------------------------------------------------------------------- */

TestCaseUtil.Element.Selector = {
  find_first: function(element, css_rule) {
    var founds = this.find_all(element, css_rule);
    return founds.length ? founds[0] : null;
  },
  find_all: function(element, css_rule) {
    var founds = [], element = TestCaseUtil.$(element);
    if (typeof css_rule == 'string')
      css_rule = this._parse_rule(css_rule);
    if (css_rule[0] instanceof Array) {
      for (var i=0; i < css_rule.length; i++)
        TestCaseUtil.Array.merge(founds, this.find_all(element, css_rule[i]));
    } else {
      var css_atom = css_rule.shift();
      switch (css_atom.rel) {
        case '>': founds = this._find_immidiate_descendants(element, css_atom); break;
        case '+': founds = this._find_next_sibling(element, css_atom);          break;
        case '~': founds = this._find_late_siblings(element, css_atom);         break;
        default:  founds = this._find_descendants(element, css_atom);
      }
      if (css_rule.length > 0) { 
        var sub_founds = [];
        for (var i=0; i < founds.length; i++) {
          var css_rule_clone = [];
          for (var j=0; j < css_rule.length; j++)
            css_rule_clone.push(TestCaseUtil.extend({}, css_rule[j]));
          TestCaseUtil.Array.merge(sub_founds, this.find_all(founds[i], css_rule_clone));
        }
        founds = sub_founds;
      }
    }
    return founds;
  },
  _find_descendants: function(element, css_atom, immidiates_only) {
    var founds = [], child = element.firstChild;
    while (child) {
      if (child.nodeType == 1) {
        if (this._fully_match_atom(child, css_atom, element) && 
            !TestCaseUtil.Array.include(founds, child)) {
              founds.push(child);
        }
        if (!immidiates_only)
          TestCaseUtil.Array.merge(founds, this._find_descendants(child, css_atom));
      }
      child = child.nextSibling;
    }
    return founds;
  },
  _find_immidiate_descendants: function(element, css_atom) {
    return this._find_descendants(element, css_atom, true);
  },
  _find_next_sibling: function(element, css_atom) {
    var founds = [], node = element.nextSibling;
    while (node) {
      if (node.nodeType == 1) {
        if (this._fully_match_atom(node, css_atom, element) &&
            !TestCaseUtil.Array.include(founds, node))
              founds.push(node);
        break;
      }
      node = node.nextSibling;
    }
    return founds;
  },
  _find_late_siblings: function(element, css_atom) {
    var founds = [], node = element.nextSibling;
    while (node) {
      if (node.nodeType == 1) {
        if (this._fully_match_atom(node, css_atom, element) &&
           !TestCaseUtil.Array.include(founds, node))
             founds.push(node);
      }
      node = node.nextSibling;
    }
    return founds;
  },
  _fully_match_atom: function(element, css_atom, parent_node) {
    return this.match_atom(element, css_atom) &&
           this._match_pseudo(parent_node, element, css_atom.pseudo);
  },
  _match_pseudo: function(element, child, pseudo) {
    switch (pseudo) {
      case 'first-child': return this._is_first_child(element, child);
      case 'last-child':  return this._is_last_child(element, child);
      case 'only-child':  return this._is_only_child(element, child);
      default: return true;
    }
  },
  _is_first_child: function(element, child) {
    var node = element.firstChild;
    while (node) {
      if (node.nodeType == 1)
        return node == child;
      node = node.nextSibling;
    }
    return false;
  },
  _is_last_child: function(element, child) {
    var node = element.lastChild;
    while (node) {
      if (node.nodeType == 1)
        return node == child;
      node = node.previousSibling;
    }
    return false;
  },
  _is_only_child: function(element, child) {
    var found = null, node = element.firstChild;
    while (node) {
      if (node.nodeType == 1) {
        if (found)
          return false; 
        else
          found = node;
      } 
      node = node.nextSibling;
    }
    return found == child;
  },
  match_atom: function(element, css_atom) {
    if (typeof css_atom == 'string')
      css_atom = TestCaseUtil.Element.Selector._parse_atom(css_atom);
    return (css_atom.tag_name == '*' || element.tagName == css_atom.tag_name)
      && TestCaseUtil.Element.has_class_names(element, css_atom.class_names) 
      && TestCaseUtil.Element.Selector._match_css_attributes(element, css_atom.attrs)
      && (!css_atom.id || element.id == css_atom.id);
  },
  _match_css_attributes: function(element, attrs_def) {
    for (var i=0; i < attrs_def.length; i++) {
      var match = false;
      switch (attrs_def[i].type) {
        case '|=':
          match = (typeof element[attrs_def[i].name] == 'string') && (
            element[attrs_def[i].name] == attrs_def[i].value ||
            new RegExp("(^|\-)" + attrs_def[i].value + "(\-|$)").test(element[attrs_def[i].name])
          );
          break;
        case '~=':
          match = (typeof element[attrs_def[i].name] == 'string') && (
            element[attrs_def[i].name] == attrs_def[i].value ||
            new RegExp("(^|\\s)" + attrs_def[i].value + "(\\s|$)").test(element[attrs_def[i].name])
          );
          break;
        default:
          match = element[attrs_def[i].name] == attrs_def[i].value;
      }
      if (!match) return false;
    }
    return true;
  },
  _parse_rule: function(css_rule) {
    var rules = css_rule.split(',');
    var recognized_rules = [];
    for (var i=0; i < rules.length; i++) {
      var rule = rules[i].replace(/^\s+|\s+$/g, '');
      var match, separator_pos, recognized_rule = [];
      var relation = null;
      while ((match = rule.match(/(\s*([~>+ ])\s*)[^=]/))) {
        separator_pos = rule.indexOf(match[0]);
        recognized_rule.push({
          rule: rule.substring(0, separator_pos).replace(/^\s+|\s+$/g, ''),
          rel: relation
        });
        relation = match[2]; 
        rule = rule.substr(separator_pos+(match[1].length==1 ? 1 : match[1].length-1)).replace(/^\s+|\s+$/g, '');
      }
      recognized_rule.push({ rule: rule, rel: relation });
      for (var j=0; j < recognized_rule.length; j++)
        TestCaseUtil.extend(recognized_rule[j], this._parse_atom(recognized_rule[j].rule));
      recognized_rules.push(recognized_rule);
    }
    return recognized_rules;
  },
  _id_re: /#[^\[\.]*/,
  _attrs_re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/, 
  _parse_atom: function(rule) {
    var atom = {
      tag_name: '*',      
      id: null,
      class_names: [],
      pseudo: null,
      attrs: []
    };
    var els = rule.split(':');
    if (els.length > 1) {
      atom.pseudo = els[1].toLowerCase();
      rule = els[0];
    }
    while ((attr_match = rule.match(this._attrs_re))) {
      atom.attrs.push({
        name: attr_match[1],
        type: attr_match[2],
        value: attr_match[5]
      });
      rule = rule.replace(attr_match[0], '');
    }
    if ((id_match = rule.match(this._id_re))) {
      atom.id = id_match[0].substr(1);
      rule = rule.replace(id_match[0], '');
    }
    atom.class_names = rule.split('.');
    atom.tag_name = atom.class_names.shift().toUpperCase();
    if (atom.tag_name == '')
      atom.tag_name = '*';
    return atom;
  }
};

/* ------------------------------------------------------------------- */

TestCaseUtil.Diff = {
  calc: function(obj1, obj2) {
    if (TestCaseUtil.get_object_class_name(obj1) != TestCaseUtil.get_object_class_name(obj2))
      throw "The given objects are instances of different classes";
    if (obj1 == null || obj2 == null) throw "One of the objects is null";
    if (obj1 == obj2) return [];
    switch (typeof obj1) {
      case 'string':   return TestCaseUtil.Diff._string_diff.apply(TestCaseUtil.Diff, [obj1, obj2]);
      case 'function': return TestCaseUtil.Diff._string_diff.apply(TestCaseUtil.Diff, [obj1.toString(), obj2.toString()]);
      case 'boolean':
      case 'number':   return obj1 == obj2 ? [] : [{del: obj1}, {ins: obj2}];
      default:         return TestCaseUtil.Diff[obj1 instanceof Array ? '_array_diff' : '_object_diff'].apply(TestCaseUtil.Diff, [obj1, obj2]);
    }
  },
  _string_diff: function(str1, str2) {
    return this.__compact(
      this.__calc_diff(str1.split("\n"), str2.split("\n")), 
      function (group, entry) { return group +"\n"+ entry; }
    );
  },
  _array_diff: function(list1, list2) {
    var diff = this.__calc_diff(list1, list2);
    for (var i=0; i < diff.length; i++) {
      var key = 'eql';
      if (typeof diff[i]['del'] != 'undefined') {
        key = 'del';
      } else if (typeof diff[i]['ins'] != 'undefined') {
        key = 'ins';
      }
    }
    return diff;
  },
  _object_diff: function(obj1, obj2) {
    var lists = [[], []], objects = [obj1, obj2];
    for (var i=0; i < 2; i++) {
      for (var key in objects[i]) {
        lists[i].push( key+"{:_key-sep_:}"+TestCaseUtil.to_s(objects[i][key]) );
      }
    }
    var diff = this.__calc_diff(lists[0], lists[1]);
    var collect = { eql: {}, ins: {}, del: {} };
    for (var i=0; i < diff.length; i++) {
      var key = 'eql';
      if (diff[i]['ins']) {
        key = 'ins';
      } else if (diff[i]['del']) {
        key = 'del';
      }
      var a_key = diff[i][key].split("{:_key-sep_:}")[0];
      collect[key][a_key] = (diff[i]['ins'] ? obj2 : obj1)[a_key];
    }
    var diff = [], keys = ['del', 'ins', 'eql'];
    for (var i=0; i < keys.length; i++) {
      if (!TestCaseUtil.equal(collect[keys[i]], {})) {
        var el = {}; el[keys[i]] = collect[keys[i]];
        diff.push(el);
      }
    }
    return diff;
  },
  __calc_diff: function(first, second) {
    var diff = [], start_k = 0;
    for (var i=0; i < first.length; i++) {
      var eql_found = false;
      var inses = [];
      for (var k=start_k; k < second.length; k++) {
        if (TestCaseUtil.equal(first[i], second[k])) {
          eql_found = second[k];
          start_k = k+1;
          break;
        } else {
          inses.push(second[k]);
        }
      }
      if (eql_found) {
        for (var j=0; j < inses.length; j++) {
          diff.push({ins: inses[j]});
        }
        diff.push({eql: eql_found});
      } else {
        diff.push({del: first[i]});
      }
    }
    for (var k=start_k; k < second.length; k++) {
      diff.push({ins: second[k]});
    }
    var only_eqls = true;
    for (var i=0; i < diff.length; i++) {
      if (diff[i]['del'] || diff[i]['ins']) {
        only_eqls = false;
        break;
      }
    }
    return only_eqls ? [] : diff;
  },
  __compact: function(diff, compactor) {
    if (diff.length == 0) { return diff; }
    var c_diff = [];
    var last_o = diff[0];
    for (var i=1; i < diff.length; i++) {
      var key = 'eql';
      if (typeof diff[i]['ins'] != 'undefined') {
        key = 'ins';
      } else if (typeof diff[i]['del'] != 'undefined') {
        key = 'del';
      }
      if (typeof last_o[key] != 'undefined') {
        last_o[key] = compactor(last_o[key], diff[i][key]);
      } else {
        c_diff.push(last_o);
        last_o = diff[i];
      }
    }
    c_diff.push(last_o);
    return c_diff;
  }
};
/* ------------------------------------------------------------------- */

TestCaseUtil.Cookie = {
  enabled: function() {
    document.cookie = "test_cookie=test";
    return document.cookie.indexOf("test_cookie=test")!=-1;
  },
  set: function(key, value, keep_days) {
    if (keep_days) {
      var date = new Date();date.setTime(date.getTime()+(100*24*60*60*1000));
    }
    document.cookie = key+"="+escape(value)+(keep_days ? ";  expires="+date.toGMTString() : "");
  },
  get: function(key) {
    var value = '', pairs = document.cookie.split(";");
    for (var i=0; i < pairs.length; i++) {
      var pair = pairs[i].split('=');
      if (pair.length == 2 && pair[0].replace(/^\s+|\s+$/g, '') == key)
        value = pair[1].replace(/^\s+|\s+$/g, '');
    }
    return unescape(value);
  }
};
/* ------------------------------------------------------------------- */

var TestCase = TestCaseUtil.create_class();
TestCaseUtil.extend(TestCase, {
  version: "2.0.2",
  create: function(defs) {
    var Test = TestCaseUtil.create_class();
    TestCaseUtil.extend(Test, TestCase);
    Test.prototype = new TestCase();
    Test.extend = function(entries) {
      TestCaseUtil.extend(this.prototype, entries);
      this.wire_custom_assertions(entries);
    };
    Test.extend(defs || {});
    return Test;
  }
});
TestCaseUtil.extend(TestCase.prototype, {
  name: null,
  title: null,
  size: 0,
  failed: 0,
  passed: 0,
  assertions: 0,
  initialize: function(title) {
    this.title = (this.name || "Unnamed Test") + (title ? ": "+title : "");
    this.size = 0;
    this.passed = 0;
    this.assertations = 0;
    for (key in this) {
      if (key.substring(0, 4) == "test") {
        this.size ++;
      }
    }
  }
});

/* ------------------------------------------------------------------- */

TestCase.Starter = {
  run: function(title) {
    var test = this._get_test_instance();
    this._register_test(test);
    test.passed = 0;
    test.failed = 0;
    test.assertions = 0;
    if (this.reporter && this.reporter.ui_builder && 
        this.reporter.ui_builder.test_is_skipped(test)) {
          test.skipped = true;
          this.reporter.ui_builder.create_summary_report(this.reporter.current_test_block, test);
          return test;
        }
    this._call_before_all(test);
    try {
      var test_names = this._get_test_methods(test);
      for (var i=0; i < test_names.length; i++) {
        var name = test_names[i];
        this._report_test_started(name);
        this._call_setup(test);
        test.manual_failure = null;
        var test_start_assertion_num = test.assertions;
        try {
          test[name]();
          if (test.manual_failure) 
            throw new TestCase.Exception(test.manual_failure);
          test.passed++;
          this._report_test_passed(name);
        } catch (e) {
          if (e instanceof TestCase.Exception) {
            test.failed++;
            this._report_failed(name, e, test.assertions - test_start_assertion_num + 1);
          } else {
            throw e;
          }
        } finally {
          this._call_teardown(test);
        }
      }
    } finally {
      this._call_after_all(test);
    }
    return test;
  },
  run_on_load: function() {
    var $this = this;
    TestCaseUtil.Event.observe(window, 'load', function() {
      $this.run();
    });
  },
  _get_test_instance: function(title) {
    if (typeof this == 'object') {
      var test = this;
    } else {
      var Test = this;
      var test = new Test(title);
    }
    return test;
  },
  _get_test_methods: function(test) {
    var test_method_names = [];
    for (key in test)
      if (key.substring(0, 4) == "test" && typeof test[key] == 'function')
        test_method_names.push(key);
    if (navigator.userAgent.indexOf("MSIE") != -1) { test_method_names.reverse(true); } 
    return test_method_names;
  },
  _call_setup: function(test) {
    if (test['setup']) test.setup.apply(test);
    else if (test['setUp']) test.setUp.apply(test);
  },
  _call_teardown: function(test) {
    if (test['teardown']) test.teardown.apply(test);
    else if (test['tearDown']) test.tearDown.apply(test);
  },
  _call_before_all: function(test) {
    if (test['before_all']) test.before_all.apply(test);
    else if (test['beforeAll']) test.beforeAll.apply(test);
  },
  _call_after_all: function(test) {
    if (test['after_all']) test.after_all.apply(test);
    else if (test['afterAll']) test.afterAll.apply(test);
  },
  _register_test: function(test) {
    if (this.reporter) { this.reporter.register(test); }
  },
  _report_test_started: function(test_name) {
    if (this.reporter) { this.reporter.test_started(test_name); }
  },
  _report_test_passed: function(test_name) {
    if (this.reporter) { this.reporter.test_passed(test_name); }
  },
  _report_failed: function(test_name, error, assertion_num) {
    if (this.reporter) {
      this.reporter.test_failed(test_name, error, assertion_num);
    } else {
      throw error; 
    }
  }
};
/* ------------------------------------------------------------------- */

TestCase.Mocking = {
  mock: function(object, name, mock) {
    if (!object[name]) throw "The object have no method '"+name+"' to mock it up";
    var o_mocks = TestCase.Mocking._get_mocks_of(object);
    o_mocks[name] = o_mocks[name] || object[name];
    return object[name] = mock;
  },
  undo_mock: function(object, name) {
    if (this._has_mock_for(object, name)) {
      var o_mocks = TestCase.Mocking._get_mocks_of(object);
      if (o_mocks[name]) {
        object[name] = o_mocks[name];
        o_mocks[name] = null;
      }
    }
    return object[name];
  },
  with_mock: function(object, name, mock, func, scope) {
    this.mock(object, name, mock);
    try {
      func.apply(scope);
    } finally {
      this.undo_mock(object, name);
    }
  },
  mock_effects: function() {
    this._run_if_mockable('mock_effects');
  },
  undo_effects_mock: function() {
    this._run_if_mockable('undo_effects_mock');
  },
  with_effects_mock: function(func, scope) {
    this.mock_effects();
    try {
      func.apply(scope);
    } finally {
      this.undo_effects_mock();
    }
  },
  mock_ajax: function(options) {
    this._run_if_mockable('mock_ajax', options);
  },
  undo_ajax_mock: function() {
    this._run_if_mockable('undo_ajax_mock');
  },
  with_ajax_mock: function(options, func, scope) {
    this.mock_ajax();
    try {
      func.apply(scope);
    } finally {
      this.undo_ajax_mock();
    }
  },
  __mock_ups: [],
  _get_mocks_of: function(object) {
    var o_mocks = null;
    var mockups = TestCase.Mocking.__mock_ups;
    for (var i=0; i < mockups.length; i++) {
      if (mockups[i]['object'] == object) {
        o_mocks = mockups[i];
        break;
      }
    }
    if (!o_mocks) {
      o_mocks = {
        'object': object, 'mocks': {}
      };
      TestCase.Mocking.__mock_ups.push(o_mocks);
    }
    return o_mocks['mocks'];
  },
  _has_mock_for: function(object, name) {
    var mockups = TestCase.Mocking.__mock_ups;
    for (var i=0; i < mockups.length; i++) {
      if (mockups[i]['object'] == object) {
        return typeof mockups[i]['mocks'][name] != 'undefined' && mockups[i]['mocks'][name] != null;
      }
    }
    return false;
  },
  _mockable_libs: ['Prototype', 'MooTools', 'jQuery'],
  _run_if_mockable: function() {
    var args = TestCaseUtil.to_a(arguments);
    var method_name = args.shift();
    if (method_name == 'mock_ajax') {
      this._raw_mockup_ajax.apply(this, args);
    } else if (method_name == 'undo_ajax_mock') {
      this._raw_undo_ajax_mockup();
    }
    for (var i=0; i < this._mockable_libs.length; i++) {
      if (self[this._mockable_libs[i]]) {
        return this[this._mockable_libs[i]][method_name].apply(this[this._mockable_libs[i]], args);
      }
    }
  },
  _raw_mockup_ajax: function(params) {
    if (TestCaseUtil.Browser.IE) {
      this.__ms_raw_ajax_mock(params);
    } else {
      this.__w3c_raw_ajax_mock(params);
    }
  },
  __w3c_raw_ajax_mock: function(params) {
    if (!(new XMLHttpRequest instanceof TestCase.Mocking.FakeXMLHttpRequest)) {
      this.mock(self, 'XMLHttpRequest', function() {
        return new TestCase.Mocking.FakeXMLHttpRequest(params);
      });
    }
  },
  __ms_raw_ajax_mock: function(params) {
    if (!(new ActiveXObject('Msxml2.XMLHTTP') instanceof TestCase.Mocking.FakeXMLHttpRequest)) {
      var _ = ActiveXObject;
      this.mock(self, 'ActiveXObject', function(param) {
        if (param.indexOf('XMLHTTP') != -1) {
          return new TestCase.Mocking.FakeXMLHttpRequest(params);
        } else {
          return  new _(param);
        }
      });
    }
  },
  _raw_undo_ajax_mockup: function() {
    if (TestCaseUtil.Browser.IE) {
      this.undo_mock(self, 'ActiveXObject');
    } else {
      this.undo_mock(self, 'XMLHttpRequest');
    }
  }
};

/* ------------------------------------------------------------------- */

TestCase.Mocking.FakeXMLHttpRequest = TestCaseUtil.create_class({
  onreadystatechange: null,
  readyState:         null,
  status:             null,
  statusText:         null,
  responseText:       null,
  responseXML:        null,
  fakeData:           null,
  initialize: function(options) {
    this.fakeData = TestCaseUtil.extend({
      status: 200,
      xml: null,
      text: '',
      headers: {}
    }, options || {});
    this.onreadystatechange = function() {};
    this.readyState = 0; 
  },
  __onSend: function() {
    this.readyState = 2; 
    this.status = this.fakeData.status;
    this.onreadystatechange();
    this.readyState = 3; 
    this.responseText = '';
    this.onreadystatechange();
    this.readyState = 4; 
    this.responseText = this.fakeData.text;
    this.responseXML = this.fakeData.xml;
    this.onreadystatechange();
  },
  open: function(method, url, async, user, password) {
    this.readyState = 1; 
  },
  send: function(body) {
    this.__onSend();
  },
  setRequestHeader: function(name, value) {
  },
  getResponseHeader: function(name) {
    for (var key in this.fakeData.headers) {
      if (key.toLowerCase() == name.toLowerCase())
        return this.fakeData.headers[key];
    }
    return null;
  },
  getAllResponseHeaders: function() {
    var headers = [];
    for (var key in this.fakeData.headers) {
      headers.push(key+": "+this.fakeData.headers[key]);
    }
    return headers.join("\n\n");
  },
  overrideMimeType: function(mimetype) {},
  addEventListener: function(type, listener, useCapture) {},
  removeEventListener: function(type, listener, useCapture) {},
  dispatchEvent: function(event) {},
  dispatchException: function(e) {
    throw e;
  },
  abort: function() {}
});
/* ------------------------------------------------------------------- */

TestCase.Mocking.Prototype = {
  hide_effects: ['Fade', 'SlideUp', 'BlindUp', 'SwitchOff'],
  show_effects: ['Appear', 'SlideDown', 'BlindDown', 'Grow'],
  mock_effects: function() {
    if (!self['Effect']) return false;
    for (var i=0; i < this.hide_effects.length; i++) {
      TestCase.mock(Effect, this.hide_effects[i], function(element, options) {
        Element.hide(element);
        if (options.afterFinish) options.afterFinish();
        return element;
      });
      TestCase.mock(Effect, this.show_effects[i], function(element, options) {
        Element.show(element);
        if (options.afterFinish) options.afterFinish();
        return element;
      });
    }
  },
  undo_effects_mock: function() {
    if (!self['Effect']) return false;
    for (var i=0; i < this.hide_effects.length; i++) {
      TestCase.undo_mock(Effect, this.hide_effects[i]);
      TestCase.undo_mock(Effect, this.show_effects[i]);
    }
  },
  mock_ajax: function(options) {
    if (!self['Ajax']) { return false; }
    TestCase.mock(Ajax, 'getTransport', function() {
      return new TestCase.Mocking.FakeXMLHttpRequest(options);
    });
  },
  undo_ajax_mock: function() {
    if (!self['Ajax']) { return false; }
    TestCase.undo_mock(Ajax, 'getTransport');
  }
};
/* ------------------------------------------------------------------- */

TestCase.Mocking.MooTools = {
  mock_effects: function() {
    if (!self['Fx']) { return false; }
    if (Fx['Tween']) {
      TestCase.mock(Fx.Tween.prototype, 'start', function(property, from, to) {
        this.onStart();
        this.set(property, from);
        if (to) {
          this.set(property, to);
        }
        this.onComplete();
      });
    }
    if (Fx['Morph']) {
      TestCase.mock(Fx.Morph.prototype, 'start', function(properties) {
        this.onStart();
        var from = {}, to = {};
        for (var key in properties) {
          if (properties[key] instanceof Array) {
            from[key] = properties[key][0];
            to[key] = properties[key][1];
          } else {
            from[key] = properties[key];
          }
        }
        this.set(from);
        if (!TestCaseUtil.equal(to, {})) {
          this.set(to);
        }
        this.onComplete();
      });
    }
  },
  undo_effects_mock: function() {
    if (!self['Fx']) { return false; }
    if (Fx['Tween']) TestCase.undo_mock(Fx.Tween.prototype, 'start');
    if (Fx['Morph']) TestCase.undo_mock(Fx.Morph.prototype, 'start');
  },
  mock_ajax: function(options) {
    if (!self['Request']) { return false; }
    var old_initialize = Request.prototype.initialize;
    TestCase.mock(Request.prototype, 'initialize', function() {
      old_initialize.apply(this, arguments);
      this.xhr = new TestCase.Mocking.FakeXMLHttpRequest(options);
    });
  },
  undo_ajax_mock: function() {
    if (!self['Request']) { return false; }
    TestCase.undo_mock(Request.prototype, 'initialize');
  }
};
/* ------------------------------------------------------------------- */

TestCase.Mocking.jQuery = {
  mock_effects: function() {
  },
  undo_effects_mock: function() {
  },
  mock_ajax: function(options) {
  },
  undo_ajax_mock: function() {
  }
};
/* ------------------------------------------------------------------- */

TestCase.Fires = {
  FIRES_DEFAULT_MOUSE_EVENT_OPTIONS: {
    pointerX:   0,
    pointerY:   0,
    button:     0,
    bubbles:    true,
    cancelable: true,
    altKey:     false,
    ctrlKey:    false,
    shiftKey:   false,
    metaKey:    false
  },
  FIRES_DEFAULT_KEYBOARD_EVENT_OPTIONS: {
    keyCode:    0,
    charCode:   0,
    bubbles:    true,
    cancelable: true,
    altKey:     false,
    ctrlKey:    false,
    shiftKey:   false,
    metaKey:    false
  },
  fire_mouse_event: function(element, eventName, options) {
    return this._fire_event(this._$(element),
      this._create_mouse_event(this._$(element), eventName, options)
    );
  },
  fire_key_event: function(element, eventName, keyCode, options) {
    return this._fire_event(this._$(element),
      this._create_key_event(this._$(element), eventName,
        TestCaseUtil.extend((options || {}), {keyCode: keyCode, charCode: keyCode})
      )
    );
  },
  fire_click: function(element, options) {
    return this.fire_mouse_event(element, 'click', options);
  },
  fire_middle_click: function(element, options) {
    return this.fire_mouse_event(element, 'middleclick', TestCaseUtil.extend({button: 1}, options || {}));
  },
  fire_right_click: function(element, options) {
    return this.fire_mouse_event(element, 'rightclick', TestCaseUtil.extend({button: 2}, options || {}));
  },
  fire_double_click: function(element, options) {
    return this.fire_mouse_event(element, 'dblclick', options);
  },
  fire_mouse_down: function(element, options) {
    return this.fire_mouse_event(element, 'mousedown', options);
  },
  fire_mouse_up: function(element, options) {
    return this.fire_mouse_event(element, 'mouseup', options);
  },
  fire_mouse_over: function(element, options) {
    return this.fire_mouse_event(element, 'mouseover', options);
  },
  fire_mouse_out: function(element, options) {
    return this.fire_mouse_event(element, 'mouseout', options);
  },
  fire_mouse_move: function(element, options) {
    return this.fire_mouse_event(element, 'mousemove', options);
  },
  fire_key_press: function(element, key, options) {
    return this.fire_key_event(element, 'keypress', key, options);
  },
  fire_key_down: function(element, key, options) {
    return this.fire_key_event(element, 'keydown', key, options);
  },
  fire_key_up: function(element, key, options) {
    return this.fire_key_event(element, 'keyup', key, options);
  },
  _$: function(element) {
    element = TestCaseUtil.$(element);
    if (element == document && document.createEvent && !element.dispatchEvent)
      element = document.documentElement;
    return element;
  },
  _fire_event: function(element, event) {
    if (document.createEvent) {
      element.dispatchEvent(event);
    } else {
      if (TestCaseUtil.Element.up(element, 'body')) { 
        if (event.eventType != 'onmiddleclick') 
          element.fireEvent(event.eventType, event);
      } else {
        this.fail("Please put your element on the page to fire an event on it in IE");
      }
    }
    return event;
  },
  _create_mouse_event: function(element, eventName, options) {
    return (document.createEvent ? this._create_w3c_mouse_event : this._create_ie_mouse_event).apply(
      this, [element, eventName, TestCaseUtil.extend(this.FIRES_DEFAULT_MOUSE_EVENT_OPTIONS, options || {})]);
  },
  _create_w3c_mouse_event: function(element, eventName, options) {
    var event = document.createEvent("MouseEvents");
    event.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
      eventName == 'dblclick' ? 2 : 1, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
      options.ctrlKey, options.altKey, options.shiftKey, options.metakey, options.button, element
    );
    event.eventName = eventName;
    return event;
  },
  _create_ie_mouse_event: function(element, eventName, options) {
    var event = this._create_ie_event(eventName, options);
    event.clientX = options.pointerX;
    event.clientY = options.pointerY;
    event.button = options.button == 1 ? 4 : options.button; 
    return event;
  },
  _create_key_event: function(element, eventName, options) {
    return (document.createEvent ? this._create_w3c_keyboard_event : this._create_ie_keyboard_event).apply(
      this, [eventName, TestCaseUtil.extend(this.FIRES_DEFAULT_KEYBOARD_EVENT_OPTIONS, options || {})]);
  },
  _create_w3c_keyboard_event: function(eventName, options) {
    return (TestCaseUtil.Browser.Gecko ? this._create_gecko_keyboard_event : (
        TestCaseUtil.Browser.WebKit ? this._create_webkit_keyboard_event : this._create_dom2_ui_event
      )).apply(this, [eventName, options]);
  },
  _create_gecko_keyboard_event: function(eventName, options) {
    var event = document.createEvent("KeyboardEvent");
    event.initKeyEvent(eventName,
      options.bubbles, options.cancelable, document.defaultView,
      options.ctrlKey, options.altKey, options.shiftKey, options.metakey,
      options.keyCode, options.charCode
    );
    event.eventName = eventName;
    return event;
  },
  _create_webkit_keyboard_event: function(eventName, options) {
    var event = document.createEvent("KeyboardEvent");
    event.initKeyboardEvent(eventName,
      options.bubbles, options.cancelable, document.defaultView,
      null, 0,
      options.ctrlKey, options.altKey, options.shiftKey, options.metakey
    );
    event.eventName = eventName;
    return event;
  },
  _create_dom2_ui_event: function(eventName, options) {
    var event = document.createEvent("UIEvent");
    event.initUIEvent(eventName, options.bubbles, options.cancelable, document.defaultView, 1);
    event.keyCode = options.keyCode;
    event.charCode = options.charCode;
    event.altKey = options.altKey;
    event.metaKey = options.metaKey;
    event.ctrlKey = options.ctrlKey;
    event.shiftKey = options.shiftKey;
    event.eventName = eventName;
    return event;
  },
  _create_ie_keyboard_event: function(eventName, options) {
    var event = this._create_ie_event(eventName, options);
    event.keyCode = options.keyCode;
    return event;
  },
  _create_ie_event: function(eventName, options) {
    var event = document.createEventObject();
    event.eventName = eventName == 'rightclick' ? 'contextmenu' : eventName;
    event.type = event.eventType = "on" + event.eventName;
    event.altKey = options.altKey;
    event.ctrlKey = options.ctrlKey;
    event.shiftKey = options.shiftKey;
    return event;
  }
};
/* ------------------------------------------------------------------- */

TestCase.Exception = TestCaseUtil.create_class(Error, {
  test: null,
  assertion: null,
  message: null,
  initialize: function(test, assertion, message) {
    this.test = test;
    this.assertion = assertion;
    this.message = message;
  },
  toString: function() {
    return "TestCase.Exception: "+this.message;
  }
});
TestCase.ProblemException = TestCaseUtil.create_class(TestCase.Exception, {
  problem: null,
  initialize: function(test, assertion, problem, message) {
    this.test = test;
    this.assertion = assertion;
    this.problem = problem;
    this.message = message;
  },
  toString: function() {
    return "TestCase.ProblemException: "+this.problem+(this.message ? " ("+this.message+")" : "");
  }
});
TestCase.WrongValueException = TestCaseUtil.create_class(TestCase.Exception, {
  expected: null,
  received: null,
  format: false,
  initialize: function(test, assertion, expected, received, message, format) {
    this.test = test;
    this.assertion = assertion;
    this.expected = expected;
    this.received = received;
    this.message = message;
    this.format = format;
  },
  toString: function() {
    return "TestCase.WrongValueException: "+this.expected+" -> "+this.received;
  }
});
/* ------------------------------------------------------------------- */

TestCase.Messaging = {
  fail: function(message) {
    this.manual_failure = message;
    throw new TestCase.Exception(this, this._last_assertion(), message);
  },
  debug: function(variable) {
    alert(TestCaseUtil.debug(variable));
  },
  throw_problem: function(error, message) {
    throw new TestCase.ProblemException(this, this._last_assertion(), error, message);
  },
  throw_unexp: function(expected, received, message, format) {
    throw new TestCase.WrongValueException(this, this._last_assertion(), expected, received, message, format);
  },
  _last_assertion: function() {
    return this['assert_'+this.last_assert_name];
  }
};

/* ------------------------------------------------------------------- */

TestCase.Assertions = {
  assert_true: function(value, message) {
    if (!value)
      this.throw_unexp(true, value, message);
  },
  assert_false: function(value, message) {
    if (value)
      this.throw_unexp(false, value, message);
  },
  assert_null: function(value, message) {
    if (value != null)
      this.throw_unexp(null, value, message);
  },
  assert_not_null: function(value, message) {
    if (value == null)
      this.throw_problem("The value is null", message);
  },
  assert_type_of: function(type, value, message) {
    if (typeof(value) != type)
      this.throw_unexp(type.toLowerCase(), typeof(value), message);
  },
  assert_instance_of: function(type, value, message) {
    if (!(value instanceof type))
      this.throw_unexp(TestCaseUtil.get_class_name(type), TestCaseUtil.get_object_class_name(value), message);
  },
  assert_equal: function(expected, testing, message) {
    if (!TestCaseUtil.equal(expected, testing))
      this.throw_unexp(expected, testing, message, true); 
  },
  assert_not_equal: function(expected, testing, message) {
    if (TestCaseUtil.equal(expected, testing))
      this.throw_problem("Received values are equal", message);
  },
  assert_same: function(expected, testing, message) {
    if (expected != testing)
      this.throw_problem("Values do not point to the same object", message);
  },
  assert_not_same: function(expected, testing, message) {
    if (expected == testing)
      this.throw_problem("Values point to the same object", message);
  },
  assert_match: function(pattern, string, message) {
    if( !string.match(pattern) )
      this.throw_problem("'"+string+"' doesn't match '"+pattern+"'", message);
  },
  assert_not_match: function(pattern, string, message) {
    if( string.match(pattern) )
      this.throw_problem("'"+string+"' match '"+pattern+"'", message);
  },
  assert_throws: function() {
    var params = this._assert_throw_params.apply(this, arguments);
    var catched = true;
    try { params.callback.apply(params.scope_element);
      catched = false;
    } catch (e) { catched = e; }
    if (!catched)
      this.throw_problem('No exception raised during the call', params.message);
    else if (params.exception_type) {
      if (!this._type_of(catched, params.exception_type))
        this.throw_unexp("Exception with type of: "+params.exception_type, catched, params.message);
    }
  },
  assert_nothing_thrown: function() {
    var params = this._assert_throw_params.apply(this, arguments);
    try { params.callback.apply(params.scope_element);
    } catch (e) {
      if (params.exception_type) {
        if (this._type_of(e, params.exception_type))
          this.throw_problem("Unexpected exception with type '"+params.exception_type+
            "' has been raised: \""+e.toString()+"\"", params.message);
      } else {
        this.throw_problem('Unexpected exception has been raised: "'+e.toString()+'"', params.message);
      }
    }
  },
  _assert_throw_params: function() {
    var params = typeof arguments[1] == 'function' ?
      { exception_type: arguments[0], callback: arguments[1]} :
      { callback: arguments[0]};
    params.message = null;
    for (var i=0; i < arguments.length; i++)
      if (arguments[i].scope)
        params.scope_element = arguments[i].scope;
    if (arguments.length > 1) {
      for (var i=arguments.length-1; i > 0; i--) {
        if (arguments[i] == params.callback) {
          break;
        } else if (typeof arguments[i] == 'string') {
          params.message = arguments[i];
        } else if (typeof params.scope_element == 'undefined') {
          params.scope_element = arguments[i];
        }
      }
    }
    return params;
  },
  _type_of: function(object, _type) { 
    switch(_type) {
      case 'number':
      case Number: return typeof object == 'number';
      case 'string':
      case String: return typeof object == 'string';
      default:     return object instanceof _type;
    }
  },
  assert_called: function() {
    var problems = [];
    this._execute_wrapped_call(arguments, problems,
      function(flag, call) {
        if (!flag) problems.push("Attribute '"+call[1]+"' was not called");
      }
    );
  },
  assert_not_called: function() {
    var problems = []; 
    this._execute_wrapped_call( arguments, problems,
      function(flag, call) {
        if (flag) problems.push("Attribute '"+call[1]+"' was unexpetedly called");
      }
    );
  },
  _execute_wrapped_call: function(args, problems, callback) {
    var params = this._assert_called_params.apply(this, args);
    try {
      var flags = [];
      for (var i=0; i < params.calls.length; i++) {
        flags[i] = false;
        this.mock(params.calls[i][0], params.calls[i][1], (function(flags, i, object, name) {
          var method = object[name];
          return function() {
            flags[i] = true;
            return method.apply(object, arguments);
          };
        })(flags, i, params.calls[i][0], params.calls[i][1]));
      }
      params.callback.apply(params['scope_element']);
      for (var i=0; i < flags.length; i++) {
        if (params.calls[i])
          callback.apply(this, [flags[i], params.calls[i]]);
      }
    } finally {
      for (var i=0; i < params.calls.length; i++) {
        this.undo_mock(params.calls[i][0], params.calls[i][1]);
      }
    }
    if (problems.length) {
      this.throw_problem(problems.join("\n"), params.message);
    }
  },
  _assert_called_params: function() {
    var params = { calls: [], callback: null, message: null };
    if (typeof arguments[1] == 'string') {
      params.calls = [[arguments[0], arguments[1]]];
      params.callback = arguments[2];
    } else if (arguments[0] instanceof Array) {
      if (arguments[0].length == 2 && typeof(arguments[0][1]) == 'string') {
        params.calls = [arguments[0]];
      } else {
        params.calls = arguments[0];
      }
      params.callback = arguments[1];
    }
    for (var i=0; i < arguments.length; i++)
      if (arguments[i].scope)
        params.scope_element = arguments[i].scope;
    if (arguments.length > 2) {
      for (var i=arguments.length-1; i > 0; i--) {
        if (arguments[i] == params.callback) {
          break;
        } else if (typeof arguments[i] == 'string') {
          params.message = arguments[i];
        } else if (typeof params.scope_element == 'undefined') {
          params.scope_element = arguments[i];
        }
      }
    }
    return params;
  },
  assert_exists: function(css_rule, message) {
    if (TestCaseUtil.$$(css_rule).length == 0)
      this.throw_problem("Cannot find any element like '"+css_rule+"'", message);
  },
  assert_not_exists: function(css_rule, message) {
    if (TestCaseUtil.$$(css_rule).length)
      this.throw_problem("Element '"+css_rule+"' presents on the page", message);
  },
  assert_has_child: function(element, css_rule, message) {
    if (!TestCaseUtil.Element.down(element, css_rule))
      this.throw_problem("Element has no child element like '"+css_rule+"'", message);
  },
  assert_has_no_child: function(element, css_rule, message) {
    if (TestCaseUtil.Element.down(element, css_rule))
      this.throw_problem("Element has child element '"+css_rule+"'", message);
  },
  assert_has_parent: function(element, css_rule, message) {
    if (!TestCaseUtil.Element.up(element, css_rule))
      this.throw_problem("Element has no parent element like '"+css_rule+"'", message);
  },
  assert_has_no_parent: function(element, css_rule, message) {
    if (TestCaseUtil.Element.up(element, css_rule))
      this.throw_problem("Element has parent element '"+css_rule+"'", message);
  },
  assert_visible: function(element_or_css_rule, message) {
    var elements = typeof element_or_css_rule == 'string' ?  TestCaseUtil.$$(element_or_css_rule) : [element_or_css_rule];
    if (!elements.length || !elements[0])
      this.throw_unexp("Element is visible", "Element is not found", message);
    else {
      for (var i=0; i < elements.length; i++) {
        var element_style = this.__get_element_style(elements[i]);
        var element_display = elements[i].style.display || element_style['display'];
        var element_visibility = elements[i].style.visibility || element_style['visibility'];
        if (element_display == 'none' || element_visibility == 'hidden')
          this.throw_problem("Element is not visible", message);
      }
    }
  },
  assert_hidden: function(element_or_css_rule, message) {
    var elements = typeof element_or_css_rule == 'string' ? TestCaseUtil.$$(element_or_css_rule) : [element_or_css_rule];
    if (!elements.length || !elements[0])
      this.throw_unexp("Element is hidden", "Element is not found", message);
    else {
      for (var i=0; i < elements.length; i++) {
        var element_style = this.__get_element_style(elements[i]);
        var element_display = elements[i].style.display || element_style['display'];
        var element_visibility = elements[i].style.visibility || element_style['visibility'];
        if (element_display != 'none' && element_visibility != 'hidden')
          this.throw_problem("Element is visible", message);
      }
    }
  },
  assert_has_class_name: function(element, class_name, message) {
    if (!TestCaseUtil.Element.has_class_name(element, class_name))
      this.throw_problem("Element has no class-name '"+class_name+"'", message);
  },
  assert_has_no_class_name: function(element, class_name, message) {
    if (TestCaseUtil.Element.has_class_name(element, class_name))
      this.throw_problem("Element have class-name '"+class_name+"'", message);
  },
  assert_has_attribute: function(element, name, message) {
    if (element.getAttribute(name) == null)
      this.throw_problem("Element has no attribute '"+name+"'", message);
  },
  assert_has_no_attribute: function(element, name, message) {
    if (element.getAttribute(name) != null)
      this.throw_problem("Element has attribute '"+name+"'", message);
  },
  assert_style: function(element, style, message) {
    var element_computed_style = this.__get_element_style(element);
    var element_style = {};
    for (var key in style) {
      if (key.toLowerCase().substring(key.length-5) == 'color') {
        var t = document.createElement('span');
        t.style[key] = style[key];
        style[key] = t.style[key];
      }
      if (element.style && element.style[key]) {
        element_style[key] = element.style[key];
      } else if (element_computed_style[key]) {
        element_style[key] = element_computed_style[key];
      } else {
        element_style[key] = null;
      }
    };
    if (!TestCaseUtil.equal(style, element_style))
      this.throw_unexp("Element has style:\n "+TestCaseUtil.to_s(style), 
        TestCaseUtil.to_s(element_style), message);
  },
  __get_element_style: function(element) {
    var style = document.defaultView ? document.defaultView.getComputedStyle(element, null) : element.currentStyle;
    return style ? style : {};
  }
};
/* ------------------------------------------------------------------- */

TestCase.AssertionsExtender = {
  add_assertion: function(name, func) {
    var name = name.substr(0, 7) == 'assert_' ? name.substr(7, name.length) : name;
    var assert = {}; assert['assert_'+name] = function() {
      this._count_assert(name);
      func.apply(this, arguments);
    };
    TestCaseUtil.extend(this.prototype ? this.prototype : this, assert);
    TestCaseUtil.extend_with_camelized_aliases(this.prototype ? this.prototype : this, assert);
  },
  add_assertions: function(module) {
    TestCaseUtil.extend(this.prototype ? this.prototype : this, module);
    TestCaseUtil.extend_with_camelized_aliases(this.prototype ? this.prototype : this, module);
    for (var key in module) {
      if (key.substr(0, 7) == 'assert_')
        this.add_assertion(key, module[key]);
    }
  },
  wire_custom_assertions: function(test_def) {
    for (var name in test_def) {
      if (name.substr(0, 6) == 'assert' && typeof(test_def[name]) == 'function') {
        this.add_assertion(name.replace(/([a-z]+)([A-Z])/g, '$1_$2').toLowerCase(), this.prototype[name]);
      }
    }
  },
  _count_assert: function(name) {
    this.assertions++;
    this.last_assert_name = name;
  }
};
/* ------------------------------------------------------------------- */

TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Starter);
TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Fires);
TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Mocking);
var __mock_aliaces = {
  mockUp:              TestCase.mock,
  mockup:              TestCase.mock,
  undo_mockup:         TestCase.undo_mock,
  undoMockup:          TestCase.undo_mock,
  undoMockUp:          TestCase.undo_mock,
  with_mocked:         TestCase.with_mock,
  withMocked:          TestCase.with_mock,
  mockupEffects:       TestCase.mock_effects,
  mockUpEffects:       TestCase.mock_effects,
  mockup_effects:      TestCase.mock_effects,
  undo_effects_mockup: TestCase.undo_effects_mock,
  undoEffectsMockup:   TestCase.undo_effects_mock,
  undoMockUpEffects:   TestCase.undo_effects_mock,
  undoMockupEffects:   TestCase.undo_effects_mock,
  undo_mockup_effects: TestCase.undo_effects_mock,
  mockUpAjax:          TestCase.mock_ajax,
  mockupAjax:          TestCase.mock_ajax,
  mockup_ajax:         TestCase.mock_ajax,
  undoMockupAjax:      TestCase.undo_ajax_mock,
  undoMockUpAjax:      TestCase.undo_ajax_mock,
  undo_mockup_ajax:    TestCase.undo_ajax_mock,
  undo_ajax_mockup:    TestCase.undo_ajax_mock,
  undoAjaxMockup:      TestCase.undo_ajax_mock,
  undoAjaxMockUp:      TestCase.undo_ajax_mock,
  with_ajax_mockup:    TestCase.with_ajax_mock
};
TestCaseUtil.extend(TestCase,           __mock_aliaces);
TestCaseUtil.extend(TestCase.prototype, __mock_aliaces);
TestCaseUtil.extend(TestCase.prototype, TestCase.Messaging);
TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.AssertionsExtender);
TestCase.add_assertions(TestCase.Assertions);
TestCaseUtil.extend(TestCase.prototype, {
  flunk: TestCase.prototype.fail,
  assert: TestCase.prototype.assert_true,
  assert_nil: TestCase.prototype.assert_null,
  assert_not_nil: TestCase.prototype.assert_not_null,
  assert_kind_of: TestCase.prototype.assert_type_of,
  assert_no_match: TestCase.prototype.assert_not_match,
  assertEquals: TestCase.prototype.assert_equal,
  assertNotEquals: TestCase.prototype.assert_not_equal,
  assertNoMatch: TestCase.prototype.assert_not_match
});
TestCase.prototype.util = TestCaseUtil;

/* ------------------------------------------------------------------- */

var TestSuite = TestCaseUtil.create_class({
  title: null,
  reporter: null,
  test_cases: [],
  initialize: function() {
    this.test_cases = [];
    this.add.apply(this, arguments);
  },
  add: function() {
    for (var i=0; arguments.length > i; i++) {
      this.test_cases.push(arguments[i]);
    }
    this.test_cases = TestCaseUtil.Array.uniq(this.test_cases);
    return this;
  },
  remove: function() {
    for (var i=0; i < arguments.length; i++)
      this.test_cases = TestCaseUtil.Array.without(this.test_cases, arguments[i]);
    return this;
  },
  run: function(title) {
    for (var i=0; i < this.test_cases.length; i++)
      this.test_cases[i].run();
    return this;
  },
  run_on_load: function() {
    var $this = this;
    TestCaseUtil.Event.observe(window, 'load', function() {
      $this.run();
    });
  }
});
TestSuite.prototype.runOnLoad = TestSuite.prototype.run_on_load
/* ------------------------------------------------------------------- */

var TestReporter = TestCaseUtil.create_class({
  ui_builder: null,
  report_container: null,
  current_test: null,
  current_test_block: null,
  current_test_done_bar: null,
  seen_tests: null,
  initialize: function(ui_builder){
    this.ui_builder = ui_builder || new TestReporter.UIBuilder();
    this.report_container = this.ui_builder.create_report_container();
    this._is_prepared = false;
    this.seen_tests = [];
  },
  register: function(test) {
    this._check_prepare();
    this.seen_tests.push(test);
    this.current_test = test;
    this.current_test_block = this.ui_builder.create_test_block(test, this.report_container);
    this.current_test_done_bar = this.ui_builder.create_done_bar(this.current_test_block);
    this.current_test.__start_time = new Date().getTime(); 
  },
  test_started: function(test_name) {},
  test_passed: function(test_name) {
    this.current_test_done_bar.style.width = ((this.current_test.passed / this.current_test.size) * 100)+'%';
    this.check_for_test_summary_report();
  },
  test_failed: function(test_name, e, assertion_num) {
    this.ui_builder.create_error_report(
      this.current_test_block, test_name, e, assertion_num, this.current_test[test_name].toString()
    );
    this.check_for_test_summary_report();
  },
  check_for_test_summary_report: function() {
    if (this.current_test.size == (this.current_test.passed + this.current_test.failed))
      this.ui_builder.create_summary_report(this.current_test_block, this.current_test);
    this.ui_builder.update_overall_summary(this.report_container, this.seen_tests);
  },
  prepare: function() {},
  _check_prepare: function() {
    if(!this._is_prepared){
      this.prepare();
      this._is_prepared = true;
    }
  }
});

/* ------------------------------------------------------------------- */

TestReporter.UIBuilder = TestCaseUtil.create_class({
  DEFAULT_VIEW_NAME: 'summary-and-failed',
  initialize: function() {},
  create_report_container: function() {
    var block = document.createElement("DIV");
    block.innerHTML = ''+
      '<div id="testcase-report-block">'+
// msch      '  <h1>Test Report</h1>'+
      '  <span style="font-weight:bold;font-size:130%">Test Report</span>'+
      '  <select id="testcase-view-switcher">'+
      '    <option value="summary-only">Display: Summary only</option>'+
      '    <option value="summary-and-failed">Display: Summary and Failed Cases</option>'+
      '    <option value="summary-and-active">Display: Summary and Active Cases</option>'+
      '    <option value="summary-and-skipped">Display: Summary and Skipped Cases</option>'+
      '    <option value="summary-and-all">Display: Summary and All Cases</option>'+
      '    <option value="cases-only">Display: Test Cases Only</option>'+
      '    <option value="active-cases-only">Display: Active Cases Only</option>'+
      '    <option value="skipped-cases-only">Display: Skipped Cases Only</option>'+
      '  </select>'+
      '  <ul id="test-cases-list">'+
      '    <li id="testcase-summary-block">'+
      '      <input type="checkbox" id="testcase-toggle-all-cases" title="Skip/Activate all TestCases" />'+
      '      <div class="test-name">Summary</div>'+
      '      <div class="progress-bar">'+
      '        <div class="done-bar"></div>'+
      '      </div>'+
      '      <div class="error-report-summary">'+
      '        Total Cases: 0 / Tests: 0 / Passed: 0 / Failed : 0 / Assertions : 0'+
      '      </div>'+
      '    </li>'+
      '  </ul>'+
      '</div>';
    this.init_view_switcher(block);
    this.init_toggle_all_cases(block);
    return block;
  },
  create_test_block: function(test, report_container) {
    var block = document.createElement("LI");
    var skipper = document.createElement("INPUT");
    skipper.type = "checkbox";
    skipper.title = "Skip/Activate Test";
    var $this = this;
    skipper.onclick = function(event) {
      $this.set_skip_test.apply($this, [event, test, skipper, block]);
    };
    block.appendChild(skipper);
    skipper.checked = !this.test_is_skipped(test);
    skipper.disabled = !TestCaseUtil.Cookie.enabled();
    if (!skipper.checked) TestCaseUtil.Element.add_class_name(block, 'skipped');
    var test_name = document.createElement("DIV");
    test_name.className = "test-name";
    test_name.innerHTML = test.title;
    block.appendChild(test_name);
    TestCaseUtil.Array.find(
      TestCaseUtil.Element.select(report_container, '*'),
      function(element){ return element.id == 'test-cases-list';}
    ).appendChild(block);
    return block;
  },
  create_done_bar: function(block) {
    var progress_bar = document.createElement("DIV");
    progress_bar.className = "progress-bar";
    var done_bar = document.createElement("DIV");
    done_bar.className = "done-bar";
    progress_bar.appendChild(done_bar);
    block.appendChild(progress_bar);
    return done_bar;
  },
  create_error_report: function(test_block, test_name, e, assertion_num, failed_test_code) {
    var block = document.createElement("DIV");
    block.className = 'error-report-case';
    block.innerHTML = '<div class="error-report-case-title">Test error in <span class="test-name" title="Show code">'+ test_name +'</span> '+
                      '<span class="assertion-name">('+this.get_assertion_name(e, assertion_num)+')</span>:</div>'+this.create_error_text(e);
    this.create_error_code_snippet(block, e, assertion_num, failed_test_code);
    test_block.appendChild(block);
    test_block.className = 'failed-case';
    return block;
  },
  create_error_text: function(e) {
    if (e instanceof TestCase.WrongValueException) {
      var e_value = this._htmlized(e.format ? this._formatted_value(e.expected) : e.expected);
      var r_value = this._htmlized(e.format ? this._formatted_value(e.received) : e.received);
      return ''+
        '<label>Expected:</label> <span class="report-text">'+ e_value +'</span> '+
        '<label>but had:</label> <span class="report-text">'+ r_value +'</span> '+
        this._create_diff_block(e) +
        this._create_message_block(e);
    } else if (e instanceof TestCase.ProblemException)
      return '<label>Problem:</label> <span class="report-text">'+this._htmlized(e.problem)+'</span> ' + this._create_message_block(e);
    else return e.message;
  },
  _htmlized: function(value) {
    return typeof(value) == 'string' ? value.replace(/\n/g, "<br/>") : value;
  },
  MAX_UNFOLDED_VALUE_LENGTH: 200,
  _formatted_value: function(value) {
    var value_s = TestCaseUtil.to_pretty_s(value);
    if (value_s.length > this.MAX_UNFOLDED_VALUE_LENGTH) {
      return "<span class='formatted-value short-version'>"+ 
               value_s.substr(0, this.MAX_UNFOLDED_VALUE_LENGTH).replace(/</g, '&lt;').replace(/ /g, '&nbsp;') +
             "</span>" +
             "<span class='formatted-value full-version'>"+ 
               value_s.replace(/</g, '&lt;').replace(/ /g, '&nbsp;') +
             "</span>" +
             '<a href="" class="folding-toggler" title="Toggle the value" onclick="'+
               "var short_version = TestCaseUtil.Element.down(TestCaseUtil.Element.up(this, 'span.report-text'), 'span.short-version');"+
               "var full_version = TestCaseUtil.Element.down(TestCaseUtil.Element.up(this, 'span.report-text'), 'span.full-version');"+
               "if (short_version.style.display != 'none') {"+
                 "short_version.style.display = 'none';"+
                 "full_version.style.display = 'block';"+
                 "this.innerHTML = 'Hide the full version';"+
               "} else {"+
                 "short_version.style.display = 'block';"+
                 "full_version.style.display = 'none';"+
                 "this.innerHTML = 'Full version';"+ 
               "}"+
             'return false;">Full version</a>';
    }
    return "<span class='formatted-value'>"+ value_s.replace(/</g, '&lt;').replace(/ /g, '&nbsp;') +"</span>";
  },
  _create_diff_block: function (e) {
    if (e.format) {
      try {
        var is_array = e.expected instanceof Array;
        var diff = TestCaseUtil.Diff.calc(e.expected, e.received);
        if (diff.length > 0) {
          var diff_entries = [];
          for (var i=0; i < diff.length; i++) {
            for (var key in diff[i]) {}
            var value = diff[i][key];
            if (typeof e.expected != 'string') {
              if (typeof e.expected == 'object' && !is_array) {
                var entries = [];
                for (var name in value) {
                  entries.push('"'+ name +'": '+TestCaseUtil._one_level_to_s(value[name]));
                }
                value = "{"+ entries.join(",\n ") +"\n}";
              } else if (typeof e.expected != 'function') {
                value = TestCaseUtil.debug(value);
              }
            }
            diff_entries.push('<span class="'+key+'">'+ value.replace(/</g, '&lt;').replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;') +'</span>');
          }
          return '<label>Diff:</label><span class="report-text">'+
                   '<span class="diff-block">'+ 
                     (is_array ? '[' : '')+
                     diff_entries.join(is_array ? ',<br/>&nbsp;' : '<br/>') +
                     (is_array ? "<br/>]" : '') +
                   '</span>'+
                   '<a href="" class="diff-toggler" title="Toggle the diff" onclick="'+
                     "var block = TestCaseUtil.Element.down(TestCaseUtil.Element.up(this, 'span.report-text'), 'span.diff-block');"+
                     "if (block.style.display != 'block') {"+
                       "block.style.display = 'block';"+
                       "this.innerHTML = 'Hide';"+
                     "} else {"+
                       "block.style.display = 'none';"+
                       "this.innerHTML = 'Show';"+
                     "}"+
                   'return false;">Show</a>'+
                 '</span>';
        }
      } catch (e) {  }
    }
    return '';
  },
  _create_message_block: function(e) {
    return e.message ? '<label>Message:</label>  <span class="report-text">'+e.message+'</span>' : '';
  },
  create_error_code_snippet: function(error_block, e, assertion_num, failed_test_code) {
    var failed_test_code = this._paint_source_code(failed_test_code, e, assertion_num);
    var failed_test_source_popup = document.createElement("DIV");
    failed_test_source_popup.className = 'failed-code-snippet';
    failed_test_source_popup.innerHTML = '<code>'+failed_test_code+'</code>';
    failed_test_source_popup.style.display = 'none';
    var test_name_pointer = TestCaseUtil.Element.select(error_block, 'span.test-name')[0];
    test_name_pointer.parentNode.insertBefore(failed_test_source_popup, test_name_pointer);
    TestCaseUtil.Event.observe(failed_test_source_popup, 'click', function(event) { TestCaseUtil.Event.stop(event); });
    TestCaseUtil.Event.observe(test_name_pointer, 'click', function(event) {
      TestCaseUtil.Event.stop(event);
      TestCaseUtil.Element.toggle(failed_test_source_popup);
    });
    TestCaseUtil.Event.observe(document, 'click', function() {
      TestCaseUtil.Element.hide(failed_test_source_popup);
    });
    return failed_test_source_popup;
  },
  _paint_source_code: function(failed_test_code, e, assertion_num) {
    if (TestCaseUtil.Browser.IE) failed_test_code = failed_test_code.replace(/\n/mg, "<br/>\n").replace(/ /mg, "&nbsp;"); 
    var assertion_num = assertion_num - 1;
    var asserts_counter = 0;
    var assertion_matches = failed_test_code.match(/this\.assert.+(\n|;)/ig);
    if (assertion_matches) {
      for (var i=0; i < assertion_matches.length; i++) {
        match = assertion_matches[i];
        asserts_counter ++;
        if (asserts_counter == assertion_num)
          failed_test_code = failed_test_code.replace(match, '<span class="problematic-code">'+match+'</span>');
      }
      failed_test_code = failed_test_code.replace(/(this)(\.)(assert[^\(]*)(\s*\()/img, '$1$2<span class="assertion-method">$3</span>$4');
    }
    return failed_test_code;
  },
  create_summary_report: function(test_block, test) {
    test.__taken_seconds = (test.__start_time && !test.skipped) ? (new Date().getTime() - test.__start_time)/1000 : 0;
    var block = document.createElement("DIV");
    block.className = 'error-report-summary';
    block.innerHTML = "Total tests: "+ test.size +
                      " / Passed: "+ test.passed +
                      " / Failed: "+ test.failed +
                      " / Assertions: "+ test.assertions +
                      " / Seconds: "+test.__taken_seconds;
    test_block.appendChild(block);
    return block;
  },
  update_overall_summary: function(block, cases_list) {
    var cases = 0, tests = 0, passed = 0, failed = 0, assertions = 0, seconds = 0;
    for (var i=0; i < cases_list.length; i++) {
      var testcase = cases_list[i];
      if (!testcase.skipped) {
        cases++;
        tests += testcase.size;
        passed += testcase.passed;
        failed += testcase.failed;
        assertions += testcase.assertions;
        seconds += testcase.__taken_seconds;
      }
    }
    var list_block = TestCaseUtil.Element.select(block, 'li#testcase-summary-block')[0];
    if (list_block) {
      var done_bar_element = TestCaseUtil.Element.select(list_block, 'div.done-bar')[0];
      if (done_bar_element)
        done_bar_element.style.width = ((tests > 0 ? (passed / tests) : 0) * 100)+'%';
      var summary_element = TestCaseUtil.Element.select(list_block, 'div.error-report-summary')[0];
      if (summary_element)
        summary_element.innerHTML = "Total Cases: "+cases+
                                    " / Tests: "+tests+
                                    " / Passed: "+passed+
                                    " / Failed : "+failed+
                                    " / Assertions : "+assertions+
                                    " / Seconds: "+seconds.toString().substr(0,5);
    }
    if (cases > 0) {
      var switcher = TestCaseUtil.Element.select(block, 'input#testcase-toggle-all-cases')[0];
      if (switcher)
        switcher.checked = true;
    }
  },
  test_is_skipped: function(test) {
    return TestCaseUtil.Array.include(this.get_skipped_cases(), escape(test.title));
  },
  get_assertion_name: function(e, assertion_num) {
    if (e.test && e.assertion) {
      for (key in e.test)
        if (e.test[key] == e.assertion)
          return key;
    } else {
      return "assertion #"+assertion_num;
    }
  },
  init_view_switcher: function(block) {
    var descendants = TestCaseUtil.Element.select(block, '*');
    var switcher = TestCaseUtil.Array.find(descendants, function(el) { return el.id == 'testcase-view-switcher' });
    var list_element = TestCaseUtil.Array.find(descendants, function(el) { return el.id == 'test-cases-list' });
    if (switcher && list_element) {
      TestCaseUtil.Event.observe(switcher, 'change', function() {
        list_element.className = switcher.value;
        TestCaseUtil.Cookie.set("testcase_view", switcher.value, 100);
      });
      switcher.value = this.get_current_view_name();
      list_element.className = switcher.value;
    }
  },
  get_current_view_name: function() {
    var view_name = TestCaseUtil.Cookie.get('testcase_view');
    return view_name == '' ? this.DEFAULT_VIEW_NAME : view_name;
  },
  init_toggle_all_cases: function(block) {
    var checkbox = TestCaseUtil.Array.find(TestCaseUtil.Element.select(block, '*'), function(el) { return el.id == 'testcase-toggle-all-cases' });
    if (checkbox) {
      var $this = this;
      TestCaseUtil.Event.observe(checkbox, 'click', function(event) {
        $this.toggle_all_cases.apply($this, [this, block, checkbox]);
      });
      checkbox.checked = false;
      checkbox.disabled = TestCaseUtil.Cookie.enabled() ? false : true;
    }
  },
  toggle_all_cases: function(event, block, checkbox) {
    var elements = TestCaseUtil.Element.select(block, 'ul li input[type="checkbox"]');
    for (var i=0; i < elements.length; i++) {
      elements[i].checked = checkbox.checked;
      if (elements[i].onclick)
        elements[i].onclick();
    }
  },
  set_skip_test: function(event, test, checkbox, block) {
    TestCaseUtil.Element[checkbox.checked ? 'remove_class_name' : 'add_class_name'](block, 'skipped');
    var skipped_cases = this.get_skipped_cases();
    if (checkbox.checked)
      skipped_cases = TestCaseUtil.Array.without(skipped_cases, escape(test.title));
    else
      skipped_cases.push(escape(test.title));
    TestCaseUtil.Cookie.set("testcase_skipped_cases", skipped_cases.join("|"));
  },
  get_skipped_cases: function() {
    return TestCaseUtil.Cookie.get('testcase_skipped_cases').split("|");
  }
});

/* ------------------------------------------------------------------- */

TestReporter.InlineReporter = TestCaseUtil.create_class(TestReporter, {
  prepare: function() {
    var source = ''+
      '<div id="testcase-inline-report-block">'+
      '  <div class="title">'+
      '    <div class="closer" title="Close" onclick="TestCaseUtil.Element.hide(\'testcase-inline-report-block\');">x</div>'+
      '  </div>'+
      '  <div class="body"></div>'+
      '</div>'+
    '';
    if (document.body) {
      var block = document.createElement("DIV");
      block.innerHTML = source;
      document.body.appendChild(block);
    } else {
      document.write(source);
    }
    TestCaseUtil.Element.down('testcase-inline-report-block', 'div.body').appendChild(this.report_container);
  }
});
/* ------------------------------------------------------------------- */

TestCaseUtil.extend(TestCase, {
  reporter: new TestReporter.InlineReporter()
});


/* ---------------- THE STYLES COLLECTION DUMPING ------------------- */
document.write('<style type="text/css">'+''+
'#testcase-report-block{'+
'  font-family: Verdana;'+
'  font-size: 9pt;'+
'}'+
'#testcase-report-block h1{'+
'  font-family: Arial;'+
'  font-size: 12pt;'+
'}'+
'#testcase-report-block div.progress-bar{'+
'  display:block;'+
'  height: 14px;'+
'  width: 100%;'+
'  background: red;'+
'  padding: 0;'+
'}'+
'#testcase-report-block div.progress-bar div.done-bar{'+
' display:block;'+
' position: relative;'+
' float: left;'+
' height: 14px;'+
' margin: 0;'+
' background: green;'+
'}'+
''+
'#testcase-report-block #testcase-view-switcher {'+
'  position: relative;'+
'  float: right;'+
'  margin-top: -20pt;'+
'}'+
''+
'#testcase-report-block ul#test-cases-list{'+
'   padding-left: 20pt;'+
'}'+
'#testcase-report-block ul#test-cases-list li{'+
'  margin-bottom: 8pt;'+
'}'+
'#testcase-report-block ul#test-cases-list li#testcase-summary-block {'+
'  padding-bottom: 4pt;'+
''+
'  margin-bottom: 4pt;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.test-name{'+
'  font-weight: bold;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case{'+
'  padding: 4pt 0;'+
'  border-bottom: 1px dotted gray;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case div.error-report-case-title{'+
'  font-weight: bold;'+
'  color: brown;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case div.error-report-case-title .test-name{'+
'  text-decoration: underline;'+
'  color: red;'+
'  cursor: pointer;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case div.error-report-case-title .assertion-name{'+
'  font-weight: normal;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case label{'+
'  display: block;'+
'  font-weight: bold;'+
'  color: #888;'+
'  margin-top: 4pt;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .report-text{'+
'  display: block;'+
'  padding-left: 4pt;'+
'  border-left: 8pt solid #FEE;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .formatted-value {'+
'  white-space: pre;'+
'  font-family: monospace;'+
'  display: block;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .formatted-value.short-version {'+
'  color: gray;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .formatted-value.short-version:after {'+
'  content: "...";'+
'  font-size: 120%;'+
'  font-weight: bold;'+
'  margin-left: 4pt;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .formatted-value.full-version {'+
'  display: none;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .folding-toggler {'+
'  color: red;'+
'}'+
''+
'#testcase-report-block ul#test-cases-list li div.error-report-case .diff-block {'+
'  white-space: pre;'+
'  font-family: monospace;'+
'  display: none;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .diff-block .del {'+
'  background: #FDD;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .diff-block .ins {'+
'  background: #DFD;'+
'}'+
'#testcase-report-block ul#test-cases-list li div.error-report-case .diff-toggler {'+
'  color: red;'+
'}'+
''+
'#testcase-report-block ul#test-cases-list {'+
'  list-style: none;'+
'}'+
'#testcase-report-block ul#test-cases-list li {'+
(TestCaseUtil.Browser.IE?'margin-bottom: 0 !important; ':'')+
(TestCaseUtil.Browser.IE?'padding-bottom: 0 !important; ':'')+
'}'+
'#testcase-report-block ul#test-cases-list li input {'+
'  position: relative;'+
'  float: left;'+
'  margin-left: -16pt;'+
(TestCaseUtil.Browser.IE?'margin-left: -20pt;':'')+
(TestCaseUtil.Browser.IE?'margin-top: 10pt;':'')+
'}'+
'#testcase-report-block ul#test-cases-list li.skipped {'+
'  color: gray;'+
'}'+
'#testcase-report-block ul#test-cases-list li.skipped div.progress-bar,'+
'#testcase-report-block ul#test-cases-list li.skipped div.progress-bar div.done-bar {'+
'  background: gray;'+
'}'+
''+
'#testcase-report-block ul.summary-only li {'+
'  display: none;'+
'}'+
'#testcase-report-block ul.summary-only li#testcase-summary-block {'+
'  display: list-item;'+
'}'+
''+
'#testcase-report-block ul.summary-and-failed li {'+
'  display: none;'+
'}'+
'#testcase-report-block ul.summary-and-failed li#testcase-summary-block,'+
'#testcase-report-block ul.summary-and-failed li.failed-case {'+
'  display: list-item;'+
'}'+
''+
'#testcase-report-block ul.summary-and-all li {'+
'  display: list-item;'+
'}'+
''+
'#testcase-report-block ul.cases-only li#testcase-summary-block {'+
'  display: none;'+
'}'+
''+
'#testcase-report-block ul.summary-and-active li.skipped {'+
'  display: none;'+
'}'+
''+
'#testcase-report-block ul.summary-and-skipped li {'+
'  display: none;'+
'}'+
'#testcase-report-block ul.summary-and-skipped li#testcase-summary-block,'+
'#testcase-report-block ul.summary-and-skipped li.failed-case,'+
'#testcase-report-block ul.summary-and-skipped li.skipped {'+
'  display: list-item;'+
'}'+
''+
'#testcase-report-block ul.active-cases-only li {'+
'  display: list-item;'+
'}'+
'#testcase-report-block ul.active-cases-only li#testcase-summary-block,'+
'#testcase-report-block ul.active-cases-only li.skipped {'+
'  display: none;'+
'}'+
''+
'#testcase-report-block ul.skipped-cases-only li {'+
'  display: none;'+
'}'+
'#testcase-report-block ul.skipped-cases-only li.skipped {'+
'  display: list-item;'+
'}'+
''+
'#testcase-report-block div.failed-code-snippet {'+
'  position: absolute;'+
'  border: 1px solid brown;'+
'  padding: 4pt;'+
'  color: black;'+
'  font-weight: normal;'+
'  background: #FFE;'+
'  cursor: default;'+
'  border-left: 4pt solid brown;'+
'  padding-left: 8pt;'+
'  z-index: 999;'+
(TestCaseUtil.Browser.IE?'margin-top: 1em;':'')+
(TestCaseUtil.Browser.IE?'margin-left: -6em;':'')+
'}'+
'#testcase-report-block div.failed-code-snippet code {'+
'  text-decoration: none !important;'+
'  white-space: pre;'+
'  margin: 0;'+
'  padding: 0;'+
'  display: block;'+
'}'+
'#testcase-report-block div.failed-code-snippet code .problematic-code {'+
'  border-bottom: 1px solid red;'+
'}'+
'#testcase-report-block div.failed-code-snippet code .assertion-method {'+
'  color: #090;'+
'}'+
''+
'#testcase-inline-report-block{'+
'  position: absolute;'+
'  left: 20%;'+
'  top: 10pt;'+
'  background: white;'+
'  z-index: 99999999;'+
'  border: 1px solid gray;'+
'  width: 500pt;'+
'  font-size: 9pt;'+
'  font-family: Verdana;'+
'}'+
'#testcase-inline-report-block div.title{'+
'  display: block;'+
'  height: 14pt;'+
'  background: #EEE;'+
'  padding: 0 4pt;'+
'  border-bottom: 1px solid gray;'+
'}'+
'#testcase-inline-report-block div.title div.closer{'+
'  border: 1px solid gray;'+
'  float: right;'+
'  position: relative;'+
'  display: block;'+
'  width: 8pt;'+
'  height: 8pt;'+
'  line-height: 8pt;'+
'  text-align: center;'+
'  padding: 1pt;'+
'  margin-top: 1pt;'+
'  cursor: pointer;'+
'  background: #EBB;'+
'}'+
'#testcase-inline-report-block div.title div.closer:hover{'+
'  background: #F88'+
'}'+
'#testcase-inline-report-block div.body{'+
'  margin: .5em;'+
'}'+
'</style>');