everqueen 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +163 -0
- data/bin/everqueen +18 -0
- data/config/routes.rb +3 -0
- data/lib/everqueen.rb +66 -0
- data/lib/everqueen/application.rb +47 -0
- data/lib/everqueen/assets/elabs.png +0 -0
- data/lib/everqueen/assets/everqueen.scss.css +370 -0
- data/lib/everqueen/assets/jquery.js +152 -0
- data/lib/everqueen/assets/json2.js +25 -0
- data/lib/everqueen/assets/list.js.coffee +35 -0
- data/lib/everqueen/assets/run.js.coffee.erb +39 -0
- data/lib/everqueen/assets/start.js.coffee +6 -0
- data/lib/everqueen/assets/test_helper.js +3 -0
- data/lib/everqueen/cli.rb +29 -0
- data/lib/everqueen/rails.rb +14 -0
- data/lib/everqueen/runner.rb +151 -0
- data/lib/everqueen/server.rb +19 -0
- data/lib/everqueen/suite.rb +32 -0
- data/lib/everqueen/tasks.rb +10 -0
- data/lib/everqueen/test.rb +44 -0
- data/lib/everqueen/version.rb +3 -0
- data/lib/everqueen/views/_test_error.erb +4 -0
- data/lib/everqueen/views/layout.erb +17 -0
- data/lib/everqueen/views/list.erb +19 -0
- data/lib/everqueen/views/run.erb +20 -0
- data/lib/qunit/History.md +330 -0
- data/lib/qunit/README.md +37 -0
- data/lib/qunit/addons/canvas/README.md +16 -0
- data/lib/qunit/addons/canvas/canvas-test.js +76 -0
- data/lib/qunit/addons/canvas/canvas.html +19 -0
- data/lib/qunit/addons/canvas/qunit-canvas.js +6 -0
- data/lib/qunit/addons/close-enough/README.md +17 -0
- data/lib/qunit/addons/close-enough/close-enough-test.js +37 -0
- data/lib/qunit/addons/close-enough/close-enough.html +18 -0
- data/lib/qunit/addons/close-enough/qunit-close-enough.js +32 -0
- data/lib/qunit/addons/composite/README.md +7 -0
- data/lib/qunit/addons/composite/composite-demo-test.html +33 -0
- data/lib/qunit/addons/composite/composite-test.html +20 -0
- data/lib/qunit/addons/composite/composite-test.js +159 -0
- data/lib/qunit/addons/composite/dummy-qunit-test.html +19 -0
- data/lib/qunit/addons/composite/dummy-same-test.html +19 -0
- data/lib/qunit/addons/composite/index.html +35 -0
- data/lib/qunit/addons/composite/qunit-composite.css +13 -0
- data/lib/qunit/addons/composite/qunit-composite.js +82 -0
- data/lib/qunit/addons/step/README.md +18 -0
- data/lib/qunit/addons/step/qunit-step.js +25 -0
- data/lib/qunit/addons/step/step-test.js +13 -0
- data/lib/qunit/addons/step/step.html +19 -0
- data/lib/qunit/package.json +28 -0
- data/lib/qunit/qunit/qunit.css +226 -0
- data/lib/qunit/qunit/qunit.js +1597 -0
- data/lib/qunit/test/headless.html +24 -0
- data/lib/qunit/test/index.html +21 -0
- data/lib/qunit/test/logs.html +17 -0
- data/lib/qunit/test/logs.js +153 -0
- data/lib/qunit/test/same.js +1432 -0
- data/lib/qunit/test/swarminject.js +9 -0
- data/lib/qunit/test/test.js +524 -0
- data/lib/tasks/everqueen.rake +8 -0
- data/spec/everqueen_spec.rb +52 -0
- data/spec/meta_spec.rb +40 -0
- data/spec/runner_spec.rb +32 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/suite1/public/jquery.js +152 -0
- data/spec/suite1/public/styles.css +3 -0
- data/spec/suite1/test/javascripts/bar_test.js +0 -0
- data/spec/suite1/test/javascripts/coffeescript_test.coffee +7 -0
- data/spec/suite1/test/javascripts/failing_test.js +9 -0
- data/spec/suite1/test/javascripts/foo_test.js +0 -0
- data/spec/suite1/test/javascripts/libs/lucid_test.js +0 -0
- data/spec/suite1/test/javascripts/models/game_test.js +0 -0
- data/spec/suite1/test/javascripts/slow_test.coffee +8 -0
- data/spec/suite1/test/javascripts/test_helper.js +1 -0
- data/spec/suite1/test/javascripts/testing_test.js +9 -0
- data/spec/suite1/test/javascripts/transactions_test.js +10 -0
- data/spec/suite1/test/javascripts/with_helper_test.js +4 -0
- data/spec/suite2/config/everqueen.rb +4 -0
- data/spec/suite2/public_html/foo.js +1 -0
- data/spec/suite2/test/awesome_test.js +7 -0
- data/spec/suite2/test/failing_test.js +4 -0
- data/spec/suite_spec.rb +19 -0
- data/spec/test_spec.rb +21 -0
- metadata +295 -0
| @@ -0,0 +1,1597 @@ | |
| 1 | 
            +
            /**
         | 
| 2 | 
            +
             * QUnit v1.2.0 - A JavaScript Unit Testing Framework
         | 
| 3 | 
            +
             *
         | 
| 4 | 
            +
             * http://docs.jquery.com/QUnit
         | 
| 5 | 
            +
             *
         | 
| 6 | 
            +
             * Copyright (c) 2011 John Resig, Jörn Zaefferer
         | 
| 7 | 
            +
             * Dual licensed under the MIT (MIT-LICENSE.txt)
         | 
| 8 | 
            +
             * or GPL (GPL-LICENSE.txt) licenses.
         | 
| 9 | 
            +
             */
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            (function(window) {
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            var defined = {
         | 
| 14 | 
            +
            	setTimeout: typeof window.setTimeout !== "undefined",
         | 
| 15 | 
            +
            	sessionStorage: (function() {
         | 
| 16 | 
            +
            		try {
         | 
| 17 | 
            +
            			return !!sessionStorage.getItem;
         | 
| 18 | 
            +
            		} catch(e) {
         | 
| 19 | 
            +
            			return false;
         | 
| 20 | 
            +
            		}
         | 
| 21 | 
            +
            	})()
         | 
| 22 | 
            +
            };
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            var	testId = 0,
         | 
| 25 | 
            +
            	toString = Object.prototype.toString,
         | 
| 26 | 
            +
            	hasOwn = Object.prototype.hasOwnProperty;
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
         | 
| 29 | 
            +
            	this.name = name;
         | 
| 30 | 
            +
            	this.testName = testName;
         | 
| 31 | 
            +
            	this.expected = expected;
         | 
| 32 | 
            +
            	this.testEnvironmentArg = testEnvironmentArg;
         | 
| 33 | 
            +
            	this.async = async;
         | 
| 34 | 
            +
            	this.callback = callback;
         | 
| 35 | 
            +
            	this.assertions = [];
         | 
| 36 | 
            +
            };
         | 
| 37 | 
            +
            Test.prototype = {
         | 
| 38 | 
            +
            	init: function() {
         | 
| 39 | 
            +
            		var tests = id("qunit-tests");
         | 
| 40 | 
            +
            		if (tests) {
         | 
| 41 | 
            +
            			var b = document.createElement("strong");
         | 
| 42 | 
            +
            				b.innerHTML = "Running " + this.name;
         | 
| 43 | 
            +
            			var li = document.createElement("li");
         | 
| 44 | 
            +
            				li.appendChild( b );
         | 
| 45 | 
            +
            				li.className = "running";
         | 
| 46 | 
            +
            				li.id = this.id = "test-output" + testId++;
         | 
| 47 | 
            +
            			tests.appendChild( li );
         | 
| 48 | 
            +
            		}
         | 
| 49 | 
            +
            	},
         | 
| 50 | 
            +
            	setup: function() {
         | 
| 51 | 
            +
            		if (this.module != config.previousModule) {
         | 
| 52 | 
            +
            			if ( config.previousModule ) {
         | 
| 53 | 
            +
            				runLoggingCallbacks('moduleDone', QUnit, {
         | 
| 54 | 
            +
            					name: config.previousModule,
         | 
| 55 | 
            +
            					failed: config.moduleStats.bad,
         | 
| 56 | 
            +
            					passed: config.moduleStats.all - config.moduleStats.bad,
         | 
| 57 | 
            +
            					total: config.moduleStats.all
         | 
| 58 | 
            +
            				} );
         | 
| 59 | 
            +
            			}
         | 
| 60 | 
            +
            			config.previousModule = this.module;
         | 
| 61 | 
            +
            			config.moduleStats = { all: 0, bad: 0 };
         | 
| 62 | 
            +
            			runLoggingCallbacks( 'moduleStart', QUnit, {
         | 
| 63 | 
            +
            				name: this.module
         | 
| 64 | 
            +
            			} );
         | 
| 65 | 
            +
            		}
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            		config.current = this;
         | 
| 68 | 
            +
            		this.testEnvironment = extend({
         | 
| 69 | 
            +
            			setup: function() {},
         | 
| 70 | 
            +
            			teardown: function() {}
         | 
| 71 | 
            +
            		}, this.moduleTestEnvironment);
         | 
| 72 | 
            +
            		if (this.testEnvironmentArg) {
         | 
| 73 | 
            +
            			extend(this.testEnvironment, this.testEnvironmentArg);
         | 
| 74 | 
            +
            		}
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            		runLoggingCallbacks( 'testStart', QUnit, {
         | 
| 77 | 
            +
            			name: this.testName,
         | 
| 78 | 
            +
            			module: this.module
         | 
| 79 | 
            +
            		});
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            		// allow utility functions to access the current test environment
         | 
| 82 | 
            +
            		// TODO why??
         | 
| 83 | 
            +
            		QUnit.current_testEnvironment = this.testEnvironment;
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            		try {
         | 
| 86 | 
            +
            			if ( !config.pollution ) {
         | 
| 87 | 
            +
            				saveGlobal();
         | 
| 88 | 
            +
            			}
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            			this.testEnvironment.setup.call(this.testEnvironment);
         | 
| 91 | 
            +
            		} catch(e) {
         | 
| 92 | 
            +
            			QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
         | 
| 93 | 
            +
            		}
         | 
| 94 | 
            +
            	},
         | 
| 95 | 
            +
            	run: function() {
         | 
| 96 | 
            +
            		config.current = this;
         | 
| 97 | 
            +
            		if ( this.async ) {
         | 
| 98 | 
            +
            			QUnit.stop();
         | 
| 99 | 
            +
            		}
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            		if ( config.notrycatch ) {
         | 
| 102 | 
            +
            			this.callback.call(this.testEnvironment);
         | 
| 103 | 
            +
            			return;
         | 
| 104 | 
            +
            		}
         | 
| 105 | 
            +
            		try {
         | 
| 106 | 
            +
            			this.callback.call(this.testEnvironment);
         | 
| 107 | 
            +
            		} catch(e) {
         | 
| 108 | 
            +
            			fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
         | 
| 109 | 
            +
            			QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
         | 
| 110 | 
            +
            			// else next test will carry the responsibility
         | 
| 111 | 
            +
            			saveGlobal();
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            			// Restart the tests if they're blocking
         | 
| 114 | 
            +
            			if ( config.blocking ) {
         | 
| 115 | 
            +
            				QUnit.start();
         | 
| 116 | 
            +
            			}
         | 
| 117 | 
            +
            		}
         | 
| 118 | 
            +
            	},
         | 
| 119 | 
            +
            	teardown: function() {
         | 
| 120 | 
            +
            		config.current = this;
         | 
| 121 | 
            +
            		try {
         | 
| 122 | 
            +
            			this.testEnvironment.teardown.call(this.testEnvironment);
         | 
| 123 | 
            +
            			checkPollution();
         | 
| 124 | 
            +
            		} catch(e) {
         | 
| 125 | 
            +
            			QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
         | 
| 126 | 
            +
            		}
         | 
| 127 | 
            +
            	},
         | 
| 128 | 
            +
            	finish: function() {
         | 
| 129 | 
            +
            		config.current = this;
         | 
| 130 | 
            +
            		if ( this.expected != null && this.expected != this.assertions.length ) {
         | 
| 131 | 
            +
            			QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
         | 
| 132 | 
            +
            		}
         | 
| 133 | 
            +
             | 
| 134 | 
            +
            		var good = 0, bad = 0,
         | 
| 135 | 
            +
            			tests = id("qunit-tests");
         | 
| 136 | 
            +
             | 
| 137 | 
            +
            		config.stats.all += this.assertions.length;
         | 
| 138 | 
            +
            		config.moduleStats.all += this.assertions.length;
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            		if ( tests ) {
         | 
| 141 | 
            +
            			var ol = document.createElement("ol");
         | 
| 142 | 
            +
             | 
| 143 | 
            +
            			for ( var i = 0; i < this.assertions.length; i++ ) {
         | 
| 144 | 
            +
            				var assertion = this.assertions[i];
         | 
| 145 | 
            +
             | 
| 146 | 
            +
            				var li = document.createElement("li");
         | 
| 147 | 
            +
            				li.className = assertion.result ? "pass" : "fail";
         | 
| 148 | 
            +
            				li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
         | 
| 149 | 
            +
            				ol.appendChild( li );
         | 
| 150 | 
            +
             | 
| 151 | 
            +
            				if ( assertion.result ) {
         | 
| 152 | 
            +
            					good++;
         | 
| 153 | 
            +
            				} else {
         | 
| 154 | 
            +
            					bad++;
         | 
| 155 | 
            +
            					config.stats.bad++;
         | 
| 156 | 
            +
            					config.moduleStats.bad++;
         | 
| 157 | 
            +
            				}
         | 
| 158 | 
            +
            			}
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            			// store result when possible
         | 
| 161 | 
            +
            			if ( QUnit.config.reorder && defined.sessionStorage ) {
         | 
| 162 | 
            +
            				if (bad) {
         | 
| 163 | 
            +
            					sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
         | 
| 164 | 
            +
            				} else {
         | 
| 165 | 
            +
            					sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
         | 
| 166 | 
            +
            				}
         | 
| 167 | 
            +
            			}
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            			if (bad == 0) {
         | 
| 170 | 
            +
            				ol.style.display = "none";
         | 
| 171 | 
            +
            			}
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            			var b = document.createElement("strong");
         | 
| 174 | 
            +
            			b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
         | 
| 175 | 
            +
             | 
| 176 | 
            +
            			var a = document.createElement("a");
         | 
| 177 | 
            +
            			a.innerHTML = "Rerun";
         | 
| 178 | 
            +
            			a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
         | 
| 179 | 
            +
             | 
| 180 | 
            +
            			addEvent(b, "click", function() {
         | 
| 181 | 
            +
            				var next = b.nextSibling.nextSibling,
         | 
| 182 | 
            +
            					display = next.style.display;
         | 
| 183 | 
            +
            				next.style.display = display === "none" ? "block" : "none";
         | 
| 184 | 
            +
            			});
         | 
| 185 | 
            +
             | 
| 186 | 
            +
            			addEvent(b, "dblclick", function(e) {
         | 
| 187 | 
            +
            				var target = e && e.target ? e.target : window.event.srcElement;
         | 
| 188 | 
            +
            				if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
         | 
| 189 | 
            +
            					target = target.parentNode;
         | 
| 190 | 
            +
            				}
         | 
| 191 | 
            +
            				if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
         | 
| 192 | 
            +
            					window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
         | 
| 193 | 
            +
            				}
         | 
| 194 | 
            +
            			});
         | 
| 195 | 
            +
             | 
| 196 | 
            +
            			var li = id(this.id);
         | 
| 197 | 
            +
            			li.className = bad ? "fail" : "pass";
         | 
| 198 | 
            +
            			li.removeChild( li.firstChild );
         | 
| 199 | 
            +
            			li.appendChild( b );
         | 
| 200 | 
            +
            			li.appendChild( a );
         | 
| 201 | 
            +
            			li.appendChild( ol );
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            		} else {
         | 
| 204 | 
            +
            			for ( var i = 0; i < this.assertions.length; i++ ) {
         | 
| 205 | 
            +
            				if ( !this.assertions[i].result ) {
         | 
| 206 | 
            +
            					bad++;
         | 
| 207 | 
            +
            					config.stats.bad++;
         | 
| 208 | 
            +
            					config.moduleStats.bad++;
         | 
| 209 | 
            +
            				}
         | 
| 210 | 
            +
            			}
         | 
| 211 | 
            +
            		}
         | 
| 212 | 
            +
             | 
| 213 | 
            +
            		try {
         | 
| 214 | 
            +
            			QUnit.reset();
         | 
| 215 | 
            +
            		} catch(e) {
         | 
| 216 | 
            +
            			fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
         | 
| 217 | 
            +
            		}
         | 
| 218 | 
            +
             | 
| 219 | 
            +
            		runLoggingCallbacks( 'testDone', QUnit, {
         | 
| 220 | 
            +
            			name: this.testName,
         | 
| 221 | 
            +
            			module: this.module,
         | 
| 222 | 
            +
            			failed: bad,
         | 
| 223 | 
            +
            			passed: this.assertions.length - bad,
         | 
| 224 | 
            +
            			total: this.assertions.length
         | 
| 225 | 
            +
            		} );
         | 
| 226 | 
            +
            	},
         | 
