PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/templates/assets/node_modules/grunt/node_modules/underscore.string/test/test_underscore/vendor/qunit.js

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