juice_extractor 0.0.0.4 → 0.0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,815 @@
1
+ /*
2
+ * QUnit - jQuery unit testrunner
3
+ *
4
+ * http://docs.jquery.com/QUnit
5
+ *
6
+ * Copyright (c) 2008 John Resig, Jörn Zaefferer
7
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
8
+ * and GPL (GPL-LICENSE.txt) licenses.
9
+ *
10
+ * $Id: testrunner.js 6173 2009-02-02 20:09:32Z jeresig $
11
+ */
12
+
13
+ (function($) {
14
+
15
+ // Tests for equality any JavaScript type and structure without unexpected results.
16
+ // Discussions and reference: http://philrathe.com/articles/equiv
17
+ // Test suites: http://philrathe.com/tests/equiv
18
+ // Author: Philippe Rathé <prathe@gmail.com>
19
+ var equiv = function () {
20
+
21
+ var innerEquiv; // the real equiv function
22
+ var callers = []; // stack to decide between skip/abort functions
23
+
24
+ // Determine what is o.
25
+ function hoozit(o) {
26
+ if (typeof o === "string") {
27
+ return "string";
28
+
29
+ } else if (typeof o === "boolean") {
30
+ return "boolean";
31
+
32
+ } else if (typeof o === "number") {
33
+
34
+ if (isNaN(o)) {
35
+ return "nan";
36
+ } else {
37
+ return "number";
38
+ }
39
+
40
+ } else if (typeof o === "undefined") {
41
+ return "undefined";
42
+
43
+ // consider: typeof null === object
44
+ } else if (o === null) {
45
+ return "null";
46
+
47
+ // consider: typeof [] === object
48
+ } else if (o instanceof Array) {
49
+ return "array";
50
+
51
+ // consider: typeof new Date() === object
52
+ } else if (o instanceof Date) {
53
+ return "date";
54
+
55
+ // consider: /./ instanceof Object;
56
+ // /./ instanceof RegExp;
57
+ // typeof /./ === "function"; // => false in IE and Opera,
58
+ // true in FF and Safari
59
+ } else if (o instanceof RegExp) {
60
+ return "regexp";
61
+
62
+ } else if (typeof o === "object") {
63
+ return "object";
64
+
65
+ } else if (o instanceof Function) {
66
+ return "function";
67
+ }
68
+ }
69
+
70
+ // Call the o related callback with the given arguments.
71
+ function bindCallbacks(o, callbacks, args) {
72
+ var prop = hoozit(o);
73
+ if (prop) {
74
+ if (hoozit(callbacks[prop]) === "function") {
75
+ return callbacks[prop].apply(callbacks, args);
76
+ } else {
77
+ return callbacks[prop]; // or undefined
78
+ }
79
+ }
80
+ }
81
+
82
+ var callbacks = function () {
83
+
84
+ // for string, boolean, number and null
85
+ function useStrictEquality(b, a) {
86
+ return a === b;
87
+ }
88
+
89
+ return {
90
+ "string": useStrictEquality,
91
+ "boolean": useStrictEquality,
92
+ "number": useStrictEquality,
93
+ "null": useStrictEquality,
94
+ "undefined": useStrictEquality,
95
+
96
+ "nan": function (b) {
97
+ return isNaN(b);
98
+ },
99
+
100
+ "date": function (b, a) {
101
+ return hoozit(b) === "date" && a.valueOf() === b.valueOf();
102
+ },
103
+
104
+ "regexp": function (b, a) {
105
+ return hoozit(b) === "regexp" &&
106
+ a.source === b.source && // the regex itself
107
+ a.global === b.global && // and its modifers (gmi) ...
108
+ a.ignoreCase === b.ignoreCase &&
109
+ a.multiline === b.multiline;
110
+ },
111
+
112
+ // - skip when the property is a method of an instance (OOP)
113
+ // - abort otherwise,
114
+ // initial === would have catch identical references anyway
115
+ "function": function () {
116
+ var caller = callers[callers.length - 1];
117
+ return caller !== Object &&
118
+ typeof caller !== "undefined";
119
+ },
120
+
121
+ "array": function (b, a) {
122
+ var i;
123
+ var len;
124
+
125
+ // b could be an object literal here
126
+ if ( ! (hoozit(b) === "array")) {
127
+ return false;
128
+ }
129
+
130
+ len = a.length;
131
+ if (len !== b.length) { // safe and faster
132
+ return false;
133
+ }
134
+ for (i = 0; i < len; i++) {
135
+ if( ! innerEquiv(a[i], b[i])) {
136
+ return false;
137
+ }
138
+ }
139
+ return true;
140
+ },
141
+
142
+ "object": function (b, a) {
143
+ var i;
144
+ var eq = true; // unless we can proove it
145
+ var aProperties = [], bProperties = []; // collection of strings
146
+
147
+ // comparing constructors is more strict than using instanceof
148
+ if ( a.constructor !== b.constructor) {
149
+ return false;
150
+ }
151
+
152
+ // stack constructor before traversing properties
153
+ callers.push(a.constructor);
154
+
155
+ for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
156
+
157
+ aProperties.push(i); // collect a's properties
158
+
159
+ if ( ! innerEquiv(a[i], b[i])) {
160
+ eq = false;
161
+ }
162
+ }
163
+
164
+ callers.pop(); // unstack, we are done
165
+
166
+ for (i in b) {
167
+ bProperties.push(i); // collect b's properties
168
+ }
169
+
170
+ // Ensures identical properties name
171
+ return eq && innerEquiv(aProperties.sort(), bProperties.sort());
172
+ }
173
+ };
174
+ }();
175
+
176
+ innerEquiv = function () { // can take multiple arguments
177
+ var args = Array.prototype.slice.apply(arguments);
178
+ if (args.length < 2) {
179
+ return true; // end transition
180
+ }
181
+
182
+ return (function (a, b) {
183
+ if (a === b) {
184
+ return true; // catch the most you can
185
+
186
+ } else if (typeof a !== typeof b || a === null || b === null || typeof a === "undefined" || typeof b === "undefined") {
187
+ return false; // don't lose time with error prone cases
188
+
189
+ } else {
190
+ return bindCallbacks(a, callbacks, [b, a]);
191
+ }
192
+
193
+ // apply transition with (1..n) arguments
194
+ })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
195
+ };
196
+
197
+ return innerEquiv;
198
+ }(); // equiv
199
+
200
+ var GETParams = $.map( location.search.slice(1).split('&'), decodeURIComponent ),
201
+ ngindex = $.inArray("noglobals", GETParams),
202
+ noglobals = ngindex !== -1;
203
+
204
+ if( noglobals )
205
+ GETParams.splice( ngindex, 1 );
206
+
207
+ var config = {
208
+ stats: {
209
+ all: 0,
210
+ bad: 0,
211
+ modules: 0,
212
+ tests: 0,
213
+ errors: 0
214
+ },
215
+ queue: [],
216
+ // block until document ready
217
+ blocking: true,
218
+ //restrict modules/tests by get parameters
219
+ // filters: GETParams,
220
+ // Made compatible with jstest.rb
221
+ filters: $.grep(GETParams, function(param, i){ return !/^resultsURL/.test(param) && !/^t/.test(param) }),
222
+ // TODO dry the selector
223
+ resultsParams: $.map(GETParams, function(param, i){
224
+ if(/^resultsURL/.test(param)){
225
+ // TODO find the proper jQuery method
226
+ return param.split("resultsURL=")[1];
227
+ } else if (/^t/.test(param)){
228
+ return param;
229
+ } else {
230
+ return null;
231
+ }
232
+ }),
233
+ isLocal: !!(window.location.protocol == 'file:')
234
+ };
235
+
236
+ // public API as global methods
237
+ $.extend(window, {
238
+ test: test,
239
+ module: module,
240
+ expect: expect,
241
+ ok: ok,
242
+ equals: equals,
243
+ start: start,
244
+ stop: stop,
245
+ reset: reset,
246
+ isLocal: config.isLocal,
247
+ same: function(a, b, message) {
248
+ push(equiv(a, b), a, b, message);
249
+ },
250
+ QUnit: {
251
+ equiv: equiv,
252
+ ok: ok,
253
+ done: function(failures, total){},
254
+ log: function(result, message){},
255
+ postResults: function(resultsParams, stats){
256
+ results = [ resultsParams[1],
257
+ "modules=" + stats.modules,
258
+ "tests=" + stats.tests,
259
+ "assertions=" + stats.all,
260
+ "failures=" + stats.bad,
261
+ "errors=" + stats.errors
262
+ ].join("&");
263
+ $.ajax({
264
+ type: "GET",
265
+ url: resultsParams[0],
266
+ data: results,
267
+ async: false
268
+ });
269
+ }
270
+ },
271
+ // legacy methods below
272
+ isSet: isSet,
273
+ isObj: isObj,
274
+ compare: function() {
275
+ throw "compare is deprecated - use same() instead";
276
+ },
277
+ compare2: function() {
278
+ throw "compare2 is deprecated - use same() instead";
279
+ },
280
+ serialArray: function() {
281
+ throw "serialArray is deprecated - use jsDump.parse() instead";
282
+ },
283
+ q: q,
284
+ t: t,
285
+ url: url,
286
+ triggerEvent: triggerEvent
287
+ });
288
+
289
+ $(window).load(function() {
290
+ $('#userAgent').html(navigator.userAgent);
291
+ var head = $('<div class="testrunner-toolbar"><label for="filter-pass">Hide passed tests</label></div>').insertAfter("#userAgent");
292
+ $('<input type="checkbox" id="filter-pass" />').attr("disabled", true).prependTo(head).click(function() {
293
+ $('li.pass')[this.checked ? 'hide' : 'show']();
294
+ });
295
+ $('<input type="checkbox" id="filter-missing">').attr("disabled", true).appendTo(head).click(function() {
296
+ $("li.fail:contains('missing test - untested code is broken code')").parent('ol').parent('li.fail')[this.checked ? 'hide' : 'show']();
297
+ });
298
+ $("#filter-missing").after('<label for="filter-missing">Hide missing tests (untested code is broken code)</label>');
299
+ runTest();
300
+ });
301
+
302
+ function synchronize(callback) {
303
+ config.queue.push(callback);
304
+ if(!config.blocking) {
305
+ process();
306
+ }
307
+ }
308
+
309
+ function process() {
310
+ while(config.queue.length && !config.blocking) {
311
+ config.queue.shift()();
312
+ }
313
+ }
314
+
315
+ function stop(timeout) {
316
+ config.blocking = true;
317
+ if (timeout)
318
+ config.timeout = setTimeout(function() {
319
+ QUnit.ok( false, "Test timed out" );
320
+ start();
321
+ }, timeout);
322
+ }
323
+ function start() {
324
+ // A slight delay, to avoid any current callbacks
325
+ setTimeout(function() {
326
+ if(config.timeout)
327
+ clearTimeout(config.timeout);
328
+ config.blocking = false;
329
+ process();
330
+ }, 13);
331
+ }
332
+
333
+ function validTest( name ) {
334
+ var i = config.filters.length,
335
+ run = false;
336
+
337
+ if( !i )
338
+ return true;
339
+
340
+ while( i-- ){
341
+ var filter = config.filters[i],
342
+ not = filter.charAt(0) == '!';
343
+ if( not )
344
+ filter = filter.slice(1);
345
+ if( name.indexOf(filter) != -1 )
346
+ return !not;
347
+ if( not )
348
+ run = true;
349
+ }
350
+ return run;
351
+ }
352
+
353
+ function runTest() {
354
+ config.blocking = false;
355
+ var started = +new Date;
356
+ config.fixture = document.getElementById('main').innerHTML;
357
+ config.ajaxSettings = $.ajaxSettings;
358
+ synchronize(function() {
359
+ $('<p id="testresult" class="result"/>').html(['Tests completed in ',
360
+ +new Date - started, ' milliseconds.<br/>',
361
+ '<span class="bad">', config.stats.bad, '</span> tests of <span class="all">', config.stats.all, '</span> failed.']
362
+ .join(''))
363
+ .appendTo("body");
364
+ $("#banner").addClass(config.stats.bad ? "fail" : "pass");
365
+ QUnit.postResults(config.resultsParams, config.stats);
366
+ QUnit.done( config.stats.bad, config.stats.all );
367
+ });
368
+ }
369
+
370
+ var pollution;
371
+
372
+ function saveGlobal(){
373
+ pollution = [ ];
374
+
375
+ if( noglobals )
376
+ for( var key in window )
377
+ pollution.push(key);
378
+ }
379
+ function checkPollution( name ){
380
+ var old = pollution;
381
+ saveGlobal();
382
+
383
+ if( pollution.length > old.length ){
384
+ ok( false, "Introduced global variable(s): " + diff(old, pollution).join(", ") );
385
+ config.expected++;
386
+ }
387
+ }
388
+
389
+ function diff( clean, dirty ){
390
+ return $.grep( dirty, function(name){
391
+ return $.inArray( name, clean ) == -1;
392
+ });
393
+ }
394
+
395
+ function test(name, callback) {
396
+ if(config.currentModule)
397
+ name = config.currentModule + " module: " + name;
398
+ var lifecycle = $.extend({
399
+ setup: function() {},
400
+ teardown: function() {}
401
+ }, config.moduleLifecycle);
402
+
403
+ if ( !validTest(name) )
404
+ return;
405
+
406
+ synchronize(function() {
407
+ config.assertions = [];
408
+ config.expected = null;
409
+ config.stats.tests++;
410
+ try {
411
+ if( !pollution )
412
+ saveGlobal();
413
+ lifecycle.setup();
414
+ } catch(e) {
415
+ QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
416
+ }
417
+ })
418
+ synchronize(function() {
419
+ try {
420
+ callback();
421
+ } catch(e) {
422
+ if( typeof console != "undefined" && console.error && console.warn ) {
423
+ config.stats.errors++;
424
+ console.error("Test " + name + " died, exception and test follows");
425
+ console.error(e);
426
+ console.warn(callback.toString());
427
+ }
428
+ QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
429
+ // else next test will carry the responsibility
430
+ saveGlobal();
431
+ }
432
+ });
433
+ synchronize(function() {
434
+ try {
435
+ checkPollution();
436
+ lifecycle.teardown();
437
+ } catch(e) {
438
+ QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
439
+ }
440
+ })
441
+ synchronize(function() {
442
+ try {
443
+ reset();
444
+ } catch(e) {
445
+ if( typeof console != "undefined" && console.error && console.warn ) {
446
+ console.error("reset() failed, following Test " + name + ", exception and reset fn follows");
447
+ console.error(e);
448
+ console.warn(reset.toString());
449
+ }
450
+ }
451
+
452
+ if(config.expected && config.expected != config.assertions.length) {
453
+ QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
454
+ }
455
+
456
+ var good = 0, bad = 0;
457
+ var ol = $("<ol/>").hide();
458
+ config.stats.all += config.assertions.length;
459
+ for ( var i = 0; i < config.assertions.length; i++ ) {
460
+ var assertion = config.assertions[i];
461
+ $("<li/>").addClass(assertion.result ? "pass" : "fail").text(assertion.message || "(no message)").appendTo(ol);
462
+ assertion.result ? good++ : bad++;
463
+ }
464
+ config.stats.bad += bad;
465
+
466
+ var b = $("<strong/>").html(name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>")
467
+ .click(function(){
468
+ $(this).next().toggle();
469
+ })
470
+ .dblclick(function(event) {
471
+ var target = $(event.target).filter("strong").clone();
472
+ if ( target.length ) {
473
+ target.children().remove();
474
+ location.href = location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent($.trim(target.text()));
475
+ }
476
+ });
477
+
478
+ $("<li/>").addClass(bad ? "fail" : "pass").append(b).append(ol).appendTo("#tests");
479
+
480
+ if(bad) {
481
+ $("#filter-pass").attr("disabled", null);
482
+ $("#filter-missing").attr("disabled", null);
483
+ }
484
+ });
485
+ }
486
+
487
+ // call on start of module test to prepend name to all tests
488
+ function module(name, lifecycle) {
489
+ config.currentModule = name;
490
+ config.moduleLifecycle = lifecycle;
491
+ config.stats.modules++;
492
+ }
493
+
494
+ /**
495
+ * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
496
+ */
497
+ function expect(asserts) {
498
+ config.expected = asserts;
499
+ }
500
+
501
+ /**
502
+ * Resets the test setup. Useful for tests that modify the DOM.
503
+ */
504
+ function reset() {
505
+ $("#main").html( config.fixture );
506
+ $.event.global = {};
507
+ $.ajaxSettings = $.extend({}, config.ajaxSettings);
508
+ }
509
+
510
+ /**
511
+ * Asserts true.
512
+ * @example ok( $("a").size() > 5, "There must be at least 5 anchors" );
513
+ */
514
+ function ok(a, msg) {
515
+ QUnit.log(a, msg);
516
+
517
+ config.assertions.push({
518
+ result: !!a,
519
+ message: msg
520
+ });
521
+ }
522
+
523
+ /**
524
+ * Asserts that two arrays are the same
525
+ */
526
+ function isSet(a, b, msg) {
527
+ function serialArray( a ) {
528
+ var r = [];
529
+
530
+ if ( a && a.length )
531
+ for ( var i = 0; i < a.length; i++ ) {
532
+ var str = a[i].nodeName;
533
+ if ( str ) {
534
+ str = str.toLowerCase();
535
+ if ( a[i].id )
536
+ str += "#" + a[i].id;
537
+ } else
538
+ str = a[i];
539
+ r.push( str );
540
+ }
541
+
542
+ return "[ " + r.join(", ") + " ]";
543
+ }
544
+ var ret = true;
545
+ if ( a && b && a.length != undefined && a.length == b.length ) {
546
+ for ( var i = 0; i < a.length; i++ )
547
+ if ( a[i] != b[i] )
548
+ ret = false;
549
+ } else
550
+ ret = false;
551
+ QUnit.ok( ret, !ret ? (msg + " expected: " + serialArray(b) + " result: " + serialArray(a)) : msg );
552
+ }
553
+
554
+ /**
555
+ * Asserts that two objects are equivalent
556
+ */
557
+ function isObj(a, b, msg) {
558
+ var ret = true;
559
+
560
+ if ( a && b ) {
561
+ for ( var i in a )
562
+ if ( a[i] != b[i] )
563
+ ret = false;
564
+
565
+ for ( i in b )
566
+ if ( a[i] != b[i] )
567
+ ret = false;
568
+ } else
569
+ ret = false;
570
+
571
+ QUnit.ok( ret, msg );
572
+ }
573
+
574
+ /**
575
+ * Returns an array of elements with the given IDs, eg.
576
+ * @example q("main", "foo", "bar")
577
+ * @result [<div id="main">, <span id="foo">, <input id="bar">]
578
+ */
579
+ function q() {
580
+ var r = [];
581
+ for ( var i = 0; i < arguments.length; i++ )
582
+ r.push( document.getElementById( arguments[i] ) );
583
+ return r;
584
+ }
585
+
586
+ /**
587
+ * Asserts that a select matches the given IDs
588
+ * @example t("Check for something", "//[a]", ["foo", "baar"]);
589
+ * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar'
590
+ */
591
+ function t(a,b,c) {
592
+ var f = $(b);
593
+ var s = "";
594
+ for ( var i = 0; i < f.length; i++ )
595
+ s += (s && ",") + '"' + f[i].id + '"';
596
+ isSet(f, q.apply(q,c), a + " (" + b + ")");
597
+ }
598
+
599
+ /**
600
+ * Add random number to url to stop IE from caching
601
+ *
602
+ * @example url("data/test.html")
603
+ * @result "data/test.html?10538358428943"
604
+ *
605
+ * @example url("data/test.php?foo=bar")
606
+ * @result "data/test.php?foo=bar&10538358345554"
607
+ */
608
+ function url(value) {
609
+ return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000);
610
+ }
611
+
612
+ /**
613
+ * Checks that the first two arguments are equal, with an optional message.
614
+ * Prints out both actual and expected values.
615
+ *
616
+ * Prefered to ok( actual == expected, message )
617
+ *
618
+ * @example equals( $.format("Received {0} bytes.", 2), "Received 2 bytes." );
619
+ *
620
+ * @param Object actual
621
+ * @param Object expected
622
+ * @param String message (optional)
623
+ */
624
+ function equals(actual, expected, message) {
625
+ push(expected == actual, actual, expected, message);
626
+ }
627
+
628
+ function push(result, actual, expected, message) {
629
+ message = message || (result ? "okay" : "failed");
630
+ QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + jsDump.parse(expected) + " result: " + jsDump.parse(actual) );
631
+ }
632
+
633
+ /**
634
+ * Trigger an event on an element.
635
+ *
636
+ * @example triggerEvent( document.body, "click" );
637
+ *
638
+ * @param DOMElement elem
639
+ * @param String type
640
+ */
641
+ function triggerEvent( elem, type, event ) {
642
+ if ( $.browser.mozilla || $.browser.opera ) {
643
+ event = document.createEvent("MouseEvents");
644
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
645
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
646
+ elem.dispatchEvent( event );
647
+ } else if ( $.browser.msie ) {
648
+ elem.fireEvent("on"+type);
649
+ }
650
+ }
651
+
652
+ })(jQuery);
653
+
654
+ /**
655
+ * jsDump
656
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
657
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
658
+ * Date: 5/15/2008
659
+ * @projectDescription Advanced and extensible data dumping for Javascript.
660
+ * @version 1.0.0
661
+ * @author Ariel Flesler
662
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
663
+ */
664
+ (function(){
665
+ function quote( str ){
666
+ return '"' + str.toString().replace(/"/g, '\\"') + '"';
667
+ };
668
+ function literal( o ){
669
+ return o + '';
670
+ };
671
+ function join( pre, arr, post ){
672
+ var s = jsDump.separator(),
673
+ base = jsDump.indent();
674
+ inner = jsDump.indent(1);
675
+ if( arr.join )
676
+ arr = arr.join( ',' + s + inner );
677
+ if( !arr )
678
+ return pre + post;
679
+ return [ pre, inner + arr, base + post ].join(s);
680
+ };
681
+ function array( arr ){
682
+ var i = arr.length, ret = Array(i);
683
+ this.up();
684
+ while( i-- )
685
+ ret[i] = this.parse( arr[i] );
686
+ this.down();
687
+ return join( '[', ret, ']' );
688
+ };
689
+
690
+ var reName = /^function (\w+)/;
691
+
692
+ var jsDump = window.jsDump = {
693
+ parse:function( obj, type ){//type is used mostly internally, you can fix a (custom)type in advance
694
+ var parser = this.parsers[ type || this.typeOf(obj) ];
695
+ type = typeof parser;
696
+
697
+ return type == 'function' ? parser.call( this, obj ) :
698
+ type == 'string' ? parser :
699
+ this.parsers.error;
700
+ },
701
+ typeOf:function( obj ){
702
+ var type = typeof obj,
703
+ f = 'function';//we'll use it 3 times, save it
704
+ return type != 'object' && type != f ? type :
705
+ !obj ? 'null' :
706
+ obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions
707
+ obj.getHours ? 'date' :
708
+ obj.scrollBy ? 'window' :
709
+ obj.nodeName == '#document' ? 'document' :
710
+ obj.nodeName ? 'node' :
711
+ obj.item ? 'nodelist' : // Safari reports nodelists as functions
712
+ obj.callee ? 'arguments' :
713
+ obj.call || obj.constructor != Array && //an array would also fall on this hack
714
+ (obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects
715
+ 'length' in obj ? 'array' :
716
+ type;
717
+ },
718
+ separator:function(){
719
+ return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
720
+ },
721
+ indent:function( extra ){// extra can be a number, shortcut for increasing-calling-decreasing
722
+ if( !this.multiline )
723
+ return '';
724
+ var chr = this.indentChar;
725
+ if( this.HTML )
726
+ chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
727
+ return Array( this._depth_ + (extra||0) ).join(chr);
728
+ },
729
+ up:function( a ){
730
+ this._depth_ += a || 1;
731
+ },
732
+ down:function( a ){
733
+ this._depth_ -= a || 1;
734
+ },
735
+ setParser:function( name, parser ){
736
+ this.parsers[name] = parser;
737
+ },
738
+ // The next 3 are exposed so you can use them
739
+ quote:quote,
740
+ literal:literal,
741
+ join:join,
742
+ //
743
+ _depth_: 1,
744
+ // This is the list of parsers, to modify them, use jsDump.setParser
745
+ parsers:{
746
+ window: '[Window]',
747
+ document: '[Document]',
748
+ error:'[ERROR]', //when no parser is found, shouldn't happen
749
+ unknown: '[Unknown]',
750
+ 'null':'null',
751
+ undefined:'undefined',
752
+ 'function':function( fn ){
753
+ var ret = 'function',
754
+ name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
755
+ if( name )
756
+ ret += ' ' + name;
757
+ ret += '(';
758
+
759
+ ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
760
+ return join( ret, this.parse(fn,'functionCode'), '}' );
761
+ },
762
+ array: array,
763
+ nodelist: array,
764
+ arguments: array,
765
+ object:function( map ){
766
+ var ret = [ ];
767
+ this.up();
768
+ for( var key in map )
769
+ ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
770
+ this.down();
771
+ return join( '{', ret, '}' );
772
+ },
773
+ node:function( node ){
774
+ var open = this.HTML ? '&lt;' : '<',
775
+ close = this.HTML ? '&gt;' : '>';
776
+
777
+ var tag = node.nodeName.toLowerCase(),
778
+ ret = open + tag;
779
+
780
+ for( var a in this.DOMAttrs ){
781
+ var val = node[this.DOMAttrs[a]];
782
+ if( val )
783
+ ret += ' ' + a + '=' + this.parse( val, 'attribute' );
784
+ }
785
+ return ret + close + open + '/' + tag + close;
786
+ },
787
+ functionArgs:function( fn ){//function calls it internally, it's the arguments part of the function
788
+ var l = fn.length;
789
+ if( !l ) return '';
790
+
791
+ var args = Array(l);
792
+ while( l-- )
793
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
794
+ return ' ' + args.join(', ') + ' ';
795
+ },
796
+ key:quote, //object calls it internally, the key part of an item in a map
797
+ functionCode:'[code]', //function calls it internally, it's the content of the function
798
+ attribute:quote, //node calls it internally, it's an html attribute value
799
+ string:quote,
800
+ date:quote,
801
+ regexp:literal, //regex
802
+ number:literal,
803
+ 'boolean':literal
804
+ },
805
+ DOMAttrs:{//attributes to dump from nodes, name=>realName
806
+ id:'id',
807
+ name:'name',
808
+ 'class':'className'
809
+ },
810
+ HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
811
+ indentChar:' ',//indentation unit
812
+ multiline:true //if true, items in a collection, are separated by a \n, else just a space.
813
+ };
814
+
815
+ })();