| 227 | 
            +
             | 
| 228 | 
            +
            	queue: function() {
         | 
| 229 | 
            +
            		var test = this;
         | 
| 230 | 
            +
            		synchronize(function() {
         | 
| 231 | 
            +
            			test.init();
         | 
| 232 | 
            +
            		});
         | 
| 233 | 
            +
            		function run() {
         | 
| 234 | 
            +
            			// each of these can by async
         | 
| 235 | 
            +
            			synchronize(function() {
         | 
| 236 | 
            +
            				test.setup();
         | 
| 237 | 
            +
            			});
         | 
| 238 | 
            +
            			synchronize(function() {
         | 
| 239 | 
            +
            				test.run();
         | 
| 240 | 
            +
            			});
         | 
| 241 | 
            +
            			synchronize(function() {
         | 
| 242 | 
            +
            				test.teardown();
         | 
| 243 | 
            +
            			});
         | 
| 244 | 
            +
            			synchronize(function() {
         | 
| 245 | 
            +
            				test.finish();
         | 
| 246 | 
            +
            			});
         | 
| 247 | 
            +
            		}
         | 
| 248 | 
            +
            		// defer when previous test run passed, if storage is available
         | 
| 249 | 
            +
            		var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
         | 
| 250 | 
            +
            		if (bad) {
         | 
| 251 | 
            +
            			run();
         | 
| 252 | 
            +
            		} else {
         | 
| 253 | 
            +
            			synchronize(run, true);
         | 
| 254 | 
            +
            		};
         | 
| 255 | 
            +
            	}
         | 
| 256 | 
            +
             | 
| 257 | 
            +
            };
         | 
| 258 | 
            +
             | 
| 259 | 
            +
            var QUnit = {
         | 
| 260 | 
            +
             | 
| 261 | 
            +
            	// call on start of module test to prepend name to all tests
         | 
| 262 | 
            +
            	module: function(name, testEnvironment) {
         | 
| 263 | 
            +
            		config.currentModule = name;
         | 
| 264 | 
            +
            		config.currentModuleTestEnviroment = testEnvironment;
         | 
| 265 | 
            +
            	},
         | 
| 266 | 
            +
             | 
| 267 | 
            +
            	asyncTest: function(testName, expected, callback) {
         | 
| 268 | 
            +
            		if ( arguments.length === 2 ) {
         | 
| 269 | 
            +
            			callback = expected;
         | 
| 270 | 
            +
            			expected = null;
         | 
| 271 | 
            +
            		}
         | 
| 272 | 
            +
             | 
| 273 | 
            +
            		QUnit.test(testName, expected, callback, true);
         | 
| 274 | 
            +
            	},
         | 
| 275 | 
            +
             | 
| 276 | 
            +
            	test: function(testName, expected, callback, async) {
         | 
| 277 | 
            +
            		var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
         | 
| 278 | 
            +
             | 
| 279 | 
            +
            		if ( arguments.length === 2 ) {
         | 
| 280 | 
            +
            			callback = expected;
         | 
| 281 | 
            +
            			expected = null;
         | 
| 282 | 
            +
            		}
         | 
| 283 | 
            +
            		// is 2nd argument a testEnvironment?
         | 
| 284 | 
            +
            		if ( expected && typeof expected === 'object') {
         | 
| 285 | 
            +
            			testEnvironmentArg = expected;
         | 
| 286 | 
            +
            			expected = null;
         | 
| 287 | 
            +
            		}
         | 
| 288 | 
            +
             | 
| 289 | 
            +
            		if ( config.currentModule ) {
         | 
| 290 | 
            +
            			name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
         | 
| 291 | 
            +
            		}
         | 
| 292 | 
            +
             | 
| 293 | 
            +
            		if ( !validTest(config.currentModule + ": " + testName) ) {
         | 
| 294 | 
            +
            			return;
         | 
| 295 | 
            +
            		}
         | 
| 296 | 
            +
             | 
| 297 | 
            +
            		var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
         | 
| 298 | 
            +
            		test.module = config.currentModule;
         | 
| 299 | 
            +
            		test.moduleTestEnvironment = config.currentModuleTestEnviroment;
         | 
| 300 | 
            +
            		test.queue();
         | 
| 301 | 
            +
            	},
         | 
| 302 | 
            +
             | 
| 303 | 
            +
            	/**
         | 
| 304 | 
            +
            	 * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
         | 
| 305 | 
            +
            	 */
         | 
| 306 | 
            +
            	expect: function(asserts) {
         | 
| 307 | 
            +
            		config.current.expected = asserts;
         | 
| 308 | 
            +
            	},
         | 
| 309 | 
            +
             | 
| 310 | 
            +
            	/**
         | 
| 311 | 
            +
            	 * Asserts true.
         | 
| 312 | 
            +
            	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
         | 
| 313 | 
            +
            	 */
         | 
| 314 | 
            +
            	ok: function(a, msg) {
         | 
| 315 | 
            +
            		a = !!a;
         | 
| 316 | 
            +
            		var details = {
         | 
| 317 | 
            +
            			result: a,
         | 
| 318 | 
            +
            			message: msg
         | 
| 319 | 
            +
            		};
         | 
| 320 | 
            +
            		msg = escapeInnerText(msg);
         | 
| 321 | 
            +
            		runLoggingCallbacks( 'log', QUnit, details );
         | 
| 322 | 
            +
            		config.current.assertions.push({
         | 
| 323 | 
            +
            			result: a,
         | 
| 324 | 
            +
            			message: msg
         | 
| 325 | 
            +
            		});
         | 
| 326 | 
            +
            	},
         | 
| 327 | 
            +
             | 
| 328 | 
            +
            	/**
         | 
| 329 | 
            +
            	 * Checks that the first two arguments are equal, with an optional message.
         | 
| 330 | 
            +
            	 * Prints out both actual and expected values.
         | 
| 331 | 
            +
            	 *
         | 
| 332 | 
            +
            	 * Prefered to ok( actual == expected, message )
         | 
| 333 | 
            +
            	 *
         | 
| 334 | 
            +
            	 * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
         | 
| 335 | 
            +
            	 *
         | 
| 336 | 
            +
            	 * @param Object actual
         | 
| 337 | 
            +
            	 * @param Object expected
         | 
| 338 | 
            +
            	 * @param String message (optional)
         | 
| 339 | 
            +
            	 */
         | 
| 340 | 
            +
            	equal: function(actual, expected, message) {
         | 
| 341 | 
            +
            		QUnit.push(expected == actual, actual, expected, message);
         | 
| 342 | 
            +
            	},
         | 
| 343 | 
            +
             | 
| 344 | 
            +
            	notEqual: function(actual, expected, message) {
         | 
| 345 | 
            +
            		QUnit.push(expected != actual, actual, expected, message);
         | 
| 346 | 
            +
            	},
         | 
| 347 | 
            +
             | 
| 348 | 
            +
            	deepEqual: function(actual, expected, message) {
         | 
| 349 | 
            +
            		QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
         | 
| 350 | 
            +
            	},
         | 
| 351 | 
            +
             | 
| 352 | 
            +
            	notDeepEqual: function(actual, expected, message) {
         | 
| 353 | 
            +
            		QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
         | 
| 354 | 
            +
            	},
         | 
| 355 | 
            +
             | 
| 356 | 
            +
            	strictEqual: function(actual, expected, message) {
         | 
| 357 | 
            +
            		QUnit.push(expected === actual, actual, expected, message);
         | 
| 358 | 
            +
            	},
         | 
| 359 | 
            +
             | 
| 360 | 
            +
            	notStrictEqual: function(actual, expected, message) {
         | 
| 361 | 
            +
            		QUnit.push(expected !== actual, actual, expected, message);
         | 
| 362 | 
            +
            	},
         | 
| 363 | 
            +
             | 
| 364 | 
            +
            	raises: function(block, expected, message) {
         | 
| 365 | 
            +
            		var actual, ok = false;
         | 
| 366 | 
            +
             | 
| 367 | 
            +
            		if (typeof expected === 'string') {
         | 
| 368 | 
            +
            			message = expected;
         | 
| 369 | 
            +
            			expected = null;
         | 
| 370 | 
            +
            		}
         | 
| 371 | 
            +
             | 
| 372 | 
            +
            		try {
         | 
| 373 | 
            +
            			block();
         | 
| 374 | 
            +
            		} catch (e) {
         | 
| 375 | 
            +
            			actual = e;
         | 
| 376 | 
            +
            		}
         | 
| 377 | 
            +
             | 
| 378 | 
            +
            		if (actual) {
         | 
| 379 | 
            +
            			// we don't want to validate thrown error
         | 
| 380 | 
            +
            			if (!expected) {
         | 
| 381 | 
            +
            				ok = true;
         | 
| 382 | 
            +
            			// expected is a regexp
         | 
| 383 | 
            +
            			} else if (QUnit.objectType(expected) === "regexp") {
         | 
| 384 | 
            +
            				ok = expected.test(actual);
         | 
| 385 | 
            +
            			// expected is a constructor
         | 
| 386 | 
            +
            			} else if (actual instanceof expected) {
         | 
| 387 | 
            +
            				ok = true;
         | 
| 388 | 
            +
            			// expected is a validation function which returns true is validation passed
         | 
| 389 | 
            +
            			} else if (expected.call({}, actual) === true) {
         | 
| 390 | 
            +
            				ok = true;
         | 
| 391 | 
            +
            			}
         | 
| 392 | 
            +
            		}
         | 
| 393 | 
            +
             | 
| 394 | 
            +
            		QUnit.ok(ok, message);
         | 
| 395 | 
            +
            	},
         | 
| 396 | 
            +
             | 
| 397 | 
            +
            	start: function(count) {
         | 
| 398 | 
            +
            		config.semaphore -= count || 1;
         | 
| 399 | 
            +
            		if (config.semaphore > 0) {
         | 
| 400 | 
            +
            			// don't start until equal number of stop-calls
         | 
| 401 | 
            +
            			return;
         | 
| 402 | 
            +
            		}
         | 
| 403 | 
            +
            		if (config.semaphore < 0) {
         | 
| 404 | 
            +
            			// ignore if start is called more often then stop
         | 
| 405 | 
            +
            			config.semaphore = 0;
         | 
| 406 | 
            +
            		}
         | 
| 407 | 
            +
            		// A slight delay, to avoid any current callbacks
         | 
| 408 | 
            +
            		if ( defined.setTimeout ) {
         | 
| 409 | 
            +
            			window.setTimeout(function() {
         | 
| 410 | 
            +
            				if (config.semaphore > 0) {
         | 
| 411 | 
            +
            					return;
         | 
| 412 | 
            +
            				}
         | 
| 413 | 
            +
            				if ( config.timeout ) {
         | 
| 414 | 
            +
            					clearTimeout(config.timeout);
         | 
| 415 | 
            +
            				}
         | 
| 416 | 
            +
             | 
| 417 | 
            +
            				config.blocking = false;
         | 
| 418 | 
            +
            				process(true);
         | 
| 419 | 
            +
            			}, 13);
         | 
| 420 | 
            +
            		} else {
         | 
| 421 | 
            +
            			config.blocking = false;
         | 
| 422 | 
            +
            			process(true);
         | 
| 423 | 
            +
            		}
         | 
| 424 | 
            +
            	},
         | 
| 425 | 
            +
             | 
| 426 | 
            +
            	stop: function(count) {
         | 
| 427 | 
            +
            		config.semaphore += count || 1;
         | 
| 428 | 
            +
            		config.blocking = true;
         | 
| 429 | 
            +
             | 
| 430 | 
            +
            		if ( config.testTimeout && defined.setTimeout ) {
         | 
| 431 | 
            +
            			clearTimeout(config.timeout);
         | 
| 432 | 
            +
            			config.timeout = window.setTimeout(function() {
         | 
| 433 | 
            +
            				QUnit.ok( false, "Test timed out" );
         | 
| 434 | 
            +
            				config.semaphore = 1;
         | 
| 435 | 
            +
            				QUnit.start();
         | 
| 436 | 
            +
            			}, config.testTimeout);
         | 
| 437 | 
            +
            		}
         | 
| 438 | 
            +
            	}
         | 
| 439 | 
            +
            };
         | 
