<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>README.rdoc</filename>
    </added>
    <added>
      <filename>test/lib/assets/test.css</filename>
    </added>
    <added>
      <filename>test/lib/assets/unittest.js</filename>
    </added>
    <added>
      <filename>test/lib/templates/default.erb</filename>
    </added>
    <added>
      <filename>test/lib/templates/prototype.erb</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/ajax.js</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/base.js</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/dom.css</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/dom.js</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/element_mixins.js</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/enumerable.js</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/hash.js</filename>
    </added>
    <added>
      <filename>test/unit/fixtures/string.js</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,51 @@
+* Improve NodeList detection for Safari's $A function. [Garrett Smith, jddalton]
+
+* Use different tactic to sniff for Opera in order to avoid false positives in IE. [Tobie Langel, jddalton]
+
+* Rename variable in Form.Element.Serializers.select. (jddalton)
+
+* Coerce Opera's version string into a number whenever we need to sniff. (Sam Holman, jddalton)
+
+* Ensure Object.isElement handles &quot;falsy&quot; values properly. (kangax)
+
+* Fix exiting test task on INT signal. (Samuel Lebeau)
+
+* Fix unit test freeze in IE. (Tobie Langel)
+
+* Ensure Hash does not return keys from the prototype chain (e.g., constructor, valueOf, toString). (kangax)
+
+* Fix toString/valueOf sharing same method reference via closure in Class#addMethods. Use plain property assignment, since Object.extend fails to enumerate over toString/valueOf. (kangax)
+
+* Stop Form.Element.disable from stealing focus. (jddalton)
+
+* Ensure Element.hide and Element.show return an element, even if you pass an element ID. (Andrew Dupont)
+
+* Fix an issue where Element#getStyle('height') returns null if the height is set to &quot;auto.&quot; (kangax, jddalton)
+
+* Add unit tests for Element#descendantOf. (jddalton)
+
+* Form#serializeElements should not serialize file inputs. (kangax, Lonesome Boy)
+
+* Fix an issue with calling Event.pointer before the DOM is loaded. (kangax, jddalton) [#4 state:resolved]
+
+* Element#down on an input element should not raise error. (humeniuc, kangax)
+
+* More unit tests for Object.isHash. (Tobie Langel)
+
+* Allow Function#argumentNames to handle line breaks between arguments. (Geoff M. Granum, Tobie Langel) [#63 state:resolved]
+
+* For consistency, add additional optional parameter &quot;context&quot; to Number.prototype.times. (Samuel Lebeau)
+
+* Replace all instances of foo.__proto__ by foo['__proto__'] for Caja-compliance. (Tobie Langel)
+
+* Speed up Function#argumentNames. Avoid Enum dependency. (Samuel Lebeau, Tobie Langel)
+
+* Fix Event#element accessing inexistent tagName property (e.g. when element is a document). (kangax)
+
+* Amended failing Element#identify test.
+
+* Refactor unit tests. Unit tests are now dynamically generated from a JavaScript test file and optional HTML, JS and CSS fixtures. [Tobie Langel]
+
 * Fix issue where Safari improperly reports an element as a descendant of itself.
 
 * Greatly simplify IE's implementation of Element#descendantOf.</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -5,11 +5,13 @@ PROTOTYPE_ROOT     = File.expand_path(File.dirname(__FILE__))
 PROTOTYPE_SRC_DIR  = File.join(PROTOTYPE_ROOT, 'src')
 PROTOTYPE_DIST_DIR = File.join(PROTOTYPE_ROOT, 'dist')
 PROTOTYPE_PKG_DIR  = File.join(PROTOTYPE_ROOT, 'pkg')
+PROTOTYPE_TEST_DIR = File.join(PROTOTYPE_ROOT, 'test')
+PROTOTYPE_TMP_DIR  = File.join(PROTOTYPE_TEST_DIR, 'unit', 'tmp')
 PROTOTYPE_VERSION  = '1.6.0.2'
 
 task :default =&gt; [:dist, :dist_helper, :package, :clean_package_source]
 
-desc &quot;Builds the distribution&quot;
+desc &quot;Builds the distribution.&quot;
 task :dist do
   $:.unshift File.join(PROTOTYPE_ROOT, 'lib')
   require 'protodoc'
@@ -21,7 +23,7 @@ task :dist do
   end
 end
 
-desc &quot;Builds the updating helper&quot;
+desc &quot;Builds the updating helper.&quot;
 task :dist_helper do
   $:.unshift File.join(PROTOTYPE_ROOT, 'lib')
   require 'protodoc'
@@ -45,12 +47,12 @@ Rake::PackageTask.new('prototype', PROTOTYPE_VERSION) do |package|
   )
 end
 
-desc &quot;Builds the distribution, runs the JavaScript unit tests and collects their results.&quot;
-task :test =&gt; [:build_tests, :dist, :test_units]
+desc &quot;Builds the distribution and the test suite, runs the tests and collects their results.&quot;
+task :test =&gt; [:dist, :test_units]
 
 require 'test/lib/jstest'
 desc &quot;Runs all the JavaScript unit tests and collects the results&quot;
-JavaScriptTestTask.new(:test_units) do |t|
+JavaScriptTestTask.new(:test_units =&gt; [:build_unit_tests]) do |t|
   testcases        = ENV['TESTCASES']
   tests_to_run     = ENV['TESTS']    &amp;&amp; ENV['TESTS'].split(',')
   browsers_to_test = ENV['BROWSERS'] &amp;&amp; ENV['BROWSERS'].split(',')
@@ -58,10 +60,13 @@ JavaScriptTestTask.new(:test_units) do |t|
   t.mount(&quot;/dist&quot;)
   t.mount(&quot;/test&quot;)
   
