PageRenderTime 361ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/wwwroot/vendor/qunit.js

http://github.com/AF83/ucengine
JavaScript | 1434 lines | 1165 code | 153 blank | 116 comment | 181 complexity | 19cabbbb3ff9cf1265dff839d40a9d0e MD5 | raw file
  1. /*
  2. * QUnit - 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. (function(window) {
  11. var defined = {
  12. setTimeout: typeof window.setTimeout !== "undefined",
  13. sessionStorage: (function() {
  14. try {
  15. return !!sessionStorage.getItem;
  16. } catch(e){
  17. return false;
  18. }
  19. })()
  20. };
  21. var testId = 0;
  22. var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
  23. this.name = name;
  24. this.testName = testName;
  25. this.expected = expected;
  26. this.testEnvironmentArg = testEnvironmentArg;
  27. this.async = async;
  28. this.callback = callback;
  29. this.assertions = [];
  30. };
  31. Test.prototype = {
  32. init: function() {
  33. var tests = id("qunit-tests");
  34. if (tests) {
  35. var b = document.createElement("strong");
  36. b.innerHTML = "Running " + this.name;
  37. var li = document.createElement("li");
  38. li.appendChild( b );
  39. li.className = "running";
  40. li.id = this.id = "test-output" + testId++;
  41. tests.appendChild( li );
  42. }
  43. },
  44. setup: function() {
  45. if (this.module != config.previousModule) {
  46. if ( config.previousModule ) {
  47. QUnit.moduleDone( {
  48. name: config.previousModule,
  49. failed: config.moduleStats.bad,
  50. passed: config.moduleStats.all - config.moduleStats.bad,
  51. total: config.moduleStats.all
  52. } );
  53. }
  54. config.previousModule = this.module;
  55. config.moduleStats = { all: 0, bad: 0 };
  56. QUnit.moduleStart( {
  57. name: this.module
  58. } );
  59. }
  60. config.current = this;
  61. this.testEnvironment = extend({
  62. setup: function() {},
  63. teardown: function() {}
  64. }, this.moduleTestEnvironment);
  65. if (this.testEnvironmentArg) {
  66. extend(this.testEnvironment, this.testEnvironmentArg);
  67. }
  68. QUnit.testStart( {
  69. name: this.testName
  70. } );
  71. // allow utility functions to access the current test environment
  72. // TODO why??
  73. QUnit.current_testEnvironment = this.testEnvironment;
  74. try {
  75. if ( !config.pollution ) {
  76. saveGlobal();
  77. }
  78. this.testEnvironment.setup.call(this.testEnvironment);
  79. } catch(e) {
  80. QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
  81. }
  82. },
  83. run: function() {
  84. if ( this.async ) {
  85. QUnit.stop();
  86. }
  87. if ( config.notrycatch ) {
  88. this.callback.call(this.testEnvironment);
  89. return;
  90. }
  91. try {
  92. this.callback.call(this.testEnvironment);
  93. } catch(e) {
  94. fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
  95. QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
  96. // else next test will carry the responsibility
  97. saveGlobal();
  98. // Restart the tests if they're blocking
  99. if ( config.blocking ) {
  100. start();
  101. }
  102. }
  103. },
  104. teardown: function() {
  105. try {
  106. checkPollution();
  107. this.testEnvironment.teardown.call(this.testEnvironment);
  108. } catch(e) {
  109. QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
  110. }
  111. },
  112. finish: function() {
  113. if ( this.expected && this.expected != this.assertions.length ) {
  114. QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
  115. }
  116. var good = 0, bad = 0,
  117. tests = id("qunit-tests");
  118. config.stats.all += this.assertions.length;
  119. config.moduleStats.all += this.assertions.length;
  120. if ( tests ) {
  121. var ol = document.createElement("ol");
  122. for ( var i = 0; i < this.assertions.length; i++ ) {
  123. var assertion = this.assertions[i];
  124. var li = document.createElement("li");
  125. li.className = assertion.result ? "pass" : "fail";
  126. li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
  127. ol.appendChild( li );
  128. if ( assertion.result ) {
  129. good++;
  130. } else {
  131. bad++;
  132. config.stats.bad++;
  133. config.moduleStats.bad++;
  134. }
  135. }
  136. // store result when possible
  137. QUnit.config.reorder && defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad);
  138. if (bad == 0) {
  139. ol.style.display = "none";
  140. }
  141. var b = document.createElement("strong");
  142. b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
  143. var a = document.createElement("a");
  144. a.innerHTML = "Rerun";
  145. a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
  146. addEvent(b, "click", function() {
  147. var next = b.nextSibling.nextSibling,
  148. display = next.style.display;
  149. next.style.display = display === "none" ? "block" : "none";
  150. });
  151. addEvent(b, "dblclick", function(e) {
  152. var target = e && e.target ? e.target : window.event.srcElement;
  153. if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
  154. target = target.parentNode;
  155. }
  156. if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
  157. window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
  158. }
  159. });
  160. var li = id(this.id);
  161. li.className = bad ? "fail" : "pass";
  162. li.removeChild( li.firstChild );
  163. li.appendChild( b );
  164. li.appendChild( a );
  165. li.appendChild( ol );
  166. } else {
  167. for ( var i = 0; i < this.assertions.length; i++ ) {
  168. if ( !this.assertions[i].result ) {
  169. bad++;
  170. config.stats.bad++;
  171. config.moduleStats.bad++;
  172. }
  173. }
  174. }
  175. try {
  176. QUnit.reset();
  177. } catch(e) {
  178. fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
  179. }
  180. QUnit.testDone( {
  181. name: this.testName,
  182. failed: bad,
  183. passed: this.assertions.length - bad,
  184. total: this.assertions.length
  185. } );
  186. },
  187. queue: function() {
  188. var test = this;
  189. synchronize(function() {
  190. test.init();
  191. });
  192. function run() {
  193. // each of these can by async
  194. synchronize(function() {
  195. test.setup();
  196. });
  197. synchronize(function() {
  198. test.run();
  199. });
  200. synchronize(function() {
  201. test.teardown();
  202. });
  203. synchronize(function() {
  204. test.finish();
  205. });
  206. }
  207. // defer when previous test run passed, if storage is available
  208. var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName);
  209. if (bad) {
  210. run();
  211. } else {
  212. synchronize(run);
  213. };
  214. }
  215. };
  216. var QUnit = {
  217. // call on start of module test to prepend name to all tests
  218. module: function(name, testEnvironment) {
  219. config.currentModule = name;
  220. config.currentModuleTestEnviroment = testEnvironment;
  221. },
  222. asyncTest: function(testName, expected, callback) {
  223. if ( arguments.length === 2 ) {
  224. callback = expected;
  225. expected = 0;
  226. }
  227. QUnit.test(testName, expected, callback, true);
  228. },
  229. test: function(testName, expected, callback, async) {
  230. var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
  231. if ( arguments.length === 2 ) {
  232. callback = expected;
  233. expected = null;
  234. }
  235. // is 2nd argument a testEnvironment?
  236. if ( expected && typeof expected === 'object') {
  237. testEnvironmentArg = expected;
  238. expected = null;
  239. }
  240. if ( config.currentModule ) {
  241. name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
  242. }
  243. if ( !validTest(config.currentModule + ": " + testName) ) {
  244. return;
  245. }
  246. var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
  247. test.module = config.currentModule;
  248. test.moduleTestEnvironment = config.currentModuleTestEnviroment;
  249. test.queue();
  250. },
  251. /**
  252. * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
  253. */
  254. expect: function(asserts) {
  255. config.current.expected = asserts;
  256. },
  257. /**
  258. * Asserts true.
  259. * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
  260. */
  261. ok: function(a, msg) {
  262. a = !!a;
  263. var details = {
  264. result: a,
  265. message: msg
  266. };
  267. msg = escapeHtml(msg);
  268. QUnit.log(details);
  269. config.current.assertions.push({
  270. result: a,
  271. message: msg
  272. });
  273. },
  274. /**
  275. * Checks that the first two arguments are equal, with an optional message.
  276. * Prints out both actual and expected values.
  277. *
  278. * Prefered to ok( actual == expected, message )
  279. *
  280. * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
  281. *
  282. * @param Object actual
  283. * @param Object expected
  284. * @param String message (optional)
  285. */
  286. equal: function(actual, expected, message) {
  287. QUnit.push(expected == actual, actual, expected, message);
  288. },
  289. notEqual: function(actual, expected, message) {
  290. QUnit.push(expected != actual, actual, expected, message);
  291. },
  292. deepEqual: function(actual, expected, message) {
  293. QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
  294. },
  295. notDeepEqual: function(actual, expected, message) {
  296. QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
  297. },
  298. strictEqual: function(actual, expected, message) {
  299. QUnit.push(expected === actual, actual, expected, message);
  300. },
  301. notStrictEqual: function(actual, expected, message) {
  302. QUnit.push(expected !== actual, actual, expected, message);
  303. },
  304. raises: function(block, expected, message) {
  305. var actual, ok = false;
  306. if (typeof expected === 'string') {
  307. message = expected;
  308. expected = null;
  309. }
  310. try {
  311. block();
  312. } catch (e) {
  313. actual = e;
  314. }
  315. if (actual) {
  316. // we don't want to validate thrown error
  317. if (!expected) {
  318. ok = true;
  319. // expected is a regexp
  320. } else if (QUnit.objectType(expected) === "regexp") {
  321. ok = expected.test(actual);
  322. // expected is a constructor
  323. } else if (actual instanceof expected) {
  324. ok = true;
  325. // expected is a validation function which returns true is validation passed
  326. } else if (expected.call({}, actual) === true) {
  327. ok = true;
  328. }
  329. }
  330. QUnit.ok(ok, message);
  331. },
  332. start: function() {
  333. config.semaphore--;
  334. if (config.semaphore > 0) {
  335. // don't start until equal number of stop-calls
  336. return;
  337. }
  338. if (config.semaphore < 0) {
  339. // ignore if start is called more often then stop
  340. config.semaphore = 0;
  341. }
  342. // A slight delay, to avoid any current callbacks
  343. if ( defined.setTimeout ) {
  344. window.setTimeout(function() {
  345. if ( config.timeout ) {
  346. clearTimeout(config.timeout);
  347. }
  348. config.blocking = false;
  349. process();
  350. }, 13);
  351. } else {
  352. config.blocking = false;
  353. process();
  354. }
  355. },
  356. stop: function(timeout) {
  357. config.semaphore++;
  358. config.blocking = true;
  359. if ( timeout && defined.setTimeout ) {
  360. clearTimeout(config.timeout);
  361. config.timeout = window.setTimeout(function() {
  362. QUnit.ok( false, "Test timed out" );
  363. QUnit.start();
  364. }, timeout);
  365. }
  366. },
  367. url: function( params ) {
  368. params = extend( extend( {}, QUnit.urlParams ), params );
  369. var querystring = "?",
  370. key;
  371. for ( key in params ) {
  372. querystring += encodeURIComponent( key ) + "=" +
  373. encodeURIComponent( params[ key ] ) + "&";
  374. }
  375. return window.location.pathname + querystring.slice( 0, -1 );
  376. }
  377. };
  378. // Backwards compatibility, deprecated
  379. QUnit.equals = QUnit.equal;
  380. QUnit.same = QUnit.deepEqual;
  381. // Maintain internal state
  382. var config = {
  383. // The queue of tests to run
  384. queue: [],
  385. // block until document ready
  386. blocking: true,
  387. // by default, run previously failed tests first
  388. // very useful in combination with "Hide passed tests" checked
  389. reorder: true,
  390. noglobals: false,
  391. notrycatch: false
  392. };
  393. // Load paramaters
  394. (function() {
  395. var location = window.location || { search: "", protocol: "file:" },
  396. params = location.search.slice( 1 ).split( "&" ),
  397. length = params.length,
  398. urlParams = {},
  399. current;
  400. if ( params[ 0 ] ) {
  401. for ( var i = 0; i < length; i++ ) {
  402. current = params[ i ].split( "=" );
  403. current[ 0 ] = decodeURIComponent( current[ 0 ] );
  404. // allow just a key to turn on a flag, e.g., test.html?noglobals
  405. current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
  406. urlParams[ current[ 0 ] ] = current[ 1 ];
  407. if ( current[ 0 ] in config ) {
  408. config[ current[ 0 ] ] = current[ 1 ];
  409. }
  410. }
  411. }
  412. QUnit.urlParams = urlParams;
  413. config.filter = urlParams.filter;
  414. // Figure out if we're running the tests from a server or not
  415. QUnit.isLocal = !!(location.protocol === 'file:');
  416. })();
  417. // Expose the API as global variables, unless an 'exports'
  418. // object exists, in that case we assume we're in CommonJS
  419. if ( typeof exports === "undefined" || typeof require === "undefined" ) {
  420. extend(window, QUnit);
  421. window.QUnit = QUnit;
  422. } else {
  423. extend(exports, QUnit);
  424. exports.QUnit = QUnit;
  425. }
  426. // define these after exposing globals to keep them in these QUnit namespace only
  427. extend(QUnit, {
  428. config: config,
  429. // Initialize the configuration options
  430. init: function() {
  431. extend(config, {
  432. stats: { all: 0, bad: 0 },
  433. moduleStats: { all: 0, bad: 0 },
  434. started: +new Date,
  435. updateRate: 1000,
  436. blocking: false,
  437. autostart: true,
  438. autorun: false,
  439. filter: "",
  440. queue: [],
  441. semaphore: 0
  442. });
  443. var tests = id( "qunit-tests" ),
  444. banner = id( "qunit-banner" ),
  445. result = id( "qunit-testresult" );
  446. if ( tests ) {
  447. tests.innerHTML = "";
  448. }
  449. if ( banner ) {
  450. banner.className = "";
  451. }
  452. if ( result ) {
  453. result.parentNode.removeChild( result );
  454. }
  455. if ( tests ) {
  456. result = document.createElement( "p" );
  457. result.id = "qunit-testresult";
  458. result.className = "result";
  459. tests.parentNode.insertBefore( result, tests );
  460. result.innerHTML = 'Running...<br/>&nbsp;';
  461. }
  462. },
  463. /**
  464. * Resets the test setup. Useful for tests that modify the DOM.
  465. *
  466. * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
  467. */
  468. reset: function() {
  469. if ( window.jQuery ) {
  470. jQuery( "#main, #qunit-fixture" ).html( config.fixture );
  471. } else {
  472. var main = id( 'main' ) || id( 'qunit-fixture' );
  473. if ( main ) {
  474. main.innerHTML = config.fixture;
  475. }
  476. }
  477. },
  478. /**
  479. * Trigger an event on an element.
  480. *
  481. * @example triggerEvent( document.body, "click" );
  482. *
  483. * @param DOMElement elem
  484. * @param String type
  485. */
  486. triggerEvent: function( elem, type, event ) {
  487. if ( document.createEvent ) {
  488. event = document.createEvent("MouseEvents");
  489. event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
  490. 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  491. elem.dispatchEvent( event );
  492. } else if ( elem.fireEvent ) {
  493. elem.fireEvent("on"+type);
  494. }
  495. },
  496. // Safe object type checking
  497. is: function( type, obj ) {
  498. return QUnit.objectType( obj ) == type;
  499. },
  500. objectType: function( obj ) {
  501. if (typeof obj === "undefined") {
  502. return "undefined";
  503. // consider: typeof null === object
  504. }
  505. if (obj === null) {
  506. return "null";
  507. }
  508. var type = Object.prototype.toString.call( obj )
  509. .match(/^\[object\s(.*)\]$/)[1] || '';
  510. switch (type) {
  511. case 'Number':
  512. if (isNaN(obj)) {
  513. return "nan";
  514. } else {
  515. return "number";
  516. }
  517. case 'String':
  518. case 'Boolean':
  519. case 'Array':
  520. case 'Date':
  521. case 'RegExp':
  522. case 'Function':
  523. return type.toLowerCase();
  524. }
  525. if (typeof obj === "object") {
  526. return "object";
  527. }
  528. return undefined;
  529. },
  530. push: function(result, actual, expected, message) {
  531. var details = {
  532. result: result,
  533. message: message,
  534. actual: actual,
  535. expected: expected
  536. };
  537. message = escapeHtml(message) || (result ? "okay" : "failed");
  538. message = '<span class="test-message">' + message + "</span>";
  539. expected = escapeHtml(QUnit.jsDump.parse(expected));
  540. actual = escapeHtml(QUnit.jsDump.parse(actual));
  541. var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
  542. if (actual != expected) {
  543. output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
  544. output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
  545. }
  546. if (!result) {
  547. var source = sourceFromStacktrace();
  548. if (source) {
  549. details.source = source;
  550. output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';
  551. }
  552. }
  553. output += "</table>";
  554. QUnit.log(details);
  555. config.current.assertions.push({
  556. result: !!result,
  557. message: output
  558. });
  559. },
  560. // Logging callbacks; all receive a single argument with the listed properties
  561. // run test/logs.html for any related changes
  562. begin: function() {},
  563. // done: { failed, passed, total, runtime }
  564. done: function() {},
  565. // log: { result, actual, expected, message }
  566. log: function() {},
  567. // testStart: { name }
  568. testStart: function() {},
  569. // testDone: { name, failed, passed, total }
  570. testDone: function() {},
  571. // moduleStart: { name }
  572. moduleStart: function() {},
  573. // moduleDone: { name, failed, passed, total }
  574. moduleDone: function() {}
  575. });
  576. if ( typeof document === "undefined" || document.readyState === "complete" ) {
  577. config.autorun = true;
  578. }
  579. addEvent(window, "load", function() {
  580. QUnit.begin({});
  581. // Initialize the config, saving the execution queue
  582. var oldconfig = extend({}, config);
  583. QUnit.init();
  584. extend(config, oldconfig);
  585. config.blocking = false;
  586. var userAgent = id("qunit-userAgent");
  587. if ( userAgent ) {
  588. userAgent.innerHTML = navigator.userAgent;
  589. }
  590. var banner = id("qunit-header");
  591. if ( banner ) {
  592. banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' +
  593. '<label><input name="noglobals" type="checkbox"' + ( config.noglobals ? ' checked="checked"' : '' ) + '>noglobals</label>' +
  594. '<label><input name="notrycatch" type="checkbox"' + ( config.notrycatch ? ' checked="checked"' : '' ) + '>notrycatch</label>';
  595. addEvent( banner, "change", function( event ) {
  596. var params = {};
  597. params[ event.target.name ] = event.target.checked ? true : undefined;
  598. window.location = QUnit.url( params );
  599. });
  600. }
  601. var toolbar = id("qunit-testrunner-toolbar");
  602. if ( toolbar ) {
  603. var filter = document.createElement("input");
  604. filter.type = "checkbox";
  605. filter.id = "qunit-filter-pass";
  606. addEvent( filter, "click", function() {
  607. var ol = document.getElementById("qunit-tests");
  608. if ( filter.checked ) {
  609. ol.className = ol.className + " hidepass";
  610. } else {
  611. var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
  612. ol.className = tmp.replace(/ hidepass /, " ");
  613. }
  614. if ( defined.sessionStorage ) {
  615. sessionStorage.setItem("qunit-filter-passed-tests", filter.checked ? "true" : "");
  616. }
  617. });
  618. if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
  619. filter.checked = true;
  620. var ol = document.getElementById("qunit-tests");
  621. ol.className = ol.className + " hidepass";
  622. }
  623. toolbar.appendChild( filter );
  624. var label = document.createElement("label");
  625. label.setAttribute("for", "qunit-filter-pass");
  626. label.innerHTML = "Hide passed tests";
  627. toolbar.appendChild( label );
  628. }
  629. var main = id('main') || id('qunit-fixture');
  630. if ( main ) {
  631. config.fixture = main.innerHTML;
  632. }
  633. if (config.autostart) {
  634. QUnit.start();
  635. }
  636. });
  637. function done() {
  638. config.autorun = true;
  639. // Log the last module results
  640. if ( config.currentModule ) {
  641. QUnit.moduleDone( {
  642. name: config.currentModule,
  643. failed: config.moduleStats.bad,
  644. passed: config.moduleStats.all - config.moduleStats.bad,
  645. total: config.moduleStats.all
  646. } );
  647. }
  648. var banner = id("qunit-banner"),
  649. tests = id("qunit-tests"),
  650. runtime = +new Date - config.started,
  651. passed = config.stats.all - config.stats.bad,
  652. html = [
  653. 'Tests completed in ',
  654. runtime,
  655. ' milliseconds.<br/>',
  656. '<span class="passed">',
  657. passed,
  658. '</span> tests of <span class="total">',
  659. config.stats.all,
  660. '</span> passed, <span class="failed">',
  661. config.stats.bad,
  662. '</span> failed.'
  663. ].join('');
  664. if ( banner ) {
  665. banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
  666. }
  667. if ( tests ) {
  668. id( "qunit-testresult" ).innerHTML = html;
  669. }
  670. QUnit.done( {
  671. failed: config.stats.bad,
  672. passed: passed,
  673. total: config.stats.all,
  674. runtime: runtime
  675. } );
  676. }
  677. function validTest( name ) {
  678. var filter = config.filter,
  679. run = false;
  680. if ( !filter ) {
  681. return true;
  682. }
  683. not = filter.charAt( 0 ) === "!";
  684. if ( not ) {
  685. filter = filter.slice( 1 );
  686. }
  687. if ( name.indexOf( filter ) !== -1 ) {
  688. return !not;
  689. }
  690. if ( not ) {
  691. run = true;
  692. }
  693. return run;
  694. }
  695. // so far supports only Firefox, Chrome and Opera (buggy)
  696. // could be extended in the future to use something like https://github.com/csnover/TraceKit
  697. function sourceFromStacktrace() {
  698. try {
  699. throw new Error();
  700. } catch ( e ) {
  701. if (e.stacktrace) {
  702. // Opera
  703. return e.stacktrace.split("\n")[6];
  704. } else if (e.stack) {
  705. // Firefox, Chrome
  706. return e.stack.split("\n")[4];
  707. }
  708. }
  709. }
  710. function escapeHtml(s) {
  711. if (!s) {
  712. return "";
  713. }
  714. s = s + "";
  715. return s.replace(/[\&"<>\\]/g, function(s) {
  716. switch(s) {
  717. case "&": return "&amp;";
  718. case "\\": return "\\\\";
  719. case '"': return '\"';
  720. case "<": return "&lt;";
  721. case ">": return "&gt;";
  722. default: return s;
  723. }
  724. });
  725. }
  726. function synchronize( callback ) {
  727. config.queue.push( callback );
  728. if ( config.autorun && !config.blocking ) {
  729. process();
  730. }
  731. }
  732. function process() {
  733. var start = (new Date()).getTime();
  734. while ( config.queue.length && !config.blocking ) {
  735. if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
  736. config.queue.shift()();
  737. } else {
  738. window.setTimeout( process, 13 );
  739. break;
  740. }
  741. }
  742. if (!config.blocking && !config.queue.length) {
  743. done();
  744. }
  745. }
  746. function saveGlobal() {
  747. config.pollution = [];
  748. if ( config.noglobals ) {
  749. for ( var key in window ) {
  750. config.pollution.push( key );
  751. }
  752. }
  753. }
  754. function checkPollution( name ) {
  755. var old = config.pollution;
  756. saveGlobal();
  757. var newGlobals = diff( old, config.pollution );
  758. if ( newGlobals.length > 0 ) {
  759. ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
  760. config.current.expected++;
  761. }
  762. var deletedGlobals = diff( config.pollution, old );
  763. if ( deletedGlobals.length > 0 ) {
  764. ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
  765. config.current.expected++;
  766. }
  767. }
  768. // returns a new Array with the elements that are in a but not in b
  769. function diff( a, b ) {
  770. var result = a.slice();
  771. for ( var i = 0; i < result.length; i++ ) {
  772. for ( var j = 0; j < b.length; j++ ) {
  773. if ( result[i] === b[j] ) {
  774. result.splice(i, 1);
  775. i--;
  776. break;
  777. }
  778. }
  779. }
  780. return result;
  781. }
  782. function fail(message, exception, callback) {
  783. if ( typeof console !== "undefined" && console.error && console.warn ) {
  784. console.error(message);
  785. console.error(exception);
  786. console.warn(callback.toString());
  787. } else if ( window.opera && opera.postError ) {
  788. opera.postError(message, exception, callback.toString);
  789. }
  790. }
  791. function extend(a, b) {
  792. for ( var prop in b ) {
  793. if ( b[prop] === undefined ) {
  794. delete a[prop];
  795. } else {
  796. a[prop] = b[prop];
  797. }
  798. }
  799. return a;
  800. }
  801. function addEvent(elem, type, fn) {
  802. if ( elem.addEventListener ) {
  803. elem.addEventListener( type, fn, false );
  804. } else if ( elem.attachEvent ) {
  805. elem.attachEvent( "on" + type, fn );
  806. } else {
  807. fn();
  808. }
  809. }
  810. function id(name) {
  811. return !!(typeof document !== "undefined" && document && document.getElementById) &&
  812. document.getElementById( name );
  813. }
  814. // Test for equality any JavaScript type.
  815. // Discussions and reference: http://philrathe.com/articles/equiv
  816. // Test suites: http://philrathe.com/tests/equiv
  817. // Author: Philippe Rathé <prathe@gmail.com>
  818. QUnit.equiv = function () {
  819. var innerEquiv; // the real equiv function
  820. var callers = []; // stack to decide between skip/abort functions
  821. var parents = []; // stack to avoiding loops from circular referencing
  822. // Call the o related callback with the given arguments.
  823. function bindCallbacks(o, callbacks, args) {
  824. var prop = QUnit.objectType(o);
  825. if (prop) {
  826. if (QUnit.objectType(callbacks[prop]) === "function") {
  827. return callbacks[prop].apply(callbacks, args);
  828. } else {
  829. return callbacks[prop]; // or undefined
  830. }
  831. }
  832. }
  833. var callbacks = function () {
  834. // for string, boolean, number and null
  835. function useStrictEquality(b, a) {
  836. if (b instanceof a.constructor || a instanceof b.constructor) {
  837. // to catch short annotaion VS 'new' annotation of a declaration
  838. // e.g. var i = 1;
  839. // var j = new Number(1);
  840. return a == b;
  841. } else {
  842. return a === b;
  843. }
  844. }
  845. return {
  846. "string": useStrictEquality,
  847. "boolean": useStrictEquality,
  848. "number": useStrictEquality,
  849. "null": useStrictEquality,
  850. "undefined": useStrictEquality,
  851. "nan": function (b) {
  852. return isNaN(b);
  853. },
  854. "date": function (b, a) {
  855. return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
  856. },
  857. "regexp": function (b, a) {
  858. return QUnit.objectType(b) === "regexp" &&
  859. a.source === b.source && // the regex itself
  860. a.global === b.global && // and its modifers (gmi) ...
  861. a.ignoreCase === b.ignoreCase &&
  862. a.multiline === b.multiline;
  863. },
  864. // - skip when the property is a method of an instance (OOP)
  865. // - abort otherwise,
  866. // initial === would have catch identical references anyway
  867. "function": function () {
  868. var caller = callers[callers.length - 1];
  869. return caller !== Object &&
  870. typeof caller !== "undefined";
  871. },
  872. "array": function (b, a) {
  873. var i, j, loop;
  874. var len;
  875. // b could be an object literal here
  876. if ( ! (QUnit.objectType(b) === "array")) {
  877. return false;
  878. }
  879. len = a.length;
  880. if (len !== b.length) { // safe and faster
  881. return false;
  882. }
  883. //track reference to avoid circular references
  884. parents.push(a);
  885. for (i = 0; i < len; i++) {
  886. loop = false;
  887. for(j=0;j<parents.length;j++){
  888. if(parents[j] === a[i]){
  889. loop = true;//dont rewalk array
  890. }
  891. }
  892. if (!loop && ! innerEquiv(a[i], b[i])) {
  893. parents.pop();
  894. return false;
  895. }
  896. }
  897. parents.pop();
  898. return true;
  899. },
  900. "object": function (b, a) {
  901. var i, j, loop;
  902. var eq = true; // unless we can proove it
  903. var aProperties = [], bProperties = []; // collection of strings
  904. // comparing constructors is more strict than using instanceof
  905. if ( a.constructor !== b.constructor) {
  906. return false;
  907. }
  908. // stack constructor before traversing properties
  909. callers.push(a.constructor);
  910. //track reference to avoid circular references
  911. parents.push(a);
  912. for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
  913. loop = false;
  914. for(j=0;j<parents.length;j++){
  915. if(parents[j] === a[i])
  916. loop = true; //don't go down the same path twice
  917. }
  918. aProperties.push(i); // collect a's properties
  919. if (!loop && ! innerEquiv(a[i], b[i])) {
  920. eq = false;
  921. break;
  922. }
  923. }
  924. callers.pop(); // unstack, we are done
  925. parents.pop();
  926. for (i in b) {
  927. bProperties.push(i); // collect b's properties
  928. }
  929. // Ensures identical properties name
  930. return eq && innerEquiv(aProperties.sort(), bProperties.sort());
  931. }
  932. };
  933. }();
  934. innerEquiv = function () { // can take multiple arguments
  935. var args = Array.prototype.slice.apply(arguments);
  936. if (args.length < 2) {
  937. return true; // end transition
  938. }
  939. return (function (a, b) {
  940. if (a === b) {
  941. return true; // catch the most you can
  942. } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
  943. return false; // don't lose time with error prone cases
  944. } else {
  945. return bindCallbacks(a, callbacks, [b, a]);
  946. }
  947. // apply transition with (1..n) arguments
  948. })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
  949. };
  950. return innerEquiv;
  951. }();
  952. /**
  953. * jsDump
  954. * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
  955. * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
  956. * Date: 5/15/2008
  957. * @projectDescription Advanced and extensible data dumping for Javascript.
  958. * @version 1.0.0
  959. * @author Ariel Flesler
  960. * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
  961. */
  962. QUnit.jsDump = (function() {
  963. function quote( str ) {
  964. return '"' + str.toString().replace(/"/g, '\\"') + '"';
  965. };
  966. function literal( o ) {
  967. return o + '';
  968. };
  969. function join( pre, arr, post ) {
  970. var s = jsDump.separator(),
  971. base = jsDump.indent(),
  972. inner = jsDump.indent(1);
  973. if ( arr.join )
  974. arr = arr.join( ',' + s + inner );
  975. if ( !arr )
  976. return pre + post;
  977. return [ pre, inner + arr, base + post ].join(s);
  978. };
  979. function array( arr ) {
  980. var i = arr.length, ret = Array(i);
  981. this.up();
  982. while ( i-- )
  983. ret[i] = this.parse( arr[i] );
  984. this.down();
  985. return join( '[', ret, ']' );
  986. };
  987. var reName = /^function (\w+)/;
  988. var jsDump = {
  989. parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
  990. var parser = this.parsers[ type || this.typeOf(obj) ];
  991. type = typeof parser;
  992. return type == 'function' ? parser.call( this, obj ) :
  993. type == 'string' ? parser :
  994. this.parsers.error;
  995. },
  996. typeOf:function( obj ) {
  997. var type;
  998. if ( obj === null ) {
  999. type = "null";
  1000. } else if (typeof obj === "undefined") {
  1001. type = "undefined";
  1002. } else if (QUnit.is("RegExp", obj)) {
  1003. type = "regexp";
  1004. } else if (QUnit.is("Date", obj)) {
  1005. type = "date";
  1006. } else if (QUnit.is("Function", obj)) {
  1007. type = "function";
  1008. } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
  1009. type = "window";
  1010. } else if (obj.nodeType === 9) {
  1011. type = "document";
  1012. } else if (obj.nodeType) {
  1013. type = "node";
  1014. } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
  1015. type = "array";
  1016. } else {
  1017. type = typeof obj;
  1018. }
  1019. return type;
  1020. },
  1021. separator:function() {
  1022. return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
  1023. },
  1024. indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
  1025. if ( !this.multiline )
  1026. return '';
  1027. var chr = this.indentChar;
  1028. if ( this.HTML )
  1029. chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
  1030. return Array( this._depth_ + (extra||0) ).join(chr);
  1031. },
  1032. up:function( a ) {
  1033. this._depth_ += a || 1;
  1034. },
  1035. down:function( a ) {
  1036. this._depth_ -= a || 1;
  1037. },
  1038. setParser:function( name, parser ) {
  1039. this.parsers[name] = parser;
  1040. },
  1041. // The next 3 are exposed so you can use them
  1042. quote:quote,
  1043. literal:literal,
  1044. join:join,
  1045. //
  1046. _depth_: 1,
  1047. // This is the list of parsers, to modify them, use jsDump.setParser
  1048. parsers:{
  1049. window: '[Window]',
  1050. document: '[Document]',
  1051. error:'[ERROR]', //when no parser is found, shouldn't happen
  1052. unknown: '[Unknown]',
  1053. 'null':'null',
  1054. 'undefined':'undefined',
  1055. 'function':function( fn ) {
  1056. var ret = 'function',
  1057. name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
  1058. if ( name )
  1059. ret += ' ' + name;
  1060. ret += '(';
  1061. ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
  1062. return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
  1063. },
  1064. array: array,
  1065. nodelist: array,
  1066. arguments: array,
  1067. object:function( map ) {
  1068. var ret = [ ];
  1069. QUnit.jsDump.up();
  1070. for ( var key in map )
  1071. ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
  1072. QUnit.jsDump.down();
  1073. return join( '{', ret, '}' );
  1074. },
  1075. node:function( node ) {
  1076. var open = QUnit.jsDump.HTML ? '&lt;' : '<',
  1077. close = QUnit.jsDump.HTML ? '&gt;' : '>';
  1078. var tag = node.nodeName.toLowerCase(),
  1079. ret = open + tag;
  1080. for ( var a in QUnit.jsDump.DOMAttrs ) {
  1081. var val = node[QUnit.jsDump.DOMAttrs[a]];
  1082. if ( val )
  1083. ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
  1084. }
  1085. return ret + close + open + '/' + tag + close;
  1086. },
  1087. functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
  1088. var l = fn.length;
  1089. if ( !l ) return '';
  1090. var args = Array(l);
  1091. while ( l-- )
  1092. args[l] = String.fromCharCode(97+l);//97 is 'a'
  1093. return ' ' + args.join(', ') + ' ';
  1094. },
  1095. key:quote, //object calls it internally, the key part of an item in a map
  1096. functionCode:'[code]', //function calls it internally, it's the content of the function
  1097. attribute:quote, //node calls it internally, it's an html attribute value
  1098. string:quote,
  1099. date:quote,
  1100. regexp:literal, //regex
  1101. number:literal,
  1102. 'boolean':literal
  1103. },
  1104. DOMAttrs:{//attributes to dump from nodes, name=>realName
  1105. id:'id',
  1106. name:'name',
  1107. 'class':'className'
  1108. },
  1109. HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
  1110. indentChar:' ',//indentation unit
  1111. multiline:true //if true, items in a collection, are separated by a \n, else just a space.
  1112. };
  1113. return jsDump;
  1114. })();
  1115. // from Sizzle.js
  1116. function getText( elems ) {
  1117. var ret = "", elem;
  1118. for ( var i = 0; elems[i]; i++ ) {
  1119. elem = elems[i];
  1120. // Get the text from text nodes and CDATA nodes
  1121. if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
  1122. ret += elem.nodeValue;
  1123. // Traverse everything else, except comment nodes
  1124. } else if ( elem.nodeType !== 8 ) {
  1125. ret += getText( elem.childNodes );
  1126. }
  1127. }
  1128. return ret;
  1129. };
  1130. /*
  1131. * Javascript Diff Algorithm
  1132. * By John Resig (http://ejohn.org/)
  1133. * Modified by Chu Alan "sprite"
  1134. *
  1135. * Released under the MIT license.
  1136. *
  1137. * More Info:
  1138. * http://ejohn.org/projects/javascript-diff-algorithm/
  1139. *
  1140. * Usage: QUnit.diff(expected, actual)
  1141. *
  1142. * 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"
  1143. */
  1144. QUnit.diff = (function() {
  1145. function diff(o, n){
  1146. var ns = new Object();
  1147. var os = new Object();
  1148. for (var i = 0; i < n.length; i++) {
  1149. if (ns[n[i]] == null)
  1150. ns[n[i]] = {
  1151. rows: new Array(),
  1152. o: null
  1153. };
  1154. ns[n[i]].rows.push(i);
  1155. }
  1156. for (var i = 0; i < o.length; i++) {
  1157. if (os[o[i]] == null)
  1158. os[o[i]] = {
  1159. rows: new Array(),
  1160. n: null
  1161. };
  1162. os[o[i]].rows.push(i);
  1163. }
  1164. for (var i in ns) {
  1165. if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
  1166. n[ns[i].rows[0]] = {
  1167. text: n[ns[i].rows[0]],
  1168. row: os[i].rows[0]
  1169. };
  1170. o[os[i].rows[0]] = {
  1171. text: o[os[i].rows[0]],
  1172. row: ns[i].rows[0]
  1173. };
  1174. }
  1175. }
  1176. for (var i = 0; i < n.length - 1; i++) {
  1177. if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
  1178. n[i + 1] == o[n[i].row + 1]) {
  1179. n[i + 1] = {
  1180. text: n[i + 1],
  1181. row: n[i].row + 1
  1182. };
  1183. o[n[i].row + 1] = {
  1184. text: o[n[i].row + 1],
  1185. row: i + 1
  1186. };
  1187. }
  1188. }
  1189. for (var i = n.length - 1; i > 0; i--) {
  1190. if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
  1191. n[i - 1] == o[n[i].row - 1]) {
  1192. n[i - 1] = {
  1193. text: n[i - 1],
  1194. row: n[i].row - 1
  1195. };
  1196. o[n[i].row - 1] = {
  1197. text: o[n[i].row - 1],
  1198. row: i - 1
  1199. };
  1200. }
  1201. }
  1202. return {
  1203. o: o,
  1204. n: n
  1205. };
  1206. }
  1207. return function(o, n){
  1208. o = o.replace(/\s+$/, '');
  1209. n = n.replace(/\s+$/, '');
  1210. var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
  1211. var str = "";
  1212. var oSpace = o.match(/\s+/g);
  1213. if (oSpace == null) {
  1214. oSpace = [" "];
  1215. }
  1216. else {
  1217. oSpace.push(" ");
  1218. }
  1219. var nSpace = n.match(/\s+/g);
  1220. if (nSpace == null) {
  1221. nSpace = [" "];
  1222. }
  1223. else {
  1224. nSpace.push(" ");
  1225. }
  1226. if (out.n.length == 0) {
  1227. for (var i = 0; i < out.o.length; i++) {
  1228. str += '<del>' + out.o[i] + oSpace[i] + "</del>";
  1229. }
  1230. }
  1231. else {
  1232. if (out.n[0].text == null) {
  1233. for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
  1234. str += '<del>' + out.o[n] + oSpace[n] + "</del>";
  1235. }
  1236. }
  1237. for (var i = 0; i < out.n.length; i++) {
  1238. if (out.n[i].text == null) {
  1239. str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
  1240. }
  1241. else {
  1242. var pre = "";
  1243. for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
  1244. pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
  1245. }
  1246. str += " " + out.n[i].text + nSpace[i] + pre;
  1247. }
  1248. }
  1249. }
  1250. return str;
  1251. };
  1252. })();
  1253. })(this);