update qunit
authorslavic <stereobooster@gmail.com>
Mon, 21 May 2012 23:10:25 +0000 (02:10 +0300)
committerslavic <stereobooster@gmail.com>
Mon, 21 May 2012 23:10:25 +0000 (02:10 +0300)
tests/lint_test.js
tests/minify_test.js
tests/qunit.js

index d819c76..5299679 100644 (file)
@@ -1,7 +1,7 @@
 (function(global){
   
   var minify, QUnit, 
-    test, equals, ok,
+    test, equal, ok,
     input, output, HTMLLint, lint;
 
   if (typeof require === 'function') {
@@ -15,7 +15,7 @@
   }
 
   test = QUnit.test;
-  equals = QUnit.equals;
+  equal = QUnit.equal;
   ok = QUnit.ok;
 
   QUnit.module('', {
   });
   
   test('lint API', function() {
-    equals(0, lint.log.length, '`log` property exists');
-    equals("function", typeof lint.populate, '`populate` method exists');
-    equals("function", typeof lint.test, '`test` method exists');
-    equals("function", typeof lint.testElement, '`testElement` method exists');
-    equals("function", typeof lint.testAttribute, '`testAttribute` method exists');
+    equal(0, lint.log.length, '`log` property exists');
+    equal("function", typeof lint.populate, '`populate` method exists');
+    equal("function", typeof lint.test, '`test` method exists');
+    equal("function", typeof lint.testElement, '`testElement` method exists');
+    equal("function", typeof lint.testAttribute, '`testAttribute` method exists');
   });
   
   test('deprecated element (font)', function(){
index 7cebc6d..13ccba1 100644 (file)
@@ -1,7 +1,7 @@
 (function(global){
   
   var minify, QUnit, 
-    test, equals, ok,
+    test, equal, ok,
     input, output;
 
   if (typeof require === 'function') {
   }
 
   test = QUnit.test;
-  equals = QUnit.equals;
+  equal = QUnit.equal;
   ok = QUnit.ok;
 
   test('parsing non-trivial markup', function() {
-    equals(minify('<p title="</p>">x</p>'), '<p title="</p>">x</p>');
-    equals(minify('<p title=" <!-- hello world --> ">x</p>'), '<p title=" <!-- hello world --> ">x</p>');
-    equals(minify('<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>'), '<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>');
-    equals(minify('<p foo-bar=baz>xxx</p>'), '<p foo-bar="baz">xxx</p>');
-    equals(minify('<p foo:bar=baz>xxx</p>'), '<p foo:bar="baz">xxx</p>');
+    equal(minify('<p title="</p>">x</p>'), '<p title="</p>">x</p>');
+    equal(minify('<p title=" <!-- hello world --> ">x</p>'), '<p title=" <!-- hello world --> ">x</p>');
+    equal(minify('<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>'), '<p title=" <![CDATA[ \n\n foobar baz ]]> ">x</p>');
+    equal(minify('<p foo-bar=baz>xxx</p>'), '<p foo-bar="baz">xxx</p>');
+    equal(minify('<p foo:bar=baz>xxx</p>'), '<p foo:bar="baz">xxx</p>');
     
     input = '<div><div><div><div><div><div><div><div><div><div>'+
                   'i\'m 10 levels deep'+
                 '</div></div></div></div></div></div></div></div></div></div>';
                 
-    equals(minify(input), input);
+    equal(minify(input), input);
     
-    equals(minify('<script>alert(\'<!--\')<\/script>'), '<script>alert(\'<!--\')<\/script>');
-    equals(minify('<script>alert(\'<!-- foo -->\')<\/script>'), '<script>alert(\'<!-- foo -->\')<\/script>');
-    equals(minify('<script>alert(\'-->\')<\/script>'), '<script>alert(\'-->\')<\/script>');
+    equal(minify('<script>alert(\'<!--\')<\/script>'), '<script>alert(\'<!--\')<\/script>');
+    equal(minify('<script>alert(\'<!-- foo -->\')<\/script>'), '<script>alert(\'<!-- foo -->\')<\/script>');
+    equal(minify('<script>alert(\'-->\')<\/script>'), '<script>alert(\'-->\')<\/script>');
     
-    equals(minify('<a title="x"href=" ">foo</a>'), '<a title="x" href="">foo</a>');
-    equals(minify('<p id=""class=""title="">x'), '<p id="" class="" title="">x</p>');
-    equals(minify('<p x="x\'"">x</p>'), '<p x="x\'">x</p>', 'trailing quote should be ignored');
+    equal(minify('<a title="x"href=" ">foo</a>'), '<a title="x" href="">foo</a>');
+    equal(minify('<p id=""class=""title="">x'), '<p id="" class="" title="">x</p>');
+    equal(minify('<p x="x\'"">x</p>'), '<p x="x\'">x</p>', 'trailing quote should be ignored');
   });
   
   test('`minifiy` exists', function() {
   
   test('options', function() {
     input = '<p>blah<span>blah 2<span>blah 3</span></span></p>';
-    equals(minify(input), input);
-    equals(minify(input, {}), input);
+    equal(minify(input), input);
+    equal(minify(input, {}), input);
   });
   
   test('case normalization', function() {
-    equals(minify('<P>foo</p>'), '<p>foo</p>');
-    equals(minify('<DIV>boo</DIV>'), '<div>boo</div>');
-    equals(minify('<DIV title="moo">boo</DiV>'), '<div title="moo">boo</div>');
-    equals(minify('<DIV TITLE="blah">boo</DIV>'), '<div title="blah">boo</div>');
-    equals(minify('<DIV tItLe="blah">boo</DIV>'), '<div title="blah">boo</div>');
-    equals(minify('<DiV tItLe="blah">boo</DIV>'), '<div title="blah">boo</div>');
+    equal(minify('<P>foo</p>'), '<p>foo</p>');
+    equal(minify('<DIV>boo</DIV>'), '<div>boo</div>');
+    equal(minify('<DIV title="moo">boo</DiV>'), '<div title="moo">boo</div>');
+    equal(minify('<DIV TITLE="blah">boo</DIV>'), '<div title="blah">boo</div>');
+    equal(minify('<DIV tItLe="blah">boo</DIV>'), '<div title="blah">boo</div>');
+    equal(minify('<DiV tItLe="blah">boo</DIV>'), '<div title="blah">boo</div>');
   });
   
   test('space normalization between attributes', function() {
-    equals(minify('<p title="bar">foo</p>'), '<p title="bar">foo</p>');
-    equals(minify('<p title = "bar">foo</p>'), '<p title="bar">foo</p>');
-    equals(minify('<p title\n\n\t  =\n     "bar">foo</p>'), '<p title="bar">foo</p>');
-    equals(minify('<input title="bar"       id="boo"    value="hello world">'), '<input title="bar" id="boo" value="hello world">');
+    equal(minify('<p title="bar">foo</p>'), '<p title="bar">foo</p>');
+    equal(minify('<p title = "bar">foo</p>'), '<p title="bar">foo</p>');
+    equal(minify('<p title\n\n\t  =\n     "bar">foo</p>'), '<p title="bar">foo</p>');
+    equal(minify('<input title="bar"       id="boo"    value="hello world">'), '<input title="bar" id="boo" value="hello world">');
   });
   
   test('space normalization around text', function(){
-    equals(minify('   <p>blah</p>\n\n\n   '), '<p>blah</p>');
+    equal(minify('   <p>blah</p>\n\n\n   '), '<p>blah</p>');
   });
   
   test('doctype normalization', function() {
     input = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n    "http://www.w3.org/TR/html4/strict.dtd">';
-    equals(minify(input, { useShortDoctype: true }), '<!DOCTYPE html>');
+    equal(minify(input, { useShortDoctype: true }), '<!DOCTYPE html>');
     
     input = '<!DOCTYPE html>';
-    equals(minify(input, { useShortDoctype: true }), input);
+    equal(minify(input, { useShortDoctype: true }), input);
     
     input = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
-    equals(minify(input, { useShortDoctype: false }), input);
+    equal(minify(input, { useShortDoctype: false }), input);
   });
   
   test('removing comments', function(){
     input = '<!-- test -->';
-    equals(minify(input, { removeComments: true }), '');
+    equal(minify(input, { removeComments: true }), '');
     
     input = '<!-- foo --><div>baz</div><!-- bar\n\n moo -->';
-    equals(minify(input, { removeComments: true }), '<div>baz</div>');
-    equals(minify(input, { removeComments: false }), input);
+    equal(minify(input, { removeComments: true }), '<div>baz</div>');
+    equal(minify(input, { removeComments: false }), input);
     
     input = '<p title="<!-- comment in attribute -->">foo</p>';
-    equals(minify(input, { removeComments: true }), input);
+    equal(minify(input, { removeComments: true }), input);
     
     input = '<script><!-- alert(1) --><\/script>';
-    equals(minify(input, { removeComments: true }), input);
+    equal(minify(input, { removeComments: true }), input);
     
     input = '<STYLE><!-- alert(1) --><\/STYLE>';
-    equals(minify(input, { removeComments: true }), '<style><!-- alert(1) --><\/style>');
+    equal(minify(input, { removeComments: true }), '<style><!-- alert(1) --><\/style>');
   });
   
   test('conditional comments', function(){
     input = '<!--[if IE 6]>test<![endif]-->';
-    equals(minify(input, { removeComments: true }), input);
+    equal(minify(input, { removeComments: true }), input);
     
     input = '<!--[if lt IE 5.5]>test<![endif]-->';
-    equals(minify(input, { removeComments: true }), input);
+    equal(minify(input, { removeComments: true }), input);
     
     input = '<!--[if (gt IE 5)&(lt IE 7)]>test<![endif]-->';
-    equals(minify(input, { removeComments: true }), input);
+    equal(minify(input, { removeComments: true }), input);
   });
   
   test('collapsing space in conditional comments', function(){
                 '<link rel="stylesheet" href="/css/ie7-fixes.css" type="text/css" />'+
              '<![endif]-->'
     
-    equals(minify(input, { removeComments: true }), output);
+    equal(minify(input, { removeComments: true }), output);
     
     
     input = '<!--[if lte IE 6]>\n    \n   \n\n\n\t' +
                '<p title=" sigificant     whitespace   ">blah blah</p>' +
             '<![endif]-->';
     
-    equals(minify(input, { removeComments: true }), output);
+    equal(minify(input, { removeComments: true }), output);
   });
   
   test('remove comments from scripts', function(){
     input = '<script><!--alert(1)--><\/script>';
     output = '<script><\/script>';
-    equals(minify(input, { removeCommentsFromCDATA: true }), output);
+    equal(minify(input, { removeCommentsFromCDATA: true }), output);
     
     input = '<script><!--alert(1)<\/script>';
     output = '<script><\/script>';
-    equals(minify(input, { removeCommentsFromCDATA: true }), output);
+    equal(minify(input, { removeCommentsFromCDATA: true }), output);
     
     input = '<script type="text/javascript"> <!--\nalert("-->"); -->\n\n   <\/script>';
     output = '<script type="text/javascript">alert("-->");<\/script>';
-    equals(minify(input, { removeCommentsFromCDATA: true }), output);
+    equal(minify(input, { removeCommentsFromCDATA: true }), output);
     
     input = '<script> //   <!--   \n  alert(1)   //  --> <\/script>';
     output = '<script>  alert(1)<\/script>';
-    equals(minify(input, { removeCommentsFromCDATA: true }), output);
+    equal(minify(input, { removeCommentsFromCDATA: true }), output);
   });
   
   test('remove comments from styles', function(){
     input = '<style type="text/css"><!-- p { color: red } --><\/style>';
     output = '<style type="text/css">p { color: red }<\/style>';
-    equals(minify(input, { removeCommentsFromCDATA: true }), output);
+    equal(minify(input, { removeCommentsFromCDATA: true }), output);
     
     input = '<style type="text/css">p::before { content: "<!--" }<\/style>';
-    equals(minify(input, { removeCommentsFromCDATA: true }), input);
+    equal(minify(input, { removeCommentsFromCDATA: true }), input);
   });
   
   test('remove CDATA sections from scripts/styles', function(){
     input = '<script>/*<![CDATA[*/alert(1)/*]]>*/<\/script>';
     output = '<script>alert(1)<\/script>';
-    equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
+    equal(minify(input, { removeCDATASectionsFromCDATA: true }), output);
     
     input = '<script>//<![CDATA[\nalert(1)\n//]]><\/script>';
     output = '<script>\nalert(1)\n<\/script>';
-    equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
+    equal(minify(input, { removeCDATASectionsFromCDATA: true }), output);
     
     input = '<script type="text/javascript"> /* \n\t  <![CDATA[  */ alert(1) /*  ]]>  */ \n <\/script>';
     output = '<script type="text/javascript"> alert(1) <\/script>';
-    equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
+    equal(minify(input, { removeCDATASectionsFromCDATA: true }), output);
     
     input = '<style>/* <![CDATA[ */p { color: red } // ]]><\/style>';
     output = '<style>p { color: red } <\/style>';
-    equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
+    equal(minify(input, { removeCDATASectionsFromCDATA: true }), output);
     
     input = '<script>\n\n//<![CDATA[\nalert(1)//]]><\/script>';
     output = '<script>\nalert(1)<\/script>';
-    equals(minify(input, { removeCDATASectionsFromCDATA: true }), output);
+    equal(minify(input, { removeCDATASectionsFromCDATA: true }), output);
     
   });
   
   test('empty attributes', function(){
     input = '<p id="" class="" STYLE=" " title="\n" lang="" dir="">x</p>';
-    equals(minify(input, { removeEmptyAttributes: true }), '<p>x</p>');
+    equal(minify(input, { removeEmptyAttributes: true }), '<p>x</p>');
     
     input = '<p onclick=""   ondblclick=" " onmousedown="" ONMOUSEUP="" onmouseover=" " onmousemove="" onmouseout="" '+
             'onkeypress=\n\n  "\n     " onkeydown=\n"" onkeyup\n="">x</p>';
-    equals(minify(input, { removeEmptyAttributes: true }), '<p>x</p>');
+    equal(minify(input, { removeEmptyAttributes: true }), '<p>x</p>');
     
     input = '<input onfocus="" onblur="" onchange=" " value=" boo ">';
-    equals(minify(input, { removeEmptyAttributes: true }), '<input value=" boo ">');
+    equal(minify(input, { removeEmptyAttributes: true }), '<input value=" boo ">');
     
     input = '<input value="" name="foo">';
-    equals(minify(input, { removeEmptyAttributes: true }), '<input name="foo">');
+    equal(minify(input, { removeEmptyAttributes: true }), '<input name="foo">');
     
     input = '<img src="" alt="">';
-    equals(minify(input, { removeEmptyAttributes: true }), '<img src="" alt="">');
+    equal(minify(input, { removeEmptyAttributes: true }), '<img src="" alt="">');
   });
   
   test('cleaning class/style attributes', function(){
     input = '<p class=" foo bar  ">foo bar baz</p>', output;
-    equals(minify(input, { cleanAttributes: true }), '<p class="foo bar">foo bar baz</p>');
+    equal(minify(input, { cleanAttributes: true }), '<p class="foo bar">foo bar baz</p>');
     
     input = '<p class=" foo      ">foo bar baz</p>';
-    equals(minify(input, { cleanAttributes: true }), '<p class="foo">foo bar baz</p>');
-    equals(minify(input, { cleanAttributes: true, removeAttributeQuotes: true }), '<p class=foo>foo bar baz</p>');
+    equal(minify(input, { cleanAttributes: true }), '<p class="foo">foo bar baz</p>');
+    equal(minify(input, { cleanAttributes: true, removeAttributeQuotes: true }), '<p class=foo>foo bar baz</p>');
     
     input = '<p class="\n  \n foo   \n\n\t  \t\n   ">foo bar baz</p>';
     output = '<p class="foo">foo bar baz</p>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<p class="\n  \n foo   \n\n\t  \t\n  class1 class-23 ">foo bar baz</p>';
     output = '<p class="foo class1 class-23">foo bar baz</p>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<p style="    color: red; background-color: rgb(100, 75, 200);  "></p>';
     output = '<p style="color: red; background-color: rgb(100, 75, 200)"></p>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<p style="font-weight: bold  ; "></p>';
     output = '<p style="font-weight: bold"></p>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
   });
   
   test('cleaning URI-based attributes', function(){
     input = '<a href="   http://example.com  ">x</a>';
     output = '<a href="http://example.com">x</a>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<a href="  \t\t  \n \t  ">x</a>';
     output = '<a href="">x</a>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<img src="   http://example.com  " title="bleh   " longdesc="  http://example.com/longdesc \n\n   \t ">';
     output = '<img src="http://example.com" title="bleh   " longdesc="http://example.com/longdesc">';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<img src="" usemap="   http://example.com  ">';
     output = '<img src="" usemap="http://example.com">';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<form action="  somePath/someSubPath/someAction?foo=bar&baz=qux     "></form>';
     output = '<form action="somePath/someSubPath/someAction?foo=bar&baz=qux"></form>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<BLOCKQUOTE cite=" \n\n\n http://www.mycom.com/tolkien/twotowers.html     "><P>foobar</P></BLOCKQUOTE>';
     output = '<blockquote cite="http://www.mycom.com/tolkien/twotowers.html"><p>foobar</p></blockquote>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<head profile="       http://gmpg.org/xfn/11    "></head>';
     output = '<head profile="http://gmpg.org/xfn/11"></head>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<object codebase="   http://example.com  "></object>';
     output = '<object codebase="http://example.com"></object>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<span profile="   1, 2, 3  ">foo</span>';
-    equals(minify(input, { cleanAttributes: true }), input);
+    equal(minify(input, { cleanAttributes: true }), input);
     
     input = '<div action="  foo-bar-baz ">blah</div>';
-    equals(minify(input, { cleanAttributes: true }), input);
+    equal(minify(input, { cleanAttributes: true }), input);
   });
   
   test('cleaning Number-based attributes', function() {
     input = '<a href="#" tabindex="   1  ">x</a><button tabindex="   2  ">y</button>';
     output = '<a href="#" tabindex="1">x</a><button tabindex="2">y</button>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<input value="" maxlength="     5 ">';
     output = '<input value="" maxlength="5">';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<select size="  10   \t\t "><option>x</option></select>';
     output = '<select size="10"><option>x</option></select>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<textarea rows="   20  " cols="  30      "></textarea>';
     output = '<textarea rows="20" cols="30"></textarea>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<COLGROUP span="   40  "><COL span="  39 "></COLGROUP>';
     output = '<colgroup span="40"><col span="39"></colgroup>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<tr><td colspan="    2   ">x</td><td rowspan="   3 "></td></tr>';
     output = '<tr><td colspan="2">x</td><td rowspan="3"></td></tr>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
   });
   
   test('cleaning other attributes', function() {
     input = '<a href="#" onclick="  window.prompt(\'boo\'); " onmouseover=" \n\n alert(123)  \t \n\t  ">blah</a>';
     output = '<a href="#" onclick="window.prompt(\'boo\')" onmouseover="alert(123)">blah</a>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
     
     input = '<body onload="  foo();   bar() ;  "><p>x</body>';
     output = '<body onload="foo();   bar()"><p>x</p></body>';
-    equals(minify(input, { cleanAttributes: true }), output);
+    equal(minify(input, { cleanAttributes: true }), output);
   });
   
   test('removing redundant attributes (&lt;form method="get" ...>)', function(){
     input = '<form method="get">hello world</form>';
-    equals(minify(input, { removeRedundantAttributes: true }), '<form>hello world</form>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<form>hello world</form>');
     
     input = '<form method="post">hello world</form>';
-    equals(minify(input, { removeRedundantAttributes: true }), '<form method="post">hello world</form>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<form method="post">hello world</form>');
   });
   
   test('removing redundant attributes (&lt;input type="text" ...>)', function(){
     input = '<input type="text">';
-    equals(minify(input, { removeRedundantAttributes: true }), '<input>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<input>');
     
     input = '<input type="  TEXT  " value="foo">';
-    equals(minify(input, { removeRedundantAttributes: true }), '<input value="foo">');
+    equal(minify(input, { removeRedundantAttributes: true }), '<input value="foo">');
     
     input = '<input type="checkbox">';
-    equals(minify(input, { removeRedundantAttributes: true }), '<input type="checkbox">');
+    equal(minify(input, { removeRedundantAttributes: true }), '<input type="checkbox">');
   });
   
   test('removing redundant attributes (&lt;a name="..." id="..." ...>)', function(){
     input = '<a id="foo" name="foo">blah</a>';
-    equals(minify(input, { removeRedundantAttributes: true }), '<a id="foo">blah</a>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<a id="foo">blah</a>');
     
     input = '<input id="foo" name="foo">';
-    equals(minify(input, { removeRedundantAttributes: true }), input);
+    equal(minify(input, { removeRedundantAttributes: true }), input);
     
     input = '<a name="foo">blah</a>';
-    equals(minify(input, { removeRedundantAttributes: true }), input);
+    equal(minify(input, { removeRedundantAttributes: true }), input);
     
     input = '<a href="..." name="  bar  " id="bar" >blah</a>';
-    equals(minify(input, { removeRedundantAttributes: true }), '<a href="..." id="bar">blah</a>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<a href="..." id="bar">blah</a>');
   });
   
   test('removing redundant attributes (&lt;script src="..." charset="...">)', function(){
     input = '<script type="text/javascript" charset="UTF-8">alert(222);<\/script>';
     output = '<script type="text/javascript">alert(222);<\/script>';
-    equals(minify(input, { removeRedundantAttributes: true }), output);
+    equal(minify(input, { removeRedundantAttributes: true }), output);
     
     input = '<script type="text/javascript" src="http://example.com" charset="UTF-8">alert(222);<\/script>';
-    equals(minify(input, { removeRedundantAttributes: true }), input);
+    equal(minify(input, { removeRedundantAttributes: true }), input);
     
     input = '<script CHARSET=" ... ">alert(222);<\/script>';
     output = '<script>alert(222);<\/script>';
-    equals(minify(input, { removeRedundantAttributes: true }), output);
+    equal(minify(input, { removeRedundantAttributes: true }), output);
   });
   
   test('removing redundant attributes (&lt;... language="javascript" ...>)', function(){
     input = '<script language="Javascript">x=2,y=4<\/script>';
-    equals(minify(input, { removeRedundantAttributes: true }), '<script>x=2,y=4<\/script>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<script>x=2,y=4<\/script>');
     
     input = '<script LANGUAGE = "  javaScript  ">x=2,y=4<\/script>';
-    equals(minify(input, { removeRedundantAttributes: true }), '<script>x=2,y=4<\/script>');
+    equal(minify(input, { removeRedundantAttributes: true }), '<script>x=2,y=4<\/script>');
   });
   
   test('removing redundant attributes (&lt;area shape="rect" ...>)', function(){
     input = '<area shape="rect" coords="696,25,958,47" href="#" title="foo">';
     output = '<area coords="696,25,958,47" href="#" title="foo">';
-    equals(minify(input, { removeRedundantAttributes: true }), output);
+    equal(minify(input, { removeRedundantAttributes: true }), output);
   });
   
   test('removing redundant attributes (&lt;... = "javascript: ..." ...>)', function(){
     input = '<p onclick="javascript:alert(1)">x</p>';
-    equals(minify(input, { cleanAttributes: true }), '<p onclick="alert(1)">x</p>');
+    equal(minify(input, { cleanAttributes: true }), '<p onclick="alert(1)">x</p>');
     
     input = '<p onclick="javascript:x">x</p>';
-    equals(minify(input, { cleanAttributes: true, removeAttributeQuotes: true }), '<p onclick=x>x</p>');
+    equal(minify(input, { cleanAttributes: true, removeAttributeQuotes: true }), '<p onclick=x>x</p>');
     
     input = '<p onclick=" JavaScript: x">x</p>';
-    equals(minify(input, { cleanAttributes: true }), '<p onclick="x">x</p>');
+    equal(minify(input, { cleanAttributes: true }), '<p onclick="x">x</p>');
     
     input = '<p title="javascript:(function(){ /* some stuff here */ })()">x</p>';
-    equals(minify(input, { cleanAttributes: true }), input);
+    equal(minify(input, { cleanAttributes: true }), input);
   });
   
   test('removing type="text/javascript" attributes', function(){
     input = '<script type="text/javascript">alert(1)<\/script>';
     output = '<script>alert(1)<\/script>';
     
-    equals(minify(input, { removeScriptTypeAttributes: true }), output);
-    equals(minify(input, { removeScriptTypeAttributes: false }), input);
+    equal(minify(input, { removeScriptTypeAttributes: true }), output);
+    equal(minify(input, { removeScriptTypeAttributes: false }), input);
     
     input = '<SCRIPT TYPE="  text/javascript ">alert(1)<\/script>';
     output = '<script>alert(1)<\/script>';
     
-    equals(minify(input, { removeScriptTypeAttributes: true }), output);
+    equal(minify(input, { removeScriptTypeAttributes: true }), output);
     
     input = '<script type="application/javascript;version=1.8">alert(1)<\/script>';
     output = '<script type="application/javascript;version=1.8">alert(1)<\/script>';
     
-    equals(minify(input, { removeScriptTypeAttributes: true }), output);
+    equal(minify(input, { removeScriptTypeAttributes: true }), output);
     
     input = '<script type="text/vbscript">MsgBox("foo bar")<\/script>';
     output = '<script type="text/vbscript">MsgBox("foo bar")<\/script>';
     
-    equals(minify(input, { removeScriptTypeAttributes: true }), output);
+    equal(minify(input, { removeScriptTypeAttributes: true }), output);
   });
   
   test('removing type="text/css" attributes', function(){
     input = '<style type="text/css">.foo { color: red }<\/style>';
     output = '<style>.foo { color: red }<\/style>';
     
-    equals(minify(input, { removeStyleLinkTypeAttributes: true }), output);
-    equals(minify(input, { removeStyleLinkTypeAttributes: false }), input);
+    equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
+    equal(minify(input, { removeStyleLinkTypeAttributes: false }), input);
     
     input = '<STYLE TYPE = "  text/CSS ">body { font-size: 1.75em }<\/style>';
     output = '<style>body { font-size: 1.75em }<\/style>';
     
-    equals(minify(input, { removeStyleLinkTypeAttributes: true }), output);
+    equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
     
     input = '<style type="text/plain">.foo { background: green }<\/style>';
     output = '<style type="text/plain">.foo { background: green }<\/style>';
     
-    equals(minify(input, { removeStyleLinkTypeAttributes: true }), output);
+    equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
     
     input = '<link rel="stylesheet" type="text/css" href="http://example.com">';
     output = '<link rel="stylesheet" href="http://example.com">';
     
-    equals(minify(input, { removeStyleLinkTypeAttributes: true }), output);
+    equal(minify(input, { removeStyleLinkTypeAttributes: true }), output);
     
     input = '<link rel="alternate" type="application/atom+xml" href="data.xml">';
     
-    equals(minify(input, { removeStyleLinkTypeAttributes: true }), input);
+    equal(minify(input, { removeStyleLinkTypeAttributes: true }), input);
   });
   
   test('removing attribute quotes', function(){
     input = '<p title="blah" class="a23B-foo.bar_baz:qux" id="moo">foo</p>';
-    equals(minify(input, { removeAttributeQuotes: true }), '<p title=blah class=a23B-foo.bar_baz:qux id=moo>foo</p>');
+    equal(minify(input, { removeAttributeQuotes: true }), '<p title=blah class=a23B-foo.bar_baz:qux id=moo>foo</p>');
     
     input = '<input value="hello world">';
-    equals(minify(input, { removeAttributeQuotes: true }), '<input value="hello world">');
+    equal(minify(input, { removeAttributeQuotes: true }), '<input value="hello world">');
     
     input = '<a href="#" title="foo#bar">x</a>';
-    equals(minify(input, { removeAttributeQuotes: true }), '<a href="#" title="foo#bar">x</a>');
+    equal(minify(input, { removeAttributeQuotes: true }), '<a href="#" title="foo#bar">x</a>');
     
     input = '<a href="http://example.com" title="blah">\nfoo\n\n</a>';
-    equals(minify(input, { removeAttributeQuotes: true }), '<a href="http://example.com" title=blah>\nfoo\n\n</a>');
+    equal(minify(input, { removeAttributeQuotes: true }), '<a href="http://example.com" title=blah>\nfoo\n\n</a>');
   });
   
   test('collapsing whitespace', function() {
     input = '<script type="text/javascript">  \n\t   alert(1) \n\n\n  \t <\/script>';
     output = '<script type="text/javascript">alert(1)<\/script>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<p>foo</p>    <p> bar</p>\n\n   \n\t\t  <div title="quz">baz  </div>';
     output = '<p>foo</p><p>bar</p><div title="quz">baz</div>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<p> foo    bar</p>';
     output = '<p>foo bar</p>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<p> foo    <span>  blah    22 </span> bar <img src=""></p>';
     output = '<p>foo<span>blah 22</span>bar<img src=""></p>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<textarea> foo bar     baz \n\n   x \t    y </textarea>';
     output = '<textarea> foo bar     baz \n\n   x \t    y </textarea>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<pre title="some title...">   hello     world </pre>';
     output = '<pre title="some title...">   hello     world </pre>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<pre title="some title..."><code>   hello     world </code></pre>';
     output = '<pre title="some title..."><code>   hello     world </code></pre>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<script>alert("foo     bar")    <\/script>';
     output = '<script>alert("foo     bar")<\/script>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
     
     input = '<style>alert("foo     bar")    <\/style>';
     output = '<style>alert("foo     bar")<\/style>';
-    equals(minify(input, { collapseWhitespace: true }), output);
+    equal(minify(input, { collapseWhitespace: true }), output);
   });
   
   test('removing empty elements', function() {
     
-    equals(minify('<p>x</p>', { removeEmptyElements: true }), '<p>x</p>');
-    equals(minify('<p></p>', { removeEmptyElements: true }), '');
+    equal(minify('<p>x</p>', { removeEmptyElements: true }), '<p>x</p>');
+    equal(minify('<p></p>', { removeEmptyElements: true }), '');
     
     input = '<p>foo<span>bar</span><span></span></p>';
     output = '<p>foo<span>bar</span></p>';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
     
     input = '<a href="http://example/com" title="hello world"></a>';
     output = '';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
     
     input = '<textarea cols="10" rows="10"></textarea>';
     output = '<textarea cols="10" rows="10"></textarea>';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
     
     input = '<div>hello<span>world</span></div>';
     output = '<div>hello<span>world</span></div>';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
     
     input = '<p>x<span title="<" class="blah-moo"></span></p>';
     output = '<p>x</p>';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
     
     input = '<div>x<div>y <div>blah</div><div></div>foo</div>z</div>';
     output = '<div>x<div>y <div>blah</div>foo</div>z</div>';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
     
     input = '<img src="">';
-    equals(minify(input, { removeEmptyElements: true }), input);
+    equal(minify(input, { removeEmptyElements: true }), input);
     
     input = '<p><!-- x --></p>';
     output = '';
-    equals(minify(input, { removeEmptyElements: true }), output);
+    equal(minify(input, { removeEmptyElements: true }), output);
   });
   
   test('collapsing boolean attributes', function(){
     input = '<input disabled="disabled">';
-    equals(minify(input, { collapseBooleanAttributes: true }), '<input disabled>');
+    equal(minify(input, { collapseBooleanAttributes: true }), '<input disabled>');
     
     input = '<input CHECKED = "checked" readonly="readonly">';
-    equals(minify(input, { collapseBooleanAttributes: true }), '<input checked readonly>');
+    equal(minify(input, { collapseBooleanAttributes: true }), '<input checked readonly>');
     
     input = '<option name="blah" selected="selected">moo</option>';
-    equals(minify(input, { collapseBooleanAttributes: true }), '<option name="blah" selected>moo</option>');
+    equal(minify(input, { collapseBooleanAttributes: true }), '<option name="blah" selected>moo</option>');
   });
   
   test('removing optional tags', function(){
     input = '<html><head><title>hello</title></head><body><p>foo<span>bar</span></p></body></html>';
     output = '<html><head><title>hello</title><body><p>foo<span>bar</span></p>';
-    equals(minify(input, { removeOptionalTags: true }), output);
-    equals(minify(input), input);
+    equal(minify(input, { removeOptionalTags: true }), output);
+    equal(minify(input), input);
   });
   
   test('removing optional tags in tables', function(){
               '<tbody><tr><td>boo</td><td>moo</td>'+
              '</table>';
               
-    equals(minify(input, { removeOptionalTags: true }), output);
-    equals(minify(input), input);
+    equal(minify(input, { removeOptionalTags: true }), output);
+    equal(minify(input), input);
   });
   
   test('removing optional tags in options', function(){
     input = '<select><option>foo</option><option>bar</option></select>';
     output = '<select><option>foo<option>bar</select>';
-    equals(minify(input, { removeOptionalTags: true }), output);
+    equal(minify(input, { removeOptionalTags: true }), output);
     
     // example from htmldog.com
     input = '<select name="catsndogs">' +
                '</optgroup>' +
             '</select>';
             
-    equals(minify(input, { removeOptionalTags: true }), output);
+    equal(minify(input, { removeOptionalTags: true }), output);
   });
 
 }(this));
\ No newline at end of file
index 8bef026..9bbbebf 100644 (file)
-/*
- * QUnit - A JavaScript Unit Testing Framework
- * 
+/**
+ * QUnit v1.7.0pre - A JavaScript Unit Testing Framework
+ *
  * http://docs.jquery.com/QUnit
  *
- * Copyright (c) 2009 John Resig, Jörn Zaefferer
+ * Copyright (c) 2012 John Resig, Jörn Zaefferer
  * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * or GPL (GPL-LICENSE.txt) licenses.
  */
 
-(function(window) {
+(function( window ) {
+
+var QUnit,
+       config,
+       testId = 0,
+       toString = Object.prototype.toString,
+       hasOwn = Object.prototype.hasOwnProperty,
+       defined = {
+       setTimeout: typeof window.setTimeout !== "undefined",
+       sessionStorage: (function() {
+               var x = "qunit-test-string";
+               try {
+                       sessionStorage.setItem( x, x );
+                       sessionStorage.removeItem( x );
+                       return true;
+               } catch( e ) {
+                       return false;
+               }
+       }())
+};
 
-var QUnit = {
+function Test( settings ) {
+       extend( this, settings );
+       this.assertions = [];
+       this.testNumber = ++Test.count;
+}
 
-       // Initialize the configuration options
-       init: function() {
-               config = {
-                       stats: { all: 0, bad: 0 },
-                       moduleStats: { all: 0, bad: 0 },
-                       started: +new Date,
-                       blocking: false,
-                       autorun: false,
-                       assertions: [],
-                       filters: [],
-                       queue: []
-               };
+Test.count = 0;
 
-               var tests = id("qunit-tests"),
-                       banner = id("qunit-banner"),
-                       result = id("qunit-testresult");
+Test.prototype = {
+       init: function() {
+               var a, b, li,
+        tests = id( "qunit-tests" );
 
                if ( tests ) {
-                       tests.innerHTML = "";
-               }
+                       b = document.createElement( "strong" );
+                       b.innerHTML = this.name;
 
-               if ( banner ) {
-                       banner.className = "";
-               }
+                       // `a` initialized at top of scope
+                       a = document.createElement( "a" );
+                       a.innerHTML = "Rerun";
+                       a.href = QUnit.url({ testNumber: this.testNumber });
 
-               if ( result ) {
-                       result.parentNode.removeChild( result );
+                       li = document.createElement( "li" );
+                       li.appendChild( b );
+                       li.appendChild( a );
+                       li.className = "running";
+                       li.id = this.id = "qunit-test-output" + testId++;
+
+                       tests.appendChild( li );
                }
        },
-       
-       // call on start of module test to prepend name to all tests
-       module: function(name, testEnvironment) {
-               config.currentModule = name;
-
-               synchronize(function() {
-                       if ( config.currentModule ) {
-                               QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+       setup: function() {
+               if ( this.module !== config.previousModule ) {
+                       if ( config.previousModule ) {
+                               runLoggingCallbacks( "moduleDone", QUnit, {
+                                       name: config.previousModule,
+                                       failed: config.moduleStats.bad,
+                                       passed: config.moduleStats.all - config.moduleStats.bad,
+                                       total: config.moduleStats.all
+                               });
                        }
-
-                       config.currentModule = name;
-                       config.moduleTestEnvironment = testEnvironment;
+                       config.previousModule = this.module;
                        config.moduleStats = { all: 0, bad: 0 };
+                       runLoggingCallbacks( "moduleStart", QUnit, {
+                               name: this.module
+                       });
+               } else if ( config.autorun ) {
+                       runLoggingCallbacks( "moduleStart", QUnit, {
+                               name: this.module
+                       });
+               }
+
+               config.current = this;
+
+               this.testEnvironment = extend({
+                       setup: function() {},
+                       teardown: function() {}
+               }, this.moduleTestEnvironment );
 
-                       QUnit.moduleStart( name, testEnvironment );
+               runLoggingCallbacks( "testStart", QUnit, {
+                       name: this.testName,
+                       module: this.module
                });
-       },
 
-       asyncTest: function(testName, expected, callback) {
-               if ( arguments.length === 2 ) {
-                       callback = expected;
-                       expected = 0;
-               }
+               // allow utility functions to access the current test environment
+               // TODO why??
+               QUnit.current_testEnvironment = this.testEnvironment;
 
-               QUnit.test(testName, expected, callback, true);
+               if ( !config.pollution ) {
+                       saveGlobal();
+               }
+               if ( config.notrycatch ) {
+                       this.testEnvironment.setup.call( this.testEnvironment );
+                       return;
+               }
+               try {
+                       this.testEnvironment.setup.call( this.testEnvironment );
+               } catch( e ) {
+                       QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+               }
        },
-       
-       test: function(testName, expected, callback, async) {
-               var name = testName, testEnvironment, testEnvironmentArg;
+       run: function() {
+               config.current = this;
 
-               if ( arguments.length === 2 ) {
-                       callback = expected;
-                       expected = null;
-               }
-               // is 2nd argument a testEnvironment?
-               if ( expected && typeof expected === 'object') {
-                       testEnvironmentArg =  expected;
-                       expected = null;
+               var running = id( "qunit-testresult" );
+
+               if ( running ) {
+                       running.innerHTML = "Running: <br/>" + this.name;
                }
 
-               if ( config.currentModule ) {
-                       name = config.currentModule + " module: " + name;
+               if ( this.async ) {
+                       QUnit.stop();
                }
 
-               if ( !validTest(name) ) {
+               if ( config.notrycatch ) {
+                       this.callback.call( this.testEnvironment, QUnit.assert );
                        return;
                }
 
-               synchronize(function() {
-                       QUnit.testStart( testName );
-
-                       testEnvironment = extend({
-                               setup: function() {},
-                               teardown: function() {}
-                       }, config.moduleTestEnvironment);
-                       if (testEnvironmentArg) {
-                               extend(testEnvironment,testEnvironmentArg);
+               try {
+                       this.callback.call( this.testEnvironment, QUnit.assert );
+               } catch( e ) {
+                       QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + ": " + e.message, extractStacktrace( e, 1 ) );
+                       // else next test will carry the responsibility
+                       saveGlobal();
+
+                       // Restart the tests if they're blocking
+                       if ( config.blocking ) {
+                               QUnit.start();
                        }
+               }
+       },
+       teardown: function() {
+               config.current = this;
+               if ( config.notrycatch ) {
+                       this.testEnvironment.teardown.call( this.testEnvironment );
+                       return;
+               } else {
+                       try {
+                               this.testEnvironment.teardown.call( this.testEnvironment );
+                       } catch( e ) {
+                               QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+                       }
+               }
+               checkPollution();
+       },
+       finish: function() {
+               config.current = this;
+               if ( this.expected != null && this.expected != this.assertions.length ) {
+                       QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
+               } else if ( this.expected == null && !this.assertions.length ) {
+                       QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
+               }
 
-                       // allow utility functions to access the current test environment
-                       QUnit.current_testEnvironment = testEnvironment;
-                       
-                       config.assertions = [];
-                       config.expected = expected;
+               var assertion, a, b, i, li, ol,
+                       test = this,
+                       good = 0,
+                       bad = 0,
+                       tests = id( "qunit-tests" );
 
-                       try {
-                               if ( !config.pollution ) {
-                                       saveGlobal();
-                               }
+               config.stats.all += this.assertions.length;
+               config.moduleStats.all += this.assertions.length;
 
-                               testEnvironment.setup.call(testEnvironment);
-                       } catch(e) {
-                               QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
-                       }
+               if ( tests ) {
+                       ol = document.createElement( "ol" );
 
-                       if ( async ) {
-                               QUnit.stop();
-                       }
+                       for ( i = 0; i < this.assertions.length; i++ ) {
+                               assertion = this.assertions[i];
 
-                       try {
-                               callback.call(testEnvironment);
-                       } catch(e) {
-                               fail("Test " + name + " died, exception and test follows", e, callback);
-                               QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
-                               // else next test will carry the responsibility
-                               saveGlobal();
-
-                               // Restart the tests if they're blocking
-                               if ( config.blocking ) {
-                                       start();
+                               li = document.createElement( "li" );
+                               li.className = assertion.result ? "pass" : "fail";
+                               li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
+                               ol.appendChild( li );
+
+                               if ( assertion.result ) {
+                                       good++;
+                               } else {
+                                       bad++;
+                                       config.stats.bad++;
+                                       config.moduleStats.bad++;
                                }
                        }
-               });
 
-               synchronize(function() {
-                       try {
-                               checkPollution();
-                               testEnvironment.teardown.call(testEnvironment);
-                       } catch(e) {
-                               QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
+                       // store result when possible
+                       if ( QUnit.config.reorder && defined.sessionStorage ) {
+                               if ( bad ) {
+                                       sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
+                               } else {
+                                       sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
+                               }
                        }
 
-                       try {
-                               QUnit.reset();
-                       } catch(e) {
-                               fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
+                       if ( bad === 0 ) {
+                               ol.style.display = "none";
                        }
 
-                       if ( config.expected && config.expected != config.assertions.length ) {
-                               QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
+                       // `b` initialized at top of scope
+                       b = document.createElement( "strong" );
+                       b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
+
+                       addEvent(b, "click", function() {
+                               var next = b.nextSibling.nextSibling,
+                                       display = next.style.display;
+                               next.style.display = display === "none" ? "block" : "none";
+                       });
+
+                       addEvent(b, "dblclick", function( e ) {
+                               var target = e && e.target ? e.target : window.event.srcElement;
+                               if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
+                                       target = target.parentNode;
+                               }
+                               if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+                                       window.location = QUnit.url({ testNumber: test.testNumber });
+                               }
+                       });
+
+                       // `li` initialized at top of scope
+                       li = id( this.id );
+                       li.className = bad ? "fail" : "pass";
+                       li.removeChild( li.firstChild );
+                       a = li.firstChild;
+                       li.appendChild( b );
+                       li.appendChild ( a );
+                       li.appendChild( ol );
+
+               } else {
+                       for ( i = 0; i < this.assertions.length; i++ ) {
+                               if ( !this.assertions[i].result ) {
+                                       bad++;
+                                       config.stats.bad++;
+                                       config.moduleStats.bad++;
+                               }
                        }
+               }
 
-                       var good = 0, bad = 0,
-                               tests = id("qunit-tests");
+               runLoggingCallbacks( "testDone", QUnit, {
+                       name: this.testName,
+                       module: this.module,
+                       failed: bad,
+                       passed: this.assertions.length - bad,
+                       total: this.assertions.length
+               });
 
-                       config.stats.all += config.assertions.length;
-                       config.moduleStats.all += config.assertions.length;
+               QUnit.reset();
+       },
 
-                       if ( tests ) {
-                               var ol  = document.createElement("ol");
-                               ol.style.display = "none";
+       queue: function() {
+               var bad,
+                       test = this;
 
-                               for ( var i = 0; i < config.assertions.length; i++ ) {
-                                       var assertion = config.assertions[i];
+               synchronize(function() {
+                       test.init();
+               });
+               function run() {
+                       // each of these can by async
+                       synchronize(function() {
+                               test.setup();
+                       });
+                       synchronize(function() {
+                               test.run();
+                       });
+                       synchronize(function() {
+                               test.teardown();
+                       });
+                       synchronize(function() {
+                               test.finish();
+                       });
+               }
 
-                                       var li = document.createElement("li");
-                                       li.className = assertion.result ? "pass" : "fail";
-                                       li.appendChild(document.createTextNode(assertion.message || "(no message)"));
-                                       ol.appendChild( li );
+               // `bad` initialized at top of scope
+               // defer when previous test run passed, if storage is available
+               bad = QUnit.config.reorder && defined.sessionStorage &&
+                                               +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
 
-                                       if ( assertion.result ) {
-                                               good++;
-                                       } else {
-                                               bad++;
-                                               config.stats.bad++;
-                                               config.moduleStats.bad++;
-                                       }
-                               }
+               if ( bad ) {
+                       run();
+               } else {
+                       synchronize( run, true );
+               }
+       }
+};
 
-                               var b = document.createElement("strong");
-                               b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
-                               
-                               addEvent(b, "click", function() {
-                                       var next = b.nextSibling, display = next.style.display;
-                                       next.style.display = display === "none" ? "block" : "none";
-                               });
-                               
-                               addEvent(b, "dblclick", function(e) {
-                                       var target = e && e.target ? e.target : window.event.srcElement;
-                                       if ( target.nodeName.toLowerCase() === "strong" ) {
-                                               var text = "", node = target.firstChild;
-
-                                               while ( node.nodeType === 3 ) {
-                                                       text += node.nodeValue;
-                                                       node = node.nextSibling;
-                                               }
+// Root QUnit object.
+// `QUnit` initialized at top of scope
+QUnit = {
 
-                                               text = text.replace(/(^\s*|\s*$)/g, "");
+       // call on start of module test to prepend name to all tests
+       module: function( name, testEnvironment ) {
+               config.currentModule = name;
+               config.currentModuleTestEnviroment = testEnvironment;
+       },
 
-                                               if ( window.location ) {
-                                                       window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
-                                               }
-                                       }
-                               });
+       asyncTest: function( testName, expected, callback ) {
+               if ( arguments.length === 2 ) {
+                       callback = expected;
+                       expected = null;
+               }
 
-                               var li = document.createElement("li");
-                               li.className = bad ? "fail" : "pass";
-                               li.appendChild( b );
-                               li.appendChild( ol );
-                               tests.appendChild( li );
+               QUnit.test( testName, expected, callback, true );
+       },
 
-                               if ( bad ) {
-                                       var toolbar = id("qunit-testrunner-toolbar");
-                                       if ( toolbar ) {
-                                               toolbar.style.display = "block";
-                                               id("qunit-filter-pass").disabled = null;
-                                               id("qunit-filter-missing").disabled = null;
-                                       }
-                               }
+       test: function( testName, expected, callback, async ) {
+               var test,
+                       name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";
 
-                       } else {
-                               for ( var i = 0; i < config.assertions.length; i++ ) {
-                                       if ( !config.assertions[i].result ) {
-                                               bad++;
-                                               config.stats.bad++;
-                                               config.moduleStats.bad++;
-                                       }
-                               }
-                       }
+               if ( arguments.length === 2 ) {
+                       callback = expected;
+                       expected = null;
+               }
 
-                       QUnit.testDone( testName, bad, config.assertions.length );
+               if ( config.currentModule ) {
+                       name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
+               }
 
-                       if ( !window.setTimeout && !config.queue.length ) {
-                               done();
-                       }
+               test = new Test({
+                       name: name,
+                       testName: testName,
+                       expected: expected,
+                       async: async,
+                       callback: callback,
+                       module: config.currentModule,
+                       moduleTestEnvironment: config.currentModuleTestEnviroment,
+                       stack: sourceFromStacktrace( 2 )
                });
 
-               if ( window.setTimeout && !config.doneTimer ) {
-                       config.doneTimer = window.setTimeout(function(){
-                               if ( !config.queue.length ) {
-                                       done();
-                               } else {
-                                       synchronize( done );
+               if ( !validTest( test ) ) {
+                       return;
+               }
+
+               test.queue();
+       },
+
+       // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
+       expect: function( asserts ) {
+               config.current.expected = asserts;
+       },
+
+       start: function( count ) {
+               config.semaphore -= count || 1;
+               // don't start until equal number of stop-calls
+               if ( config.semaphore > 0 ) {
+                       return;
+               }
+               // ignore if start is called more often then stop
+               if ( config.semaphore < 0 ) {
+                       config.semaphore = 0;
+               }
+               // A slight delay, to avoid any current callbacks
+               if ( defined.setTimeout ) {
+                       window.setTimeout(function() {
+                               if ( config.semaphore > 0 ) {
+                                       return;
+                               }
+                               if ( config.timeout ) {
+                                       clearTimeout( config.timeout );
                                }
+
+                               config.blocking = false;
+                               process( true );
                        }, 13);
+               } else {
+                       config.blocking = false;
+                       process( true );
                }
        },
-       
-       /**
-        * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
-        */
-       expect: function(asserts) {
-               config.expected = asserts;
-       },
 
+       stop: function( count ) {
+               config.semaphore += count || 1;
+               config.blocking = true;
+
+               if ( config.testTimeout && defined.setTimeout ) {
+                       clearTimeout( config.timeout );
+                       config.timeout = window.setTimeout(function() {
+                               QUnit.ok( false, "Test timed out" );
+                               config.semaphore = 1;
+                               QUnit.start();
+                       }, config.testTimeout );
+               }
+       }
+};
+
+// Asssert helpers
+// All of these must call either QUnit.push() or manually do:
+// - runLoggingCallbacks( "log", .. );
+// - config.current.assertions.push({ .. });
+QUnit.assert = {
        /**
-        * Asserts true.
+        * Asserts rough true-ish result.
         * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
         */
-       ok: function(a, msg) {
-               QUnit.log(a, msg);
+       ok: function( result, msg ) {
+               if ( !config.current ) {
+                       throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
+               }
+               result = !!result;
+
+               var source,
+                       details = {
+                               result: result,
+                               message: msg
+                       };
+
+               msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
+               msg = "<span class='test-message'>" + msg + "</span>";
 
-               config.assertions.push({
-                       result: !!a,
+               if ( !result ) {
+                       source = sourceFromStacktrace( 2 );
+                       if ( source ) {
+                               details.source = source;
+                               msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
+                       }
+               }
+               runLoggingCallbacks( "log", QUnit, details );
+               config.current.assertions.push({
+                       result: result,
                        message: msg
                });
        },
 
        /**
-        * Checks that the first two arguments are equal, with an optional message.
+        * Assert that the first two arguments are equal, with an optional message.
         * Prints out both actual and expected values.
-        *
-        * Prefered to ok( actual == expected, message )
-        *
-        * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
-        *
-        * @param Object actual
-        * @param Object expected
-        * @param String message (optional)
+        * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
         */
-       equal: function(actual, expected, message) {
-               push(expected == actual, actual, expected, message);
+       equal: function( actual, expected, message ) {
+               QUnit.push( expected == actual, actual, expected, message );
        },
 
-       notEqual: function(actual, expected, message) {
-               push(expected != actual, actual, expected, message);
+       notEqual: function( actual, expected, message ) {
+               QUnit.push( expected != actual, actual, expected, message );
        },
-       
-       deepEqual: function(a, b, message) {
-               push(QUnit.equiv(a, b), a, b, message);
+
+       deepEqual: function( actual, expected, message ) {
+               QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
        },
 
-       notDeepEqual: function(a, b, message) {
-               push(!QUnit.equiv(a, b), a, b, message);
+       notDeepEqual: function( actual, expected, message ) {
+               QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
        },
 
-       strictEqual: function(actual, expected, message) {
-               push(expected === actual, actual, expected, message);
+       strictEqual: function( actual, expected, message ) {
+               QUnit.push( expected === actual, actual, expected, message );
        },
 
-       notStrictEqual: function(actual, expected, message) {
-               push(expected !== actual, actual, expected, message);
+       notStrictEqual: function( actual, expected, message ) {
+               QUnit.push( expected !== actual, actual, expected, message );
        },
-       
-       start: function() {
-               // A slight delay, to avoid any current callbacks
-               if ( window.setTimeout ) {
-                       window.setTimeout(function() {
-                               if ( config.timeout ) {
-                                       clearTimeout(config.timeout);
-                               }
 
-                               config.blocking = false;
-                               process();
-                       }, 13);
-               } else {
-                       config.blocking = false;
-                       process();
+       raises: function( block, expected, message ) {
+               var actual,
+                       ok = false;
+
+               if ( typeof expected === "string" ) {
+                       message = expected;
+                       expected = null;
                }
-       },
-       
-       stop: function(timeout) {
-               config.blocking = true;
 
-               if ( timeout && window.setTimeout ) {
-                       config.timeout = window.setTimeout(function() {
-                               QUnit.ok( false, "Test timed out" );
-                               QUnit.start();
-                       }, timeout);
+               try {
+                       block.call( config.current.testEnvironment );
+               } catch (e) {
+                       actual = e;
+               }
+
+               if ( actual ) {
+                       // we don't want to validate thrown error
+                       if ( !expected ) {
+                               ok = true;
+                       // expected is a regexp
+                       } else if ( QUnit.objectType( expected ) === "regexp" ) {
+                               ok = expected.test( actual );
+                       // expected is a constructor
+                       } else if ( actual instanceof expected ) {
+                               ok = true;
+                       // expected is a validation function which returns true is validation passed
+                       } else if ( expected.call( {}, actual ) === true ) {
+                               ok = true;
+                       }
+               }
+
+               QUnit.push( ok, actual, null, message );
+       }
+};
+
+// @deprecated: Kept assertion helpers in root for backwards compatibility
+extend( QUnit, QUnit.assert );
+
+/**
+ * @deprecated: Kept for backwards compatibility
+ * next step: remove entirely
+ */
+QUnit.equals = function() {
+       QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
+};
+QUnit.same = function() {
+       QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
+};
+
+// We want access to the constructor's prototype
+(function() {
+       function F() {}
+       F.prototype = QUnit;
+       QUnit = new F();
+       // Make F QUnit's constructor so that we can add to the prototype later
+       QUnit.constructor = F;
+}());
+
+/**
+ * Config object: Maintain internal state
+ * Later exposed as QUnit.config
+ * `config` initialized at top of scope
+ */
+config = {
+       // The queue of tests to run
+       queue: [],
+
+       // block until document ready
+       blocking: true,
+
+       // when enabled, show only failing tests
+       // gets persisted through sessionStorage and can be changed in UI via checkbox
+       hidepassed: false,
+
+       // by default, run previously failed tests first
+       // very useful in combination with "Hide passed tests" checked
+       reorder: true,
+
+       // by default, modify document.title when suite is done
+       altertitle: true,
+
+       urlConfig: [ "noglobals", "notrycatch" ],
+
+       // logging callback queues
+       begin: [],
+       done: [],
+       log: [],
+       testStart: [],
+       testDone: [],
+       moduleStart: [],
+       moduleDone: []
+};
+
+// Initialize more QUnit.config and QUnit.urlParams
+(function() {
+       var i,
+               location = window.location || { search: "", protocol: "file:" },
+               params = location.search.slice( 1 ).split( "&" ),
+               length = params.length,
+               urlParams = {},
+               current;
+
+       if ( params[ 0 ] ) {
+               for ( i = 0; i < length; i++ ) {
+                       current = params[ i ].split( "=" );
+                       current[ 0 ] = decodeURIComponent( current[ 0 ] );
+                       // allow just a key to turn on a flag, e.g., test.html?noglobals
+                       current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
+                       urlParams[ current[ 0 ] ] = current[ 1 ];
+               }
+       }
+
+       QUnit.urlParams = urlParams;
+       config.filter = urlParams.filter;
+       config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
+
+       // Figure out if we're running the tests from a server or not
+       QUnit.isLocal = location.protocol === "file:";
+}());
+
+// Export global variables, unless an 'exports' object exists,
+// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
+if ( typeof exports === "undefined" ) {
+       extend( window, QUnit );
+
+       // Expose QUnit object
+       window.QUnit = QUnit;
+}
+
+// Extend QUnit object,
+// these after set here because they should not be exposed as global functions
+extend( QUnit, {
+       config: config,
+
+       // Initialize the configuration options
+       init: function() {
+               extend( config, {
+                       stats: { all: 0, bad: 0 },
+                       moduleStats: { all: 0, bad: 0 },
+                       started: +new Date(),
+                       updateRate: 1000,
+                       blocking: false,
+                       autostart: true,
+                       autorun: false,
+                       filter: "",
+                       queue: [],
+                       semaphore: 0
+               });
+
+               var tests, banner, result,
+                       qunit = id( "qunit" );
+
+               if ( qunit ) {
+                       qunit.innerHTML =
+                               "<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" +
+                               "<h2 id='qunit-banner'></h2>" +
+                               "<div id='qunit-testrunner-toolbar'></div>" +
+                               "<h2 id='qunit-userAgent'></h2>" +
+                               "<ol id='qunit-tests'></ol>";
+               }
+
+               tests = id( "qunit-tests" );
+               banner = id( "qunit-banner" );
+               result = id( "qunit-testresult" );
+
+               if ( tests ) {
+                       tests.innerHTML = "";
+               }
+
+               if ( banner ) {
+                       banner.className = "";
+               }
+
+               if ( result ) {
+                       result.parentNode.removeChild( result );
+               }
+
+               if ( tests ) {
+                       result = document.createElement( "p" );
+                       result.id = "qunit-testresult";
+                       result.className = "result";
+                       tests.parentNode.insertBefore( result, tests );
+                       result.innerHTML = "Running...<br/>&nbsp;";
                }
        },
-       
-       /**
-        * Resets the test setup. Useful for tests that modify the DOM.
-        */
+
+       // Resets the test setup. Useful for tests that modify the DOM.
+       // If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
        reset: function() {
+               var fixture;
+
                if ( window.jQuery ) {
-                       jQuery("#main").html( config.fixture );
-                       jQuery.event.global = {};
-                       jQuery.ajaxSettings = extend({}, config.ajaxSettings);
+                       jQuery( "#qunit-fixture" ).html( config.fixture );
+               } else {
+                       fixture = id( "qunit-fixture" );
+                       if ( fixture ) {
+                               fixture.innerHTML = config.fixture;
+                       }
                }
        },
-       
-       /**
-        * Trigger an event on an element.
-        *
-        * @example triggerEvent( document.body, "click" );
-        *
-        * @param DOMElement elem
-        * @param String type
-        */
+
+       // Trigger an event on an element.
+       // @example triggerEvent( document.body, "click" );
        triggerEvent: function( elem, type, event ) {
                if ( document.createEvent ) {
-                       event = document.createEvent("MouseEvents");
+                       event = document.createEvent( "MouseEvents" );
                        event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
                                0, 0, 0, 0, 0, false, false, false, false, 0, null);
-                       elem.dispatchEvent( event );
 
+                       elem.dispatchEvent( event );
                } else if ( elem.fireEvent ) {
-                       elem.fireEvent("on"+type);
+                       elem.fireEvent( "on" + type );
                }
        },
-       
+
        // Safe object type checking
        is: function( type, obj ) {
-               return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
+               return QUnit.objectType( obj ) == type;
+       },
+
+       objectType: function( obj ) {
+               if ( typeof obj === "undefined" ) {
+                               return "undefined";
+               // consider: typeof null === object
+               }
+               if ( obj === null ) {
+                               return "null";
+               }
+
+               var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
+
+               switch ( type ) {
+                       case "Number":
+                               if ( isNaN(obj) ) {
+                                       return "nan";
+                               }
+                               return "number";
+                       case "String":
+                       case "Boolean":
+                       case "Array":
+                       case "Date":
+                       case "RegExp":
+                       case "Function":
+                               return type.toLowerCase();
+               }
+               if ( typeof obj === "object" ) {
+                       return "object";
+               }
+               return undefined;
+       },
+
+       push: function( result, actual, expected, message ) {
+               if ( !config.current ) {
+                       throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
+               }
+
+               var output, source,
+                       details = {
+                               result: result,
+                               message: message,
+                               actual: actual,
+                               expected: expected
+                       };
+
+               message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
+               message = "<span class='test-message'>" + message + "</span>";
+               output = message;
+
+               if ( !result ) {
+                       expected = escapeInnerText( QUnit.jsDump.parse(expected) );
+                       actual = escapeInnerText( QUnit.jsDump.parse(actual) );
+                       output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
+
+                       if ( actual != expected ) {
+                               output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
+                               output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
+                       }
+
+                       source = sourceFromStacktrace();
+
+                       if ( source ) {
+                               details.source = source;
+                               output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
+                       }
+
+                       output += "</table>";
+               }
+
+               runLoggingCallbacks( "log", QUnit, details );
+
+               config.current.assertions.push({
+                       result: !!result,
+                       message: output
+               });
+       },
+
+       pushFailure: function( message, source ) {
+               var output,
+                       details = {
+                               result: false,
+                               message: message
+                       };
+
+               message = escapeInnerText(message ) || "error";
+               message = "<span class='test-message'>" + message + "</span>";
+               output = message;
+
+               if ( source ) {
+                       details.source = source;
+                       output += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
+               }
+
+               runLoggingCallbacks( "log", QUnit, details );
+
+               config.current.assertions.push({
+                       result: false,
+                       message: output
+               });
+       },
+
+       url: function( params ) {
+               params = extend( extend( {}, QUnit.urlParams ), params );
+               var key,
+                       querystring = "?";
+
+               for ( key in params ) {
+                       if ( !hasOwn.call( params, key ) ) {
+                               continue;
+                       }
+                       querystring += encodeURIComponent( key ) + "=" +
+                               encodeURIComponent( params[ key ] ) + "&";
+               }
+               return window.location.pathname + querystring.slice( 0, -1 );
        },
-       
-       // Logging callbacks
-       done: function(failures, total) {},
-       log: function(result, message) {},
-       testStart: function(name) {},
-       testDone: function(name, failures, total) {},
-       moduleStart: function(name, testEnvironment) {},
-       moduleDone: function(name, failures, total) {}
-};
 
-// Backwards compatibility, deprecated
-QUnit.equals = QUnit.equal;
-QUnit.same = QUnit.deepEqual;
+       extend: extend,
+       id: id,
+       addEvent: addEvent
+       // load, equiv, jsDump, diff: Attached later
+});
 
-// Maintain internal state
-var config = {
-       // The queue of tests to run
-       queue: [],
+/**
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
+ * QUnit object.
+ */
+extend( QUnit.constructor.prototype, {
 
-       // block until document ready
-       blocking: true
-};
+       // Logging callbacks; all receive a single argument with the listed properties
+       // run test/logs.html for any related changes
+       begin: registerLoggingCallback( "begin" ),
 
-// Load paramaters
-(function() {
-       var location = window.location || { search: "", protocol: "file:" },
-               GETParams = location.search.slice(1).split('&');
-
-       for ( var i = 0; i < GETParams.length; i++ ) {
-               GETParams[i] = decodeURIComponent( GETParams[i] );
-               if ( GETParams[i] === "noglobals" ) {
-                       GETParams.splice( i, 1 );
-                       i--;
-                       config.noglobals = true;
-               } else if ( GETParams[i].search('=') > -1 ) {
-                       GETParams.splice( i, 1 );
-                       i--;
-               }
-       }
-       
-       // restrict modules/tests by get parameters
-       config.filters = GETParams;
-       
-       // Figure out if we're running the tests from a server or not
-       QUnit.isLocal = !!(location.protocol === 'file:');
-})();
+       // done: { failed, passed, total, runtime }
+       done: registerLoggingCallback( "done" ),
 
-// Expose the API as global variables, unless an 'exports'
-// object exists, in that case we assume we're in CommonJS
-if ( typeof exports === "undefined" || typeof require === "undefined" ) {
-       extend(window, QUnit);
-       window.QUnit = QUnit;
-} else {
-       extend(exports, QUnit);
-       exports.QUnit = QUnit;
-}
+       // log: { result, actual, expected, message }
+       log: registerLoggingCallback( "log" ),
+
+       // testStart: { name }
+       testStart: registerLoggingCallback( "testStart" ),
+
+       // testDone: { name, failed, passed, total }
+       testDone: registerLoggingCallback( "testDone" ),
+
+       // moduleStart: { name }
+       moduleStart: registerLoggingCallback( "moduleStart" ),
+
+       // moduleDone: { name, failed, passed, total }
+       moduleDone: registerLoggingCallback( "moduleDone" )
+});
 
 if ( typeof document === "undefined" || document.readyState === "complete" ) {
        config.autorun = true;
 }
 
-addEvent(window, "load", function() {
+QUnit.load = function() {
+       runLoggingCallbacks( "begin", QUnit, {} );
+
        // Initialize the config, saving the execution queue
-       var oldconfig = extend({}, config);
+       var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
+               urlConfigHtml = "",
+               oldconfig = extend( {}, config );
+
        QUnit.init();
        extend(config, oldconfig);
 
-       config.blocking = false;
+       // config.blocking = false;
 
-       var userAgent = id("qunit-userAgent");
+       len = config.urlConfig.length;
+
+       for ( i = 0; i < len; i++ ) {
+               val = config.urlConfig[i];
+               config[val] = QUnit.urlParams[val];
+               urlConfigHtml += "<label><input name='" + val + "' type='checkbox'" + ( config[val] ? " checked='checked'" : "" ) + ">" + val + "</label>";
+       }
+
+       // `userAgent` initialized at top of scope
+       userAgent = id( "qunit-userAgent" );
        if ( userAgent ) {
                userAgent.innerHTML = navigator.userAgent;
        }
-       
-       var toolbar = id("qunit-testrunner-toolbar");
+
+       // `banner` initialized at top of scope
+       banner = id( "qunit-header" );
+       if ( banner ) {
+               banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined }) + "'>" + banner.innerHTML + "</a> " + urlConfigHtml;
+               addEvent( banner, "change", function( event ) {
+                       var params = {};
+                       params[ event.target.name ] = event.target.checked ? true : undefined;
+                       window.location = QUnit.url( params );
+               });
+       }
+
+       // `toolbar` initialized at top of scope
+       toolbar = id( "qunit-testrunner-toolbar" );
        if ( toolbar ) {
-               toolbar.style.display = "none";
-               
-               var filter = document.createElement("input");
+               // `filter` initialized at top of scope
+               filter = document.createElement( "input" );
                filter.type = "checkbox";
                filter.id = "qunit-filter-pass";
-               filter.disabled = true;
+
                addEvent( filter, "click", function() {
-                       var li = document.getElementsByTagName("li");
-                       for ( var i = 0; i < li.length; i++ ) {
-                               if ( li[i].className.indexOf("pass") > -1 ) {
-                                       li[i].style.display = filter.checked ? "none" : "";
+                       var tmp,
+                               ol = document.getElementById( "qunit-tests" );
+
+                       if ( filter.checked ) {
+                               ol.className = ol.className + " hidepass";
+                       } else {
+                               tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
+                               ol.className = tmp.replace( / hidepass /, " " );
+                       }
+                       if ( defined.sessionStorage ) {
+                               if (filter.checked) {
+                                       sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
+                               } else {
+                                       sessionStorage.removeItem( "qunit-filter-passed-tests" );
                                }
                        }
                });
+
+               if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
+                       filter.checked = true;
+                       // `ol` initialized at top of scope
+                       ol = document.getElementById( "qunit-tests" );
+                       ol.className = ol.className + " hidepass";
+               }
                toolbar.appendChild( filter );
 
-               var label = document.createElement("label");
-               label.setAttribute("for", "qunit-filter-pass");
+               // `label` initialized at top of scope
+               label = document.createElement( "label" );
+               label.setAttribute( "for", "qunit-filter-pass" );
                label.innerHTML = "Hide passed tests";
                toolbar.appendChild( label );
-
-               var missing = document.createElement("input");
-               missing.type = "checkbox";
-               missing.id = "qunit-filter-missing";
-               missing.disabled = true;
-               addEvent( missing, "click", function() {
-                       var li = document.getElementsByTagName("li");
-                       for ( var i = 0; i < li.length; i++ ) {
-                               if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
-                                       li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
-                               }
-                       }
-               });
-               toolbar.appendChild( missing );
-
-               label = document.createElement("label");
-               label.setAttribute("for", "qunit-filter-missing");
-               label.innerHTML = "Hide missing tests (untested code is broken code)";
-               toolbar.appendChild( label );
        }
 
-       var main = id('main');
+       // `main` initialized at top of scope
+       main = id( "qunit-fixture" );
        if ( main ) {
                config.fixture = main.innerHTML;
        }
 
-       if ( window.jQuery ) {
-               config.ajaxSettings = window.jQuery.ajaxSettings;
-       }
-
-       QUnit.start();
-});
-
-function done() {
-       if ( config.doneTimer && window.clearTimeout ) {
-               window.clearTimeout( config.doneTimer );
-               config.doneTimer = null;
+       if ( config.autostart ) {
+               QUnit.start();
        }
+};
 
-       if ( config.queue.length ) {
-               config.doneTimer = window.setTimeout(function(){
-                       if ( !config.queue.length ) {
-                               done();
-                       } else {
-                               synchronize( done );
-                       }
-               }, 13);
+addEvent( window, "load", QUnit.load );
 
-               return;
+// addEvent(window, "error" ) gives us a useless event object
+window.onerror = function( message, file, line ) {
+       if ( QUnit.config.current ) {
+               QUnit.pushFailure( message, file + ":" + line );
+       } else {
+               QUnit.test( "global failure", function() {
+                       QUnit.pushFailure( message, file + ":" + line );
+               });
        }
+};
 
+function done() {
        config.autorun = true;
 
        // Log the last module results
        if ( config.currentModule ) {
-               QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+               runLoggingCallbacks( "moduleDone", QUnit, {
+                       name: config.currentModule,
+                       failed: config.moduleStats.bad,
+                       passed: config.moduleStats.all - config.moduleStats.bad,
+                       total: config.moduleStats.all
+               });
        }
 
-       var banner = id("qunit-banner"),
-               tests = id("qunit-tests"),
-               html = ['Tests completed in ',
-               +new Date - config.started, ' milliseconds.<br/>',
-               '<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
+       var i, key,
+               banner = id( "qunit-banner" ),
+               tests = id( "qunit-tests" ),
+               runtime = +new Date() - config.started,
+               passed = config.stats.all - config.stats.bad,
+               html = [
+                       "Tests completed in ",
+                       runtime,
+                       " milliseconds.<br/>",
+                       "<span class='passed'>",
+                       passed,
+                       "</span> tests of <span class='total'>",
+                       config.stats.all,
+                       "</span> passed, <span class='failed'>",
+                       config.stats.bad,
+                       "</span> failed."
+               ].join( "" );
 
        if ( banner ) {
-               banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
+               banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
        }
 
-       if ( tests ) {  
-               var result = id("qunit-testresult");
+       if ( tests ) {
+               id( "qunit-testresult" ).innerHTML = html;
+       }
 
-               if ( !result ) {
-                       result = document.createElement("p");
-                       result.id = "qunit-testresult";
-                       result.className = "result";
-                       tests.parentNode.insertBefore( result, tests.nextSibling );
-               }
+       if ( config.altertitle && typeof document !== "undefined" && document.title ) {
+               // show âœ– for good, âœ” for bad suite result in title
+               // use escape sequences in case file gets loaded with non-utf-8-charset
+               document.title = [
+                       ( config.stats.bad ? "\u2716" : "\u2714" ),
+                       document.title.replace( /^[\u2714\u2716] /i, "" )
+               ].join( " " );
+       }
 
-               result.innerHTML = html;
+       // clear own sessionStorage items if all tests passed
+       if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
+               // `key` & `i` initialized at top of scope
+               for ( i = 0; i < sessionStorage.length; i++ ) {
+                       key = sessionStorage.key( i++ );
+                       if ( key.indexOf( "qunit-test-" ) === 0 ) {
+                               sessionStorage.removeItem( key );
+                       }
+               }
        }
 
-       QUnit.done( config.stats.bad, config.stats.all );
+       runLoggingCallbacks( "done", QUnit, {
+               failed: config.stats.bad,
+               passed: passed,
+               total: config.stats.all,
+               runtime: runtime
+       });
 }
 
-function validTest( name ) {
-       var i = config.filters.length,
-               run = false;
+function validTest( test ) {
+       return true;
+       var include,
+               filter = config.filter,
+               fullName = test.module + ": " + test.testName;
+
+       if ( config.testNumber ) {
+               return test.testNumber === config.testNumber;
+       }
 
-       if ( !i ) {
+       if ( !filter ) {
                return true;
        }
-       
-       while ( i-- ) {
-               var filter = config.filters[i],
-                       not = filter.charAt(0) == '!';
 
-               if ( not ) {
-                       filter = filter.slice(1);
-               }
+       include = filter.charAt( 0 ) !== "!";
+       if ( !include ) {
+               filter = filter.slice( 1 );
+       }
 
-               if ( name.indexOf(filter) !== -1 ) {
-                       return !not;
-               }
+       // If the filter matches, we need to honour include
+       if ( fullName.indexOf( filter ) !== -1 ) {
+               return include;
+       }
 
-               if ( not ) {
-                       run = true;
+       // Otherwise, do the opposite
+       return !include;
+}
+
+// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
+// Later Safari and IE10 are supposed to support error.stack as well
+// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+function extractStacktrace( e, offset ) {
+       offset = offset || 3;
+
+       var stack;
+
+       if ( e.stacktrace ) {
+               // Opera
+               return e.stacktrace.split( "\n" )[ offset + 3 ];
+       } else if ( e.stack ) {
+               // Firefox, Chrome
+               stack = e.stack.split( "\n" );
+               if (/^error$/i.test( stack[0] ) ) {
+                       stack.shift();
+               }
+               return stack[ offset ];
+       } else if ( e.sourceURL ) {
+               // Safari, PhantomJS
+               // hopefully one day Safari provides actual stacktraces
+               // exclude useless self-reference for generated Error objects
+               if ( /qunit.js$/.test( e.sourceURL ) ) {
+                       return;
                }
+               // for actual exceptions, this is useful
+               return e.sourceURL + ":" + e.line;
+       }
+}
+function sourceFromStacktrace( offset ) {
+       try {
+               throw new Error();
+       } catch ( e ) {
+               return extractStacktrace( e, offset );
        }
-
-       return run;
 }
 
-function push(result, actual, expected, message) {
-       message = message || (result ? "okay" : "failed");
-       QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
+function escapeInnerText( s ) {
+       if ( !s ) {
+               return "";
+       }
+       s = s + "";
+       return s.replace( /[\&<>]/g, function( s ) {
+               switch( s ) {
+                       case "&": return "&amp;";
+                       case "<": return "&lt;";
+                       case ">": return "&gt;";
+                       default: return s;
+               }
+       });
 }
 
-function synchronize( callback ) {
+function synchronize( callback, last ) {
        config.queue.push( callback );
 
        if ( config.autorun && !config.blocking ) {
-               process();
+               process( last );
        }
 }
 
-function process() {
+function process( last ) {
+       function next() {
+               process( last );
+       }
+       var start = new Date().getTime();
+       config.depth = config.depth ? config.depth + 1 : 1;
+
        while ( config.queue.length && !config.blocking ) {
-               config.queue.shift()();
+               if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
+                       config.queue.shift()();
+               } else {
+                       window.setTimeout( next, 13 );
+                       break;
+               }
+       }
+       config.depth--;
+       if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
+               done();
        }
 }
 
 function saveGlobal() {
        config.pollution = [];
-       
+
        if ( config.noglobals ) {
                for ( var key in window ) {
+                       // in Opera sometimes DOM element ids show up here, ignore them
+                       if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
+                               continue;
+                       }
                        config.pollution.push( key );
                }
        }
 }
 
 function checkPollution( name ) {
-       var old = config.pollution;
+       var newGlobals,
+               deletedGlobals,
+               old = config.pollution;
+
        saveGlobal();
-       
-       var newGlobals = diff( old, config.pollution );
+
+       newGlobals = diff( config.pollution, old );
        if ( newGlobals.length > 0 ) {
-               ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
-               config.expected++;
+               QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
        }
 
-       var deletedGlobals = diff( config.pollution, old );
+       deletedGlobals = diff( old, config.pollution );
        if ( deletedGlobals.length > 0 ) {
-               ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
-               config.expected++;
+               QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
        }
 }
 
 // returns a new Array with the elements that are in a but not in b
 function diff( a, b ) {
-       var result = a.slice();
-       for ( var i = 0; i < result.length; i++ ) {
-               for ( var j = 0; j < b.length; j++ ) {
+       var i, j,
+               result = a.slice();
+
+       for ( i = 0; i < result.length; i++ ) {
+               for ( j = 0; j < b.length; j++ ) {
                        if ( result[i] === b[j] ) {
-                               result.splice(i, 1);
+                               result.splice( i, 1 );
                                i--;
                                break;
                        }
@@ -637,26 +1163,21 @@ function diff( a, b ) {
        return result;
 }
 
-function fail(message, exception, callback) {
-       if ( typeof console !== "undefined" && console.error && console.warn ) {
-               console.error(message);
-               console.error(exception);
-               console.warn(callback.toString());
-
-       } else if ( window.opera && opera.postError ) {
-               opera.postError(message, exception, callback.toString);
-       }
-}
-
-function extend(a, b) {
+function extend( a, b ) {
        for ( var prop in b ) {
-               a[prop] = b[prop];
+               if ( b[ prop ] === undefined ) {
+                       delete a[ prop ];
+
+               // Avoid "Member not found" error in IE8 caused by setting window.constructor
+               } else if ( prop !== "constructor" || a !== window ) {
+                       a[ prop ] = b[ prop ];
+               }
        }
 
        return a;
 }
 
-function addEvent(elem, type, fn) {
+function addEvent( elem, type, fn ) {
        if ( elem.addEventListener ) {
                elem.addEventListener( type, fn, false );
        } else if ( elem.attachEvent ) {
@@ -666,210 +1187,220 @@ function addEvent(elem, type, fn) {
        }
 }
 
-function id(name) {
-       return !!(typeof document !== "undefined" && document && document.getElementById) &&
+function id( name ) {
+       return !!( typeof document !== "undefined" && document && document.getElementById ) &&
                document.getElementById( name );
 }
 
+function registerLoggingCallback( key ) {
+       return function( callback ) {
+               config[key].push( callback );
+       };
+}
+
+// Supports deprecated method of completely overwriting logging callbacks
+function runLoggingCallbacks( key, scope, args ) {
+       //debugger;
+       var i, callbacks;
+       if ( QUnit.hasOwnProperty( key ) ) {
+               QUnit[ key ].call(scope, args );
+       } else {
+               callbacks = config[ key ];
+               for ( i = 0; i < callbacks.length; i++ ) {
+                       callbacks[ i ].call( scope, args );
+               }
+       }
+}
+
 // Test for equality any JavaScript type.
-// Discussions and reference: http://philrathe.com/articles/equiv
-// Test suites: http://philrathe.com/tests/equiv
 // Author: Philippe Rathé <prathe@gmail.com>
-QUnit.equiv = function () {
-
-    var innerEquiv; // the real equiv function
-    var callers = []; // stack to decide between skip/abort functions
-
-
-    // Determine what is o.
-    function hoozit(o) {
-        if (QUnit.is("String", o)) {
-            return "string";
-            
-        } else if (QUnit.is("Boolean", o)) {
-            return "boolean";
-
-        } else if (QUnit.is("Number", o)) {
-
-            if (isNaN(o)) {
-                return "nan";
-            } else {
-                return "number";
-            }
-
-        } else if (typeof o === "undefined") {
-            return "undefined";
-
-        // consider: typeof null === object
-        } else if (o === null) {
-            return "null";
-
-        // consider: typeof [] === object
-        } else if (QUnit.is( "Array", o)) {
-            return "array";
-        
-        // consider: typeof new Date() === object
-        } else if (QUnit.is( "Date", o)) {
-            return "date";
-
-        // consider: /./ instanceof Object;
-        //           /./ instanceof RegExp;
-        //          typeof /./ === "function"; // => false in IE and Opera,
-        //                                          true in FF and Safari
-        } else if (QUnit.is( "RegExp", o)) {
-            return "regexp";
-
-        } else if (typeof o === "object") {
-            return "object";
-
-        } else if (QUnit.is( "Function", o)) {
-            return "function";
-        } else {
-            return undefined;
-        }
-    }
-
-    // Call the o related callback with the given arguments.
-    function bindCallbacks(o, callbacks, args) {
-        var prop = hoozit(o);
-        if (prop) {
-            if (hoozit(callbacks[prop]) === "function") {
-                return callbacks[prop].apply(callbacks, args);
-            } else {
-                return callbacks[prop]; // or undefined
-            }
-        }
-    }
-    
-    var callbacks = function () {
-
-        // for string, boolean, number and null
-        function useStrictEquality(b, a) {
-            if (b instanceof a.constructor || a instanceof b.constructor) {
-                // to catch short annotaion VS 'new' annotation of a declaration
-                // e.g. var i = 1;
-                //      var j = new Number(1);
-                return a == b;
-            } else {
-                return a === b;
-            }
-        }
-
-        return {
-            "string": useStrictEquality,
-            "boolean": useStrictEquality,
-            "number": useStrictEquality,
-            "null": useStrictEquality,
-            "undefined": useStrictEquality,
-
-            "nan": function (b) {
-                return isNaN(b);
-            },
-
-            "date": function (b, a) {
-                return hoozit(b) === "date" && a.valueOf() === b.valueOf();
-            },
-
-            "regexp": function (b, a) {
-                return hoozit(b) === "regexp" &&
-                    a.source === b.source && // the regex itself
-                    a.global === b.global && // and its modifers (gmi) ...
-                    a.ignoreCase === b.ignoreCase &&
-                    a.multiline === b.multiline;
-            },
-
-            // - skip when the property is a method of an instance (OOP)
-            // - abort otherwise,
-            //   initial === would have catch identical references anyway
-            "function": function () {
-                var caller = callers[callers.length - 1];
-                return caller !== Object &&
-                        typeof caller !== "undefined";
-            },
-
-            "array": function (b, a) {
-                var i;
-                var len;
-
-                // b could be an object literal here
-                if ( ! (hoozit(b) === "array")) {
-                    return false;
-                }
-
-                len = a.length;
-                if (len !== b.length) { // safe and faster
-                    return false;
-                }
-                for (i = 0; i < len; i++) {
-                    if ( ! innerEquiv(a[i], b[i])) {
-                        return false;
-                    }
-                }
-                return true;
-            },
-
-            "object": function (b, a) {
-                var i;
-                var eq = true; // unless we can proove it
-                var aProperties = [], bProperties = []; // collection of strings
-
-                // comparing constructors is more strict than using instanceof
-                if ( a.constructor !== b.constructor) {
-                    return false;
-                }
-
-                // stack constructor before traversing properties
-                callers.push(a.constructor);
-
-                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
-
-                    aProperties.push(i); // collect a's properties
-
-                    if ( ! innerEquiv(a[i], b[i])) {
-                        eq = false;
-                    }
-                }
-
-                callers.pop(); // unstack, we are done
-
-                for (i in b) {
-                    bProperties.push(i); // collect b's properties
-                }
-
-                // Ensures identical properties name
-                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
-            }
-        };
-    }();
-
-    innerEquiv = function () { // can take multiple arguments
-        var args = Array.prototype.slice.apply(arguments);
-        if (args.length < 2) {
-            return true; // end transition
-        }
-
-        return (function (a, b) {
-            if (a === b) {
-                return true; // catch the most you can
-            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
-                return false; // don't lose time with error prone cases
-            } else {
-                return bindCallbacks(a, callbacks, [b, a]);
-            }
-
-        // apply transition with (1..n) arguments
-        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
-    };
-
-    return innerEquiv;
-
-}();
+QUnit.equiv = (function() {
+
+       // Call the o related callback with the given arguments.
+       function bindCallbacks( o, callbacks, args ) {
+               var prop = QUnit.objectType( o );
+               if ( prop ) {
+                       if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
+                               return callbacks[ prop ].apply( callbacks, args );
+                       } else {
+                               return callbacks[ prop ]; // or undefined
+                       }
+               }
+       }
+
+       // the real equiv function
+       var innerEquiv,
+               // stack to decide between skip/abort functions
+               callers = [],
+               // stack to avoiding loops from circular referencing
+               parents = [],
+
+               getProto = Object.getPrototypeOf || function ( obj ) {
+                       return obj.__proto__;
+               },
+               callbacks = (function () {
+
+                       // for string, boolean, number and null
+                       function useStrictEquality( b, a ) {
+                               if ( b instanceof a.constructor || a instanceof b.constructor ) {
+                                       // to catch short annotaion VS 'new' annotation of a
+                                       // declaration
+                                       // e.g. var i = 1;
+                                       // var j = new Number(1);
+                                       return a == b;
+                               } else {
+                                       return a === b;
+                               }
+                       }
+
+                       return {
+                               "string": useStrictEquality,
+                               "boolean": useStrictEquality,
+                               "number": useStrictEquality,
+                               "null": useStrictEquality,
+                               "undefined": useStrictEquality,
+
+                               "nan": function( b ) {
+                                       return isNaN( b );
+                               },
+
+                               "date": function( b, a ) {
+                                       return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
+                               },
+
+                               "regexp": function( b, a ) {
+                                       return QUnit.objectType( b ) === "regexp" &&
+                                               // the regex itself
+                                               a.source === b.source &&
+                                               // and its modifers
+                                               a.global === b.global &&
+                                               // (gmi) ...
+                                               a.ignoreCase === b.ignoreCase &&
+                                               a.multiline === b.multiline;
+                               },
+
+                               // - skip when the property is a method of an instance (OOP)
+                               // - abort otherwise,
+                               // initial === would have catch identical references anyway
+                               "function": function() {
+                                       var caller = callers[callers.length - 1];
+                                       return caller !== Object && typeof caller !== "undefined";
+                               },
+
+                               "array": function( b, a ) {
+                                       var i, j, len, loop;
+
+                                       // b could be an object literal here
+                                       if ( QUnit.objectType( b ) !== "array" ) {
+                                               return false;
+                                       }
+
+                                       len = a.length;
+                                       if ( len !== b.length ) {
+                                               // safe and faster
+                                               return false;
+                                       }
+
+                                       // track reference to avoid circular references
+                                       parents.push( a );
+                                       for ( i = 0; i < len; i++ ) {
+                                               loop = false;
+                                               for ( j = 0; j < parents.length; j++ ) {
+                                                       if ( parents[j] === a[i] ) {
+                                                               loop = true;// dont rewalk array
+                                                       }
+                                               }
+                                               if ( !loop && !innerEquiv(a[i], b[i]) ) {
+                                                       parents.pop();
+                                                       return false;
+                                               }
+                                       }
+                                       parents.pop();
+                                       return true;
+                               },
+
+                               "object": function( b, a ) {
+                                       var i, j, loop,
+                                               // Default to true
+                                               eq = true,
+                                               aProperties = [],
+                                               bProperties = [];
+
+                                       // comparing constructors is more strict than using
+                                       // instanceof
+                                       if ( a.constructor !== b.constructor ) {
+                                               // Allow objects with no prototype to be equivalent to
+                                               // objects with Object as their constructor.
+                                               if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
+                                                       ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
+                                                               return false;
+                                               }
+                                       }
+
+                                       // stack constructor before traversing properties
+                                       callers.push( a.constructor );
+                                       // track reference to avoid circular references
+                                       parents.push( a );
+
+                                       for ( i in a ) { // be strict: don't ensures hasOwnProperty
+                                                                       // and go deep
+                                               loop = false;
+                                               for ( j = 0; j < parents.length; j++ ) {
+                                                       if ( parents[j] === a[i] ) {
+                                                               // don't go down the same path twice
+                                                               loop = true;
+                                                       }
+                                               }
+                                               aProperties.push(i); // collect a's properties
+
+                                               if (!loop && !innerEquiv( a[i], b[i] ) ) {
+                                                       eq = false;
+                                                       break;
+                                               }
+                                       }
+
+                                       callers.pop(); // unstack, we are done
+                                       parents.pop();
+
+                                       for ( i in b ) {
+                                               bProperties.push( i ); // collect b's properties
+                                       }
+
+                                       // Ensures identical properties name
+                                       return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
+                               }
+                       };
+               }());
+
+       innerEquiv = function() { // can take multiple arguments
+               var args = [].slice.apply( arguments );
+               if ( args.length < 2 ) {
+                       return true; // end transition
+               }
+
+               return (function( a, b ) {
+                       if ( a === b ) {
+                               return true; // catch the most you can
+                       } else if ( a === null || b === null || typeof a === "undefined" ||
+                                       typeof b === "undefined" ||
+                                       QUnit.objectType(a) !== QUnit.objectType(b) ) {
+                               return false; // don't lose time with error prone cases
+                       } else {
+                               return bindCallbacks(a, callbacks, [ b, a ]);
+                       }
+
+                       // apply transition with (1..n) arguments
+               }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
+       };
+
+       return innerEquiv;
+}());
 
 /**
- * jsDump
- * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
- * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
- * Date: 5/15/2008
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
+ * http://flesler.blogspot.com Licensed under BSD
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
+ *
  * @projectDescription Advanced and extensible data dumping for Javascript.
  * @version 1.0.0
  * @author Ariel Flesler
@@ -877,166 +1408,397 @@ QUnit.equiv = function () {
  */
 QUnit.jsDump = (function() {
        function quote( str ) {
-               return '"' + str.toString().replace(/"/g, '\\"') + '"';
-       };
+               return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
+       }
        function literal( o ) {
-               return o + '';  
-       };
+               return o + "";
+       }
        function join( pre, arr, post ) {
                var s = jsDump.separator(),
                        base = jsDump.indent(),
                        inner = jsDump.indent(1);
-               if ( arr.join )
-                       arr = arr.join( ',' + s + inner );
-               if ( !arr )
+               if ( arr.join ) {
+                       arr = arr.join( "," + s + inner );
+               }
+               if ( !arr ) {
                        return pre + post;
+               }
                return [ pre, inner + arr, base + post ].join(s);
-       };
-       function array( arr ) {
-               var i = arr.length,     ret = Array(i);                                 
+       }
+       function array( arr, stack ) {
+               var i = arr.length, ret = new Array(i);
                this.up();
-               while ( i-- )
-                       ret[i] = this.parse( arr[i] );                          
+               while ( i-- ) {
+                       ret[i] = this.parse( arr[i] , undefined , stack);
+               }
                this.down();
-               return join( '[', ret, ']' );
-       };
-       
-       var reName = /^function (\w+)/;
-       
-       var jsDump = {
-               parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
-                       var     parser = this.parsers[ type || this.typeOf(obj) ];
-                       type = typeof parser;                   
-                       
-                       return type == 'function' ? parser.call( this, obj ) :
-                                  type == 'string' ? parser :
-                                  this.parsers.error;
-               },
-               typeOf:function( obj ) {
-                       var type;
-                       if ( obj === null ) {
-                               type = "null";
-                       } else if (typeof obj === "undefined") {
-                               type = "undefined";
-                       } else if (QUnit.is("RegExp", obj)) {
-                               type = "regexp";
-                       } else if (QUnit.is("Date", obj)) {
-                               type = "date";
-                       } else if (QUnit.is("Function", obj)) {
-                               type = "function";
-                       } else if (QUnit.is("Array", obj)) {
-                               type = "array";
-                       } else if (QUnit.is("Window", obj) || QUnit.is("global", obj)) {
-                               type = "window";
-                       } else if (QUnit.is("HTMLDocument", obj)) {
-                               type = "document";
-                       } else if (QUnit.is("HTMLCollection", obj) || QUnit.is("NodeList", obj)) {
-                               type = "nodelist";
-                       } else if (/^\[object HTML/.test(Object.prototype.toString.call( obj ))) {
-                               type = "node";
-                       } else {
-                               type = typeof obj;
-                       }
-                       return type;
-               },
-               separator:function() {
-                       return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
-               },
-               indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
-                       if ( !this.multiline )
-                               return '';
-                       var chr = this.indentChar;
-                       if ( this.HTML )
-                               chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');
-                       return Array( this._depth_ + (extra||0) ).join(chr);
-               },
-               up:function( a ) {
-                       this._depth_ += a || 1;
-               },
-               down:function( a ) {
-                       this._depth_ -= a || 1;
-               },
-               setParser:function( name, parser ) {
-                       this.parsers[name] = parser;
-               },
-               // The next 3 are exposed so you can use them
-               quote:quote, 
-               literal:literal,
-               join:join,
-               //
-               _depth_: 1,
-               // This is the list of parsers, to modify them, use jsDump.setParser
-               parsers:{
-                       window: '[Window]',
-                       document: '[Document]',
-                       error:'[ERROR]', //when no parser is found, shouldn't happen
-                       unknown: '[Unknown]',
-                       'null':'null',
-                       undefined:'undefined',
-                       'function':function( fn ) {
-                               var ret = 'function',
-                                       name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
-                               if ( name )
-                                       ret += ' ' + name;
-                               ret += '(';
-                               
-                               ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
-                               return join( ret, this.parse(fn,'functionCode'), '}' );
+               return join( "[", ret, "]" );
+       }
+
+       var reName = /^function (\w+)/,
+               jsDump = {
+                       parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
+                               stack = stack || [ ];
+                               var inStack, res,
+                                       parser = this.parsers[ type || this.typeOf(obj) ];
+
+                               type = typeof parser;
+                               inStack = inArray( obj, stack );
+
+                               if ( inStack != -1 ) {
+                                       return "recursion(" + (inStack - stack.length) + ")";
+                               }
+                               //else
+                               if ( type == "function" )  {
+                                       stack.push( obj );
+                                       res = parser.call( this, obj, stack );
+                                       stack.pop();
+                                       return res;
+                               }
+                               // else
+                               return ( type == "string" ) ? parser : this.parsers.error;
+                       },
+                       typeOf: function( obj ) {
+                               var type;
+                               if ( obj === null ) {
+                                       type = "null";
+                               } else if ( typeof obj === "undefined" ) {
+                                       type = "undefined";
+                               } else if ( QUnit.is( "RegExp", obj) ) {
+                                       type = "regexp";
+                               } else if ( QUnit.is( "Date", obj) ) {
+                                       type = "date";
+                               } else if ( QUnit.is( "Function", obj) ) {
+                                       type = "function";
+                               } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
+                                       type = "window";
+                               } else if ( obj.nodeType === 9 ) {
+                                       type = "document";
+                               } else if ( obj.nodeType ) {
+                                       type = "node";
+                               } else if (
+                                       // native arrays
+                                       toString.call( obj ) === "[object Array]" ||
+                                       // NodeList objects
+                                       ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
+                               ) {
+                                       type = "array";
+                               } else {
+                                       type = typeof obj;
+                               }
+                               return type;
                        },
-                       array: array,
-                       nodelist: array,
-                       arguments: array,
-                       object:function( map ) {
-                               var ret = [ ];
-                               this.up();
-                               for ( var key in map )
-                                       ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
-                               this.down();
-                               return join( '{', ret, '}' );
+                       separator: function() {
+                               return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
                        },
-                       node:function( node ) {
-                               var open = this.HTML ? '&lt;' : '<',
-                                       close = this.HTML ? '&gt;' : '>';
-                                       
-                               var tag = node.nodeName.toLowerCase(),
-                                       ret = open + tag;
-                                       
-                               for ( var a in this.DOMAttrs ) {
-                                       var val = node[this.DOMAttrs[a]];
-                                       if ( val )
-                                               ret += ' ' + a + '=' + this.parse( val, 'attribute' );
+                       indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
+                               if ( !this.multiline ) {
+                                       return "";
                                }
-                               return ret + close + open + '/' + tag + close;
+                               var chr = this.indentChar;
+                               if ( this.HTML ) {
+                                       chr = chr.replace( /\t/g, "   " ).replace( / /g, "&nbsp;" );
+                               }
+                               return new Array( this._depth_ + (extra||0) ).join(chr);
                        },
-                       functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
-                               var l = fn.length;
-                               if ( !l ) return '';                            
-                               
-                               var args = Array(l);
-                               while ( l-- )
-                                       args[l] = String.fromCharCode(97+l);//97 is 'a'
-                               return ' ' + args.join(', ') + ' ';
+                       up: function( a ) {
+                               this._depth_ += a || 1;
                        },
-                       key:quote, //object calls it internally, the key part of an item in a map
-                       functionCode:'[code]', //function calls it internally, it's the content of the function
-                       attribute:quote, //node calls it internally, it's an html attribute value
-                       string:quote,
-                       date:quote,
-                       regexp:literal, //regex
-                       number:literal,
-                       'boolean':literal
-               },
-               DOMAttrs:{//attributes to dump from nodes, name=>realName
-                       id:'id',
-                       name:'name',
-                       'class':'className'
-               },
-               HTML:true,//if true, entities are escaped ( <, >, \t, space and \n )
-               indentChar:'   ',//indentation unit
-               multiline:true //if true, items in a collection, are separated by a \n, else just a space.
-       };
+                       down: function( a ) {
+                               this._depth_ -= a || 1;
+                       },
+                       setParser: function( name, parser ) {
+                               this.parsers[name] = parser;
+                       },
+                       // The next 3 are exposed so you can use them
+                       quote: quote,
+                       literal: literal,
+                       join: join,
+                       //
+                       _depth_: 1,
+                       // This is the list of parsers, to modify them, use jsDump.setParser
+                       parsers: {
+                               window: "[Window]",
+                               document: "[Document]",
+                               error: "[ERROR]", //when no parser is found, shouldn"t happen
+                               unknown: "[Unknown]",
+                               "null": "null",
+                               "undefined": "undefined",
+                               "function": function( fn ) {
+                                       var ret = "function",
+                                               name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
+
+                                       if ( name ) {
+                                               ret += " " + name;
+                                       }
+                                       ret += "( ";
+
+                                       ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
+                                       return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
+                               },
+                               array: array,
+                               nodelist: array,
+                               "arguments": array,
+                               object: function( map, stack ) {
+                                       var ret = [ ], keys, key, val, i;
+                                       QUnit.jsDump.up();
+                                       if ( Object.keys ) {
+                                               keys = Object.keys( map );
+                                       } else {
+                                               keys = [];
+                                               for ( key in map ) {
+                                                       keys.push( key );
+                                               }
+                                       }
+                                       keys.sort();
+                                       for ( i = 0; i < keys.length; i++ ) {
+                                               key = keys[ i ];
+                                               val = map[ key ];
+                                               ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
+                                       }
+                                       QUnit.jsDump.down();
+                                       return join( "{", ret, "}" );
+                               },
+                               node: function( node ) {
+                                       var a, val,
+                                               open = QUnit.jsDump.HTML ? "&lt;" : "<",
+                                               close = QUnit.jsDump.HTML ? "&gt;" : ">",
+                                               tag = node.nodeName.toLowerCase(),
+                                               ret = open + tag;
+
+                                       for ( a in QUnit.jsDump.DOMAttrs ) {
+                                               val = node[ QUnit.jsDump.DOMAttrs[a] ];
+                                               if ( val ) {
+                                                       ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
+                                               }
+                                       }
+                                       return ret + close + open + "/" + tag + close;
+                               },
+                               functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
+                                       var args,
+                                               l = fn.length;
+
+                                       if ( !l ) {
+                                               return "";
+                                       }
+
+                                       args = new Array(l);
+                                       while ( l-- ) {
+                                               args[l] = String.fromCharCode(97+l);//97 is 'a'
+                                       }
+                                       return " " + args.join( ", " ) + " ";
+                               },
+                               key: quote, //object calls it internally, the key part of an item in a map
+                               functionCode: "[code]", //function calls it internally, it's the content of the function
+                               attribute: quote, //node calls it internally, it's an html attribute value
+                               string: quote,
+                               date: quote,
+                               regexp: literal, //regex
+                               number: literal,
+                               "boolean": literal
+                       },
+                       DOMAttrs: {
+                               //attributes to dump from nodes, name=>realName
+                               id: "id",
+                               name: "name",
+                               "class": "className"
+                       },
+                       HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
+                       indentChar: "  ",//indentation unit
+                       multiline: true //if true, items in a collection, are separated by a \n, else just a space.
+               };
 
        return jsDump;
-})();
+}());
+
+// from Sizzle.js
+function getText( elems ) {
+       var i, elem,
+               ret = "";
+
+       for ( i = 0; elems[i]; i++ ) {
+               elem = elems[i];
+
+               // Get the text from text nodes and CDATA nodes
+               if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+                       ret += elem.nodeValue;
+
+               // Traverse everything else, except comment nodes
+               } else if ( elem.nodeType !== 8 ) {
+                       ret += getText( elem.childNodes );
+               }
+       }
+
+       return ret;
+}
+
+// from jquery.js
+function inArray( elem, array ) {
+       if ( array.indexOf ) {
+               return array.indexOf( elem );
+       }
+
+       for ( var i = 0, length = array.length; i < length; i++ ) {
+               if ( array[ i ] === elem ) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+/*
+ * Javascript Diff Algorithm
+ *  By John Resig (http://ejohn.org/)
+ *  Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ *  http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
+ */
+QUnit.diff = (function() {
+       function diff( o, n ) {
+               var i,
+                       ns = {},
+                       os = {};
+
+               for ( i = 0; i < n.length; i++ ) {
+                       if ( ns[ n[i] ] == null ) {
+                               ns[ n[i] ] = {
+                                       rows: [],
+                                       o: null
+                               };
+                       }
+                       ns[ n[i] ].rows.push( i );
+               }
+
+               for ( i = 0; i < o.length; i++ ) {
+                       if ( os[ o[i] ] == null ) {
+                               os[ o[i] ] = {
+                                       rows: [],
+                                       n: null
+                               };
+                       }
+                       os[ o[i] ].rows.push( i );
+               }
+
+               for ( i in ns ) {
+                       if ( !hasOwn.call( ns, i ) ) {
+                               continue;
+                       }
+                       if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
+                               n[ ns[i].rows[0] ] = {
+                                       text: n[ ns[i].rows[0] ],
+                                       row: os[i].rows[0]
+                               };
+                               o[ os[i].rows[0] ] = {
+                                       text: o[ os[i].rows[0] ],
+                                       row: ns[i].rows[0]
+                               };
+                       }
+               }
+
+               for ( i = 0; i < n.length - 1; i++ ) {
+                       if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
+                                               n[ i + 1 ] == o[ n[i].row + 1 ] ) {
+
+                               n[ i + 1 ] = {
+                                       text: n[ i + 1 ],
+                                       row: n[i].row + 1
+                               };
+                               o[ n[i].row + 1 ] = {
+                                       text: o[ n[i].row + 1 ],
+                                       row: i + 1
+                               };
+                       }
+               }
+
+               for ( i = n.length - 1; i > 0; i-- ) {
+                       if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
+                                               n[ i - 1 ] == o[ n[i].row - 1 ]) {
+
+                               n[ i - 1 ] = {
+                                       text: n[ i - 1 ],
+                                       row: n[i].row - 1
+                               };
+                               o[ n[i].row - 1 ] = {
+                                       text: o[ n[i].row - 1 ],
+                                       row: i - 1
+                               };
+                       }
+               }
+
+               return {
+                       o: o,
+                       n: n
+               };
+       }
+
+       return function( o, n ) {
+               o = o.replace( /\s+$/, "" );
+               n = n.replace( /\s+$/, "" );
+
+               var i, pre,
+                       str = "",
+                       out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
+                       oSpace = o.match(/\s+/g),
+                       nSpace = n.match(/\s+/g);
+
+               if ( oSpace == null ) {
+                       oSpace = [ " " ];
+               }
+               else {
+                       oSpace.push( " " );
+               }
+
+               if ( nSpace == null ) {
+                       nSpace = [ " " ];
+               }
+               else {
+                       nSpace.push( " " );
+               }
+
+               if ( out.n.length === 0 ) {
+                       for ( i = 0; i < out.o.length; i++ ) {
+                               str += "<del>" + out.o[i] + oSpace[i] + "</del>";
+                       }
+               }
+               else {
+                       if ( out.n[0].text == null ) {
+                               for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
+                                       str += "<del>" + out.o[n] + oSpace[n] + "</del>";
+                               }
+                       }
+
+                       for ( i = 0; i < out.n.length; i++ ) {
+                               if (out.n[i].text == null) {
+                                       str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
+                               }
+                               else {
+                                       // `pre` initialized at top of scope
+                                       pre = "";
+
+                                       for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
+                                               pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
+                                       }
+                                       str += " " + out.n[i].text + nSpace[i] + pre;
+                               }
+                       }
+               }
+
+               return str;
+       };
+}());
+
+// for CommonJS enviroments, export everything
+if ( typeof exports !== "undefined" ) {
+       extend(exports, QUnit);
+}
 
-})(this);
+// get at whatever the global object is, like window in browsers
+}( (function() {return this;}.call()) ));
\ No newline at end of file