minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / uri-js / tests / qunit.js
1 /*\r
2  * QUnit - A JavaScript Unit Testing Framework\r
3  * \r
4  * http://docs.jquery.com/QUnit\r
5  *\r
6  * Copyright (c) 2009 John Resig, Jörn Zaefferer\r
7  * Dual licensed under the MIT (MIT-LICENSE.txt)\r
8  * and GPL (GPL-LICENSE.txt) licenses.\r
9  */\r
10 \r
11 (function(window) {\r
12 \r
13 var QUnit = {\r
14 \r
15         // Initialize the configuration options\r
16         init: function() {\r
17                 config = {\r
18                         stats: { all: 0, bad: 0 },\r
19                         moduleStats: { all: 0, bad: 0 },\r
20                         started: +new Date,\r
21                         blocking: false,\r
22                         autorun: false,\r
23                         assertions: [],\r
24                         filters: [],\r
25                         queue: []\r
26                 };\r
27 \r
28                 var tests = id("qunit-tests"),\r
29                         banner = id("qunit-banner"),\r
30                         result = id("qunit-testresult");\r
31 \r
32                 if ( tests ) {\r
33                         tests.innerHTML = "";\r
34                 }\r
35 \r
36                 if ( banner ) {\r
37                         banner.className = "";\r
38                 }\r
39 \r
40                 if ( result ) {\r
41                         result.parentNode.removeChild( result );\r
42                 }\r
43         },\r
44         \r
45         // call on start of module test to prepend name to all tests\r
46         module: function(name, testEnvironment) {\r
47                 config.currentModule = name;\r
48 \r
49                 synchronize(function() {\r
50                         if ( config.currentModule ) {\r
51                                 QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );\r
52                         }\r
53 \r
54                         config.currentModule = name;\r
55                         config.moduleTestEnvironment = testEnvironment;\r
56                         config.moduleStats = { all: 0, bad: 0 };\r
57 \r
58                         QUnit.moduleStart( name, testEnvironment );\r
59                 });\r
60         },\r
61 \r
62         asyncTest: function(testName, expected, callback) {\r
63                 if ( arguments.length === 2 ) {\r
64                         callback = expected;\r
65                         expected = 0;\r
66                 }\r
67 \r
68                 QUnit.test(testName, expected, callback, true);\r
69         },\r
70         \r
71         test: function(testName, expected, callback, async) {\r
72                 var name = testName, testEnvironment, testEnvironmentArg;\r
73 \r
74                 if ( arguments.length === 2 ) {\r
75                         callback = expected;\r
76                         expected = null;\r
77                 }\r
78                 // is 2nd argument a testEnvironment?\r
79                 if ( expected && typeof expected === 'object') {\r
80                         testEnvironmentArg =  expected;\r
81                         expected = null;\r
82                 }\r
83 \r
84                 if ( config.currentModule ) {\r
85                         name = config.currentModule + " module: " + name;\r
86                 }\r
87 \r
88                 if ( !validTest(name) ) {\r
89                         return;\r
90                 }\r
91 \r
92                 synchronize(function() {\r
93                         QUnit.testStart( testName );\r
94 \r
95                         testEnvironment = extend({\r
96                                 setup: function() {},\r
97                                 teardown: function() {}\r
98                         }, config.moduleTestEnvironment);\r
99                         if (testEnvironmentArg) {\r
100                                 extend(testEnvironment,testEnvironmentArg);\r
101                         }\r
102 \r
103                         // allow utility functions to access the current test environment\r
104                         QUnit.current_testEnvironment = testEnvironment;\r
105                         \r
106                         config.assertions = [];\r
107                         config.expected = expected;\r
108 \r
109                         try {\r
110                                 if ( !config.pollution ) {\r
111                                         saveGlobal();\r
112                                 }\r
113 \r
114                                 testEnvironment.setup.call(testEnvironment);\r
115                         } catch(e) {\r
116                                 QUnit.ok( false, "Setup failed on " + name + ": " + e.message );\r
117                         }\r
118 \r
119                         if ( async ) {\r
120                                 QUnit.stop();\r
121                         }\r
122 \r
123                         try {\r
124                                 callback.call(testEnvironment);\r
125                         } catch(e) {\r
126                                 fail("Test " + name + " died, exception and test follows", e, callback);\r
127                                 QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );\r
128                                 // else next test will carry the responsibility\r
129                                 saveGlobal();\r
130 \r
131                                 // Restart the tests if they're blocking\r
132                                 if ( config.blocking ) {\r
133                                         start();\r
134                                 }\r
135                         }\r
136                 });\r
137 \r
138                 synchronize(function() {\r
139                         try {\r
140                                 checkPollution();\r
141                                 testEnvironment.teardown.call(testEnvironment);\r
142                         } catch(e) {\r
143                                 QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );\r
144                         }\r
145 \r
146                         try {\r
147                                 QUnit.reset();\r
148                         } catch(e) {\r
149                                 fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);\r
150                         }\r
151 \r
152                         if ( config.expected && config.expected != config.assertions.length ) {\r
153                                 QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );\r
154                         }\r
155 \r
156                         var good = 0, bad = 0,\r
157                                 tests = id("qunit-tests");\r
158 \r
159                         config.stats.all += config.assertions.length;\r
160                         config.moduleStats.all += config.assertions.length;\r
161 \r
162                         if ( tests ) {\r
163                                 var ol  = document.createElement("ol");\r
164                                 ol.style.display = "none";\r
165 \r
166                                 for ( var i = 0; i < config.assertions.length; i++ ) {\r
167                                         var assertion = config.assertions[i];\r
168 \r
169                                         var li = document.createElement("li");\r
170                                         li.className = assertion.result ? "pass" : "fail";\r
171                                         li.appendChild(document.createTextNode(assertion.message || "(no message)"));\r
172                                         ol.appendChild( li );\r
173 \r
174                                         if ( assertion.result ) {\r
175                                                 good++;\r
176                                         } else {\r
177                                                 bad++;\r
178                                                 config.stats.bad++;\r
179                                                 config.moduleStats.bad++;\r
180                                         }\r
181                                 }\r
182 \r
183                                 var b = document.createElement("strong");\r
184                                 b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";\r
185                                 \r
186                                 addEvent(b, "click", function() {\r
187                                         var next = b.nextSibling, display = next.style.display;\r
188                                         next.style.display = display === "none" ? "block" : "none";\r
189                                 });\r
190                                 \r
191                                 addEvent(b, "dblclick", function(e) {\r
192                                         var target = e && e.target ? e.target : window.event.srcElement;\r
193                                         if ( target.nodeName.toLowerCase() === "strong" ) {\r
194                                                 var text = "", node = target.firstChild;\r
195 \r
196                                                 while ( node.nodeType === 3 ) {\r
197                                                         text += node.nodeValue;\r
198                                                         node = node.nextSibling;\r
199                                                 }\r
200 \r
201                                                 text = text.replace(/(^\s*|\s*$)/g, "");\r
202 \r
203                                                 if ( window.location ) {\r
204                                                         window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);\r
205                                                 }\r
206                                         }\r
207                                 });\r
208 \r
209                                 var li = document.createElement("li");\r
210                                 li.className = bad ? "fail" : "pass";\r
211                                 li.appendChild( b );\r
212                                 li.appendChild( ol );\r
213                                 tests.appendChild( li );\r
214 \r
215                                 if ( bad ) {\r
216                                         var toolbar = id("qunit-testrunner-toolbar");\r
217                                         if ( toolbar ) {\r
218                                                 toolbar.style.display = "block";\r
219                                                 id("qunit-filter-pass").disabled = null;\r
220                                                 id("qunit-filter-missing").disabled = null;\r
221                                         }\r
222                                 }\r
223 \r
224                         } else {\r
225                                 for ( var i = 0; i < config.assertions.length; i++ ) {\r
226                                         if ( !config.assertions[i].result ) {\r
227                                                 bad++;\r
228                                                 config.stats.bad++;\r
229                                                 config.moduleStats.bad++;\r
230                                         }\r
231                                 }\r
232                         }\r
233 \r
234                         QUnit.testDone( testName, bad, config.assertions.length );\r
235 \r
236                         if ( !window.setTimeout && !config.queue.length ) {\r
237                                 done();\r
238                         }\r
239                 });\r
240 \r
241                 if ( window.setTimeout && !config.doneTimer ) {\r
242                         config.doneTimer = window.setTimeout(function(){\r
243                                 if ( !config.queue.length ) {\r
244                                         done();\r
245                                 } else {\r
246                                         synchronize( done );\r
247                                 }\r
248                         }, 13);\r
249                 }\r
250         },\r
251         \r
252         /**\r
253          * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.\r
254          */\r
255         expect: function(asserts) {\r
256                 config.expected = asserts;\r
257         },\r
258 \r
259         /**\r
260          * Asserts true.\r
261          * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );\r
262          */\r
263         ok: function(a, msg) {\r
264                 QUnit.log(a, msg);\r
265 \r
266                 config.assertions.push({\r
267                         result: !!a,\r
268                         message: msg\r
269                 });\r
270         },\r
271 \r
272         /**\r
273          * Checks that the first two arguments are equal, with an optional message.\r
274          * Prints out both actual and expected values.\r
275          *\r
276          * Prefered to ok( actual == expected, message )\r
277          *\r
278          * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );\r
279          *\r
280          * @param Object actual\r
281          * @param Object expected\r
282          * @param String message (optional)\r
283          */\r
284         equal: function(actual, expected, message) {\r
285                 push(expected == actual, actual, expected, message);\r
286         },\r
287 \r
288         notEqual: function(actual, expected, message) {\r
289                 push(expected != actual, actual, expected, message);\r
290         },\r
291         \r
292         deepEqual: function(a, b, message) {\r
293                 push(QUnit.equiv(a, b), a, b, message);\r
294         },\r
295 \r
296         notDeepEqual: function(a, b, message) {\r
297                 push(!QUnit.equiv(a, b), a, b, message);\r
298         },\r
299 \r
300         strictEqual: function(actual, expected, message) {\r
301                 push(expected === actual, actual, expected, message);\r
302         },\r
303 \r
304         notStrictEqual: function(actual, expected, message) {\r
305                 push(expected !== actual, actual, expected, message);\r
306         },\r
307         \r
308         start: function() {\r
309                 // A slight delay, to avoid any current callbacks\r
310                 if ( window.setTimeout ) {\r
311                         window.setTimeout(function() {\r
312                                 if ( config.timeout ) {\r
313                                         clearTimeout(config.timeout);\r
314                                 }\r
315 \r
316                                 config.blocking = false;\r
317                                 process();\r
318                         }, 13);\r
319                 } else {\r
320                         config.blocking = false;\r
321                         process();\r
322                 }\r
323         },\r
324         \r
325         stop: function(timeout) {\r
326                 config.blocking = true;\r
327 \r
328                 if ( timeout && window.setTimeout ) {\r
329                         config.timeout = window.setTimeout(function() {\r
330                                 QUnit.ok( false, "Test timed out" );\r
331                                 QUnit.start();\r
332                         }, timeout);\r
333                 }\r
334         },\r
335         \r
336         /**\r
337          * Resets the test setup. Useful for tests that modify the DOM.\r
338          */\r
339         reset: function() {\r
340                 if ( window.jQuery ) {\r
341                         jQuery("#main").html( config.fixture );\r
342                         jQuery.event.global = {};\r
343                         jQuery.ajaxSettings = extend({}, config.ajaxSettings);\r
344                 }\r
345         },\r
346         \r
347         /**\r
348          * Trigger an event on an element.\r
349          *\r
350          * @example triggerEvent( document.body, "click" );\r
351          *\r
352          * @param DOMElement elem\r
353          * @param String type\r
354          */\r
355         triggerEvent: function( elem, type, event ) {\r
356                 if ( document.createEvent ) {\r
357                         event = document.createEvent("MouseEvents");\r
358                         event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,\r
359                                 0, 0, 0, 0, 0, false, false, false, false, 0, null);\r
360                         elem.dispatchEvent( event );\r
361 \r
362                 } else if ( elem.fireEvent ) {\r
363                         elem.fireEvent("on"+type);\r
364                 }\r
365         },\r
366         \r
367         // Safe object type checking\r
368         is: function( type, obj ) {\r
369                 return Object.prototype.toString.call( obj ) === "[object "+ type +"]";\r
370         },\r
371         \r
372         // Logging callbacks\r
373         done: function(failures, total) {},\r
374         log: function(result, message) {},\r
375         testStart: function(name) {},\r
376         testDone: function(name, failures, total) {},\r
377         moduleStart: function(name, testEnvironment) {},\r
378         moduleDone: function(name, failures, total) {}\r
379 };\r
380 \r
381 // Backwards compatibility, deprecated\r
382 QUnit.equals = QUnit.equal;\r
383 QUnit.same = QUnit.deepEqual;\r
384 \r
385 // Maintain internal state\r
386 var config = {\r
387         // The queue of tests to run\r
388         queue: [],\r
389 \r
390         // block until document ready\r
391         blocking: true\r
392 };\r
393 \r
394 // Load paramaters\r
395 (function() {\r
396         var location = window.location || { search: "", protocol: "file:" },\r
397                 GETParams = location.search.slice(1).split('&');\r
398 \r
399         for ( var i = 0; i < GETParams.length; i++ ) {\r
400                 GETParams[i] = decodeURIComponent( GETParams[i] );\r
401                 if ( GETParams[i] === "noglobals" ) {\r
402                         GETParams.splice( i, 1 );\r
403                         i--;\r
404                         config.noglobals = true;\r
405                 } else if ( GETParams[i].search('=') > -1 ) {\r
406                         GETParams.splice( i, 1 );\r
407                         i--;\r
408                 }\r
409         }\r
410         \r
411         // restrict modules/tests by get parameters\r
412         config.filters = GETParams;\r
413         \r
414         // Figure out if we're running the tests from a server or not\r
415         QUnit.isLocal = !!(location.protocol === 'file:');\r
416 })();\r
417 \r
418 // Expose the API as global variables, unless an 'exports'\r
419 // object exists, in that case we assume we're in CommonJS\r
420 if ( typeof exports === "undefined" || typeof require === "undefined" ) {\r
421         extend(window, QUnit);\r
422         window.QUnit = QUnit;\r
423 } else {\r
424         extend(exports, QUnit);\r
425         exports.QUnit = QUnit;\r
426 }\r
427 \r
428 if ( typeof document === "undefined" || document.readyState === "complete" ) {\r
429         config.autorun = true;\r
430 }\r
431 \r
432 addEvent(window, "load", function() {\r
433         // Initialize the config, saving the execution queue\r
434         var oldconfig = extend({}, config);\r
435         QUnit.init();\r
436         extend(config, oldconfig);\r
437 \r
438         config.blocking = false;\r
439 \r
440         var userAgent = id("qunit-userAgent");\r
441         if ( userAgent ) {\r
442                 userAgent.innerHTML = navigator.userAgent;\r
443         }\r
444         \r
445         var toolbar = id("qunit-testrunner-toolbar");\r
446         if ( toolbar ) {\r
447                 toolbar.style.display = "none";\r
448                 \r
449                 var filter = document.createElement("input");\r
450                 filter.type = "checkbox";\r
451                 filter.id = "qunit-filter-pass";\r
452                 filter.disabled = true;\r
453                 addEvent( filter, "click", function() {\r
454                         var li = document.getElementsByTagName("li");\r
455                         for ( var i = 0; i < li.length; i++ ) {\r
456                                 if ( li[i].className.indexOf("pass") > -1 ) {\r
457                                         li[i].style.display = filter.checked ? "none" : "";\r
458                                 }\r
459                         }\r
460                 });\r
461                 toolbar.appendChild( filter );\r
462 \r
463                 var label = document.createElement("label");\r
464                 label.setAttribute("for", "qunit-filter-pass");\r
465                 label.innerHTML = "Hide passed tests";\r
466                 toolbar.appendChild( label );\r
467 \r
468                 var missing = document.createElement("input");\r
469                 missing.type = "checkbox";\r
470                 missing.id = "qunit-filter-missing";\r
471                 missing.disabled = true;\r
472                 addEvent( missing, "click", function() {\r
473                         var li = document.getElementsByTagName("li");\r
474                         for ( var i = 0; i < li.length; i++ ) {\r
475                                 if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {\r
476                                         li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";\r
477                                 }\r
478                         }\r
479                 });\r
480                 toolbar.appendChild( missing );\r
481 \r
482                 label = document.createElement("label");\r
483                 label.setAttribute("for", "qunit-filter-missing");\r
484                 label.innerHTML = "Hide missing tests (untested code is broken code)";\r
485                 toolbar.appendChild( label );\r
486         }\r
487 \r
488         var main = id('main');\r
489         if ( main ) {\r
490                 config.fixture = main.innerHTML;\r
491         }\r
492 \r
493         if ( window.jQuery ) {\r
494                 config.ajaxSettings = window.jQuery.ajaxSettings;\r
495         }\r
496 \r
497         QUnit.start();\r
498 });\r
499 \r
500 function done() {\r
501         if ( config.doneTimer && window.clearTimeout ) {\r
502                 window.clearTimeout( config.doneTimer );\r
503                 config.doneTimer = null;\r
504         }\r
505 \r
506         if ( config.queue.length ) {\r
507                 config.doneTimer = window.setTimeout(function(){\r
508                         if ( !config.queue.length ) {\r
509                                 done();\r
510                         } else {\r
511                                 synchronize( done );\r
512                         }\r
513                 }, 13);\r
514 \r
515                 return;\r
516         }\r
517 \r
518         config.autorun = true;\r
519 \r
520         // Log the last module results\r
521         if ( config.currentModule ) {\r
522                 QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );\r
523         }\r
524 \r
525         var banner = id("qunit-banner"),\r
526                 tests = id("qunit-tests"),\r
527                 html = ['Tests completed in ',\r
528                 +new Date - config.started, ' milliseconds.<br/>',\r
529                 '<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('');\r
530 \r
531         if ( banner ) {\r
532                 banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");\r
533         }\r
534 \r
535         if ( tests ) {  \r
536                 var result = id("qunit-testresult");\r
537 \r
538                 if ( !result ) {\r
539                         result = document.createElement("p");\r
540                         result.id = "qunit-testresult";\r
541                         result.className = "result";\r
542                         tests.parentNode.insertBefore( result, tests.nextSibling );\r
543                 }\r
544 \r
545                 result.innerHTML = html;\r
546         }\r
547 \r
548         QUnit.done( config.stats.bad, config.stats.all );\r
549 }\r
550 \r
551 function validTest( name ) {\r
552         var i = config.filters.length,\r
553                 run = false;\r
554 \r
555         if ( !i ) {\r
556                 return true;\r
557         }\r
558         \r
559         while ( i-- ) {\r
560                 var filter = config.filters[i],\r
561                         not = filter.charAt(0) == '!';\r
562 \r
563                 if ( not ) {\r
564                         filter = filter.slice(1);\r
565                 }\r
566 \r
567                 if ( name.indexOf(filter) !== -1 ) {\r
568                         return !not;\r
569                 }\r
570 \r
571                 if ( not ) {\r
572                         run = true;\r
573                 }\r
574         }\r
575 \r
576         return run;\r
577 }\r
578 \r
579 function push(result, actual, expected, message) {\r
580         message = message || (result ? "okay" : "failed");\r
581         QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );\r
582 }\r
583 \r
584 function synchronize( callback ) {\r
585         config.queue.push( callback );\r
586 \r
587         if ( config.autorun && !config.blocking ) {\r
588                 process();\r
589         }\r
590 }\r
591 \r
592 function process() {\r
593         while ( config.queue.length && !config.blocking ) {\r
594                 config.queue.shift()();\r
595         }\r
596 }\r
597 \r
598 function saveGlobal() {\r
599         config.pollution = [];\r
600         \r
601         if ( config.noglobals ) {\r
602                 for ( var key in window ) {\r
603                         config.pollution.push( key );\r
604                 }\r
605         }\r
606 }\r
607 \r
608 function checkPollution( name ) {\r
609         var old = config.pollution;\r
610         saveGlobal();\r
611         \r
612         var newGlobals = diff( old, config.pollution );\r
613         if ( newGlobals.length > 0 ) {\r
614                 ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );\r
615                 config.expected++;\r
616         }\r
617 \r
618         var deletedGlobals = diff( config.pollution, old );\r
619         if ( deletedGlobals.length > 0 ) {\r
620                 ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );\r
621                 config.expected++;\r
622         }\r
623 }\r
624 \r
625 // returns a new Array with the elements that are in a but not in b\r
626 function diff( a, b ) {\r
627         var result = a.slice();\r
628         for ( var i = 0; i < result.length; i++ ) {\r
629                 for ( var j = 0; j < b.length; j++ ) {\r
630                         if ( result[i] === b[j] ) {\r
631                                 result.splice(i, 1);\r
632                                 i--;\r
633                                 break;\r
634                         }\r
635                 }\r
636         }\r
637         return result;\r
638 }\r
639 \r
640 function fail(message, exception, callback) {\r
641         if ( typeof console !== "undefined" && console.error && console.warn ) {\r
642                 console.error(message);\r
643                 console.error(exception);\r
644                 console.warn(callback.toString());\r
645 \r
646         } else if ( window.opera && opera.postError ) {\r
647                 opera.postError(message, exception, callback.toString);\r
648         }\r
649 }\r
650 \r
651 function extend(a, b) {\r
652         for ( var prop in b ) {\r
653                 a[prop] = b[prop];\r
654         }\r
655 \r
656         return a;\r
657 }\r
658 \r
659 function addEvent(elem, type, fn) {\r
660         if ( elem.addEventListener ) {\r
661                 elem.addEventListener( type, fn, false );\r
662         } else if ( elem.attachEvent ) {\r
663                 elem.attachEvent( "on" + type, fn );\r
664         } else {\r
665                 fn();\r
666         }\r
667 }\r
668 \r
669 function id(name) {\r
670         return !!(typeof document !== "undefined" && document && document.getElementById) &&\r
671                 document.getElementById( name );\r
672 }\r
673 \r
674 // Test for equality any JavaScript type.\r
675 // Discussions and reference: http://philrathe.com/articles/equiv\r
676 // Test suites: http://philrathe.com/tests/equiv\r
677 // Author: Philippe Rathé <prathe@gmail.com>\r
678 QUnit.equiv = function () {\r
679 \r
680     var innerEquiv; // the real equiv function\r
681     var callers = []; // stack to decide between skip/abort functions\r
682 \r
683 \r
684     // Determine what is o.\r
685     function hoozit(o) {\r
686         if (QUnit.is("String", o)) {\r
687             return "string";\r
688             \r
689         } else if (QUnit.is("Boolean", o)) {\r
690             return "boolean";\r
691 \r
692         } else if (QUnit.is("Number", o)) {\r
693 \r
694             if (isNaN(o)) {\r
695                 return "nan";\r
696             } else {\r
697                 return "number";\r
698             }\r
699 \r
700         } else if (typeof o === "undefined") {\r
701             return "undefined";\r
702 \r
703         // consider: typeof null === object\r
704         } else if (o === null) {\r
705             return "null";\r
706 \r
707         // consider: typeof [] === object\r
708         } else if (QUnit.is( "Array", o)) {\r
709             return "array";\r
710         \r
711         // consider: typeof new Date() === object\r
712         } else if (QUnit.is( "Date", o)) {\r
713             return "date";\r
714 \r
715         // consider: /./ instanceof Object;\r
716         //           /./ instanceof RegExp;\r
717         //          typeof /./ === "function"; // => false in IE and Opera,\r
718         //                                          true in FF and Safari\r
719         } else if (QUnit.is( "RegExp", o)) {\r
720             return "regexp";\r
721 \r
722         } else if (typeof o === "object") {\r
723             return "object";\r
724 \r
725         } else if (QUnit.is( "Function", o)) {\r
726             return "function";\r
727         } else {\r
728             return undefined;\r
729         }\r
730     }\r
731 \r
732     // Call the o related callback with the given arguments.\r
733     function bindCallbacks(o, callbacks, args) {\r
734         var prop = hoozit(o);\r
735         if (prop) {\r
736             if (hoozit(callbacks[prop]) === "function") {\r
737                 return callbacks[prop].apply(callbacks, args);\r
738             } else {\r
739                 return callbacks[prop]; // or undefined\r
740             }\r
741         }\r
742     }\r
743     \r
744     var callbacks = function () {\r
745 \r
746         // for string, boolean, number and null\r
747         function useStrictEquality(b, a) {\r
748             if (b instanceof a.constructor || a instanceof b.constructor) {\r
749                 // to catch short annotaion VS 'new' annotation of a declaration\r
750                 // e.g. var i = 1;\r
751                 //      var j = new Number(1);\r
752                 return a == b;\r
753             } else {\r
754                 return a === b;\r
755             }\r
756         }\r
757 \r
758         return {\r
759             "string": useStrictEquality,\r
760             "boolean": useStrictEquality,\r
761             "number": useStrictEquality,\r
762             "null": useStrictEquality,\r
763             "undefined": useStrictEquality,\r
764 \r
765             "nan": function (b) {\r
766                 return isNaN(b);\r
767             },\r
768 \r
769             "date": function (b, a) {\r
770                 return hoozit(b) === "date" && a.valueOf() === b.valueOf();\r
771             },\r
772 \r
773             "regexp": function (b, a) {\r
774                 return hoozit(b) === "regexp" &&\r
775                     a.source === b.source && // the regex itself\r
776                     a.global === b.global && // and its modifers (gmi) ...\r
777                     a.ignoreCase === b.ignoreCase &&\r
778                     a.multiline === b.multiline;\r
779             },\r
780 \r
781             // - skip when the property is a method of an instance (OOP)\r
782             // - abort otherwise,\r
783             //   initial === would have catch identical references anyway\r
784             "function": function () {\r
785                 var caller = callers[callers.length - 1];\r
786                 return caller !== Object &&\r
787                         typeof caller !== "undefined";\r
788             },\r
789 \r
790             "array": function (b, a) {\r
791                 var i;\r
792                 var len;\r
793 \r
794                 // b could be an object literal here\r
795                 if ( ! (hoozit(b) === "array")) {\r
796                     return false;\r
797                 }\r
798 \r
799                 len = a.length;\r
800                 if (len !== b.length) { // safe and faster\r
801                     return false;\r
802                 }\r
803                 for (i = 0; i < len; i++) {\r
804                     if ( ! innerEquiv(a[i], b[i])) {\r
805                         return false;\r
806                     }\r
807                 }\r
808                 return true;\r
809             },\r
810 \r
811             "object": function (b, a) {\r
812                 var i;\r
813                 var eq = true; // unless we can proove it\r
814                 var aProperties = [], bProperties = []; // collection of strings\r
815 \r
816                 // comparing constructors is more strict than using instanceof\r
817                 if ( a.constructor !== b.constructor) {\r
818                     return false;\r
819                 }\r
820 \r
821                 // stack constructor before traversing properties\r
822                 callers.push(a.constructor);\r
823 \r
824                 for (i in a) { // be strict: don't ensures hasOwnProperty and go deep\r
825 \r
826                     aProperties.push(i); // collect a's properties\r
827 \r
828                     if ( ! innerEquiv(a[i], b[i])) {\r
829                         eq = false;\r
830                     }\r
831                 }\r
832 \r
833                 callers.pop(); // unstack, we are done\r
834 \r
835                 for (i in b) {\r
836                     bProperties.push(i); // collect b's properties\r
837                 }\r
838 \r
839                 // Ensures identical properties name\r
840                 return eq && innerEquiv(aProperties.sort(), bProperties.sort());\r
841             }\r
842         };\r
843     }();\r
844 \r
845     innerEquiv = function () { // can take multiple arguments\r
846         var args = Array.prototype.slice.apply(arguments);\r
847         if (args.length < 2) {\r
848             return true; // end transition\r
849         }\r
850 \r
851         return (function (a, b) {\r
852             if (a === b) {\r
853                 return true; // catch the most you can\r
854             } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {\r
855                 return false; // don't lose time with error prone cases\r
856             } else {\r
857                 return bindCallbacks(a, callbacks, [b, a]);\r
858             }\r
859 \r
860         // apply transition with (1..n) arguments\r
861         })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));\r
862     };\r
863 \r
864     return innerEquiv;\r
865 \r
866 }();\r
867 \r
868 /**\r
869  * jsDump\r
870  * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com\r
871  * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)\r
872  * Date: 5/15/2008\r
873  * @projectDescription Advanced and extensible data dumping for Javascript.\r
874  * @version 1.0.0\r
875  * @author Ariel Flesler\r
876  * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}\r
877  */\r
878 QUnit.jsDump = (function() {\r
879         function quote( str ) {\r
880                 return '"' + str.toString().replace(/"/g, '\\"') + '"';\r
881         };\r
882         function literal( o ) {\r
883                 return o + '';  \r
884         };\r
885         function join( pre, arr, post ) {\r
886                 var s = jsDump.separator(),\r
887                         base = jsDump.indent(),\r
888                         inner = jsDump.indent(1);\r
889                 if ( arr.join )\r
890                         arr = arr.join( ',' + s + inner );\r
891                 if ( !arr )\r
892                         return pre + post;\r
893                 return [ pre, inner + arr, base + post ].join(s);\r
894         };\r
895         function array( arr ) {\r
896                 var i = arr.length,     ret = Array(i);                                 \r
897                 this.up();\r
898                 while ( i-- )\r
899                         ret[i] = this.parse( arr[i] );                          \r
900                 this.down();\r
901                 return join( '[', ret, ']' );\r
902         };\r
903         \r
904         var reName = /^function (\w+)/;\r
905         \r
906         var jsDump = {\r
907                 parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance\r
908                         var     parser = this.parsers[ type || this.typeOf(obj) ];\r
909                         type = typeof parser;                   \r
910                         \r
911                         return type == 'function' ? parser.call( this, obj ) :\r
912                                    type == 'string' ? parser :\r
913                                    this.parsers.error;\r
914                 },\r
915                 typeOf:function( obj ) {\r
916                         var type;\r
917                         if ( obj === null ) {\r
918                                 type = "null";\r
919                         } else if (typeof obj === "undefined") {\r
920                                 type = "undefined";\r
921                         } else if (QUnit.is("RegExp", obj)) {\r
922                                 type = "regexp";\r
923                         } else if (QUnit.is("Date", obj)) {\r
924                                 type = "date";\r
925                         } else if (QUnit.is("Function", obj)) {\r
926                                 type = "function";\r
927                         } else if (QUnit.is("Array", obj)) {\r
928                                 type = "array";\r
929                         } else if (QUnit.is("Window", obj) || QUnit.is("global", obj)) {\r
930                                 type = "window";\r
931                         } else if (QUnit.is("HTMLDocument", obj)) {\r
932                                 type = "document";\r
933                         } else if (QUnit.is("HTMLCollection", obj) || QUnit.is("NodeList", obj)) {\r
934                                 type = "nodelist";\r
935                         } else if (/^\[object HTML/.test(Object.prototype.toString.call( obj ))) {\r
936                                 type = "node";\r
937                         } else {\r
938                                 type = typeof obj;\r
939                         }\r
940                         return type;\r
941                 },\r
942                 separator:function() {\r
943                         return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';\r
944                 },\r
945                 indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing\r
946                         if ( !this.multiline )\r
947                                 return '';\r
948                         var chr = this.indentChar;\r
949                         if ( this.HTML )\r
950                                 chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');\r
951                         return Array( this._depth_ + (extra||0) ).join(chr);\r
952                 },\r
953                 up:function( a ) {\r
954                         this._depth_ += a || 1;\r
955                 },\r
956                 down:function( a ) {\r
957                         this._depth_ -= a || 1;\r
958                 },\r
959                 setParser:function( name, parser ) {\r
960                         this.parsers[name] = parser;\r
961                 },\r
962                 // The next 3 are exposed so you can use them\r
963                 quote:quote, \r
964                 literal:literal,\r
965                 join:join,\r
966                 //\r
967                 _depth_: 1,\r
968                 // This is the list of parsers, to modify them, use jsDump.setParser\r
969                 parsers:{\r
970                         window: '[Window]',\r
971                         document: '[Document]',\r
972                         error:'[ERROR]', //when no parser is found, shouldn't happen\r
973                         unknown: '[Unknown]',\r
974                         'null':'null',\r
975                         undefined:'undefined',\r
976                         'function':function( fn ) {\r
977                                 var ret = 'function',\r
978                                         name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE\r
979                                 if ( name )\r
980                                         ret += ' ' + name;\r
981                                 ret += '(';\r
982                                 \r
983                                 ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');\r
984                                 return join( ret, this.parse(fn,'functionCode'), '}' );\r
985                         },\r
986                         array: array,\r
987                         nodelist: array,\r
988                         arguments: array,\r
989                         object:function( map ) {\r
990                                 var ret = [ ];\r
991                                 this.up();\r
992                                 for ( var key in map )\r
993                                         ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );\r
994                                 this.down();\r
995                                 return join( '{', ret, '}' );\r
996                         },\r
997                         node:function( node ) {\r
998                                 var open = this.HTML ? '&lt;' : '<',\r
999                                         close = this.HTML ? '&gt;' : '>';\r
1000                                         \r
1001                                 var tag = node.nodeName.toLowerCase(),\r
1002                                         ret = open + tag;\r
1003                                         \r
1004                                 for ( var a in this.DOMAttrs ) {\r
1005                                         var val = node[this.DOMAttrs[a]];\r
1006                                         if ( val )\r
1007                                                 ret += ' ' + a + '=' + this.parse( val, 'attribute' );\r
1008                                 }\r
1009                                 return ret + close + open + '/' + tag + close;\r
1010                         },\r
1011                         functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function\r
1012                                 var l = fn.length;\r
1013                                 if ( !l ) return '';                            \r
1014                                 \r
1015                                 var args = Array(l);\r
1016                                 while ( l-- )\r
1017                                         args[l] = String.fromCharCode(97+l);//97 is 'a'\r
1018                                 return ' ' + args.join(', ') + ' ';\r
1019                         },\r
1020                         key:quote, //object calls it internally, the key part of an item in a map\r
1021                         functionCode:'[code]', //function calls it internally, it's the content of the function\r
1022                         attribute:quote, //node calls it internally, it's an html attribute value\r
1023                         string:quote,\r
1024                         date:quote,\r
1025                         regexp:literal, //regex\r
1026                         number:literal,\r
1027                         'boolean':literal\r
1028                 },\r
1029                 DOMAttrs:{//attributes to dump from nodes, name=>realName\r
1030                         id:'id',\r
1031                         name:'name',\r
1032                         'class':'className'\r
1033                 },\r
1034                 HTML:true,//if true, entities are escaped ( <, >, \t, space and \n )\r
1035                 indentChar:'   ',//indentation unit\r
1036                 multiline:true //if true, items in a collection, are separated by a \n, else just a space.\r
1037         };\r
1038 \r
1039         return jsDump;\r
1040 })();\r
1041 \r
1042 })(this);\r