| 440 | 
            +
             | 
| 441 | 
            +
            //We want access to the constructor's prototype
         | 
| 442 | 
            +
            (function() {
         | 
| 443 | 
            +
            	function F(){};
         | 
| 444 | 
            +
            	F.prototype = QUnit;
         | 
| 445 | 
            +
            	QUnit = new F();
         | 
| 446 | 
            +
            	//Make F QUnit's constructor so that we can add to the prototype later
         | 
| 447 | 
            +
            	QUnit.constructor = F;
         | 
| 448 | 
            +
            })();
         | 
| 449 | 
            +
             | 
| 450 | 
            +
            // Backwards compatibility, deprecated
         | 
| 451 | 
            +
            QUnit.equals = QUnit.equal;
         | 
| 452 | 
            +
            QUnit.same = QUnit.deepEqual;
         | 
| 453 | 
            +
             | 
| 454 | 
            +
            // Maintain internal state
         | 
| 455 | 
            +
            var config = {
         | 
| 456 | 
            +
            	// The queue of tests to run
         | 
| 457 | 
            +
            	queue: [],
         | 
| 458 | 
            +
             | 
| 459 | 
            +
            	// block until document ready
         | 
| 460 | 
            +
            	blocking: true,
         | 
| 461 | 
            +
             | 
| 462 | 
            +
            	// when enabled, show only failing tests
         | 
| 463 | 
            +
            	// gets persisted through sessionStorage and can be changed in UI via checkbox
         | 
| 464 | 
            +
            	hidepassed: false,
         | 
| 465 | 
            +
             | 
| 466 | 
            +
            	// by default, run previously failed tests first
         | 
| 467 | 
            +
            	// very useful in combination with "Hide passed tests" checked
         | 
| 468 | 
            +
            	reorder: true,
         | 
| 469 | 
            +
             | 
| 470 | 
            +
            	// by default, modify document.title when suite is done
         | 
| 471 | 
            +
            	altertitle: true,
         | 
| 472 | 
            +
             | 
| 473 | 
            +
            	urlConfig: ['noglobals', 'notrycatch'],
         | 
| 474 | 
            +
             | 
| 475 | 
            +
            	//logging callback queues
         | 
| 476 | 
            +
            	begin: [],
         | 
| 477 | 
            +
            	done: [],
         | 
| 478 | 
            +
            	log: [],
         | 
| 479 | 
            +
            	testStart: [],
         | 
| 480 | 
            +
            	testDone: [],
         | 
| 481 | 
            +
            	moduleStart: [],
         | 
| 482 | 
            +
            	moduleDone: []
         | 
| 483 | 
            +
            };
         | 
| 484 | 
            +
             | 
| 485 | 
            +
            // Load paramaters
         | 
| 486 | 
            +
            (function() {
         | 
| 487 | 
            +
            	var location = window.location || { search: "", protocol: "file:" },
         | 
| 488 | 
            +
            		params = location.search.slice( 1 ).split( "&" ),
         | 
| 489 | 
            +
            		length = params.length,
         | 
| 490 | 
            +
            		urlParams = {},
         | 
| 491 | 
            +
            		current;
         | 
| 492 | 
            +
             | 
| 493 | 
            +
            	if ( params[ 0 ] ) {
         | 
| 494 | 
            +
            		for ( var i = 0; i < length; i++ ) {
         | 
| 495 | 
            +
            			current = params[ i ].split( "=" );
         | 
| 496 | 
            +
            			current[ 0 ] = decodeURIComponent( current[ 0 ] );
         | 
| 497 | 
            +
            			// allow just a key to turn on a flag, e.g., test.html?noglobals
         | 
| 498 | 
            +
            			current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
         | 
| 499 | 
            +
            			urlParams[ current[ 0 ] ] = current[ 1 ];
         | 
| 500 | 
            +
            		}
         | 
| 501 | 
            +
            	}
         | 
| 502 | 
            +
             | 
| 503 | 
            +
            	QUnit.urlParams = urlParams;
         | 
| 504 | 
            +
            	config.filter = urlParams.filter;
         | 
| 505 | 
            +
             | 
| 506 | 
            +
            	// Figure out if we're running the tests from a server or not
         | 
| 507 | 
            +
            	QUnit.isLocal = !!(location.protocol === 'file:');
         | 
| 508 | 
            +
            })();
         | 
| 509 | 
            +
             | 
| 510 | 
            +
            // Expose the API as global variables, unless an 'exports'
         | 
| 511 | 
            +
            // object exists, in that case we assume we're in CommonJS
         | 
| 512 | 
            +
            if ( typeof exports === "undefined" || typeof require === "undefined" ) {
         | 
| 513 | 
            +
            	extend(window, QUnit);
         | 
| 514 | 
            +
            	window.QUnit = QUnit;
         | 
| 515 | 
            +
            } else {
         | 
| 516 | 
            +
            	extend(exports, QUnit);
         | 
| 517 | 
            +
            	exports.QUnit = QUnit;
         | 
| 518 | 
            +
            }
         | 
| 519 | 
            +
             | 
| 520 | 
            +
            // define these after exposing globals to keep them in these QUnit namespace only
         | 