-  Dir[&quot;test/unit/tmp/*_test.html&quot;].sort.each do |test_file|
-    tests = testcases ? { :url =&gt; &quot;/#{test_file}&quot;, :testcases =&gt; testcases } : &quot;/#{test_file}&quot;
-    test_filename = test_file[/.*\/(.+?)_test\.html/, 1]
-    t.run(tests) unless tests_to_run &amp;&amp; !tests_to_run.include?(test_filename)
+  Dir.mkdir(PROTOTYPE_TMP_DIR) unless File.exist?(PROTOTYPE_TMP_DIR)
+  
+  Dir[&quot;test/unit/tmp/*_test.html&quot;].each do |file|
+    test_name = File.basename(file).sub(&quot;_test.html&quot;, &quot;&quot;)
+    unless tests_to_run &amp;&amp; !tests_to_run.include?(test_name)
+      t.run(&quot;/#{file}&quot;, testcases)
+    end
   end
   
   %w( safari firefox ie konqueror opera ).each do |browser|
@@ -69,8 +74,19 @@ JavaScriptTestTask.new(:test_units) do |t|
   end
 end
 
-task :build_tests do
-  Dir[&quot;test/unit/*_test.js&quot;].each do |test_file|
-    TestBuilder.new(test_file).render
+task :build_unit_tests do
+  Dir[File.join('test', 'unit', '*_test.js')].each do |file|
+    PageBuilder.new(file, 'prototype.erb').render
   end
-end
\ No newline at end of file
+end
+
+task :clean_package_source do
+  rm_rf File.join(PROTOTYPE_PKG_DIR, &quot;prototype-#{PROTOTYPE_VERSION}&quot;)
+end
+
+desc 'Generates an empty tmp directory for building tests.'
+task :clean_tmp do
+  puts 'Generating an empty tmp directory for building tests.'
+  FileUtils.rm_rf(PROTOTYPE_TMP_DIR) if File.exist?(PROTOTYPE_TMP_DIR)
+  Dir.mkdir(PROTOTYPE_TMP_DIR)
+end</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 /*  Prototype JavaScript framework, version &lt;%= PROTOTYPE_VERSION %&gt;
- *  (c) 2005-2007 Sam Stephenson
+ *  (c) 2005-2008 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://www.prototypejs.org/</diff>
      <filename>src/HEADER</filename>
    </modified>
    <modified>
      <diff>@@ -8,9 +8,13 @@ function $A(iterable) {
 
 if (Prototype.Browser.WebKit) {
   $A = function(iterable) {
-    if (!iterable) return [];
-    if (!(Object.isFunction(iterable) &amp;&amp; iterable == '[object NodeList]') &amp;&amp;
-        iterable.toArray) return iterable.toArray();
+    if (!iterable) return [];    
+    // In Safari, only use the `toArray` method if it's not a NodeList.
+    // A NodeList is a function, has an function `item` property, and a numeric
+    // `length` property. Adapted from Google Doctype.
+    if (!(typeof iterable === 'function' &amp;&amp; typeof iterable.length ===
+        'number' &amp;&amp; typeof iterable.item === 'function') &amp;&amp; iterable.toArray)
+      return iterable.toArray();
     var length = iterable.length || 0, results = new Array(length);
     while (length--) results[length] = iterable[length];
     return results;</diff>
      <filename>src/array.js</filename>
    </modified>
    <modified>
      <diff>@@ -44,12 +44,13 @@ Class.Methods = {
       var property = properties[i], value = source[property];
       if (ancestor &amp;&amp; Object.isFunction(value) &amp;&amp;
           value.argumentNames().first() == &quot;$super&quot;) {
-        var method = value, value = Object.extend((function(m) { 
+        var method = value;
+        value = (function(m) {
           return function() { return ancestor[m].apply(this, arguments) };
-        })(property).wrap(method), {
-          valueOf:  function() { return method },
-          toString: function() { return method.toString() }  
-        });
+        })(property).wrap(method);
+
+        value.valueOf = method.valueOf.bind(method);
+        value.toString = method.toString.bind(method);
       }
       this.prototype[property] = value;
     }
@@ -128,7 +129,7 @@ Object.extend(Object, {
   },
   
   isElement: function(object) {
-    return object &amp;&amp; object.nodeType == 1;
+    return !!(object &amp;&amp; object.nodeType == 1);
   },
   
   isArray: function(object) {
@@ -159,7 +160,8 @@ Object.extend(Object, {
 
 Object.extend(Function.prototype, {
   argumentNames: function() {
-    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(&quot;,&quot;).invoke(&quot;strip&quot;);
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+      .replace(/\s+/g, '').split(',');
     return names.length == 1 &amp;&amp; !names[0] ? [] : names;
   },
   
@@ -193,6 +195,11 @@ Object.extend(Function.prototype, {
     }, timeout);
   },
   
+  defer: function() {
+    var args = [0.01].concat($A(arguments));
+    return this.delay.apply(this, args);
+  },
+  
   wrap: function(wrapper) {
     var __method = this;
     return function() {
@@ -209,8 +216,6 @@ Object.extend(Function.prototype, {
   }
 });
 
-Function.prototype.defer = Function.prototype.delay.curry(0.01);
-
 Date.prototype.toJSON = function() {
   return '&quot;' + this.getUTCFullYear() + '-' +
     (this.getUTCMonth() + 1).toPaddedString(2) + '-' +</diff>
      <filename>src/base.js</filename>
    </modified>
    <modified>
      <diff>@@ -74,12 +74,14 @@ Element.Methods = {
   },
 
   hide: function(element) {
-    $(element).style.display = 'none';
+    element = $(element);
+    element.style.display = 'none';
     return element;
   },
   
   show: function(element) {
-    $(element).style.display = '';
+    element = $(element);
+    element.style.display = '';
     return element;
   },
 
@@ -233,7 +235,7 @@ Element.Methods = {
     element = $(element);
     if (arguments.length == 1) return element.firstDescendant();
     return Object.isNumber(expression) ? element.descendants()[expression] :
-      element.select(expression)[index || 0];
+      Element.select(element, expression)[index || 0];
   },
 
   previous: function(element, expression, index) {
@@ -387,7 +389,7 @@ Element.Methods = {
     element = $(element);
     style = style == 'float' ? 'cssFloat' : style.camelize();
     var value = element.style[style];
-    if (!value) {
+    if (!value || value == 'auto') {
       var css = document.defaultView.getComputedStyle(element, null);
       value = css ? css[style] : null;
     }
@@ -455,7 +457,7 @@ Element.Methods = {
       element.style.position = 'relative';
       // Opera returns the offset relative to the positioning context, when an
       // element is position relative but top and left have not been defined
-      if (window.opera) {
+      if (Prototype.Browser.Opera) {
         element.style.top = 0;
         element.style.left = 0;
       }  
@@ -1026,7 +1028,7 @@ Element.Methods.Simulated = {
   hasAttribute: function(element, attribute) {
     attribute = Element._attributeTranslations.has[attribute] || attribute;
     var node = $(element).getAttributeNode(attribute);
-    return node &amp;&amp; node.specified;
+    return !!(node &amp;&amp; node.specified);
   }
 };
 
@@ -1035,9 +1037,9 @@ Element.Methods.ByTag = { };
 Object.extend(Element, Element.Methods);
 
 if (!Prototype.BrowserFeatures.ElementExtensions &amp;&amp; 
-    document.createElement('div').__proto__) {
+    document.createElement('div')['__proto__']) {
   window.HTMLElement = { };
-  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
   Prototype.BrowserFeatures.ElementExtensions = true;
 }
 
@@ -1148,7 +1150,7 @@ Element.addMethods = function(methods) {
     if (window[klass]) return window[klass];
     
     window[klass] = { };
-    window[klass].prototype = document.createElement(tagName).__proto__;
+    window[klass].prototype = document.createElement(tagName)['__proto__'];
     return window[klass];
   }
   
@@ -1174,12 +1176,18 @@ Element.addMethods = function(methods) {
 
 document.viewport = {
   getDimensions: function() {
-    var dimensions = { };
-    var B = Prototype.Browser;
+    var dimensions = { }, B = Prototype.Browser;
     $w('width height').each(function(d) {
       var D = d.capitalize();
-      dimensions[d] = (B.WebKit &amp;&amp; !document.evaluate) ? self['inner' + D] :
-        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+      if (B.WebKit &amp;&amp; !document.evaluate) {
+        // Safari &lt;3.0 needs self.innerWidth/Height
+        dimensions[d] = self['inner' + D];
+      } else if (B.Opera &amp;&amp; parseFloat(window.opera.version()) &lt; 9.5) {
+        // Opera &lt;9.5 needs document.body.clientWidth/Height
+        dimensions[d] = document.body['client' + D]
+      } else {
+        dimensions[d] = document.documentElement['client' + D];
+      }
     });
     return dimensions;
   },</diff>
      <filename>src/dom.js</filename>
    </modified>
    <modified>
      <diff>@@ -61,21 +61,21 @@ Event.Methods = (function() {
     element: function(event) {
       event = Event.extend(event);
       
-      var node = event.target, type = event.type;
+      var node          = event.target,
+          type          = event.type,
+          currentTarget = event.currentTarget;
       
-      if (event.currentTarget) {
+      if (currentTarget &amp;&amp; currentTarget.tagName) {
         // Firefox screws up the &quot;click&quot; event when moving between radio buttons
         // via arrow keys. It also screws up the &quot;load&quot; and &quot;error&quot; events on images,
         // reporting the document as the target instead of the original image.
-        var currentTarget = event.currentTarget;
-        var tagName = currentTarget.tagName.toUpperCase();
-        if (['load', 'error'].include(type) ||
-         (tagName === 'INPUT' &amp;&amp; currentTarget.type === 'radio' &amp;&amp; type === 'click'))
-          node = currentTarget;
+        if (type === 'load' || type === 'error' ||
+          (type === 'click' &amp;&amp; currentTarget.tagName.toLowerCase() === 'input'
+            &amp;&amp; currentTarget.type === 'radio'))
+              node = currentTarget;
       }
-      
-      return Element.extend(node &amp;&amp; node.nodeType == Node.TEXT_NODE ?
-       node.parentNode : node);
+      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+      return Element.extend(node);
     },
 
     findElement: function(event, expression) {
@@ -86,7 +86,8 @@ Event.Methods = (function() {
     },
 
     pointer: function(event) {
-      var docElement = document.documentElement, body = document.body;
+      var docElement = document.documentElement,
+      body = document.body || { scrollLeft: 0, scrollTop: 0 };
       return {
         x: event.pageX || (event.clientX + 
           (docElement.scrollLeft || body.scrollLeft) -
@@ -138,7 +139,7 @@ Event.extend = (function() {
     };
     
   } else {
-    Event.prototype = Event.prototype || document.createEvent(&quot;HTMLEvents&quot;).__proto__;
+    Event.prototype = Event.prototype || document.createEvent(&quot;HTMLEvents&quot;)['__proto__'];
     Object.extend(Event.prototype, methods);
     return Prototype.K;
   }</diff>
      <filename>src/event.js</filename>
    </modified>
    <modified>
      <diff>@@ -12,7 +12,7 @@ var Form = {
     var data = elements.inject({ }, function(result, element) {
       if (!element.disabled &amp;&amp; element.name) {
         key = element.name; value = $(element).getValue();
-        if (value != null &amp;&amp; (element.type != 'submit' || (!submitted &amp;&amp;
+        if (value != null &amp;&amp; element.type != 'file' &amp;&amp; (element.type != 'submit' || (!submitted &amp;&amp;
             submit !== false &amp;&amp; (!submit || key == submit) &amp;&amp; (submitted = true)))) { 
           if (key in result) {
             // a key is already present; construct an array of values
@@ -173,7 +173,6 @@ Form.Element.Methods = {
   
   disable: function(element) {
     element = $(element);
-    element.blur();
     element.disabled = true;
     return element;
   },
@@ -213,22 +212,22 @@ Form.Element.Serializers = {
     else element.value = value;
   },
   
-  select: function(element, index) {
-    if (Object.isUndefined(index))
+  select: function(element, value) {
+    if (Object.isUndefined(value))
       return this[element.type == 'select-one' ? 
         'selectOne' : 'selectMany'](element);
     else {
-      var opt, value, single = !Object.isArray(index);
+      var opt, currentValue, single = !Object.isArray(value);
       for (var i = 0, length = element.length; i &lt; length; i++) {
         opt = element.options[i];
-        value = this.optionValue(opt);
+        currentValue = this.optionValue(opt);
         if (single) {
-          if (value == index) {
+          if (currentValue == value) {
             opt.selected = true;
             return;
           }
         }
-        else opt.selected = index.include(value);
+        else opt.selected = value.include(currentValue);
       }
     }
   },</diff>
      <filename>src/form.js</filename>
    </modified>
    <modified>
      <diff>@@ -28,7 +28,9 @@ var Hash = Class.create(Enumerable, (function() {
     },
 
     get: function(key) {
-      return this._object[key];
+      // simulating poorly supported hasOwnProperty
+      if (this._object[key] !== Object.prototype[key])
+        return this._object[key];
     },
 
     unset: function(key) {</diff>
      <filename>src/hash.js</filename>
    </modified>
    <modified>
      <diff>@@ -7,8 +7,8 @@ Object.extend(Number.prototype, {
     return this + 1;
   },
   
-  times: function(iterator) {
-    $R(0, this, true).each(iterator);
+  times: function(iterator, context) {
+    $R(0, this, true).each(iterator, context);
     return this;
   },
   </diff>
      <filename>src/number.js</filename>
    </modified>
    <modified>
      <diff>@@ -4,10 +4,12 @@ var Prototype = {
   Version: '&lt;%= PROTOTYPE_VERSION %&gt;',
   
   Browser: {
-    IE:     !!(window.attachEvent &amp;&amp; !window.opera),
-    Opera:  !!window.opera,
+    IE:     !!(window.attachEvent &amp;&amp;
+      navigator.userAgent.indexOf('Opera') === -1),
+    Opera:  navigator.userAgent.indexOf('Opera') &gt; -1,
     WebKit: navigator.userAgent.indexOf('AppleWebKit/') &gt; -1,
-    Gecko:  navigator.userAgent.indexOf('Gecko') &gt; -1 &amp;&amp; navigator.userAgent.indexOf('KHTML') == -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') &gt; -1 &amp;&amp; 
+      navigator.userAgent.indexOf('KHTML') === -1,
     MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
   },
 
@@ -16,9 +18,9 @@ var Prototype = {
     SelectorsAPI: !!document.querySelector,
     ElementExtensions: !!window.HTMLElement,
     SpecificElementExtensions: 
-      document.createElement('div').__proto__ &amp;&amp;
-      document.createElement('div').__proto__ !== 
-        document.createElement('form').__proto__
+      document.createElement('div')['__proto__'] &amp;&amp;
+      document.createElement('div')['__proto__'] !== 
+        document.createElement('form')['__proto__']
   },
 
   ScriptFragment: '&lt;script[^&gt;]*&gt;([\\S\\s]*?)&lt;\/script&gt;',</diff>
      <filename>src/prototype.js</filename>
    </modified>
    <modified>
      <diff>@@ -655,6 +655,7 @@ Object.extend(Selector, {
     
   operators: {
     '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
     '^=': function(nv, v) { return nv == v || nv &amp;&amp; nv.startsWith(v); },
     '$=': function(nv, v) { return nv == v || nv &amp;&amp; nv.endsWith(v); },
     '*=': function(nv, v) { return nv == v || nv &amp;&amp; nv.include(v); },</diff>
      <filename>src/selector.js</filename>
    </modified>
    <modified>
      <diff>@@ -86,9 +86,7 @@ class IEBrowser &lt; Browser
       ie = WIN32OLE.new('InternetExplorer.Application')
       ie.visible = true
       ie.Navigate(url)
-      while ie.ReadyState != 4 do
-        sleep(1)
-      end
+      sleep 0.01 while ie.Busy || ie.ReadyState != 4
     end
   end
 
@@ -278,12 +276,7 @@ class JavaScriptTestTask &lt; ::Rake::TaskLib
 
     @server = WEBrick::HTTPServer.new(:Port =&gt; 4711) # TODO: make port configurable
     @server.mount_proc(&quot;/results&quot;) do |req, res|
-      @queue.push({
-        :tests =&gt; req.query['tests'].to_i,
-        :assertions =&gt; req.query['assertions'].to_i,
-        :failures =&gt; req.query['failures'].to_i,
-        :errors =&gt; req.query['errors'].to_i
-      })
+      @queue.push(req)
       res.body = &quot;OK&quot;
     end
     @server.mount(&quot;/response&quot;, BasicServlet)
@@ -296,50 +289,30 @@ class JavaScriptTestTask &lt; ::Rake::TaskLib
 
   def define
     task @name do
-      trap(&quot;INT&quot;) { @server.shutdown }
+      trap(&quot;INT&quot;) { @server.shutdown; exit }
       t = Thread.new { @server.start }
       
       # run all combinations of browsers and tests
       @browsers.each do |browser|
         if browser.supported?
           t0 = Time.now
-          results = {:tests =&gt; 0, :assertions =&gt; 0, :failures =&gt; 0, :errors =&gt; 0}
-          errors = []
-          failures = []
+          test_suite_results = TestSuiteResults.new
+
           browser.setup
-          puts &quot;\nStarted tests in #{browser}&quot;
+          puts &quot;\nStarted tests in #{browser}.&quot;
+          
           @tests.each do |test|
-            params = &quot;resultsURL=http://localhost:4711/results&amp;t=&quot; + (&quot;%.6f&quot; % Time.now.to_f)
-            if test.is_a?(Hash)
-              params &lt;&lt; &quot;&amp;tests=#{test[:testcases]}&quot; if test[:testcases]
-              test = test[:url]
-            end
-            browser.visit(&quot;http://localhost:4711#{test}?#{params}&quot;)
- 
-            result = @queue.pop
-            result.each { |k, v| results[k] += v }
-            value = &quot;.&quot;
-            
-            if result[:failures] &gt; 0
-              value = &quot;F&quot;
-              failures.push(test)
-            end
-            
-            if result[:errors] &gt; 0
-              value = &quot;E&quot;
-              errors.push(test)
-            end
-            
-            print value
+            browser.visit(get_url(test))
+            results = TestResults.new(@queue.pop.query, test[:url])
+            print results
+            test_suite_results &lt;&lt; results
           end
           
-          puts &quot;\nFinished in #{(Time.now - t0).round.to_s} seconds.&quot;
-          puts &quot;  Failures: #{failures.join(', ')}&quot; unless failures.empty?
-          puts &quot;  Errors:   #{errors.join(', ')}&quot; unless errors.empty?
-          puts &quot;#{results[:tests]} tests, #{results[:assertions]} assertions, #{results[:failures]} failures, #{results[:errors]} errors&quot;
+          print &quot;\nFinished in #{Time.now - t0} seconds.&quot;
+          print test_suite_results
           browser.teardown
         else
-          puts &quot;\nSkipping #{browser}, not supported on this OS&quot;
+          puts &quot;\nSkipping #{browser}, not supported on this OS.&quot;
         end
       end
 
@@ -347,7 +320,13 @@ class JavaScriptTestTask &lt; ::Rake::TaskLib
       t.join
     end
   end
-
+  
+  def get_url(test)
+    params = &quot;resultsURL=http://localhost:4711/results&amp;t=&quot; + (&quot;%.6f&quot; % Time.now.to_f)
+    params &lt;&lt; &quot;&amp;tests=#{test[:testcases]}&quot; unless test[:testcases] == :all
+    &quot;http://localhost:4711#{test[:url]}?#{params}&quot;
+  end
+  
   def mount(path, dir=nil)
     dir = Dir.pwd + path unless dir
 
@@ -355,10 +334,11 @@ class JavaScriptTestTask &lt; ::Rake::TaskLib
     @server.mount(path, NonCachingFileHandler, dir)
   end
 
-  # test should be specified as a url or as a hash of the form
-  # {:url =&gt; &quot;url&quot;, :testcases =&gt; &quot;testFoo,testBar&quot;}
-  def run(test)
-    @tests&lt;&lt;test
+  # test should be specified as a hash of the form
+  # {:url =&gt; &quot;url&quot;, :testcases =&gt; &quot;testFoo,testBar&quot;}.
+  # specifying :testcases is optional
+  def run(url, testcases = :all)
+    @tests &lt;&lt;  { :url =&gt; url, :testcases =&gt; testcases }
   end
 
   def browser(browser)
@@ -382,37 +362,115 @@ class JavaScriptTestTask &lt; ::Rake::TaskLib
   end
 end
 
-class TestBuilder
-  UNITTEST_DIR       = File.expand_path('test')
-  TEMPLATE           = File.join(UNITTEST_DIR, 'lib', 'template.erb')
-  FIXTURES_EXTENSION = &quot;html&quot;
-  FIXTURES_DIR       = File.join(UNITTEST_DIR, 'unit', 'fixtures')
+class TestResults
+  attr_reader :tests, :assertions, :failures, :errors, :filename
+  def initialize(query, filename)
+    @tests      = query['tests'].to_i
+    @assertions = query['assertions'].to_i
+    @failures   = query['failures'].to_i
+    @errors     = query['errors'].to_i
+    @filename   = filename
+  end
+  
+  def error?
+    @errors &gt; 0
+  end
   
-  def initialize(filename)
+  def failure?
+    @failures &gt; 0
+  end
+  
+  def to_s
+    return &quot;E&quot; if error?
+    return &quot;F&quot; if failure?
+    &quot;.&quot;
+  end
+end
+
+class TestSuiteResults
+  def initialize
+    @tests      = 0
+    @assertions = 0
+    @failures   = 0
+    @errors     = 0
+    @error_files   = []
+    @failure_files = []
+  end
+  
+  def &lt;&lt;(result)
+    @tests      += result.tests
+    @assertions += result.assertions
+    @failures   += result.failures
+    @errors     += result.errors
+    @error_files.push(result.filename)   if result.error?
+    @failure_files.push(result.filename) if result.failure?
+  end
+  
+  def error?
+    @errors &gt; 0
+  end
+  
+  def failure?
+    @failures &gt; 0
+  end
+  
+  def to_s
+    str = &quot;&quot;
+    str &lt;&lt; &quot;\n  Failures: #{@failure_files.join(', ')}&quot; if failure?
+    str &lt;&lt; &quot;\n  Errors:   #{@error_files.join(', ')}&quot; if error?
+    &quot;#{str}\n#{summary}\n\n&quot;
+  end
+  
+  def summary
+    &quot;#{@tests} tests, #{@assertions} assertions, #{@failures} failures, #{@errors} errors.&quot;
+  end
+end
+
+class PageBuilder
+  UNITTEST_DIR  = File.expand_path('test')
+  FIXTURES_DIR  = File.join(UNITTEST_DIR, 'unit', 'fixtures')
+  TMP_DIR       = File.join(UNITTEST_DIR, 'unit', 'tmp')
+  TEMPLATES_DIR = File.join(UNITTEST_DIR, 'lib', 'templates')
+  
+  def initialize(filename, template = 'default.erb')
     @filename          = filename
+    @template          = File.join(self.class::TEMPLATES_DIR, template)
     @js_filename       = File.basename(@filename)
-    @basename          = @js_filename.sub(&quot;_test.js&quot;, &quot;&quot;)
-    @fixtures_filename = &quot;#{@basename}.#{FIXTURES_EXTENSION}&quot;
-    @title             = @basename.gsub(&quot;_&quot;, &quot; &quot;).strip.capitalize
+    @basename          = @js_filename.sub('_test.js', '')
   end
   
-  def find_fixtures
-    @fixtures = &quot;&quot;
-    file = File.join(FIXTURES_DIR, @fixtures_filename)
-    if File.exists?(file)
-      File.open(file).each { |line| @fixtures &lt;&lt; line }
-    end
+  def html_fixtures
+    content = &quot;&quot;
+    file = File.join(FIXTURES_DIR, &quot;#{@basename}.html&quot;)
+    File.open(file).each { |l| content &lt;&lt; l } if File.exists?(file)
+    content
+  end
+  
+  def external_fixtures(extension)
+    filename = &quot;#{@basename}.#{extension}&quot;
+    File.exists?(File.join(FIXTURES_DIR, filename)) ? filename : nil
   end
   
   def render
-    find_fixtures
-    File.open(destination, &quot;w+&quot;) do |file|
-      file &lt;&lt; ERB.new(IO.read(TEMPLATE), nil, &quot;%&quot;).result(binding)
+    @title                 = @basename.gsub('_', ' ').strip.capitalize
+    @html_fixtures         = html_fixtures
+    @js_fixtures_filename  = external_fixtures('js')
+    @css_fixtures_filename = external_fixtures('css')
+    
+    File.open(destination, 'w+') do |file|
+      file &lt;&lt; ERB.new(IO.read(@template), nil, '%').result(binding)
     end
   end
   
   def destination
-    basename = File.basename(@filename, &quot;.js&quot;)
-    File.join(UNITTEST_DIR, 'unit', 'tmp', &quot;#{basename}.html&quot;)
+    name_file(:ext =&gt; 'html')
+  end
+  
+  def name_file(options = {})
+    prefix = options[:prefix] ? &quot;#{options[:prefix]}_&quot; : &quot;&quot;
+    suffix = options[:suffix] ? &quot;_#{options[:suffix]}&quot; : &quot;&quot;
+    ext    = options[:ext] ? options[:ext] : &quot;js&quot;
+    filename = File.basename(@filename, '.js')
+    File.join(TMP_DIR, &quot;#{prefix}#{filename}#{suffix}.#{ext}&quot;)
   end
 end</diff>
      <filename>test/lib/jstest.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,38 +1,3 @@
-var Fixtures = {
-  js: {
-    responseBody:   '$(&quot;content&quot;).update(&quot;&lt;H2&gt;Hello world!&lt;/H2&gt;&quot;);', 
-    'Content-Type': '           text/javascript     '
-  },
-  
-  html: {
-    responseBody: &quot;Pack my box with &lt;em&gt;five dozen&lt;/em&gt; liquor jugs! &quot; +
-      &quot;Oh, how &lt;strong&gt;quickly&lt;/strong&gt; daft jumping zebras vex...&quot;
-  },
-  
-  xml: {
-    responseBody:   '&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;&lt;name attr=&quot;foo&quot;&gt;bar&lt;/name&gt;', 
-    'Content-Type': 'application/xml'
-  },
-  
-  json: {
-    responseBody:   '{\n\r&quot;test&quot;: 123}', 
-    'Content-Type': 'application/json'
-  },
-  
-  jsonWithoutContentType: {
-    responseBody:   '{&quot;test&quot;: 123}'
-  },
-  
-  invalidJson: {
-    responseBody:   '{});window.attacked = true;({}',
-    'Content-Type': 'application/json'
-  },
-  
-  headerJson: {
-    'X-JSON': '{&quot;test&quot;: &quot;hello #&#233;&#224;&quot;}'
-  }
-};
-
 var extendDefault = function(options) {
   return Object.extend({
     asynchronous: false,
@@ -41,14 +6,6 @@ var extendDefault = function(options) {
   }, options);
 };
 
-var responderCounter = 0;
-
-// lowercase comparison because of MSIE which presents HTML tags in uppercase
-var sentence = (&quot;Pack my box with &lt;em&gt;five dozen&lt;/em&gt; liquor jugs! &quot; +
-&quot;Oh, how &lt;strong&gt;quickly&lt;/strong&gt; daft jumping zebras vex...&quot;).toLowerCase();
-
-var message = 'You must be running your tests from rake to test this feature.';
-
 new Test.Unit.Runner({
   setup: function() {
     $('content').update('');</diff>
      <filename>test/unit/ajax_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,603 +1,522 @@
-var Person = function(name){
-      this.name = name;
-  };
+new Test.Unit.Runner({
+  testFunctionArgumentNames: function() {
+    this.assertEnumEqual([], (function() {}).argumentNames());
+    this.assertEnumEqual([&quot;one&quot;], (function(one) {}).argumentNames());
+    this.assertEnumEqual([&quot;one&quot;, &quot;two&quot;, &quot;three&quot;], (function(one, two, three) {}).argumentNames());
+    this.assertEnumEqual([&quot;one&quot;, &quot;two&quot;, &quot;three&quot;], (function(  one  , two 
+       , three   ) {}).argumentNames());
+    this.assertEqual(&quot;$super&quot;, (function($super) {}).argumentNames().first());
+    
+    function named1() {};
+    this.assertEnumEqual([], named1.argumentNames());
+    function named2(one) {};
+    this.assertEnumEqual([&quot;one&quot;], named2.argumentNames());
+    function named3(one, two, three) {};
+    this.assertEnumEqual([&quot;one&quot;, &quot;two&quot;, &quot;three&quot;], named3.argumentNames());
+  },
   
-  Person.prototype.toJSON = function() {
-    return '-' + this.name;
-  };
+  testFunctionBind: function() {
+    function methodWithoutArguments() { return this.hi };
+    function methodWithArguments()    { return this.hi + ',' + $A(arguments).join(',') };
+    var func = Prototype.emptyFunction;
+
+    this.assertIdentical(func, func.bind());
+    this.assertIdentical(func, func.bind(undefined));
+    this.assertNotIdentical(func, func.bind(null));
 
-  var arg1 = 1;
-  var arg2 = 2;
-  var arg3 = 3;
-  function TestObj() { };
-  TestObj.prototype.assertingEventHandler = 
-    function(event, assertEvent, assert1, assert2, assert3, a1, a2, a3) {
-      assertEvent(event);
-      assert1(a1);
-      assert2(a2);
-      assert3(a3);
+    this.assertEqual('without', methodWithoutArguments.bind({ hi: 'without' })());
+    this.assertEqual('with,arg1,arg2', methodWithArguments.bind({ hi: 'with' })('arg1','arg2'));
+    this.assertEqual('withBindArgs,arg1,arg2',
+      methodWithArguments.bind({ hi: 'withBindArgs' }, 'arg1', 'arg2')());
+    this.assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4',
+      methodWithArguments.bind({ hi: 'withBindArgsAndArgs' }, 'arg1', 'arg2')('arg3', 'arg4'));
+  },
+  
+  testFunctionCurry: function() {
+    var split = function(delimiter, string) { return string.split(delimiter); };
+    var splitOnColons = split.curry(&quot;:&quot;);
+    this.assertNotIdentical(split, splitOnColons);
+    this.assertEnumEqual(split(&quot;:&quot;, &quot;0:1:2:3:4:5&quot;), splitOnColons(&quot;0:1:2:3:4:5&quot;));
+    this.assertIdentical(split, split.curry());
+  },
+  
+  testFunctionDelay: function() {
+    window.delayed = undefined;
+    var delayedFunction = function() { window.delayed = true; };
+    var delayedFunctionWithArgs = function() { window.delayedWithArgs = $A(arguments).join(' '); };
+    delayedFunction.delay(0.8);
+    delayedFunctionWithArgs.delay(0.8, 'hello', 'world');
+    this.assertUndefined(window.delayed);
+    this.wait(1000, function() {
+      this.assert(window.delayed);
+      this.assertEqual('hello world', window.delayedWithArgs);
+    });
+  },
+  
+  testFunctionWrap: function() {
+    function sayHello(){
+      return 'hello world';
     };
     
-  var globalBindTest = null;
+    this.assertEqual('HELLO WORLD', sayHello.wrap(function(proceed) {
+      return proceed().toUpperCase();
+    })());
+    
+    var temp = String.prototype.capitalize;
+    String.prototype.capitalize = String.prototype.capitalize.wrap(function(proceed, eachWord) {
+      if(eachWord &amp;&amp; this.include(' ')) return this.split(' ').map(function(str){
+        return str.capitalize();
+      }).join(' ');
+      return proceed();
+    });
+    this.assertEqual('Hello world', 'hello world'.capitalize());
+    this.assertEqual('Hello World', 'hello world'.capitalize(true));
+    this.assertEqual('Hello', 'hello'.capitalize());
+    String.prototype.capitalize = temp;
+  },
   
+  testFunctionDefer: function() {
+    window.deferred = undefined;
+    var deferredFunction = function() { window.deferred = true; };
+    deferredFunction.defer();
+    this.assertUndefined(window.deferred);      
+    this.wait(50, function() {
+      this.assert(window.deferred);
+      
+      window.deferredValue = 0;
+      var deferredFunction2 = function(arg) { window.deferredValue = arg; };
+      deferredFunction2.defer('test');
+      this.wait(50, function() {
+        this.assertEqual('test', window.deferredValue);
+      });
+    });
+  },
   
-  // base class
-  var Animal = Class.create({
-    initialize: function(name) {
-      this.name = name;
-    },
-    name: &quot;&quot;,
-    eat: function() {
-      return this.say(&quot;Yum!&quot;);
-    },
-    say: function(message) {
-      return this.name + &quot;: &quot; + message;
-    }
-  });
+  testFunctionMethodize: function() {
+    var Foo = { bar: function(baz) { return baz } };
+    var baz = { quux: Foo.bar.methodize() };
+    
+    this.assertEqual(Foo.bar.methodize(), baz.quux);
+    this.assertEqual(baz, Foo.bar(baz));
+    this.assertEqual(baz, baz.quux());
+  },
 
-  // subclass that augments a method
-  var Cat = Class.create(Animal, {
-    eat: function($super, food) {
-      if (food instanceof Mouse) return $super();
-      else return this.say(&quot;Yuk! I only eat mice.&quot;);
-    }
-  });
+  testObjectExtend: function() {
+    var object = {foo: 'foo', bar: [1, 2, 3]};
+    this.assertIdentical(object, Object.extend(object));
+    this.assertHashEqual({foo: 'foo', bar: [1, 2, 3]}, object);
+    this.assertIdentical(object, Object.extend(object, {bla: 123}));
+    this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: 123}, object);
+    this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: null},
+      Object.extend(object, {bla: null}));
+  },
+  
+  testObjectToQueryString: function() {
+    this.assertEqual('a=A&amp;b=B&amp;c=C&amp;d=D%23', Object.toQueryString({a: 'A', b: 'B', c: 'C', d: 'D#'}));
+  },
+  
+  testObjectClone: function() {
+    var object = {foo: 'foo', bar: [1, 2, 3]};
+    this.assertNotIdentical(object, Object.clone(object));
+    this.assertHashEqual(object, Object.clone(object));
+    this.assertHashEqual({}, Object.clone());
+    var clone = Object.clone(object);
+    delete clone.bar;
+    this.assertHashEqual({foo: 'foo'}, clone, 
+      &quot;Optimizing Object.clone perf using prototyping doesn't allow properties to be deleted.&quot;);
+  },
 
-  // empty subclass
-  var Mouse = Class.create(Animal, {});
+  testObjectInspect: function() {
+    this.assertEqual('undefined', Object.inspect());
+    this.assertEqual('undefined', Object.inspect(undefined));
+    this.assertEqual('null', Object.inspect(null));
+    this.assertEqual(&quot;'foo\\\\b\\\'ar'&quot;, Object.inspect('foo\\b\'ar'));
+    this.assertEqual('[]', Object.inspect([]));
+    this.assertNothingRaised(function() { Object.inspect(window.Node) });
+  },
   
-  //mixins 
-  var Sellable = {
-    getValue: function(pricePerKilo) {
-      return this.weight * pricePerKilo;
-    },
-    
-    inspect: function() {
-      return '#&lt;Sellable: #{weight}kg&gt;'.interpolate(this);
-    }
-  };
+  testObjectToJSON: function() {
+    this.assertUndefined(Object.toJSON(undefined));
+    this.assertUndefined(Object.toJSON(Prototype.K));
+    this.assertEqual('\&quot;\&quot;', Object.toJSON(''));
+    this.assertEqual('[]', Object.toJSON([]));
+    this.assertEqual('[\&quot;a\&quot;]', Object.toJSON(['a']));
+    this.assertEqual('[\&quot;a\&quot;, 1]', Object.toJSON(['a', 1]));
+    this.assertEqual('[\&quot;a\&quot;, {\&quot;b\&quot;: null}]', Object.toJSON(['a', {'b': null}]));
+    this.assertEqual('{\&quot;a\&quot;: \&quot;hello!\&quot;}', Object.toJSON({a: 'hello!'}));
+    this.assertEqual('{}', Object.toJSON({}));
+    this.assertEqual('{}', Object.toJSON({a: undefined, b: undefined, c: Prototype.K}));
+    this.assertEqual('{\&quot;b\&quot;: [false, true], \&quot;c\&quot;: {\&quot;a\&quot;: \&quot;hello!\&quot;}}',
+      Object.toJSON({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}));
+    this.assertEqual('{\&quot;b\&quot;: [false, true], \&quot;c\&quot;: {\&quot;a\&quot;: \&quot;hello!\&quot;}}',
+      Object.toJSON($H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}})));
+    this.assertEqual('true', Object.toJSON(true));
+    this.assertEqual('false', Object.toJSON(false));
+    this.assertEqual('null', Object.toJSON(null));
+    var sam = new Person('sam');
+    this.assertEqual('-sam', Object.toJSON(sam));
+    this.assertEqual('-sam', sam.toJSON());
+    var element = $('test');
+    this.assertUndefined(Object.toJSON(element));
+    element.toJSON = function(){return 'I\'m a div with id test'};
+    this.assertEqual('I\'m a div with id test', Object.toJSON(element));
+  },
+  
+  testObjectToHTML: function() {
+    this.assertIdentical('', Object.toHTML());
+    this.assertIdentical('', Object.toHTML(''));
+    this.assertIdentical('', Object.toHTML(null));
+    this.assertIdentical('0', Object.toHTML(0));
+    this.assertIdentical('123', Object.toHTML(123));
+    this.assertEqual('hello world', Object.toHTML('hello world'));
+    this.assertEqual('hello world', Object.toHTML({toHTML: function() { return 'hello world' }}));
+  },
+  
+  testObjectIsArray: function() {
+    this.assert(Object.isArray([]));
+    this.assert(Object.isArray([0]));
+    this.assert(Object.isArray([0, 1]));
+    this.assert(!Object.isArray({}));
+    this.assert(!Object.isArray($('list').childNodes));
+    this.assert(!Object.isArray());
+    this.assert(!Object.isArray(''));
+    this.assert(!Object.isArray('foo'));
+    this.assert(!Object.isArray(0));
+    this.assert(!Object.isArray(1));
+    this.assert(!Object.isArray(null));
+    this.assert(!Object.isArray(true));
+    this.assert(!Object.isArray(false));
+    this.assert(!Object.isArray(undefined));
+  },
+  
+  testObjectIsHash: function() {
+    this.assert(Object.isHash($H()));
+    this.assert(Object.isHash(new Hash()));
+    this.assert(!Object.isHash({}));
+    this.assert(!Object.isHash(null));
+    this.assert(!Object.isHash());
+    this.assert(!Object.isHash(''));
+    this.assert(!Object.isHash(2));
+    this.assert(!Object.isHash(false));
+    this.assert(!Object.isHash(true));
+    this.assert(!Object.isHash([]));
+  },
+  
+  testObjectIsElement: function() {
+    this.assert(Object.isElement(document.createElement('div')));
+    this.assert(Object.isElement(new Element('div')));
+    this.assert(Object.isElement($('testlog')));
+    this.assert(!Object.isElement(document.createTextNode('bla')));
 
-  var Reproduceable = {
-    reproduce: function(partner) {
-      if (partner.constructor != this.constructor || partner.sex == this.sex)
-        return null;
-      var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female';
-      return new this.constructor('baby', weight, sex);
-    }
-  };
+    // falsy variables should not mess up return value type
+    this.assertIdentical(false, Object.isElement(0));
+    this.assertIdentical(false, Object.isElement(''));
+    this.assertIdentical(false, Object.isElement(NaN));
+    this.assertIdentical(false, Object.isElement(null));
+    this.assertIdentical(false, Object.isElement(undefined));
+  },
+  
+  testObjectIsFunction: function() {
+    this.assert(Object.isFunction(function() { }));
+    this.assert(Object.isFunction(Class.create()));
+    this.assert(!Object.isFunction(&quot;a string&quot;));
+    this.assert(!Object.isFunction($(&quot;testlog&quot;)));
+    this.assert(!Object.isFunction([]));
+    this.assert(!Object.isFunction({}));
+    this.assert(!Object.isFunction(0));
+    this.assert(!Object.isFunction(false));
+    this.assert(!Object.isFunction(undefined));
+  },
+  
+  testObjectIsString: function() {
+    this.assert(!Object.isString(function() { }));
+    this.assert(Object.isString(&quot;a string&quot;));
+    this.assert(!Object.isString(0));
+    this.assert(!Object.isString([]));
+    this.assert(!Object.isString({}));
+    this.assert(!Object.isString(false));
+    this.assert(!Object.isString(undefined));
+  },
   
-  // base class with mixin
-  var Plant = Class.create(Sellable, {
-    initialize: function(name, weight) {
-      this.name = name;
-      this.weight = weight;
-    },
+  testObjectIsNumber: function() {
+    this.assert(Object.isNumber(0));
+    this.assert(Object.isNumber(1.0));
+    this.assert(!Object.isNumber(function() { }));
+    this.assert(!Object.isNumber(&quot;a string&quot;));
+    this.assert(!Object.isNumber([]));
+    this.assert(!Object.isNumber({}));
+    this.assert(!Object.isNumber(false));
+    this.assert(!Object.isNumber(undefined));
+  },
+  
+  testObjectIsUndefined: function() {
+    this.assert(Object.isUndefined(undefined));
+    this.assert(!Object.isUndefined(null));
+    this.assert(!Object.isUndefined(false));
+    this.assert(!Object.isUndefined(0));
+    this.assert(!Object.isUndefined(&quot;&quot;));
+    this.assert(!Object.isUndefined(function() { }));
+    this.assert(!Object.isUndefined([]));
+    this.assert(!Object.isUndefined({}));
+  },
+  
+  // sanity check
+  testDoesntExtendObjectPrototype: function() {
+    // for-in is supported with objects
+    var iterations = 0, obj = { a: 1, b: 2, c: 3 };
+    for(property in obj) iterations++;
+    this.assertEqual(3, iterations);
+    
+    // for-in is not supported with arrays
+    iterations = 0;
+    var arr = [1,2,3];
+    for(property in arr) iterations++;
+    this.assert(iterations &gt; 3);
+  },
+  
+  testPeriodicalExecuterStop: function() {
+    var peEventCount = 0;
+    function peEventFired(pe) {
+      if (++peEventCount &gt; 2) pe.stop();
+    }
+    
+    // peEventFired will stop the PeriodicalExecuter after 3 callbacks
+    new PeriodicalExecuter(peEventFired, 0.05);
+    
+    this.wait(600, function() {
+      this.assertEqual(3, peEventCount);
+    });
+  },
 
-    inspect: function() {
-      return '#&lt;Plant: #{name}&gt;'.interpolate(this);
+  testBindAsEventListener: function() {
+    for( var i = 0; i &lt; 10; ++i ){
+      var div = document.createElement('div');
+      div.setAttribute('id','test-'+i);
+      document.body.appendChild(div);
+      var tobj = new TestObj();
+      var eventTest = { test: true };
+      var call = tobj.assertingEventHandler.bindAsEventListener(tobj,
+        this.assertEqual.bind(this, eventTest),
+        this.assertEqual.bind(this, arg1),
+        this.assertEqual.bind(this, arg2),
+        this.assertEqual.bind(this, arg3), arg1, arg2, arg3 );
+      call(eventTest);
     }
-  });
+  },
   
-  // subclass with mixin
-  var Dog = Class.create(Animal, Reproduceable, {
-    initialize: function($super, name, weight, sex) {
-      this.weight = weight;
-      this.sex = sex;
-      $super(name);
-    }
-  });
+  testDateToJSON: function() {
+    this.assertEqual('\&quot;1970-01-01T00:00:00Z\&quot;', new Date(Date.UTC(1970, 0, 1)).toJSON());
+  },
   
-  // subclass with mixins
-  var Ox = Class.create(Animal, Sellable, Reproduceable, {
-    initialize: function($super, name, weight, sex) {
-      this.weight = weight;
-      this.sex = sex;
-      $super(name);
-    },
-    
-    eat: function(food) {
-      if (food instanceof Plant)
-        this.weight += food.weight;
-    },
-    
-    inspect: function() {
-      return '#&lt;Ox: #{name}&gt;'.interpolate(this);
-    }
-  });
+  testRegExpEscape: function() {
+    this.assertEqual('word', RegExp.escape('word'));
+    this.assertEqual('\\/slashes\\/', RegExp.escape('/slashes/'));
+    this.assertEqual('\\\\backslashes\\\\', RegExp.escape('\\backslashes\\'));
+    this.assertEqual('\\\\border of word', RegExp.escape('\\border of word'));
+    
+    this.assertEqual('\\(\\?\\:non-capturing\\)', RegExp.escape('(?:non-capturing)'));
+    this.assertEqual('non-capturing', new RegExp(RegExp.escape('(?:') + '([^)]+)').exec('(?:non-capturing)')[1]);
+    
+    this.assertEqual('\\(\\?\\=positive-lookahead\\)', RegExp.escape('(?=positive-lookahead)'));
+    this.assertEqual('positive-lookahead', new RegExp(RegExp.escape('(?=') + '([^)]+)').exec('(?=positive-lookahead)')[1]);
+    
+    this.assertEqual('\\(\\?&lt;\\=positive-lookbehind\\)', RegExp.escape('(?&lt;=positive-lookbehind)'));
+    this.assertEqual('positive-lookbehind', new RegExp(RegExp.escape('(?&lt;=') + '([^)]+)').exec('(?&lt;=positive-lookbehind)')[1]);
+    
+    this.assertEqual('\\(\\?\\!negative-lookahead\\)', RegExp.escape('(?!negative-lookahead)'));
+    this.assertEqual('negative-lookahead', new RegExp(RegExp.escape('(?!') + '([^)]+)').exec('(?!negative-lookahead)')[1]);
+    
+    this.assertEqual('\\(\\?&lt;\\!negative-lookbehind\\)', RegExp.escape('(?&lt;!negative-lookbehind)'));
+    this.assertEqual('negative-lookbehind', new RegExp(RegExp.escape('(?&lt;!') + '([^)]+)').exec('(?&lt;!negative-lookbehind)')[1]);
+    
+    this.assertEqual('\\[\\\\w\\]\\+', RegExp.escape('[\\w]+'));
+    this.assertEqual('character class', new RegExp(RegExp.escape('[') + '([^\\]]+)').exec('[character class]')[1]);      
+    
+    this.assertEqual('&lt;div&gt;', new RegExp(RegExp.escape('&lt;div&gt;')).exec('&lt;td&gt;&lt;div&gt;&lt;/td&gt;')[0]);      
+    
+    this.assertEqual('false', RegExp.escape(false));
+    this.assertEqual('undefined', RegExp.escape());
+    this.assertEqual('null', RegExp.escape(null));
+    this.assertEqual('42', RegExp.escape(42));
+    
+    this.assertEqual('\\\\n\\\\r\\\\t', RegExp.escape('\\n\\r\\t'));
+    this.assertEqual('\n\r\t', RegExp.escape('\n\r\t'));
+    this.assertEqual('\\{5,2\\}', RegExp.escape('{5,2}'));
+    
+    this.assertEqual(
+      '\\/\\(\\[\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\\\\\]\\\\\\\/\\\\\\\\\\]\\)\\/g',
+      RegExp.escape('/([.*+?^=!:${}()|[\\]\\/\\\\])/g')
+    );
+  },
   
-  new Test.Unit.Runner({
+  testBrowserDetection: function() {
+    var results = $H(Prototype.Browser).map(function(engine){
+      return engine;
+    }).partition(function(engine){
+      return engine[1] === true
+    });
+    var trues = results[0], falses = results[1];
     
-    testFunctionArgumentNames: function() {
-      this.assertEnumEqual([], (function() {}).argumentNames());
-      this.assertEnumEqual([&quot;one&quot;], (function(one) {}).argumentNames());
-      this.assertEnumEqual([&quot;one&quot;, &quot;two&quot;, &quot;three&quot;], (function(one, two, three) {}).argumentNames());
-      this.assertEqual(&quot;$super&quot;, (function($super) {}).argumentNames().first());
-      
-      function named1() {};
-      this.assertEnumEqual([], named1.argumentNames());
-      function named2(one) {};
-      this.assertEnumEqual([&quot;one&quot;], named2.argumentNames());
-      function named3(one, two, three) {};
-      this.assertEnumEqual([&quot;one&quot;, &quot;two&quot;, &quot;three&quot;], named3.argumentNames());
-    },
-    
-    testFunctionBind: function() {
-      function methodWithoutArguments() { return this.hi };
-      function methodWithArguments()    { return this.hi + ',' + $A(arguments).join(',') };
-      var func = Prototype.emptyFunction;
-
-      this.assertIdentical(func, func.bind());
-      this.assertIdentical(func, func.bind(undefined));
-      this.assertNotIdentical(func, func.bind(null));
-
-      this.assertEqual('without', methodWithoutArguments.bind({ hi: 'without' })());
-      this.assertEqual('with,arg1,arg2', methodWithArguments.bind({ hi: 'with' })('arg1','arg2'));
-      this.assertEqual('withBindArgs,arg1,arg2',
-        methodWithArguments.bind({ hi: 'withBindArgs' }, 'arg1', 'arg2')());
-      this.assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4',
-        methodWithArguments.bind({ hi: 'withBindArgsAndArgs' }, 'arg1', 'arg2')('arg3', 'arg4'));
-    },
-    
-    testFunctionCurry: function() {
-      var split = function(delimiter, string) { return string.split(delimiter); };
-      var splitOnColons = split.curry(&quot;:&quot;);
-      this.assertNotIdentical(split, splitOnColons);
-      this.assertEnumEqual(split(&quot;:&quot;, &quot;0:1:2:3:4:5&quot;), splitOnColons(&quot;0:1:2:3:4:5&quot;));
-      this.assertIdentical(split, split.curry());
-    },
-    
-    testFunctionDelay: function() {
-      window.delayed = undefined;
-      var delayedFunction = function() { window.delayed = true; };
-      var delayedFunctionWithArgs = function() { window.delayedWithArgs = $A(arguments).join(' '); };
-      delayedFunction.delay(0.8);
-      delayedFunctionWithArgs.delay(0.8, 'hello', 'world');
-      this.assertUndefined(window.delayed);
-      this.wait(1000, function() {
-        this.assert(window.delayed);
-        this.assertEqual('hello world', window.delayedWithArgs);
-      });
-    },
+    this.info('User agent string is: ' + navigator.userAgent);
     
-    testFunctionWrap: function() {
-      function sayHello(){
-        return 'hello world';
-      };
-      
-      this.assertEqual('HELLO WORLD', sayHello.wrap(function(proceed) {
-        return proceed().toUpperCase();
-      })());
-      
-      var temp = String.prototype.capitalize;
-      String.prototype.capitalize = String.prototype.capitalize.wrap(function(proceed, eachWord) {
-        if(eachWord &amp;&amp; this.include(' ')) return this.split(' ').map(function(str){
-          return str.capitalize();
-        }).join(' ');
-        return proceed();
-      });
-      this.assertEqual('Hello world', 'hello world'.capitalize());
-      this.assertEqual('Hello World', 'hello world'.capitalize(true));
-      this.assertEqual('Hello', 'hello'.capitalize());
-      String.prototype.capitalize = temp;
-    },
-    
-    testFunctionDefer: function() {
-      window.deferred = undefined;
-      var deferredFunction = function() { window.deferred = true; };
-      deferredFunction.defer();
-      this.assertUndefined(window.deferred);      
-      this.wait(50, function() {
-        this.assert(window.deferred);
-        
-        window.deferredValue = 0;
-        var deferredFunction2 = function(arg) { window.deferredValue = arg; };
-        deferredFunction2.defer('test');
-        this.wait(50, function() {
-          this.assertEqual('test', window.deferredValue);
-        });
-      });
-    },
+    this.assert(trues.size() == 0 || trues.size() == 1, 
+      'There should be only one or no browser detected.');
     
-    testFunctionMethodize: function() {
-      var Foo = { bar: function(baz) { return baz } };
-      var baz = { quux: Foo.bar.methodize() };
-      
-      this.assertEqual(Foo.bar.methodize(), baz.quux);
-      this.assertEqual(baz, Foo.bar(baz));
-      this.assertEqual(baz, baz.quux());
-    },
-
-    testObjectExtend: function() {
-      var object = {foo: 'foo', bar: [1, 2, 3]};
-      this.assertIdentical(object, Object.extend(object));
-      this.assertHashEqual({foo: 'foo', bar: [1, 2, 3]}, object);
-      this.assertIdentical(object, Object.extend(object, {bla: 123}));
-      this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: 123}, object);
-      this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: null},
-        Object.extend(object, {bla: null}));
-    },
-    
-    testObjectToQueryString: function() {
-      this.assertEqual('a=A&amp;b=B&amp;c=C&amp;d=D%23', Object.toQueryString({a: 'A', b: 'B', c: 'C', d: 'D#'}));
-    },
-    
-    testObjectClone: function() {
-      var object = {foo: 'foo', bar: [1, 2, 3]};
-      this.assertNotIdentical(object, Object.clone(object));
-      this.assertHashEqual(object, Object.clone(object));
-      this.assertHashEqual({}, Object.clone());
-      var clone = Object.clone(object);
-      delete clone.bar;
-      this.assertHashEqual({foo: 'foo'}, clone, 
-        &quot;Optimizing Object.clone perf using prototyping doesn't allow properties to be deleted.&quot;);
-    },
-
-    testObjectInspect: function() {
-      this.assertEqual('undefined', Object.inspect());
-      this.assertEqual('undefined', Object.inspect(undefined));
-      this.assertEqual('null', Object.inspect(null));
-      this.assertEqual(&quot;'foo\\\\b\\\'ar'&quot;, Object.inspect('foo\\b\'ar'));
-      this.assertEqual('[]', Object.inspect([]));
-      this.assertNothingRaised(function() { Object.inspect(window.Node) });
-    },
-    
-    testObjectToJSON: function() {
-      this.assertUndefined(Object.toJSON(undefined));
-      this.assertUndefined(Object.toJSON(Prototype.K));
-      this.assertEqual('\&quot;\&quot;', Object.toJSON(''));
-      this.assertEqual('[]', Object.toJSON([]));
-      this.assertEqual('[\&quot;a\&quot;]', Object.toJSON(['a']));
-      this.assertEqual('[\&quot;a\&quot;, 1]', Object.toJSON(['a', 1]));
-      this.assertEqual('[\&quot;a\&quot;, {\&quot;b\&quot;: null}]', Object.toJSON(['a', {'b': null}]));
-      this.assertEqual('{\&quot;a\&quot;: \&quot;hello!\&quot;}', Object.toJSON({a: 'hello!'}));
-      this.assertEqual('{}', Object.toJSON({}));
-      this.assertEqual('{}', Object.toJSON({a: undefined, b: undefined, c: Prototype.K}));
-      this.assertEqual('{\&quot;b\&quot;: [false, true], \&quot;c\&quot;: {\&quot;a\&quot;: \&quot;hello!\&quot;}}',
-        Object.toJSON({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}));
-      this.assertEqual('{\&quot;b\&quot;: [false, true], \&quot;c\&quot;: {\&quot;a\&quot;: \&quot;hello!\&quot;}}',
-        Object.toJSON($H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}})));
-      this.assertEqual('true', Object.toJSON(true));
-      this.assertEqual('false', Object.toJSON(false));
-      this.assertEqual('null', Object.toJSON(null));
-      var sam = new Person('sam');
-      this.assertEqual('-sam', Object.toJSON(sam));
-      this.assertEqual('-sam', sam.toJSON());
-      var element = $('test');
-      this.assertUndefined(Object.toJSON(element));
-      element.toJSON = function(){return 'I\'m a div with id test'};
-      this.assertEqual('I\'m a div with id test', Object.toJSON(element));
-    },
-    
-    testObjectToHTML: function() {
-      this.assertIdentical('', Object.toHTML());
-      this.assertIdentical('', Object.toHTML(''));
-      this.assertIdentical('', Object.toHTML(null));
-      this.assertIdentical('0', Object.toHTML(0));
-      this.assertIdentical('123', Object.toHTML(123));
-      this.assertEqual('hello world', Object.toHTML('hello world'));
-      this.assertEqual('hello world', Object.toHTML({toHTML: function() { return 'hello world' }}));
-    },
-    
-    testObjectIsArray: function() {
-      this.assert(Object.isArray([]));
-      this.assert(Object.isArray([0]));
-      this.assert(Object.isArray([0, 1]));
-      this.assert(!Object.isArray({}));
-      this.assert(!Object.isArray($('list').childNodes));
-      this.assert(!Object.isArray());
-      this.assert(!Object.isArray(''));
-      this.assert(!Object.isArray('foo'));
-      this.assert(!Object.isArray(0));
-      this.assert(!Object.isArray(1));
-      this.assert(!Object.isArray(null));
-      this.assert(!Object.isArray(true));
-      this.assert(!Object.isArray(false));
-      this.assert(!Object.isArray(undefined));
-    },
-    
-    testObjectIsHash: function() {
-      this.assert(Object.isHash($H()));
-      this.assert(Object.isHash(new Hash()));
-      this.assert(!Object.isHash({}));
-    },
-    
-    testObjectIsElement: function() {
-      this.assert(Object.isElement(document.createElement('div')));
-      this.assert(Object.isElement(new Element('div')));
-      this.assert(Object.isElement($('testlog')));
-      this.assert(!Object.isElement(document.createTextNode('bla')));
-    },
-    
-    testObjectIsFunction: function() {
-      this.assert(Object.isFunction(function() { }));
-      this.assert(Object.isFunction(Class.create()));
-      this.assert(!Object.isFunction(&quot;a string&quot;));
-      this.assert(!Object.isFunction($(&quot;testlog&quot;)));
-      this.assert(!Object.isFunction([]));
-      this.assert(!Object.isFunction({}));
-      this.assert(!Object.isFunction(0));
-      this.assert(!Object.isFunction(false));
-      this.assert(!Object.isFunction(undefined));
-    },
-    
-    testObjectIsString: function() {
-      this.assert(!Object.isString(function() { }));
-      this.assert(Object.isString(&quot;a string&quot;));
-      this.assert(!Object.isString(0));
-      this.assert(!Object.isString([]));
-      this.assert(!Object.isString({}));
-      this.assert(!Object.isString(false));
-      this.assert(!Object.isString(undefined));
-    },
-    
-    testObjectIsNumber: function() {
-      this.assert(Object.isNumber(0));
-      this.assert(Object.isNumber(1.0));
-      this.assert(!Object.isNumber(function() { }));
-      this.assert(!Object.isNumber(&quot;a string&quot;));
-      this.assert(!Object.isNumber([]));
-      this.assert(!Object.isNumber({}));
-      this.assert(!Object.isNumber(false));
-      this.assert(!Object.isNumber(undefined));
-    },
-    
-    testObjectIsUndefined: function() {
-      this.assert(Object.isUndefined(undefined));
-      this.assert(!Object.isUndefined(null));
-      this.assert(!Object.isUndefined(false));
-      this.assert(!Object.isUndefined(0));
-      this.assert(!Object.isUndefined(&quot;&quot;));
-      this.assert(!Object.isUndefined(function() { }));
-      this.assert(!Object.isUndefined([]));
-      this.assert(!Object.isUndefined({}));
-    },
-    
-    // sanity check
-    testDoesntExtendObjectPrototype: function() {
-      // for-in is supported with objects
-      var iterations = 0, obj = { a: 1, b: 2, c: 3 };
-      for(property in obj) iterations++;
-      this.assertEqual(3, iterations);
-      
-      // for-in is not supported with arrays
-      iterations = 0;
-      var arr = [1,2,3];
-      for(property in arr) iterations++;
-      this.assert(iterations &gt; 3);
-    },
-    
-    testPeriodicalExecuterStop: function() {
-      var peEventCount = 0;
-      function peEventFired(pe) {
-        if (++peEventCount &gt; 2) pe.stop();
-      }
-      
-      // peEventFired will stop the PeriodicalExecuter after 3 callbacks
-      new PeriodicalExecuter(peEventFired, 0.05);
-      
-      this.wait(600, function() {
-        this.assertEqual(3, peEventCount);
-      });
-    },
-
-    testBindAsEventListener: function() {
-      for( var i = 0; i &lt; 10; ++i ){
-        var div = document.createElement('div');
-        div.setAttribute('id','test-'+i);
-        document.body.appendChild(div);
-        var tobj = new TestObj();
-        var eventTest = { test: true };
-        var call = tobj.assertingEventHandler.bindAsEventListener(tobj,
-          this.assertEqual.bind(this, eventTest),
-          this.assertEqual.bind(this, arg1),
-          this.assertEqual.bind(this, arg2),
-          this.assertEqual.bind(this, arg3), arg1, arg2, arg3 );
-        call(eventTest);
-      }
-    },
+    // we should have definite trues or falses here
+    trues.each(function(result) {
+      this.assert(result[1] === true);
+    }, this);
+    falses.each(function(result) {
+      this.assert(result[1] === false);
+    }, this);
     
-    testDateToJSON: function() {
-      this.assertEqual('\&quot;1970-01-01T00:00:00Z\&quot;', new Date(Date.UTC(1970, 0, 1)).toJSON());
-    },
+    if(navigator.userAgent.indexOf('AppleWebKit/') &gt; -1) {
+      this.info('Running on WebKit');
+      this.assert(Prototype.Browser.WebKit);
+    }
     
-    testRegExpEscape: function() {
-      this.assertEqual('word', RegExp.escape('word'));
-      this.assertEqual('\\/slashes\\/', RegExp.escape('/slashes/'));
-      this.assertEqual('\\\\backslashes\\\\', RegExp.escape('\\backslashes\\'));
-      this.assertEqual('\\\\border of word', RegExp.escape('\\border of word'));
-      
-      this.assertEqual('\\(\\?\\:non-capturing\\)', RegExp.escape('(?:non-capturing)'));
-      this.assertEqual('non-capturing', new RegExp(RegExp.escape('(?:') + '([^)]+)').exec('(?:non-capturing)')[1]);
-      
-      this.assertEqual('\\(\\?\\=positive-lookahead\\)', RegExp.escape('(?=positive-lookahead)'));
-      this.assertEqual('positive-lookahead', new RegExp(RegExp.escape('(?=') + '([^)]+)').exec('(?=positive-lookahead)')[1]);
-      
-      this.assertEqual('\\(\\?&lt;\\=positive-lookbehind\\)', RegExp.escape('(?&lt;=positive-lookbehind)'));
-      this.assertEqual('positive-lookbehind', new RegExp(RegExp.escape('(?&lt;=') + '([^)]+)').exec('(?&lt;=positive-lookbehind)')[1]);
-      
-      this.assertEqual('\\(\\?\\!negative-lookahead\\)', RegExp.escape('(?!negative-lookahead)'));
-      this.assertEqual('negative-lookahead', new RegExp(RegExp.escape('(?!') + '([^)]+)').exec('(?!negative-lookahead)')[1]);
-      
-      this.assertEqual('\\(\\?&lt;\\!negative-lookbehind\\)', RegExp.escape('(?&lt;!negative-lookbehind)'));
-      this.assertEqual('negative-lookbehind', new RegExp(RegExp.escape('(?&lt;!') + '([^)]+)').exec('(?&lt;!negative-lookbehind)')[1]);
-      
-      this.assertEqual('\\[\\\\w\\]\\+', RegExp.escape('[\\w]+'));
-      this.assertEqual('character class', new RegExp(RegExp.escape('[') + '([^\\]]+)').exec('[character class]')[1]);      
-      
-      this.assertEqual('&lt;div&gt;', new RegExp(RegExp.escape('&lt;div&gt;')).exec('&lt;td&gt;&lt;div&gt;&lt;/td&gt;')[0]);      
-      
-      this.assertEqual('false', RegExp.escape(false));
-      this.assertEqual('undefined', RegExp.escape());
-      this.assertEqual('null', RegExp.escape(null));
-      this.assertEqual('42', RegExp.escape(42));
-      
-      this.assertEqual('\\\\n\\\\r\\\\t', RegExp.escape('\\n\\r\\t'));
-      this.assertEqual('\n\r\t', RegExp.escape('\n\r\t'));
-      this.assertEqual('\\{5,2\\}', RegExp.escape('{5,2}'));
-      
-      this.assertEqual(
-        '\\/\\(\\[\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\\\\\]\\\\\\\/\\\\\\\\\\]\\)\\/g',
-        RegExp.escape('/([.*+?^=!:${}()|[\\]\\/\\\\])/g')
-      );
-    },
-    
-    testBrowserDetection: function() {
-      var results = $H(Prototype.Browser).map(function(engine){
-        return engine;
-      }).partition(function(engine){
-        return engine[1] === true
-      });
-      var trues = results[0], falses = results[1];
-      
-      this.info('User agent string is: ' + navigator.userAgent);
-      
-      this.assert(trues.size() == 0 || trues.size() == 1, 
-        'There should be only one or no browser detected.');
-      
-      // we should have definite trues or falses here
-      trues.each(function(result) {
-        this.assert(result[1] === true);
-      }, this);
-      falses.each(function(result) {
-        this.assert(result[1] === false);
-      }, this);
-      
-      if(navigator.userAgent.indexOf('AppleWebKit/') &gt; -1) {
-        this.info('Running on WebKit');
-        this.assert(Prototype.Browser.WebKit);
-      }
-      
-      if(!!window.opera) {
-        this.info('Running on Opera');
-        this.assert(Prototype.Browser.Opera);
-      }
-      
-      if(!!(window.attachEvent &amp;&amp; !window.opera)) {
-        this.info('Running on IE');
-        this.assert(Prototype.Browser.IE);
-      }
-      
-      if(navigator.userAgent.indexOf('Gecko') &gt; -1 &amp;&amp; navigator.userAgent.indexOf('KHTML') == -1) {
-        this.info('Running on Gecko');
-        this.assert(Prototype.Browser.Gecko);
-      } 
-    },
-    
-    testClassCreate: function() { 
-      this.assert(Object.isFunction(Animal), 'Animal is not a constructor');
-      this.assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses);
-      Animal.subclasses.each(function(subclass) {
-        this.assertEqual(Animal, subclass.superclass);
-      }, this);
+    if(!!window.opera) {
+      this.info('Running on Opera');
+      this.assert(Prototype.Browser.Opera);
+    }
+    
+    if(!!(window.attachEvent &amp;&amp; !window.opera)) {
+      this.info('Running on IE');
+      this.assert(Prototype.Browser.IE);
+    }
+    
+    if(navigator.userAgent.indexOf('Gecko') &gt; -1 &amp;&amp; navigator.userAgent.indexOf('KHTML') == -1) {
+      this.info('Running on Gecko');
+      this.assert(Prototype.Browser.Gecko);
+    } 
+  },
+  
+  testClassCreate: function() { 
+    this.assert(Object.isFunction(Animal), 'Animal is not a constructor');
+    this.assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses);
+    Animal.subclasses.each(function(subclass) {
+      this.assertEqual(Animal, subclass.superclass);
+    }, this);
 
-      var Bird = Class.create(Animal);
-      this.assertEqual(Bird, Animal.subclasses.last());
-      // for..in loop (for some reason) doesn't iterate over the constructor property in top-level classes
-      this.assertEnumEqual(Object.keys(new Animal).sort(), Object.keys(new Bird).without('constructor').sort());
-    },
+    var Bird = Class.create(Animal);
+    this.assertEqual(Bird, Animal.subclasses.last());
+    // for..in loop (for some reason) doesn't iterate over the constructor property in top-level classes
+    this.assertEnumEqual(Object.keys(new Animal).sort(), Object.keys(new Bird).without('constructor').sort());
+  },
 
-    testClassInstantiation: function() { 
-      var pet = new Animal(&quot;Nibbles&quot;);
-      this.assertEqual(&quot;Nibbles&quot;, pet.name, &quot;property not initialized&quot;);
-      this.assertEqual('Nibbles: Hi!', pet.say('Hi!'));
-      this.assertEqual(Animal, pet.constructor, &quot;bad constructor reference&quot;);
-      this.assertUndefined(pet.superclass);
+  testClassInstantiation: function() { 
+    var pet = new Animal(&quot;Nibbles&quot;);
+    this.assertEqual(&quot;Nibbles&quot;, pet.name, &quot;property not initialized&quot;);
+    this.assertEqual('Nibbles: Hi!', pet.say('Hi!'));
+    this.assertEqual(Animal, pet.constructor, &quot;bad constructor reference&quot;);
+    this.assertUndefined(pet.superclass);
 
-      var Empty = Class.create();
-      this.assert('object', typeof new Empty);
-    },
+    var Empty = Class.create();
+    this.assert('object', typeof new Empty);
+  },
 
-    testInheritance: function() {
-      var tom = new Cat('Tom');
-      this.assertEqual(Cat, tom.constructor, &quot;bad constructor reference&quot;);
-      this.assertEqual(Animal, tom.constructor.superclass, 'bad superclass reference');
-      this.assertEqual('Tom', tom.name);
-      this.assertEqual('Tom: meow', tom.say('meow'));
-      this.assertEqual('Tom: Yuk! I only eat mice.', tom.eat(new Animal));
-    },
+  testInheritance: function() {
+    var tom = new Cat('Tom');
+    this.assertEqual(Cat, tom.constructor, &quot;bad constructor reference&quot;);
+    this.assertEqual(Animal, tom.constructor.superclass, 'bad superclass reference');
+    this.assertEqual('Tom', tom.name);
+    this.assertEqual('Tom: meow', tom.say('meow'));
+    this.assertEqual('Tom: Yuk! I only eat mice.', tom.eat(new Animal));
+  },
 
-    testSuperclassMethodCall: function() {
-      var tom = new Cat('Tom');
-      this.assertEqual('Tom: Yum!', tom.eat(new Mouse));
+  testSuperclassMethodCall: function() {
+    var tom = new Cat('Tom');
+    this.assertEqual('Tom: Yum!', tom.eat(new Mouse));
 
-      // augment the constructor and test
-      var Dodo = Class.create(Animal, {
-        initialize: function($super, name) {
-          $super(name);
-          this.extinct = true;
-        },
-        
-        say: function($super, message) {
-          return $super(message) + &quot; honk honk&quot;;
-        }
-      });
+    // augment the constructor and test
+    var Dodo = Class.create(Animal, {
+      initialize: function($super, name) {
+        $super(name);
+        this.extinct = true;
+      },
+      
+      say: function($super, message) {
+        return $super(message) + &quot; honk honk&quot;;
+      }
+    });
 
-      var gonzo = new Dodo('Gonzo');
-      this.assertEqual('Gonzo', gonzo.name);
-      this.assert(gonzo.extinct, 'Dodo birds should be extinct');
-      this.assertEqual(&quot;Gonzo: hello honk honk&quot;, gonzo.say(&quot;hello&quot;));
-    },
+    var gonzo = new Dodo('Gonzo');
+    this.assertEqual('Gonzo', gonzo.name);
+    this.assert(gonzo.extinct, 'Dodo birds should be extinct');
+    this.assertEqual(&quot;Gonzo: hello honk honk&quot;, gonzo.say(&quot;hello&quot;));
+  },
 
-    testClassAddMethods: function() {
-      var tom   = new Cat('Tom');
-      var jerry = new Mouse('Jerry');
-      
-      Animal.addMethods({
-        sleep: function() {
-          return this.say('ZZZ');
-        }
-      });
-      
-      Mouse.addMethods({
-        sleep: function($super) {
-          return $super() + &quot; ... no, can't sleep! Gotta steal cheese!&quot;;
-        },
-        escape: function(cat) {
-          return this.say('(from a mousehole) Take that, ' + cat.name + '!');
-        }
-      });
-      
-      this.assertEqual('Tom: ZZZ', tom.sleep(), &quot;added instance method not available to subclass&quot;);
-      this.assertEqual(&quot;Jerry: ZZZ ... no, can't sleep! Gotta steal cheese!&quot;, jerry.sleep());
-      this.assertEqual(&quot;Jerry: (from a mousehole) Take that, Tom!&quot;, jerry.escape(tom));
-      // insure that a method has not propagated *up* the prototype chain:
-      this.assertUndefined(tom.escape);
-      this.assertUndefined(new Animal().escape);
-      
-      Animal.addMethods({
-        sleep: function() {
-          return this.say('zZzZ');
-        }
-      });
-      
-      this.assertEqual(&quot;Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!&quot;, jerry.sleep());
-    },
-    
-    testBaseClassWithMixin: function() {
-      var grass = new Plant('grass', 3);
-      this.assertRespondsTo('getValue', grass);      
-      this.assertEqual('#&lt;Plant: grass&gt;', grass.inspect());
-    },
-    
-    testSubclassWithMixin: function() {
-      var snoopy = new Dog('Snoopy', 12, 'male');
-      this.assertRespondsTo('reproduce', snoopy);      
-    },
-   
-    testSubclassWithMixins: function() {
-      var cow = new Ox('cow', 400, 'female');
-      this.assertEqual('#&lt;Ox: cow&gt;', cow.inspect());
-      this.assertRespondsTo('reproduce', cow);
-      this.assertRespondsTo('getValue', cow);
-    },
-   
-    testClassWithToStringAndValueOfMethods: function() {
-      var Foo = Class.create({
-        toString: function() { return &quot;toString&quot; },
-        valueOf: function() { return &quot;valueOf&quot; }
-      });
-      
-      this.assertEqual(&quot;toString&quot;, new Foo().toString());
-      this.assertEqual(&quot;valueOf&quot;, new Foo().valueOf());
-    }
-  });
\ No newline at end of file
+  testClassAddMethods: function() {
+    var tom   = new Cat('Tom');
+    var jerry = new Mouse('Jerry');
+    
+    Animal.addMethods({
+      sleep: function() {
+        return this.say('ZZZ');
+      }
+    });
+    
+    Mouse.addMethods({
+      sleep: function($super) {
+        return $super() + &quot; ... no, can't sleep! Gotta steal cheese!&quot;;
+      },
+      escape: function(cat) {
+        return this.say('(from a mousehole) Take that, ' + cat.name + '!');
+      }
+    });
+    
+    this.assertEqual('Tom: ZZZ', tom.sleep(), &quot;added instance method not available to subclass&quot;);
+    this.assertEqual(&quot;Jerry: ZZZ ... no, can't sleep! Gotta steal cheese!&quot;, jerry.sleep());
+    this.assertEqual(&quot;Jerry: (from a mousehole) Take that, Tom!&quot;, jerry.escape(tom));
+    // insure that a method has not propagated *up* the prototype chain:
+    this.assertUndefined(tom.escape);
+    this.assertUndefined(new Animal().escape);
+    
+    Animal.addMethods({
+      sleep: function() {
+        return this.say('zZzZ');
+      }
+    });
+    
+    this.assertEqual(&quot;Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!&quot;, jerry.sleep());
+  },
+  
+  testBaseClassWithMixin: function() {
+    var grass = new Plant('grass', 3);
+    this.assertRespondsTo('getValue', grass);      
+    this.assertEqual('#&lt;Plant: grass&gt;', grass.inspect());
+  },
+  
+  testSubclassWithMixin: function() {
+    var snoopy = new Dog('Snoopy', 12, 'male');
+    this.assertRespondsTo('reproduce', snoopy);      
+  },
+ 
+  testSubclassWithMixins: function() {
+    var cow = new Ox('cow', 400, 'female');
+    this.assertEqual('#&lt;Ox: cow&gt;', cow.inspect());
+    this.assertRespondsTo('reproduce', cow);
+    this.assertRespondsTo('getValue', cow);
+  },
+ 
+  testClassWithToStringAndValueOfMethods: function() {
+    var Foo = Class.create({
+      toString: function() { return &quot;toString&quot; },
+      valueOf: function() { return &quot;valueOf&quot; }
+    });
+    
+    var Parent = Class.create({
+      m1: function(){ return 'm1' },
+      m2: function(){ return 'm2' }
+    });
+    var Child = Class.create(Parent, {
+      m1: function($super) { return 'm1 child' },
+      m2: function($super) { return 'm2 child' }
+    });
+    
+    this.assert(new Child().m1.toString().indexOf('m1 child') &gt; -1);
+    
+    this.assertEqual(&quot;toString&quot;, new Foo().toString());
+    this.assertEqual(&quot;valueOf&quot;, new Foo().valueOf());
+  }
+});
\ No newline at end of file</diff>
      <filename>test/unit/base_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,3 @@
-var testVar = 'to be updated', testVar2 = '';
 var getInnerHTML = function(id) {
   return $(id).innerHTML.toString().toLowerCase().gsub(/[\r\n\t]/, '');
 };
@@ -7,23 +6,6 @@ var createParagraph = function(text) {
   p.appendChild(document.createTextNode(text));
   return p;
 }
-Element.addMethods({
-  hashBrowns: function(element) { return 'hash browns'; }
-});
-
-Element.addMethods(&quot;LI&quot;, {
-  pancakes: function(element) { return &quot;pancakes&quot;; }
-});
-
-Element.addMethods(&quot;DIV&quot;, {
-  waffles: function(element) { return &quot;waffles&quot;; }
-});
-
-Element.addMethods($w(&quot;li div&quot;), {
-  orangeJuice: function(element) { return &quot;orange juice&quot;; }
-});
-
-var documentViewportProperties;
 
 new Test.Unit.Runner({
   setup: function() {
@@ -213,8 +195,7 @@ new Test.Unit.Runner({
   },
   
   testNewElementInsert: function() {
-    var container = new Element('div');
-    element = new Element('div');
+    var container = new Element('div'), element = new Element('div');
     container.insert(element);
     
     element.insert({ before: '&lt;p&gt;a paragraph&lt;/p&gt;' });
@@ -403,11 +384,11 @@ new Test.Unit.Runner({
     
     $('testoption-replace').replace('&lt;option&gt;hello&lt;/option&gt;');
     this.assert($('testoption-replace-container').innerHTML.include('hello'));
-         
-    $('testinput-replace').replace('&lt;p&gt;hello world&lt;/p&gt;');
+    
+    Element.replace('testinput-replace', '&lt;p&gt;hello world&lt;/p&gt;');
     this.assertEqual('&lt;p&gt;hello world&lt;/p&gt;', getInnerHTML('testform-replace'));
 
-    $('testform-replace').replace('&lt;form&gt;&lt;/form&gt;');
+    Element.replace('testform-replace', '&lt;form&gt;&lt;/form&gt;');
     this.assertEqual('&lt;p&gt;some text&lt;/p&gt;&lt;form&gt;&lt;/form&gt;&lt;p&gt;some text&lt;/p&gt;', getInnerHTML('testform-replace-container'));
   },
   
@@ -462,9 +443,11 @@ new Test.Unit.Runner({
   testElementIdentify: function() {
     var parent = $('identification');
     this.assertEqual(parent.down().identify(), 'predefined_id');
-    this.assertEqual(parent.down(1).identify(), 'anonymous_element_1');
-    this.assertEqual(parent.down(2).identify(), 'anonymous_element_2');
-    this.assertEqual(parent.down(3).identify(), 'anonymous_element_4');
+    this.assert(parent.down(1).identify().startsWith('anonymous_element_'));
+    this.assert(parent.down(2).identify().startsWith('anonymous_element_'));
+    this.assert(parent.down(3).identify().startsWith('anonymous_element_'));
+    
+    this.assert(parent.down(3).id !== parent.down(2).id);
   },
      
   testElementClassNameMethod: function() {
@@ -577,6 +560,10 @@ new Test.Unit.Runner({
     var dummy = $(document.createElement('DIV'));
     dummy.innerHTML = '&lt;div&gt;&lt;/div&gt;'.times(3);
     this.assert(typeof dummy.down().setStyle == 'function');
+    
+    var input = $$('input')[0];
+    this.assertNothingRaised(function(){ input.down('span') });
+    this.assertUndefined(input.down('span'));
   },
   
   testElementPrevious: function() {
@@ -727,6 +714,10 @@ new Test.Unit.Runner({
     
     $(document.body).insert(new Element('div', { id: 'impostor' }));
     this.assert(!$('impostor').descendantOf('ancestor'));
+    
+    // test descendantOf document
+    this.assert($(document.body).descendantOf(document));  
+    this.assert($(document.documentElement).descendantOf(document));  
   },  
   
   testChildOf: function() {
@@ -899,6 +890,10 @@ new Test.Unit.Runner({
       this.assertEqual(&quot;14px&quot;, $('style_test_dimensions').getStyle('width'));
       this.assertEqual(&quot;17px&quot;, $('style_test_dimensions').getStyle('height'));
     }
+    
+    // height/width could always be calculated if it's set to &quot;auto&quot; (Firefox)
+    this.assertNotNull($('auto_dimensions').getStyle('height'));
+    this.assertNotNull($('auto_dimensions').getStyle('width'));
   },
   
   testElementGetOpacity: function() {
@@ -1006,6 +1001,18 @@ new Test.Unit.Runner({
     this.assertEqual('26',        p.readAttribute('age'));
   },
   
+  testElementHasAttribute: function() {
+    var label = $('write_attribute_label');
+    this.assertIdentical(true,  label.hasAttribute('for'));
+    this.assertIdentical(false, label.hasAttribute('htmlFor'));
+    this.assertIdentical(false, label.hasAttribute('className'));
+    this.assertIdentical(false, label.hasAttribute('rainbows'));
+    
+    var input = $('write_attribute_input');
+    this.assertNotIdentical(null, input.hasAttribute('readonly'));
+    this.assertNotIdentical(null, input.hasAttribute('readOnly'));
+  },
+  
   testNewElement: function() {
     this.assert(new Element('h1'));
     </diff>
      <filename>test/unit/dom_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,3 @@
-Form.Element.Methods.coffee = Prototype.K;
-Element.addMethods();
-
 new Test.Unit.Runner({
   testInput: function() {
     this.assert($(&quot;input&quot;).present != null);</diff>
      <filename>test/unit/element_mixins_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,27 +1,3 @@
-var Fixtures = {
-  People: [
-    {name: 'Sam Stephenson',    nickname: 'sam-'},
-    {name: 'Marcel Molina Jr.', nickname: 'noradio'},
-    {name: 'Scott Barron',      nickname: 'htonl'},
-    {name: 'Nicholas Seckar',   nickname: 'Ulysses'}
-  ],
-  
-  Nicknames: $w('sam- noradio htonl Ulysses'),
-  
-  Basic: [1, 2, 3],
-  
-  Primes: [
-     1,  2,  3,  5,  7,  11, 13, 17, 19, 23,
-    29, 31, 37, 41, 43,  47, 53, 59, 61, 67,
-    71, 73, 79, 83, 89,  97
-  ],
-  
-  Z: []
-};
-
-for (var i = 1; i &lt;= 100; i++) 
-  Fixtures.Z.push(i);    
-
 function prime(value) {
   for (var i = 2; i &lt; value; i++)
     if (value % i == 0) return false;</diff>
      <filename>test/unit/enumerable_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,92 +1,3 @@
-&lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
-/* &lt;![CDATA[ */
-  #style_test_1 { cursor: pointer; font-size:12px;}
-  div.style-test { margin-left: 1px }
-  
-  #style_test_dimensions_container { 
-    position: absolute;
-    top: 0;
-    left: 500px;
-    width: 20px;
-    height: 30px;
-    margin: 10px;
-    padding: 10px;
-    border: 3px solid red;
-  }
-  
-  #not_floating_style { float: none }
-  #floating_style { float: left }
-  #op2 { opacity:0.5;filter:alpha(opacity=50)progid:DXImageTransform.Microsoft.Blur(strength=10);}
-  
-  #scroll_test_1 { 
-    margin: 10px;
-    padding: 10px;
-    position: relative;
-  }
-  
-  #scroll_test_2 {
-    position: absolute;
-    left: 10px;
-    top: 10px;
-  }
-  
-  #dimensions-visible,
-  #dimensions-display-none,
-  #dimensions-visible-pos-rel,
-  #dimensions-display-none-pos-rel,
-  #dimensions-visible-pos-abs,
-  #dimensions-display-none-pos-abs {
-      font-size: 10px;
-      height: 10em;
-      width: 20em;
-  }
-  
-  #dimensions-visible-pos-abs,
-  #dimensions-display-none-pos-abs {
-    position: absolute;
-    top: 15px;
-    left: 15px;
-  }
-  
-  #dimensions-visible-pos-rel,
-  #dimensions-display-none-pos-rel {
-    position: relative;
-    top: 15px;
-    left: 15px;
-  }
-  
-  #dimensions-display-none, #imensions-display-none-pos-rel, #dimensions-display-none-pos-abs {
-      display: none;
-  }
-  
-  #dimensions-table, #dimensions-tbody, #dimensions-tr, #dimensions-td {
-      font-size: 10px;
-      margin: 0;
-      padding: 0;
-      border: 0;
-      border-spacing: 0;
-      height: 10em;
-      width: 20em;
-  }
-
-  #notInlineAbsoluted { position: absolute; }
-  
-  #elementToViewportDimensions {
-    position: absolute;
-    top: 0;
-    left: 0;
-    height: 10px;
-    width: 10px;
-    background: #000;
-  }
-  
-  /* for scroll test on really big screens */
-  body {
-    height: 40000px;
-  }
-/* ]]&gt; */
-&lt;/style&gt;
-
 &lt;div id=&quot;scroll_test_1&quot;&gt;
   &lt;p id=&quot;scroll_test_2&quot;&gt;Scroll test&lt;/p&gt;
 &lt;/div&gt;
