test_libs_qunit.js.html 45 KB

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