| 521 | 
            +
            extend(QUnit, {
         | 
| 522 | 
            +
            	config: config,
         | 
| 523 | 
            +
             | 
| 524 | 
            +
            	// Initialize the configuration options
         | 
| 525 | 
            +
            	init: function() {
         | 
| 526 | 
            +
            		extend(config, {
         | 
| 527 | 
            +
            			stats: { all: 0, bad: 0 },
         | 
| 528 | 
            +
            			moduleStats: { all: 0, bad: 0 },
         | 
| 529 | 
            +
            			started: +new Date,
         | 
| 530 | 
            +
            			updateRate: 1000,
         | 
| 531 | 
            +
            			blocking: false,
         | 
| 532 | 
            +
            			autostart: true,
         | 
| 533 | 
            +
            			autorun: false,
         | 
| 534 | 
            +
            			filter: "",
         | 
| 535 | 
            +
            			queue: [],
         | 
| 536 | 
            +
            			semaphore: 0
         | 
| 537 | 
            +
            		});
         | 
| 538 | 
            +
             | 
| 539 | 
            +
            		var tests = id( "qunit-tests" ),
         | 
| 540 | 
            +
            			banner = id( "qunit-banner" ),
         | 
| 541 | 
            +
            			result = id( "qunit-testresult" );
         | 
| 542 | 
            +
             | 
| 543 | 
            +
            		if ( tests ) {
         | 
| 544 | 
            +
            			tests.innerHTML = "";
         | 
| 545 | 
            +
            		}
         | 
| 546 | 
            +
             | 
| 547 | 
            +
            		if ( banner ) {
         | 
| 548 | 
            +
            			banner.className = "";
         | 
| 549 | 
            +
            		}
         | 
| 550 | 
            +
             | 
| 551 | 
            +
            		if ( result ) {
         | 
| 552 | 
            +
            			result.parentNode.removeChild( result );
         | 
| 553 | 
            +
            		}
         | 
| 554 | 
            +
             | 
| 555 | 
            +
            		if ( tests ) {
         | 
| 556 | 
            +
            			result = document.createElement( "p" );
         | 
| 557 | 
            +
            			result.id = "qunit-testresult";
         | 
| 558 | 
            +
            			result.className = "result";
         | 
| 559 | 
            +
            			tests.parentNode.insertBefore( result, tests );
         | 
| 560 | 
            +
            			result.innerHTML = 'Running...<br/> ';
         | 
| 561 | 
            +
            		}
         | 
| 562 | 
            +
            	},
         | 
| 563 | 
            +
             | 
| 564 | 
            +
            	/**
         | 
| 565 | 
            +
            	 * Resets the test setup. Useful for tests that modify the DOM.
         | 
| 566 | 
            +
            	 *
         | 
| 567 | 
            +
            	 * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
         | 
| 568 | 
            +
            	 */
         | 
| 569 | 
            +
            	reset: function() {
         | 
| 570 | 
            +
            		if ( window.jQuery ) {
         | 
| 571 | 
            +
            			jQuery( "#qunit-fixture" ).html( config.fixture );
         | 
| 572 | 
            +
            		} else {
         | 
| 573 | 
            +
            			var main = id( 'qunit-fixture' );
         | 
| 574 | 
            +
            			if ( main ) {
         | 
| 575 | 
            +
            				main.innerHTML = config.fixture;
         | 
| 576 | 
            +
            			}
         | 
| 577 | 
            +
            		}
         | 
| 578 | 
            +
            	},
         | 
| 579 | 
            +
             | 
| 580 | 
            +
            	/**
         | 
| 581 | 
            +
            	 * Trigger an event on an element.
         | 
| 582 | 
            +
            	 *
         | 
| 583 | 
            +
            	 * @example triggerEvent( document.body, "click" );
         | 
| 584 | 
            +
            	 *
         | 
| 585 | 
            +
            	 * @param DOMElement elem
         | 
| 586 | 
            +
            	 * @param String type
         | 
| 587 | 
            +
            	 */
         | 
| 588 | 
            +
            	triggerEvent: function( elem, type, event ) {
         | 
| 589 | 
            +
            		if ( document.createEvent ) {
         | 
| 590 | 
            +
            			event = document.createEvent("MouseEvents");
         | 
| 591 | 
            +
            			event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
         | 
| 592 | 
            +
            				0, 0, 0, 0, 0, false, false, false, false, 0, null);
         | 
| 593 | 
            +
            			elem.dispatchEvent( event );
         | 
| 594 | 
            +
             | 
| 595 | 
            +
            		} else if ( elem.fireEvent ) {
         | 
| 596 | 
            +
            			elem.fireEvent("on"+type);
         | 
| 597 | 
            +
            		}
         | 
| 598 | 
            +
            	},
         | 
| 599 | 
            +
             | 
| 600 | 
            +
            	// Safe object type checking
         | 
| 601 | 
            +
            	is: function( type, obj ) {
         | 
| 602 | 
            +
            		return QUnit.objectType( obj ) == type;
         | 
| 603 | 
            +
            	},
         | 
| 604 | 
            +
             | 
| 605 | 
            +
            	objectType: function( obj ) {
         | 
| 606 | 
            +
            		if (typeof obj === "undefined") {
         | 
| 607 | 
            +
            				return "undefined";
         | 
| 608 | 
            +
             | 
| 609 | 
            +
            		// consider: typeof null === object
         | 
| 610 | 
            +
            		}
         | 
| 611 | 
            +
            		if (obj === null) {
         | 
| 612 | 
            +
            				return "null";
         | 
| 613 | 
            +
            		}
         | 
| 614 | 
            +
             | 
| 615 | 
            +
            		var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || '';
         | 
| 616 | 
            +
             | 
| 617 | 
            +
            		switch (type) {
         | 
| 618 | 
            +
            				case 'Number':
         | 
| 619 | 
            +
            						if (isNaN(obj)) {
         | 
| 620 | 
            +
            								return "nan";
         | 
| 621 | 
            +
            						} else {
         | 
| 622 | 
            +
            								return "number";
         | 
| 623 | 
            +
            						}
         | 
| 624 | 
            +
            				case 'String':
         | 
| 625 | 
            +
            				case 'Boolean':
         | 
| 626 | 
            +
            				case 'Array':
         | 
| 627 | 
            +
            				case 'Date':
         | 
| 628 | 
            +
            				case 'RegExp':
         | 
| 629 | 
            +
            				case 'Function':
         | 
| 630 | 
            +
            						return type.toLowerCase();
         | 
| 631 | 
            +
            		}
         | 
| 632 | 
            +
            		if (typeof obj === "object") {
         | 
| 633 | 
            +
            				return "object";
         | 
| 634 | 
            +
            		}
         | 
| 635 | 
            +
            		return undefined;
         | 
| 636 | 
            +
            	},
         | 
| 637 | 
            +
             | 
| 638 | 
            +
            	push: function(result, actual, expected, message) {
         | 
| 639 | 
            +
            		var details = {
         | 
| 640 | 
            +
            			result: result,
         | 
| 641 | 
            +
            			message: message,
         | 
| 642 | 
            +
            			actual: actual,
         | 
| 643 | 
            +
            			expected: expected
         | 
| 644 | 
            +
            		};
         | 
| 645 | 
            +
             | 
| 646 | 
            +
            		message = escapeInnerText(message) || (result ? "okay" : "failed");
         | 
| 647 | 
            +
            		message = '<span class="test-message">' + message + "</span>";
         | 
| 648 | 
            +
            		expected = escapeInnerText(QUnit.jsDump.parse(expected));
         | 
| 649 | 
            +
            		actual = escapeInnerText(QUnit.jsDump.parse(actual));
         | 
| 650 | 
            +
            		var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
         | 
| 651 | 
            +
            		if (actual != expected) {
         | 
| 652 | 
            +
            			output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
         | 
| 653 | 
            +
            			output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
         | 
| 654 | 
            +
            		}
         | 
| 655 | 
            +
            		if (!result) {
         | 
| 656 | 
            +
            			var source = sourceFromStacktrace();
         | 
| 657 | 
            +
            			if (source) {
         | 
| 658 | 
            +
            				details.source = source;
         | 
| 659 | 
            +
            				output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
         | 
| 660 | 
            +
            			}
         | 
| 661 | 
            +
            		}
         | 
| 662 | 
            +
            		output += "</table>";
         | 
| 663 | 
            +
             | 
| 664 | 
            +
            		runLoggingCallbacks( 'log', QUnit, details );
         | 
| 665 | 
            +
             | 
| 666 | 
            +
            		config.current.assertions.push({
         | 
| 667 | 
            +
            			result: !!result,
         | 
| 668 | 
            +
            			message: output
         | 
| 669 | 
            +
            		});
         | 
| 670 | 
            +
            	},
         | 
| 671 | 
            +
             | 
| 672 | 
            +
            	url: function( params ) {
         | 
| 673 | 
            +
            		params = extend( extend( {}, QUnit.urlParams ), params );
         | 
| 674 | 
            +
            		var querystring = "?",
         | 
| 675 | 
            +
            			key;
         | 
| 676 | 
            +
            		for ( key in params ) {
         | 
| 677 | 
            +
            			if ( !hasOwn.call( params, key ) ) {
         | 
| 678 | 
            +
            				continue;
         | 
| 679 | 
            +
            			}
         | 
| 680 | 
            +
            			querystring += encodeURIComponent( key ) + "=" +
         | 
| 681 | 
            +
            				encodeURIComponent( params[ key ] ) + "&";
         | 
| 682 | 
            +
            		}
         | 
| 683 | 
            +
            		return window.location.pathname + querystring.slice( 0, -1 );
         | 
| 684 | 
            +
            	},
         | 
| 685 | 
            +
             | 
| 686 | 
            +
            	extend: extend,
         | 
| 687 | 
            +
            	id: id,
         | 
| 688 | 
            +
            	addEvent: addEvent
         | 
| 689 | 
            +
            });
         | 
| 690 | 
            +
             | 
| 691 | 
            +
            //QUnit.constructor is set to the empty F() above so that we can add to it's prototype later
         | 
| 692 | 
            +
            //Doing this allows us to tell if the following methods have been overwritten on the actual
         | 
| 693 | 
            +
            //QUnit object, which is a deprecated way of using the callbacks.
         | 
| 694 | 
            +
            extend(QUnit.constructor.prototype, {
         | 
| 695 | 
            +
            	// Logging callbacks; all receive a single argument with the listed properties
         | 
| 696 | 
            +
            	// run test/logs.html for any related changes
         | 
| 697 | 
            +
            	begin: registerLoggingCallback('begin'),
         | 
| 698 | 
            +
            	// done: { failed, passed, total, runtime }
         | 
| 699 | 
            +
            	done: registerLoggingCallback('done'),
         | 
| 700 | 
            +
            	// log: { result, actual, expected, message }
         | 
| 701 | 
            +
            	log: registerLoggingCallback('log'),
         | 
| 702 | 
            +
            	// testStart: { name }
         | 
| 703 | 
            +
            	testStart: registerLoggingCallback('testStart'),
         | 
| 704 | 
            +
            	// testDone: { name, failed, passed, total }
         | 
| 705 | 
            +
            	testDone: registerLoggingCallback('testDone'),
         | 
| 706 | 
            +
            	// moduleStart: { name }
         | 
| 707 | 
            +
            	moduleStart: registerLoggingCallback('moduleStart'),
         | 
| 708 | 
            +
            	// moduleDone: { name, failed, passed, total }
         | 
| 709 | 
            +
            	moduleDone: registerLoggingCallback('moduleDone')
         | 
| 710 | 
            +
            });
         | 
| 711 | 
            +
             | 
| 712 | 
            +
            if ( typeof document === "undefined" || document.readyState === "complete" ) {
         | 
| 713 | 
            +
            	config.autorun = true;
         | 
| 714 | 
            +
            }
         | 
| 715 | 
            +
             | 
| 716 | 
            +
            QUnit.load = function() {
         | 
| 717 | 
            +
            	runLoggingCallbacks( 'begin', QUnit, {} );
         | 
| 718 | 
            +
             | 
| 719 | 
            +
            	// Initialize the config, saving the execution queue
         | 
| 720 | 
            +
            	var oldconfig = extend({}, config);
         | 
| 721 | 
            +
            	QUnit.init();
         | 
| 722 | 
            +
            	extend(config, oldconfig);
         | 
| 723 | 
            +
             | 
| 724 | 
            +
            	config.blocking = false;
         | 
| 725 | 
            +
             | 
| 726 | 
            +
            	var urlConfigHtml = '', len = config.urlConfig.length;
         | 
| 727 | 
            +
            	for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {
         | 
| 728 | 
            +
            		config[val] = QUnit.urlParams[val];
         | 
| 729 | 
            +
            		urlConfigHtml += '<label><input name="' + val + '" type="checkbox"' + ( config[val] ? ' checked="checked"' : '' ) + '>' + val + '</label>';
         | 
| 730 | 
            +
            	}
         | 
| 731 | 
            +
             | 
| 732 | 
            +
            	var userAgent = id("qunit-userAgent");
         | 
| 733 | 
            +
            	if ( userAgent ) {
         | 
| 734 | 
            +
            		userAgent.innerHTML = navigator.userAgent;
         | 
| 735 | 
            +
            	}
         | 
| 736 | 
            +
            	var banner = id("qunit-header");
         | 
| 737 | 
            +
            	if ( banner ) {
         | 
| 738 | 
            +
            		banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;
         | 
| 739 | 
            +
            		addEvent( banner, "change", function( event ) {
         | 
| 740 | 
            +
            			var params = {};
         | 
| 741 | 
            +
            			params[ event.target.name ] = event.target.checked ? true : undefined;
         | 
| 742 | 
            +
            			window.location = QUnit.url( params );
         | 
| 743 | 
            +
            		});
         | 
| 744 | 
            +
            	}
         | 
| 745 | 
            +
             | 
| 746 | 
            +
            	var toolbar = id("qunit-testrunner-toolbar");
         | 
| 747 | 
            +
            	if ( toolbar ) {
         | 
| 748 | 
            +
            		var filter = document.createElement("input");
         | 
| 749 | 
            +
            		filter.type = "checkbox";
         | 
| 750 | 
            +
            		filter.id = "qunit-filter-pass";
         | 
| 751 | 
            +
            		addEvent( filter, "click", function() {
         | 
| 752 | 
            +
            			var ol = document.getElementById("qunit-tests");
         | 
| 753 | 
            +
            			if ( filter.checked ) {
         | 
| 754 | 
            +
            				ol.className = ol.className + " hidepass";
         | 
| 755 | 
            +
            			} else {
         | 
| 756 | 
            +
            				var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
         | 
| 757 | 
            +
            				ol.className = tmp.replace(/ hidepass /, " ");
         | 
| 758 | 
            +
            			}
         | 
| 759 | 
            +
            			if ( defined.sessionStorage ) {
         | 
| 760 | 
            +
            				if (filter.checked) {
         | 
| 761 | 
            +
            					sessionStorage.setItem("qunit-filter-passed-tests", "true");
         | 
| 762 | 
            +
            				} else {
         | 
| 763 | 
            +
            					sessionStorage.removeItem("qunit-filter-passed-tests");
         | 
| 764 | 
            +
            				}
         | 
| 765 | 
            +
            			}
         | 
| 766 | 
            +
            		});
         | 
| 767 | 
            +
            		if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
         | 
| 768 | 
            +
            			filter.checked = true;
         | 
| 769 | 
            +
            			var ol = document.getElementById("qunit-tests");
         | 
| 770 | 
            +
            			ol.className = ol.className + " hidepass";
         | 
| 771 | 
            +
            		}
         | 
| 772 | 
            +
            		toolbar.appendChild( filter );
         | 
| 773 | 
            +
             | 
| 774 | 
            +
            		var label = document.createElement("label");
         | 
| 775 | 
            +
            		label.setAttribute("for", "qunit-filter-pass");
         | 
| 776 | 
            +
            		label.innerHTML = "Hide passed tests";
         | 
| 777 | 
            +
            		toolbar.appendChild( label );
         | 
| 778 | 
            +
            	}
         | 