@@ -248,7 +159,7 @@
 &lt;/form&gt;
 
 &lt;!-- writeAttributes  --&gt;
-&lt;p id=&quot;write_attribute_para&quot;&gt;&lt;/a&gt;
+&lt;p id=&quot;write_attribute_para&quot;&gt;&lt;/p&gt;
 &lt;a id=&quot;write_attribute_link&quot; href=&quot;test.html&quot;&gt;&lt;/a&gt;
 &lt;form action=&quot;/dev/null&quot; id=&quot;write_attribute_form&quot; method=&quot;get&quot; accept-charset=&quot;utf-8&quot;&gt;
   &lt;label id=&quot;write_attribute_label&quot;&gt;&lt;/label&gt;
@@ -363,4 +274,5 @@
   &lt;div id=&quot;anonymous_element_3&quot;&gt;&lt;/div&gt;
 &lt;/div&gt;
 
-&lt;div id='elementToViewportDimensions' style='display: none'&gt;&lt;/div&gt;
\ No newline at end of file
+&lt;div id='elementToViewportDimensions' style='display: none'&gt;&lt;/div&gt;
+&lt;div id=&quot;auto_dimensions&quot; style=&quot;height:auto&quot;&gt;&lt;/div&gt;
\ No newline at end of file</diff>
      <filename>test/unit/fixtures/dom.html</filename>
    </modified>
    <modified>
      <diff>@@ -79,6 +79,10 @@
   &lt;input type=&quot;text&quot; /&gt;
 &lt;/form&gt;
 
+&lt;form id=&quot;form_with_file_input&quot;&gt;
+  &lt;input type=&quot;file&quot; name=&quot;file_name&quot; value=&quot;foo&quot; /&gt;
+&lt;/form&gt;
+
 &lt;!-- tabindexed forms --&gt;
 &lt;div id=&quot;tabindex&quot;&gt;
   &lt;form id=&quot;ffe&quot;&gt;</diff>
      <filename>test/unit/fixtures/form.html</filename>
    </modified>
    <modified>
      <diff>@@ -271,7 +271,9 @@ new Test.Unit.Runner({
                     $('form').serialize({ submit: false }));
     this.assertHashEqual({ val1:4, action:'blah' },
                     $('form').serialize({ submit: 'inexistent' }));
-                    
+
+    // file input should not be serialized  
+    this.assertEqual('', $('form_with_file_input').serialize());   
   },
   
   testFormMethodsOnExtendedElements: function() {
@@ -297,7 +299,7 @@ new Test.Unit.Runner({
   },
   
   testFormRequest: function() {
-    request = $(&quot;form&quot;).request();
+    var request = $(&quot;form&quot;).request();
     this.assert($(&quot;form&quot;).hasAttribute(&quot;method&quot;));
     this.assert(request.url.include(&quot;fixtures/empty.js?val1=4&quot;));
     this.assertEqual(&quot;get&quot;, request.method);</diff>
      <filename>test/unit/form_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,29 +1,3 @@
-var Fixtures = {
-  one: { a: 'A#' },
-  
-  many: {
-    a: 'A',
-    b: 'B',
-    c: 'C',
-    d: 'D#'
-  },
-
-  functions: {
-    quad: function(n) { return n*n },
-    plus: function(n) { return n+n }
-  },
-  
-  multiple:         { color: $w('r g b') },
-  multiple_nil:     { color: ['r', null, 'g', undefined, 0] },
-  multiple_all_nil: { color: [null, undefined] },
-  multiple_empty:   { color: [] },
-  multiple_special: { 'stuff[]': $w('$ a ;') },
-
-  value_undefined:  { a:&quot;b&quot;, c:undefined },
-  value_null:       { a:&quot;b&quot;, c:null },
-  value_zero:       { a:&quot;b&quot;, c:0 }
-};
-
 new Test.Unit.Runner({
   testSet: function() {
     var h = $H({a: 'A'})
@@ -40,6 +14,9 @@ new Test.Unit.Runner({
     this.assertEqual('A', h.get('a'));
     this.assertUndefined(h.a);
     this.assertUndefined($H({}).get('a'));
+
+    this.assertUndefined($H({}).get('toString'));
+    this.assertUndefined($H({}).get('constructor'));
   },
   
   testUnset: function() {</diff>
      <filename>test/unit/hash_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -30,5 +30,15 @@ new Test.Unit.Runner({
     this.assertEqual('null', Number.NaN.toJSON());
     this.assertEqual('0', (0).toJSON());
     this.assertEqual('-293', (-293).toJSON());
+  },
+  
+  testNumberTimes: function() {
+    var results = [];
+    (5).times(function(i) { results.push(i) });
+    this.assertEnumEqual($R(0, 4), results);
+    
+    results = [];
+    (5).times(function(i) { results.push(i * this.i) }, { i: 2 });
+    this.assertEnumEqual([0, 2, 4, 6, 8], results);
   }
 });
\ No newline at end of file</diff>
      <filename>test/unit/number_test.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,3 @@
-var attackTarget;
-var evalScriptsCounter = 0,
-    largeTextEscaped = '&amp;lt;span&amp;gt;test&amp;lt;/span&amp;gt;', 
-    largeTextUnescaped = '&lt;span&gt;test&lt;/span&gt;';
-(2048).times(function(){ 
-  largeTextEscaped += ' ABC';
-  largeTextUnescaped += ' ABC';
-});
-
 new Test.Unit.Runner({
   testInterpret: function(){
     this.assertIdentical('true', String.interpret(true));
@@ -284,8 +275,8 @@ new Test.Unit.Runner({
   },
 
   testTemplateEvaluationWithFalses: function() {
-    var source = '&lt;tr&gt;&lt;td&gt;#{zero}&lt;/td&gt;&lt;td&gt;#{false_}&lt;/td&gt;&lt;td&gt;#{undef}&lt;/td&gt;&lt;td&gt;#{null_}&lt;/td&gt;&lt;td&gt;#{empty}&lt;/td&gt;&lt;/tr&gt;';
-    var falses = {zero:0, false_:false, undef:undefined, null_:null, empty:&quot;&quot;};
+    var source = '&lt;tr&gt;&lt;td&gt;#{zero}&lt;/td&gt;&lt;td&gt;#{_false}&lt;/td&gt;&lt;td&gt;#{undef}&lt;/td&gt;&lt;td&gt;#{_null}&lt;/td&gt;&lt;td&gt;#{empty}&lt;/td&gt;&lt;/tr&gt;';
+    var falses = {zero:0, _false:false, undef:undefined, _null:null, empty:&quot;&quot;};
     var template = new Template(source);
     
     this.assertEqual('&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;false&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;',</diff>
      <filename>test/unit/string_test.js</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>README</filename>
    </removed>
    <removed>
      <filename>test/lib/template.erb</filename>
    </removed>
    <removed>
      <filename>test/lib/unittest.js</filename>
    </removed>
    <removed>
      <filename>test/test.css</filename>
    </removed>
    <removed>
      <filename>test/unit/.DS_Store</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>1828fb5644246b612e0ce4b036d6360f1f5b3fc2</id>
    </parent>
    <parent>
      <id>6416d28080e2d7fdc9726670329a840eb16b6b72</id>
    </parent>
  </parents>
  <author>
    <name>Andrew Dupont</name>
    <email>prototype@andrewdupont.net</email>
  </author>
  <url>http://github.com/savetheclocktower/prototype/commit/36a7e7852086fb413ea661bfd5c59c1e6652024b</url>
  <id>36a7e7852086fb413ea661bfd5c59c1e6652024b</id>
  <committed-date>2008-09-27T20:21:22-07:00</committed-date>
  <authored-date>2008-09-27T20:21:22-07:00</authored-date>
  <message>Merge branch 'master' of git@github.com:sstephenson/prototype

Conflicts:

	CHANGELOG
	README.rdoc
	Rakefile
	src/base.js
	src/dom.js
	test/lib/jstest.rb
	test/lib/templates/prototype.erb
	test/unit/base_test.js</message>
  <tree>709e67141f7daad75d3a2068ea20fce3e83a44a8</tree>
  <committer>
    <name>Andrew Dupont</name>
    <email>prototype@andrewdupont.net</email>
  </committer>
</commit>