| 779 | 
            +
             | 
| 780 | 
            +
            	var main = id('qunit-fixture');
         | 
| 781 | 
            +
            	if ( main ) {
         | 
| 782 | 
            +
            		config.fixture = main.innerHTML;
         | 
| 783 | 
            +
            	}
         | 
| 784 | 
            +
             | 
| 785 | 
            +
            	if (config.autostart) {
         | 
| 786 | 
            +
            		QUnit.start();
         | 
| 787 | 
            +
            	}
         | 
| 788 | 
            +
            };
         | 
| 789 | 
            +
             | 
| 790 | 
            +
            addEvent(window, "load", QUnit.load);
         | 
| 791 | 
            +
             | 
| 792 | 
            +
            // addEvent(window, "error") gives us a useless event object
         | 
| 793 | 
            +
            window.onerror = function( message, file, line ) {
         | 
| 794 | 
            +
            	if ( QUnit.config.current ) {
         | 
| 795 | 
            +
            		ok( false, message + ", " + file + ":" + line );
         | 
| 796 | 
            +
            	} else {
         | 
| 797 | 
            +
            		test( "global failure", function() {
         | 
| 798 | 
            +
            			ok( false, message + ", " + file + ":" + line );
         | 
| 799 | 
            +
            		});
         | 
| 800 | 
            +
            	}
         | 
| 801 | 
            +
            };
         | 
| 802 | 
            +
             | 
| 803 | 
            +
            function done() {
         | 
| 804 | 
            +
            	config.autorun = true;
         | 
| 805 | 
            +
             | 
| 806 | 
            +
            	// Log the last module results
         | 
| 807 | 
            +
            	if ( config.currentModule ) {
         | 
| 808 | 
            +
            		runLoggingCallbacks( 'moduleDone', QUnit, {
         | 
| 809 | 
            +
            			name: config.currentModule,
         | 
| 810 | 
            +
            			failed: config.moduleStats.bad,
         | 
| 811 | 
            +
            			passed: config.moduleStats.all - config.moduleStats.bad,
         | 
| 812 | 
            +
            			total: config.moduleStats.all
         | 
| 813 | 
            +
            		} );
         | 
| 814 | 
            +
            	}
         | 
| 815 | 
            +
             | 
| 816 | 
            +
            	var banner = id("qunit-banner"),
         | 
| 817 | 
            +
            		tests = id("qunit-tests"),
         | 
| 818 | 
            +
            		runtime = +new Date - config.started,
         | 
| 819 | 
            +
            		passed = config.stats.all - config.stats.bad,
         | 
| 820 | 
            +
            		html = [
         | 
| 821 | 
            +
            			'Tests completed in ',
         | 
| 822 | 
            +
            			runtime,
         | 
| 823 | 
            +
            			' milliseconds.<br/>',
         | 
| 824 | 
            +
            			'<span class="passed">',
         | 
| 825 | 
            +
            			passed,
         | 
| 826 | 
            +
            			'</span> tests of <span class="total">',
         | 
| 827 | 
            +
            			config.stats.all,
         | 
| 828 | 
            +
            			'</span> passed, <span class="failed">',
         | 
| 829 | 
            +
            			config.stats.bad,
         | 
| 830 | 
            +
            			'</span> failed.'
         | 
| 831 | 
            +
            		].join('');
         | 
| 832 | 
            +
             | 
| 833 | 
            +
            	if ( banner ) {
         | 
| 834 | 
            +
            		banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
         | 
| 835 | 
            +
            	}
         | 
| 836 | 
            +
             | 
| 837 | 
            +
            	if ( tests ) {
         | 
| 838 | 
            +
            		id( "qunit-testresult" ).innerHTML = html;
         | 
| 839 | 
            +
            	}
         | 
| 840 | 
            +
             | 
| 841 | 
            +
            	if ( config.altertitle && typeof document !== "undefined" && document.title ) {
         | 
| 842 | 
            +
            		// show ✖ for good, ✔ for bad suite result in title
         | 
| 843 | 
            +
            		// use escape sequences in case file gets loaded with non-utf-8-charset
         | 
| 844 | 
            +
            		document.title = [
         | 
| 845 | 
            +
            			(config.stats.bad ? "\u2716" : "\u2714"),
         | 
| 846 | 
            +
            			document.title.replace(/^[\u2714\u2716] /i, "")
         | 
| 847 | 
            +
            		].join(" ");
         | 
| 848 | 
            +
            	}
         | 
| 849 | 
            +
             | 
| 850 | 
            +
            	runLoggingCallbacks( 'done', QUnit, {
         | 
| 851 | 
            +
            		failed: config.stats.bad,
         | 
| 852 | 
            +
            		passed: passed,
         | 
| 853 | 
            +
            		total: config.stats.all,
         | 
| 854 | 
            +
            		runtime: runtime
         | 
| 855 | 
            +
            	} );
         | 
| 856 | 
            +
            }
         | 
| 857 | 
            +
             | 
| 858 | 
            +
            function validTest( name ) {
         | 
| 859 | 
            +
            	var filter = config.filter,
         | 
| 860 | 
            +
            		run = false;
         | 
| 861 | 
            +
             | 
| 862 | 
            +
            	if ( !filter ) {
         | 
| 863 | 
            +
            		return true;
         | 
| 864 | 
            +
            	}
         | 
| 865 | 
            +
             | 
| 866 | 
            +
            	var not = filter.charAt( 0 ) === "!";
         | 
| 867 | 
            +
            	if ( not ) {
         | 
| 868 | 
            +
            		filter = filter.slice( 1 );
         | 
| 869 | 
            +
            	}
         | 
| 870 | 
            +
             | 
| 871 | 
            +
            	if ( name.indexOf( filter ) !== -1 ) {
         | 
| 872 | 
            +
            		return !not;
         | 
| 873 | 
            +
            	}
         | 
| 874 | 
            +
             | 
| 875 | 
            +
            	if ( not ) {
         | 
| 876 | 
            +
            		run = true;
         | 
| 877 | 
            +
            	}
         | 
| 878 | 
            +
             | 
| 879 | 
            +
            	return run;
         | 
| 880 | 
            +
            }
         | 
| 881 | 
            +
             | 
| 882 | 
            +
            // so far supports only Firefox, Chrome and Opera (buggy)
         | 
| 883 | 
            +
            // could be extended in the future to use something like https://github.com/csnover/TraceKit
         | 
| 884 | 
            +
            function sourceFromStacktrace() {
         | 
| 885 | 
            +
            	try {
         | 
| 886 | 
            +
            		throw new Error();
         | 
| 887 | 
            +
            	} catch ( e ) {
         | 
| 888 | 
            +
            		if (e.stacktrace) {
         | 
| 889 | 
            +
            			// Opera
         | 
| 890 | 
            +
            			return e.stacktrace.split("\n")[6];
         | 
| 891 | 
            +
            		} else if (e.stack) {
         | 
| 892 | 
            +
            			// Firefox, Chrome
         | 
| 893 | 
            +
            			return e.stack.split("\n")[4];
         | 
| 894 | 
            +
            		} else if (e.sourceURL) {
         | 
| 895 | 
            +
            			// Safari, PhantomJS
         | 
| 896 | 
            +
            			// TODO sourceURL points at the 'throw new Error' line above, useless
         | 
| 897 | 
            +
            			//return e.sourceURL + ":" + e.line;
         | 
| 898 | 
            +
            		}
         | 
| 899 | 
            +
            	}
         | 
| 900 | 
            +
            }
         | 
| 901 | 
            +
             | 
| 902 | 
            +
            function escapeInnerText(s) {
         | 
| 903 | 
            +
            	if (!s) {
         | 
| 904 | 
            +
            		return "";
         | 
| 905 | 
            +
            	}
         | 
| 906 | 
            +
            	s = s + "";
         | 
| 907 | 
            +
            	return s.replace(/[\&<>]/g, function(s) {
         | 
| 908 | 
            +
            		switch(s) {
         | 
| 909 | 
            +
            			case "&": return "&";
         | 
| 910 | 
            +
            			case "<": return "<";
         | 
| 911 | 
            +
            			case ">": return ">";
         | 
| 912 | 
            +
            			default: return s;
         | 
| 913 | 
            +
            		}
         | 
| 914 | 
            +
            	});
         | 
| 915 | 
            +
            }
         | 
| 916 | 
            +
             | 
| 917 | 
            +
            function synchronize( callback, last ) {
         | 
| 918 | 
            +
            	config.queue.push( callback );
         | 
| 919 | 
            +
             | 
| 920 | 
            +
            	if ( config.autorun && !config.blocking ) {
         | 
| 921 | 
            +
            		process(last);
         | 
| 922 | 
            +
            	}
         | 
| 923 | 
            +
            }
         | 
| 924 | 
            +
             | 
| 925 | 
            +
            function process( last ) {
         | 
| 926 | 
            +
            	var start = new Date().getTime();
         | 
| 927 | 
            +
            	config.depth = config.depth ? config.depth + 1 : 1;
         | 
| 928 | 
            +
             | 
| 929 | 
            +
            	while ( config.queue.length && !config.blocking ) {
         | 
| 930 | 
            +
            		if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
         | 
| 931 | 
            +
            			config.queue.shift()();
         | 
| 932 | 
            +
            		} else {
         | 
| 933 | 
            +
            			window.setTimeout( function(){
         | 
| 934 | 
            +
            				process( last );
         | 
| 935 | 
            +
            			}, 13 );
         | 
| 936 | 
            +
            			break;
         | 
| 937 | 
            +
            		}
         | 
| 938 | 
            +
            	}
         | 
| 939 | 
            +
            	config.depth--;
         | 
| 940 | 
            +
            	if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
         | 
| 941 | 
            +
            		done();
         | 
| 942 | 
            +
            	}
         | 
| 943 | 
            +
            }
         | 
| 944 | 
            +
             | 
| 945 | 
            +
            function saveGlobal() {
         | 
| 946 | 
            +
            	config.pollution = [];
         | 
| 947 | 
            +
             | 
| 948 | 
            +
            	if ( config.noglobals ) {
         | 
| 949 | 
            +
            		for ( var key in window ) {
         | 
| 950 | 
            +
            			if ( !hasOwn.call( window, key ) ) {
         | 
| 951 | 
            +
            				continue;
         | 
| 952 | 
            +
            			}
         | 
| 953 | 
            +
            			config.pollution.push( key );
         | 
| 954 | 
            +
            		}
         | 
| 955 | 
            +
            	}
         | 
| 956 | 
            +
            }
         | 
| 957 | 
            +
             | 
| 958 | 
            +
            function checkPollution( name ) {
         | 
| 959 | 
            +
            	var old = config.pollution;
         | 
| 960 | 
            +
            	saveGlobal();
         | 
| 961 | 
            +
             | 
| 962 | 
            +
            	var newGlobals = diff( config.pollution, old );
         | 
| 963 | 
            +
            	if ( newGlobals.length > 0 ) {
         | 
| 964 | 
            +
            		ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
         | 
| 965 | 
            +
            	}
         | 
| 966 | 
            +
             | 
| 967 | 
            +
            	var deletedGlobals = diff( old, config.pollution );
         | 
| 968 | 
            +
            	if ( deletedGlobals.length > 0 ) {
         | 
| 969 | 
            +
            		ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
         | 
| 970 | 
            +
            	}
         | 
| 971 | 
            +
            }
         | 
| 972 | 
            +
             | 
| 973 | 
            +
            // returns a new Array with the elements that are in a but not in b
         | 
| 974 | 
            +
            function diff( a, b ) {
         | 
| 975 | 
            +
            	var result = a.slice();
         | 
| 976 | 
            +
            	for ( var i = 0; i < result.length; i++ ) {
         | 
| 977 | 
            +
            		for ( var j = 0; j < b.length; j++ ) {
         | 
| 978 | 
            +
            			if ( result[i] === b[j] ) {
         | 
| 979 | 
            +
            				result.splice(i, 1);
         | 
| 980 | 
            +
            				i--;
         | 
| 981 | 
            +
            				break;
         | 
| 982 | 
            +
            			}
         | 
| 983 | 
            +
            		}
         | 
| 984 | 
            +
            	}
         | 
| 985 | 
            +
            	return result;
         | 
| 986 | 
            +
            }
         | 
| 987 | 
            +
             | 
| 988 | 
            +
            function fail(message, exception, callback) {
         | 
| 989 | 
            +
            	if ( typeof console !== "undefined" && console.error && console.warn ) {
         | 
| 990 | 
            +
            		console.error(message);
         | 
| 991 | 
            +
            		console.error(exception);
         | 
| 992 | 
            +
            		console.warn(callback.toString());
         | 
| 993 | 
            +
             | 
| 994 | 
            +
            	} else if ( window.opera && opera.postError ) {
         | 
| 995 | 
            +
            		opera.postError(message, exception, callback.toString);
         | 
| 996 | 
            +
            	}
         | 
| 997 | 
            +
            }
         | 
| 998 | 
            +
             | 
| 999 | 
            +
            function extend(a, b) {
         | 
| 1000 | 
            +
            	for ( var prop in b ) {
         | 
| 1001 | 
            +
            		if ( b[prop] === undefined ) {
         | 
| 1002 | 
            +
            			delete a[prop];
         | 
| 1003 | 
            +
             | 
| 1004 | 
            +
            		// Avoid "Member not found" error in IE8 caused by setting window.constructor
         | 
| 1005 | 
            +
            		} else if ( prop !== "constructor" || a !== window ) {
         | 
| 1006 | 
            +
            			a[prop] = b[prop];
         | 
| 1007 | 
            +
            		}
         | 
| 1008 | 
            +
            	}
         | 
| 1009 | 
            +
             | 
| 1010 | 
            +
            	return a;
         | 
| 1011 | 
            +
            }
         | 
| 1012 | 
            +
             | 
| 1013 | 
            +
            function addEvent(elem, type, fn) {
         | 
| 1014 | 
            +
            	if ( elem.addEventListener ) {
         | 
| 1015 | 
            +
            		elem.addEventListener( type, fn, false );
         | 
| 1016 | 
            +
            	} else if ( elem.attachEvent ) {
         | 
| 1017 | 
            +
            		elem.attachEvent( "on" + type, fn );
         | 
| 1018 | 
            +
            	} else {
         | 
| 1019 | 
            +
            		fn();
         | 
| 1020 | 
            +
            	}
         | 
| 1021 | 
            +
            }
         | 
| 1022 | 
            +
             | 
| 1023 | 
            +
            function id(name) {
         | 
| 1024 | 
            +
            	return !!(typeof document !== "undefined" && document && document.getElementById) &&
         | 
| 1025 | 
            +
            		document.getElementById( name );
         | 
| 1026 | 
            +
            }
         | 
| 1027 | 
            +
             | 
| 1028 | 
            +
            function registerLoggingCallback(key){
         | 
| 1029 | 
            +
            	return function(callback){
         | 
| 1030 | 
            +
            		config[key].push( callback );
         | 
| 1031 | 
            +
            	};
         | 
| 1032 | 
            +
            }
         | 
| 1033 | 
            +
             | 
| 1034 | 
            +
            // Supports deprecated method of completely overwriting logging callbacks
         | 
| 1035 | 
            +
            function runLoggingCallbacks(key, scope, args) {
         | 
| 1036 | 
            +
            	//debugger;
         | 
| 1037 | 
            +
            	var callbacks;
         | 
| 1038 | 
            +
            	if ( QUnit.hasOwnProperty(key) ) {
         | 
| 1039 | 
            +
            		QUnit[key].call(scope, args);
         | 
| 1040 | 
            +
            	} else {
         | 
| 1041 | 
            +
            		callbacks = config[key];
         | 
| 1042 | 
            +
            		for( var i = 0; i < callbacks.length; i++ ) {
         | 
| 1043 | 
            +
            			callbacks[i].call( scope, args );
         | 
| 1044 | 
            +
            		}
         | 
| 1045 | 
            +
            	}
         | 
| 1046 | 
            +
            }
         | 
| 1047 | 
            +
             | 
| 1048 | 
            +
            // Test for equality any JavaScript type.
         | 
| 1049 | 
            +
            // Author: Philippe Rathé <prathe@gmail.com>
         | 
| 1050 | 
            +
            QUnit.equiv = function () {
         | 
| 1051 | 
            +
             | 
| 1052 | 
            +
            	var innerEquiv; // the real equiv function
         | 
| 1053 | 
            +
            	var callers = []; // stack to decide between skip/abort functions
         | 
| 1054 | 
            +
            	var parents = []; // stack to avoiding loops from circular referencing
         | 
| 1055 | 
            +
             | 
| 1056 | 
            +
            	// Call the o related callback with the given arguments.
         | 
| 1057 | 
            +
            	function bindCallbacks(o, callbacks, args) {
         | 
| 1058 | 
            +
            		var prop = QUnit.objectType(o);
         | 
| 1059 | 
            +
            		if (prop) {
         | 
| 1060 | 
            +
            			if (QUnit.objectType(callbacks[prop]) === "function") {
         | 
| 1061 | 
            +
            				return callbacks[prop].apply(callbacks, args);
         | 
| 1062 | 
            +
            			} else {
         | 
| 1063 | 
            +
            				return callbacks[prop]; // or undefined
         | 
| 1064 | 
            +
            			}
         | 
| 1065 | 
            +
            		}
         | 
| 1066 | 
            +
            	}
         | 
| 1067 | 
            +
             | 
| 1068 | 
            +
            	var getProto = Object.getPrototypeOf || function (obj) {
         | 
| 1069 | 
            +
            		return obj.__proto__;
         | 
| 1070 | 
            +
            	};
         | 
| 1071 | 
            +
             | 
| 1072 | 
            +
            	var callbacks = function () {
         | 
| 1073 | 
            +
             | 
| 1074 | 
            +
            		// for string, boolean, number and null
         | 
| 1075 | 
            +
            		function useStrictEquality(b, a) {
         | 
| 1076 | 
            +
            			if (b instanceof a.constructor || a instanceof b.constructor) {
         | 
| 1077 | 
            +
            				// to catch short annotaion VS 'new' annotation of a
         | 
| 1078 | 
            +
            				// declaration
         | 
| 1079 | 
            +
            				// e.g. var i = 1;
         | 
| 1080 | 
            +
            				// var j = new Number(1);
         | 
| 1081 | 
            +
            				return a == b;
         | 
| 1082 | 
            +
            			} else {
         | 
| 1083 | 
            +
            				return a === b;
         | 
| 1084 | 
            +
            			}
         | 
| 1085 | 
            +
            		}
         | 
| 1086 | 
            +
             | 
| 1087 | 
            +
            		return {
         | 
| 1088 | 
            +
            			"string" : useStrictEquality,
         | 
| 1089 | 
            +
            			"boolean" : useStrictEquality,
         | 
| 1090 | 
            +
            			"number" : useStrictEquality,
         | 
| 1091 | 
            +
            			"null" : useStrictEquality,
         | 
| 1092 | 
            +
            			"undefined" : useStrictEquality,
         | 
| 1093 | 
            +
             | 
| 1094 | 
            +
            			"nan" : function(b) {
         | 
| 1095 | 
            +
            				return isNaN(b);
         | 
| 1096 | 
            +
            			},
         | 
| 1097 | 
            +
             | 
| 1098 | 
            +
            			"date" : function(b, a) {
         | 
| 1099 | 
            +
            				return QUnit.objectType(b) === "date"
         | 
| 1100 | 
            +
            						&& a.valueOf() === b.valueOf();
         | 
| 1101 | 
            +
            			},
         | 
| 1102 | 
            +
             | 
| 1103 | 
            +
            			"regexp" : function(b, a) {
         | 
| 1104 | 
            +
            				return QUnit.objectType(b) === "regexp"
         | 
| 1105 | 
            +
            						&& a.source === b.source && // the regex itself
         | 
| 1106 | 
            +
            						a.global === b.global && // and its modifers
         | 
| 1107 | 
            +
            													// (gmi) ...
         | 
| 1108 | 
            +
            						a.ignoreCase === b.ignoreCase
         | 
| 1109 | 
            +
            						&& a.multiline === b.multiline;
         | 
| 1110 | 
            +
            			},
         | 
| 1111 | 
            +
             | 
| 1112 | 
            +
            			// - skip when the property is a method of an instance (OOP)
         | 
| 1113 | 
            +
            			// - abort otherwise,
         | 
| 1114 | 
            +
            			// initial === would have catch identical references anyway
         | 
| 1115 | 
            +
            			"function" : function() {
         | 
| 1116 | 
            +
            				var caller = callers[callers.length - 1];
         | 
| 1117 | 
            +
            				return caller !== Object && typeof caller !== "undefined";
         | 
| 1118 | 
            +
            			},
         | 
| 1119 | 
            +
             | 
| 1120 | 
            +
            			"array" : function(b, a) {
         | 
| 1121 | 
            +
            				var i, j, loop;
         | 
| 1122 | 
            +
            				var len;
         | 
| 1123 | 
            +
             | 
| 1124 | 
            +
            				// b could be an object literal here
         | 
| 1125 | 
            +
            				if (!(QUnit.objectType(b) === "array")) {
         | 
| 1126 | 
            +
            					return false;
         | 
| 1127 | 
            +
            				}
         | 
| 1128 | 
            +
             | 
| 1129 | 
            +
            				len = a.length;
         | 
| 1130 | 
            +
            				if (len !== b.length) { // safe and faster
         | 
| 1131 | 
            +
            					return false;
         | 
| 1132 | 
            +
            				}
         | 
| 1133 | 
            +
             | 
| 1134 | 
            +
            				// track reference to avoid circular references
         | 
| 1135 | 
            +
            				parents.push(a);
         | 
| 1136 | 
            +
            				for (i = 0; i < len; i++) {
         | 
| 1137 | 
            +
            					loop = false;
         | 
| 1138 | 
            +
            					for (j = 0; j < parents.length; j++) {
         | 
| 1139 | 
            +
            						if (parents[j] === a[i]) {
         | 
| 1140 | 
            +
            							loop = true;// dont rewalk array
         | 
| 1141 | 
            +
            						}
         | 
| 1142 | 
            +
            					}
         | 
| 1143 | 
            +
            					if (!loop && !innerEquiv(a[i], b[i])) {
         | 
| 1144 | 
            +
            						parents.pop();
         | 
| 1145 | 
            +
            						return false;
         | 
| 1146 | 
            +
            					}
         | 
| 1147 | 
            +
            				}
         | 
| 1148 | 
            +
            				parents.pop();
         | 
| 1149 | 
            +
            				return true;
         | 
| 1150 | 
            +
            			},
         | 
| 1151 | 
            +
             | 
| 1152 | 
            +
            			"object" : function(b, a) {
         | 
| 1153 | 
            +
            				var i, j, loop;
         | 
| 1154 | 
            +
            				var eq = true; // unless we can proove it
         | 
| 1155 | 
            +
            				var aProperties = [], bProperties = []; // collection of
         | 
| 1156 | 
            +
            														// strings
         | 
| 1157 | 
            +
             | 
| 1158 | 
            +
            				// comparing constructors is more strict than using
         | 
| 1159 | 
            +
            				// instanceof
         | 
| 1160 | 
            +
            				if (a.constructor !== b.constructor) {
         | 
| 1161 | 
            +
            					// Allow objects with no prototype to be equivalent to
         | 
| 1162 | 
            +
            					// objects with Object as their constructor.
         | 
| 1163 | 
            +
            					if (!((getProto(a) === null && getProto(b) === Object.prototype) ||
         | 
| 1164 | 
            +
            						  (getProto(b) === null && getProto(a) === Object.prototype)))
         | 
| 1165 | 
            +
            					{
         | 
| 1166 | 
            +
            						return false;
         | 
| 1167 | 
            +
            					}
         | 
| 1168 | 
            +
            				}
         | 
| 1169 | 
            +
             | 
| 1170 | 
            +
            				// stack constructor before traversing properties
         | 
| 1171 | 
            +
            				callers.push(a.constructor);
         | 
| 1172 | 
            +
            				// track reference to avoid circular references
         | 
| 1173 | 
            +
            				parents.push(a);
         | 
| 1174 | 
            +
             | 
| 1175 | 
            +
            				for (i in a) { // be strict: don't ensures hasOwnProperty
         | 
| 1176 | 
            +
            								// and go deep
         | 
| 1177 | 
            +
            					loop = false;
         | 
| 1178 | 
            +
            					for (j = 0; j < parents.length; j++) {
         | 
| 1179 | 
            +
            						if (parents[j] === a[i])
         | 
| 1180 | 
            +
            							loop = true; // don't go down the same path
         | 
| 1181 | 
            +
            											// twice
         | 
| 1182 | 
            +
            					}
         | 
| 1183 | 
            +
            					aProperties.push(i); // collect a's properties
         | 
| 1184 | 
            +
             | 
| 1185 | 
            +
            					if (!loop && !innerEquiv(a[i], b[i])) {
         | 
| 1186 | 
            +
            						eq = false;
         | 
| 1187 | 
            +
            						break;
         | 
| 1188 | 
            +
            					}
         | 
| 1189 | 
            +
            				}
         | 
| 1190 | 
            +
             | 
| 1191 | 
            +
            				callers.pop(); // unstack, we are done
         | 
| 1192 | 
            +
            				parents.pop();
         | 
| 1193 | 
            +
             | 
| 1194 | 
            +
            				for (i in b) {
         | 
| 1195 | 
            +
            					bProperties.push(i); // collect b's properties
         | 
| 1196 | 
            +
            				}
         | 
| 1197 | 
            +
             | 
| 1198 | 
            +
            				// Ensures identical properties name
         | 
| 1199 | 
            +
            				return eq
         | 
| 1200 | 
            +
            						&& innerEquiv(aProperties.sort(), bProperties
         | 
| 1201 | 
            +
            								.sort());
         | 
| 1202 | 
            +
            			}
         | 
| 1203 | 
            +
            		};
         | 
| 1204 | 
            +
            	}();
         | 
| 1205 | 
            +
             | 
| 1206 | 
            +
            	innerEquiv = function() { // can take multiple arguments
         | 
| 1207 | 
            +
            		var args = Array.prototype.slice.apply(arguments);
         | 
| 1208 | 
            +
            		if (args.length < 2) {
         | 
| 1209 | 
            +
            			return true; // end transition
         | 
| 1210 | 
            +
            		}
         | 
| 1211 | 
            +
             | 
| 1212 | 
            +
            		return (function(a, b) {
         | 
| 1213 | 
            +
            			if (a === b) {
         | 
| 1214 | 
            +
            				return true; // catch the most you can
         | 
| 1215 | 
            +
            			} else if (a === null || b === null || typeof a === "undefined"
         | 
| 1216 | 
            +
            					|| typeof b === "undefined"
         | 
| 1217 | 
            +
            					|| QUnit.objectType(a) !== QUnit.objectType(b)) {
         | 
| 1218 | 
            +
            				return false; // don't lose time with error prone cases
         | 
| 1219 | 
            +
            			} else {
         | 
| 1220 | 
            +
            				return bindCallbacks(a, callbacks, [ b, a ]);
         | 
| 1221 | 
            +
            			}
         | 
| 1222 | 
            +
             | 
| 1223 | 
            +
            			// apply transition with (1..n) arguments
         | 
| 1224 | 
            +
            		})(args[0], args[1])
         | 
| 1225 | 
            +
            				&& arguments.callee.apply(this, args.splice(1,
         | 
| 1226 | 
            +
            						args.length - 1));
         | 
| 1227 | 
            +
            	};
         | 
| 1228 | 
            +
             | 
| 1229 | 
            +
            	return innerEquiv;
         | 
| 1230 | 
            +
             | 
| 1231 | 
            +
            }();
         | 
| 1232 | 
            +
             | 
| 1233 | 
            +
            /**
         | 
| 1234 | 
            +
             * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
         | 
| 1235 | 
            +
             * http://flesler.blogspot.com Licensed under BSD
         | 
| 1236 | 
            +
             * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
         | 
| 1237 | 
            +
             *
         | 
| 1238 | 
            +
             * @projectDescription Advanced and extensible data dumping for Javascript.
         | 
| 1239 | 
            +
             * @version 1.0.0
         | 
| 1240 | 
            +
             * @author Ariel Flesler
         | 
| 1241 | 
            +
             * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
         | 
| 1242 | 
            +
             */
         | 
| 1243 | 
            +
            QUnit.jsDump = (function() {
         | 
| 1244 | 
            +
            	function quote( str ) {
         | 
| 1245 | 
            +
            		return '"' + str.toString().replace(/"/g, '\\"') + '"';
         | 
| 1246 | 
            +
            	};
         | 
| 1247 | 
            +
            	function literal( o ) {
         | 
| 1248 | 
            +
            		return o + '';
         | 
| 1249 | 
            +
            	};
         | 
| 1250 | 
            +
            	function join( pre, arr, post ) {
         | 
| 1251 | 
            +
            		var s = jsDump.separator(),
         | 
| 1252 | 
            +
            			base = jsDump.indent(),
         | 
| 1253 | 
            +
            			inner = jsDump.indent(1);
         | 
| 1254 | 
            +
            		if ( arr.join )
         | 
| 1255 | 
            +
            			arr = arr.join( ',' + s + inner );
         | 
| 1256 | 
            +
            		if ( !arr )
         | 
| 1257 | 
            +
            			return pre + post;
         | 
| 1258 | 
            +
            		return [ pre, inner + arr, base + post ].join(s);
         | 
| 1259 | 
            +
            	};
         | 
| 1260 | 
            +
            	function array( arr, stack ) {
         | 
| 1261 | 
            +
            		var i = arr.length, ret = Array(i);
         | 
| 1262 | 
            +
            		this.up();
         | 
| 1263 | 
            +
            		while ( i-- )
         | 
| 1264 | 
            +
            			ret[i] = this.parse( arr[i] , undefined , stack);
         | 
| 1265 | 
            +
            		this.down();
         | 
| 1266 | 
            +
            		return join( '[', ret, ']' );
         | 
| 1267 | 
            +
            	};
         | 
| 1268 | 
            +
             | 
| 1269 | 
            +
            	var reName = /^function (\w+)/;
         | 
| 1270 | 
            +
             | 
| 1271 | 
            +
            	var jsDump = {
         | 
| 1272 | 
            +
            		parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
         | 
| 1273 | 
            +
            			stack = stack || [ ];
         | 
| 1274 | 
            +
            			var parser = this.parsers[ type || this.typeOf(obj) ];
         | 
| 1275 | 
            +
            			type = typeof parser;
         | 
| 1276 | 
            +
            			var inStack = inArray(obj, stack);
         | 
| 1277 | 
            +
            			if (inStack != -1) {
         | 
| 1278 | 
            +
            				return 'recursion('+(inStack - stack.length)+')';
         | 
| 1279 | 
            +
            			}
         | 
| 1280 | 
            +
            			//else
         | 
| 1281 | 
            +
            			if (type == 'function')  {
         | 
| 1282 | 
            +
            					stack.push(obj);
         | 
| 1283 | 
            +
            					var res = parser.call( this, obj, stack );
         | 
| 1284 | 
            +
            					stack.pop();
         | 
| 1285 | 
            +
            					return res;
         | 
| 1286 | 
            +
            			}
         | 
| 1287 | 
            +
            			// else
         | 
| 1288 | 
            +
            			return (type == 'string') ? parser : this.parsers.error;
         | 
| 1289 | 
            +
            		},
         | 
| 1290 | 
            +
            		typeOf:function( obj ) {
         | 
| 1291 | 
            +
            			var type;
         | 
| 1292 | 
            +
            			if ( obj === null ) {
         | 
| 1293 | 
            +
            				type = "null";
         | 
| 1294 | 
            +
            			} else if (typeof obj === "undefined") {
         | 
| 1295 | 
            +
            				type = "undefined";
         | 
| 1296 | 
            +
            			} else if (QUnit.is("RegExp", obj)) {
         | 
| 1297 | 
            +
            				type = "regexp";
         | 
| 1298 | 
            +
            			} else if (QUnit.is("Date", obj)) {
         | 
| 1299 | 
            +
            				type = "date";
         | 
| 1300 | 
            +
            			} else if (QUnit.is("Function", obj)) {
         | 
| 1301 | 
            +
            				type = "function";
         | 
| 1302 | 
            +
            			} else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
         | 
| 1303 | 
            +
            				type = "window";
         | 
| 1304 | 
            +
            			} else if (obj.nodeType === 9) {
         | 
| 1305 | 
            +
            				type = "document";
         | 
| 1306 | 
            +
            			} else if (obj.nodeType) {
         | 
| 1307 | 
            +
            				type = "node";
         | 
| 1308 | 
            +
            			} else if (
         | 
| 1309 | 
            +
            				// native arrays
         | 
| 1310 | 
            +
            				toString.call( obj ) === "[object Array]" ||
         | 
| 1311 | 
            +
            				// NodeList objects
         | 
| 1312 | 
            +
            				( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
         | 
| 1313 | 
            +
            			) {
         | 
| 1314 | 
            +
            				type = "array";
         | 
| 1315 | 
            +
            			} else {
         | 
| 1316 | 
            +
            				type = typeof obj;
         | 
| 1317 | 
            +
            			}
         | 
| 1318 | 
            +
            			return type;
         | 
| 1319 | 
            +
            		},
         | 
| 1320 | 
            +
            		separator:function() {
         | 
| 1321 | 
            +
            			return this.multiline ?	this.HTML ? '<br />' : '\n' : this.HTML ? ' ' : ' ';
         | 
| 1322 | 
            +
            		},
         | 
| 1323 | 
            +
            		indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
         | 
| 1324 | 
            +
            			if ( !this.multiline )
         | 
| 1325 | 
            +
            				return '';
         | 
| 1326 | 
            +
            			var chr = this.indentChar;
         | 
| 1327 | 
            +
            			if ( this.HTML )
         | 
| 1328 | 
            +
            				chr = chr.replace(/\t/g,'   ').replace(/ /g,' ');
         | 
| 1329 | 
            +
            			return Array( this._depth_ + (extra||0) ).join(chr);
         | 
| 1330 | 
            +
            		},
         | 
| 1331 | 
            +
            		up:function( a ) {
         | 
| 1332 | 
            +
            			this._depth_ += a || 1;
         | 
| 1333 | 
            +
            		},
         | 
| 1334 | 
            +
            		down:function( a ) {
         | 
| 1335 | 
            +
            			this._depth_ -= a || 1;
         | 
| 1336 | 
            +
            		},
         | 
| 1337 | 
            +
            		setParser:function( name, parser ) {
         | 
| 1338 | 
            +
            			this.parsers[name] = parser;
         | 
| 1339 | 
            +
            		},
         | 
| 1340 | 
            +
            		// The next 3 are exposed so you can use them
         | 
| 1341 | 
            +
            		quote:quote,
         | 
| 1342 | 
            +
            		literal:literal,
         | 
| 1343 | 
            +
            		join:join,
         | 
| 1344 | 
            +
            		//
         | 
| 1345 | 
            +
            		_depth_: 1,
         | 
| 1346 | 
            +
            		// This is the list of parsers, to modify them, use jsDump.setParser
         | 
| 1347 | 
            +
            		parsers:{
         | 
| 1348 | 
            +
            			window: '[Window]',
         | 
| 1349 | 
            +
            			document: '[Document]',
         | 
| 1350 | 
            +
            			error:'[ERROR]', //when no parser is found, shouldn't happen
         | 
| 1351 | 
            +
            			unknown: '[Unknown]',
         | 
| 1352 | 
            +
            			'null':'null',
         | 
| 1353 | 
            +
            			'undefined':'undefined',
         | 
| 1354 | 
            +
            			'function':function( fn ) {
         | 
| 1355 | 
            +
            				var ret = 'function',
         | 
| 1356 | 
            +
            					name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
         | 
| 1357 | 
            +
            				if ( name )
         | 
| 1358 | 
            +
            					ret += ' ' + name;
         | 
| 1359 | 
            +
            				ret += '(';
         | 
| 1360 | 
            +
             | 
| 1361 | 
            +
            				ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
         | 
| 1362 | 
            +
            				return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
         | 
| 1363 | 
            +
            			},
         | 
| 1364 | 
            +
            			array: array,
         | 
| 1365 | 
            +
            			nodelist: array,
         | 
| 1366 | 
            +
            			arguments: array,
         | 
| 1367 | 
            +
            			object:function( map, stack ) {
         | 
| 1368 | 
            +
            				var ret = [ ];
         | 
| 1369 | 
            +
            				QUnit.jsDump.up();
         | 
| 1370 | 
            +
            				for ( var key in map ) {
         | 
| 1371 | 
            +
            				    var val = map[key];
         | 
| 1372 | 
            +
            					ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));
         | 
| 1373 | 
            +
                            }
         | 
| 1374 | 
            +
            				QUnit.jsDump.down();
         | 
| 1375 | 
            +
            				return join( '{', ret, '}' );
         | 
| 1376 | 
            +
            			},
         | 
| 1377 | 
            +
            			node:function( node ) {
         | 
| 1378 | 
            +
            				var open = QUnit.jsDump.HTML ? '<' : '<',
         | 
| 1379 | 
            +
            					close = QUnit.jsDump.HTML ? '>' : '>';
         | 
| 1380 | 
            +
             | 
| 1381 | 
            +
            				var tag = node.nodeName.toLowerCase(),
         | 
| 1382 | 
            +
            					ret = open + tag;
         | 
| 1383 | 
            +
             | 
| 1384 | 
            +
            				for ( var a in QUnit.jsDump.DOMAttrs ) {
         | 
| 1385 | 
            +
            					var val = node[QUnit.jsDump.DOMAttrs[a]];
         | 
| 1386 | 
            +
            					if ( val )
         | 
| 1387 | 
            +
            						ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
         | 
| 1388 | 
            +
            				}
         | 
| 1389 | 
            +
            				return ret + close + open + '/' + tag + close;
         | 
| 1390 | 
            +
            			},
         | 
| 1391 | 
            +
            			functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
         | 
| 1392 | 
            +
            				var l = fn.length;
         | 
| 1393 | 
            +
            				if ( !l ) return '';
         | 
| 1394 | 
            +
             | 
| 1395 | 
            +
            				var args = Array(l);
         | 
| 1396 | 
            +
            				while ( l-- )
         | 
| 1397 | 
            +
            					args[l] = String.fromCharCode(97+l);//97 is 'a'
         | 
| 1398 | 
            +
            				return ' ' + args.join(', ') + ' ';
         | 
| 1399 | 
            +
            			},
         | 
| 1400 | 
            +
            			key:quote, //object calls it internally, the key part of an item in a map
         | 
| 1401 | 
            +
            			functionCode:'[code]', //function calls it internally, it's the content of the function
         | 
| 1402 | 
            +
            			attribute:quote, //node calls it internally, it's an html attribute value
         | 
| 1403 | 
            +
            			string:quote,
         | 
| 1404 | 
            +
            			date:quote,
         | 
| 1405 | 
            +
            			regexp:literal, //regex
         | 
| 1406 | 
            +
            			number:literal,
         | 
| 1407 | 
            +
            			'boolean':literal
         | 
| 1408 | 
            +
            		},
         | 
| 1409 | 
            +
            		DOMAttrs:{//attributes to dump from nodes, name=>realName
         | 
| 1410 | 
            +
            			id:'id',
         | 
| 1411 | 
            +
            			name:'name',
         | 
| 1412 | 
            +
            			'class':'className'
         | 
| 1413 | 
            +
            		},
         | 
| 1414 | 
            +
            		HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
         | 
| 1415 | 
            +
            		indentChar:'  ',//indentation unit
         | 
| 1416 | 
            +
            		multiline:true //if true, items in a collection, are separated by a \n, else just a space.
         | 
| 1417 | 
            +
            	};
         | 
| 1418 | 
            +
             | 
| 1419 | 
            +
            	return jsDump;
         | 
| 1420 | 
            +
            })();
         | 
| 1421 | 
            +
             | 
| 1422 | 
            +
            // from Sizzle.js
         | 
| 1423 | 
            +
            function getText( elems ) {
         | 
| 1424 | 
            +
            	var ret = "", elem;
         | 
| 1425 | 
            +
             | 
| 1426 | 
            +
            	for ( var i = 0; elems[i]; i++ ) {
         | 
| 1427 | 
            +
            		elem = elems[i];
         | 
| 1428 | 
            +
             | 
| 1429 | 
            +
            		// Get the text from text nodes and CDATA nodes
         | 
| 1430 | 
            +
            		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
         | 
| 1431 | 
            +
            			ret += elem.nodeValue;
         | 
| 1432 | 
            +
             | 
| 1433 | 
            +
            		// Traverse everything else, except comment nodes
         | 
| 1434 | 
            +
            		} else if ( elem.nodeType !== 8 ) {
         | 
| 1435 | 
            +
            			ret += getText( elem.childNodes );
         | 
| 1436 | 
            +
            		}
         | 
| 1437 | 
            +
            	}
         | 
| 1438 | 
            +
             | 
| 1439 | 
            +
            	return ret;
         | 
| 1440 | 
            +
            };
         | 
| 1441 | 
            +
             | 
| 1442 | 
            +
            //from jquery.js
         | 
| 1443 | 
            +
            function inArray( elem, array ) {
         | 
| 1444 | 
            +
            	if ( array.indexOf ) {
         | 
| 1445 | 
            +
            		return array.indexOf( elem );
         | 
| 1446 | 
            +
            	}
         | 
| 1447 | 
            +
             | 
| 1448 | 
            +
            	for ( var i = 0, length = array.length; i < length; i++ ) {
         | 
| 1449 | 
            +
            		if ( array[ i ] === elem ) {
         | 
| 1450 | 
            +
            			return i;
         | 
| 1451 | 
            +
            		}
         | 
| 1452 | 
            +
            	}
         | 
| 1453 | 
            +
             | 
| 1454 | 
            +
            	return -1;
         | 
| 1455 | 
            +
            }
         | 
| 1456 | 
            +
             | 
| 1457 | 
            +
            /*
         | 
| 1458 | 
            +
             * Javascript Diff Algorithm
         | 
| 1459 | 
            +
             *  By John Resig (http://ejohn.org/)
         | 
| 1460 | 
            +
             *  Modified by Chu Alan "sprite"
         | 
| 1461 | 
            +
             *
         | 
| 1462 | 
            +
             * Released under the MIT license.
         | 
| 1463 | 
            +
             *
         | 
| 1464 | 
            +
             * More Info:
         | 
| 1465 | 
            +
             *  http://ejohn.org/projects/javascript-diff-algorithm/
         | 
| 1466 | 
            +
             *
         | 
| 1467 | 
            +
             * Usage: QUnit.diff(expected, actual)
         | 
| 1468 | 
            +
             *
         | 
| 1469 | 
            +
             * 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"
         | 
| 1470 | 
            +
             */
         | 
| 1471 | 
            +
            QUnit.diff = (function() {
         | 
| 1472 | 
            +
            	function diff(o, n) {
         | 
| 1473 | 
            +
            		var ns = {};
         | 
| 1474 | 
            +
            		var os = {};
         | 
| 1475 | 
            +
             | 
| 1476 | 
            +
            		for (var i = 0; i < n.length; i++) {
         | 
| 1477 | 
            +
            			if (ns[n[i]] == null)
         | 
| 1478 | 
            +
            				ns[n[i]] = {
         | 
| 1479 | 
            +
            					rows: [],
         | 
| 1480 | 
            +
            					o: null
         | 
| 1481 | 
            +
            				};
         | 
| 1482 | 
            +
            			ns[n[i]].rows.push(i);
         | 
| 1483 | 
            +
            		}
         | 
| 1484 | 
            +
             | 
| 1485 | 
            +
            		for (var i = 0; i < o.length; i++) {
         | 
| 1486 | 
            +
            			if (os[o[i]] == null)
         | 
| 1487 | 
            +
            				os[o[i]] = {
         | 
| 1488 | 
            +
            					rows: [],
         | 
| 1489 | 
            +
            					n: null
         | 
| 1490 | 
            +
            				};
         | 
| 1491 | 
            +
            			os[o[i]].rows.push(i);
         | 
| 1492 | 
            +
            		}
         | 
| 1493 | 
            +
             | 
| 1494 | 
            +
            		for (var i in ns) {
         | 
| 1495 | 
            +
            			if ( !hasOwn.call( ns, i ) ) {
         | 
| 1496 | 
            +
            				continue;
         | 
| 1497 | 
            +
            			}
         | 
| 1498 | 
            +
            			if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
         | 
| 1499 | 
            +
            				n[ns[i].rows[0]] = {
         | 
| 1500 | 
            +
            					text: n[ns[i].rows[0]],
         | 
| 1501 | 
            +
            					row: os[i].rows[0]
         | 
| 1502 | 
            +
            				};
         | 
| 1503 | 
            +
            				o[os[i].rows[0]] = {
         | 
| 1504 | 
            +
            					text: o[os[i].rows[0]],
         | 
| 1505 | 
            +
            					row: ns[i].rows[0]
         | 
| 1506 | 
            +
            				};
         | 
| 1507 | 
            +
            			}
         | 
| 1508 | 
            +
            		}
         | 
| 1509 | 
            +
             | 
| 1510 | 
            +
            		for (var i = 0; i < n.length - 1; i++) {
         | 
| 1511 | 
            +
            			if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
         | 
| 1512 | 
            +
            			n[i + 1] == o[n[i].row + 1]) {
         | 
| 1513 | 
            +
            				n[i + 1] = {
         | 
| 1514 | 
            +
            					text: n[i + 1],
         | 
| 1515 | 
            +
            					row: n[i].row + 1
         | 
| 1516 | 
            +
            				};
         | 
| 1517 | 
            +
            				o[n[i].row + 1] = {
         | 
| 1518 | 
            +
            					text: o[n[i].row + 1],
         | 
| 1519 | 
            +
            					row: i + 1
         | 
| 1520 | 
            +
            				};
         | 
| 1521 | 
            +
            			}
         | 
| 1522 | 
            +
            		}
         | 
| 1523 | 
            +
             | 
| 1524 | 
            +
            		for (var i = n.length - 1; i > 0; i--) {
         | 
| 1525 | 
            +
            			if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
         | 
| 1526 | 
            +
            			n[i - 1] == o[n[i].row - 1]) {
         | 
| 1527 | 
            +
            				n[i - 1] = {
         | 
| 1528 | 
            +
            					text: n[i - 1],
         | 
| 1529 | 
            +
            					row: n[i].row - 1
         | 
| 1530 | 
            +
            				};
         | 
| 1531 | 
            +
            				o[n[i].row - 1] = {
         | 
| 1532 | 
            +
            					text: o[n[i].row - 1],
         | 
| 1533 | 
            +
            					row: i - 1
         | 
| 1534 | 
            +
            				};
         | 
| 1535 | 
            +
            			}
         | 
| 1536 | 
            +
            		}
         | 
| 1537 | 
            +
             | 
| 1538 | 
            +
            		return {
         | 
| 1539 | 
            +
            			o: o,
         | 
| 1540 | 
            +
            			n: n
         | 
| 1541 | 
            +
            		};
         | 
| 1542 | 
            +
            	}
         | 
| 1543 | 
            +
             | 
| 1544 | 
            +
            	return function(o, n) {
         | 
| 1545 | 
            +
            		o = o.replace(/\s+$/, '');
         | 
| 1546 | 
            +
            		n = n.replace(/\s+$/, '');
         | 
| 1547 | 
            +
            		var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
         | 
| 1548 | 
            +
             | 
| 1549 | 
            +
            		var str = "";
         | 
| 1550 | 
            +
             | 
| 1551 | 
            +
            		var oSpace = o.match(/\s+/g);
         | 
| 1552 | 
            +
            		if (oSpace == null) {
         | 
| 1553 | 
            +
            			oSpace = [" "];
         | 
| 1554 | 
            +
            		}
         | 
| 1555 | 
            +
            		else {
         | 
| 1556 | 
            +
            			oSpace.push(" ");
         | 
| 1557 | 
            +
            		}
         | 
| 1558 | 
            +
            		var nSpace = n.match(/\s+/g);
         | 
| 1559 | 
            +
            		if (nSpace == null) {
         | 
| 1560 | 
            +
            			nSpace = [" "];
         | 
| 1561 | 
            +
            		}
         | 
| 1562 | 
            +
            		else {
         | 
| 1563 | 
            +
            			nSpace.push(" ");
         | 
| 1564 | 
            +
            		}
         | 
| 1565 | 
            +
             | 
| 1566 | 
            +
            		if (out.n.length == 0) {
         | 
| 1567 | 
            +
            			for (var i = 0; i < out.o.length; i++) {
         | 
| 1568 | 
            +
            				str += '<del>' + out.o[i] + oSpace[i] + "</del>";
         | 
| 1569 | 
            +
            			}
         | 
| 1570 | 
            +
            		}
         | 
| 1571 | 
            +
            		else {
         | 
| 1572 | 
            +
            			if (out.n[0].text == null) {
         | 
| 1573 | 
            +
            				for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
         | 
| 1574 | 
            +
            					str += '<del>' + out.o[n] + oSpace[n] + "</del>";
         | 
| 1575 | 
            +
            				}
         | 
| 1576 | 
            +
            			}
         | 
| 1577 | 
            +
             | 
| 1578 | 
            +
            			for (var i = 0; i < out.n.length; i++) {
         | 
| 1579 | 
            +
            				if (out.n[i].text == null) {
         | 
| 1580 | 
            +
            					str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
         | 
| 1581 | 
            +
            				}
         | 
| 1582 | 
            +
            				else {
         | 
| 1583 | 
            +
            					var pre = "";
         | 
| 1584 | 
            +
             | 
| 1585 | 
            +
            					for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
         | 
| 1586 | 
            +
            						pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
         | 
| 1587 | 
            +
            					}
         | 
| 1588 | 
            +
            					str += " " + out.n[i].text + nSpace[i] + pre;
         | 
| 1589 | 
            +
            				}
         | 
| 1590 | 
            +
            			}
         | 
| 1591 | 
            +
            		}
         | 
| 1592 | 
            +
             | 
| 1593 | 
            +
            		return str;
         | 
| 1594 | 
            +
            	};
         | 
| 1595 | 
            +
            })();
         | 
| 1596 | 
            +
             | 
| 1597 | 
            +
            })(this);
         |