Skip to content
This repository has been archived by the owner. It is now read-only.

Commit ac7ca33

Browse files
committed
better object printing. closes #183
1 parent e6b792d commit ac7ca33

File tree

6 files changed

+470
-8
lines changed

6 files changed

+470
-8
lines changed

app/debug/client.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
style = arguments[1];
1212
}
1313
var data = {
14-
msg: msg,
14+
msg: JSON.stringify(JSON.decycle(msg, true), null, ' '),
1515
style: style,
1616
type: func
1717
};
@@ -26,6 +26,7 @@
2626
window.onerror = function(msg, url, num, column, errorObj) {
2727
var data = {
2828
num: num,
29+
// msg: JSON.stringify(JSON.decycle(msg, true), null, ' '),
2930
msg: msg,
3031
type: 'error'
3132
};

app/debug/cycle.js

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
cycle.js
3+
2013-02-19
4+
5+
Public Domain.
6+
7+
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8+
9+
This code should be minified before deployment.
10+
See http://javascript.crockford.com/jsmin.html
11+
12+
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
13+
NOT CONTROL.
14+
*/
15+
16+
/*jslint evil: true, regexp: true */
17+
18+
/*members $ref, apply, call, decycle, hasOwnProperty, length, prototype, push,
19+
retrocycle, stringify, test, toString
20+
*/
21+
22+
if (typeof JSON.decycle !== 'function') {
23+
(function(){
24+
25+
/**
26+
* Allows stringifing DOM elements.
27+
*
28+
* This is done in hope to identify the node when dumping.
29+
*
30+
* @param {Element} node DOM Node (works best for DOM Elements).
31+
* @returns {String}
32+
*/
33+
function stringifyNode(node) {
34+
var text = "";
35+
switch (node.nodeType) {
36+
case node.ELEMENT_NODE:
37+
text = node.nodeName.toLowerCase();
38+
if (node.id.length) {
39+
text += '#' + node.id;
40+
}
41+
else {
42+
if (node.className.length) {
43+
text += '.' + node.className.replace(/ /, '.');
44+
}
45+
if ('textContent' in node) {
46+
text += '{textContent:'
47+
+ (node.textContent.length < 20 ? node.textContent : node.textContent.substr(0, 20) + '...')
48+
+ '}'
49+
;
50+
}
51+
}
52+
break;
53+
// info on values: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1841493061
54+
default:
55+
text = node.nodeName;
56+
if (node.nodeValue !== null) {
57+
text += '{value:'
58+
+ (node.nodeValue.length < 20 ? node.nodeValue : node.nodeValue.substr(0, 20) + '...')
59+
+ '}'
60+
;
61+
}
62+
break;
63+
}
64+
return text;
65+
}
66+
67+
JSON.decycle = function decycle(object, stringifyNodes) {
68+
'use strict';
69+
70+
// Make a deep copy of an object or array, assuring that there is at most
71+
// one instance of each object or array in the resulting structure. The
72+
// duplicate references (which might be forming cycles) are replaced with
73+
// an object of the form
74+
// {$ref: PATH}
75+
// where the PATH is a JSONPath string that locates the first occurance.
76+
// So,
77+
// var a = [];
78+
// a[0] = a;
79+
// return JSON.stringify(JSON.decycle(a));
80+
// produces the string '[{"$ref":"$"}]'.
81+
82+
// NOTE! If your object contains DOM Nodes you might want to use `stringifyNodes` option
83+
// This will dump e.g. `div` with id="some-id" to string: `div#some-id`.
84+
// You will avoid some problems, but you won't to be able to fully retro-cycle.
85+
// To dump almost any variable use: `alert(JSON.stringify(JSON.decycle(variable, true)));`
86+
87+
// JSONPath is used to locate the unique object. $ indicates the top level of
88+
// the object or array. [NUMBER] or [STRING] indicates a child member or
89+
// property.
90+
91+
var objects = [], // Keep a reference to each unique object or array
92+
stringifyNodes = typeof(stringifyNodes) === 'undefined' ? false : stringifyNodes,
93+
paths = []; // Keep the path to each unique object or array
94+
95+
return (function derez(value, path) {
96+
97+
// The derez recurses through the object, producing the deep copy.
98+
99+
var i, // The loop counter
100+
name, // Property name
101+
nu; // The new object or array
102+
103+
// if we have a DOM Element/Node convert it to textual info.
104+
105+
if (stringifyNodes && typeof value === 'object' && value !== null && 'nodeType' in value) {
106+
return stringifyNode(value);
107+
}
108+
109+
// typeof null === 'object', so go on if this value is really an object but not
110+
// one of the weird builtin objects.
111+
112+
if (typeof value === 'object' && value !== null &&
113+
!(value instanceof Boolean) &&
114+
!(value instanceof Date) &&
115+
!(value instanceof Number) &&
116+
!(value instanceof RegExp) &&
117+
!(value instanceof String)) {
118+
119+
// If the value is an object or array, look to see if we have already
120+
// encountered it. If so, return a $ref/path object. This is a hard way,
121+
// linear search that will get slower as the number of unique objects grows.
122+
123+
for (i = 0; i < objects.length; i += 1) {
124+
if (objects[i] === value) {
125+
return {$ref: paths[i]};
126+
}
127+
}
128+
129+
// Otherwise, accumulate the unique value and its path.
130+
131+
objects.push(value);
132+
paths.push(path);
133+
134+
// If it is an array, replicate the array.
135+
136+
if (Object.prototype.toString.apply(value) === '[object Array]') {
137+
nu = [];
138+
for (i = 0; i < value.length; i += 1) {
139+
nu[i] = derez(value[i], path + '[' + i + ']');
140+
}
141+
} else {
142+
143+
// If it is an object, replicate the object.
144+
145+
nu = {};
146+
for (name in value) {
147+
if (Object.prototype.hasOwnProperty.call(value, name)) {
148+
nu[name] = derez(value[name],
149+
path + '[' + JSON.stringify(name) + ']');
150+
}
151+
}
152+
}
153+
return nu;
154+
}
155+
return value;
156+
}(object, '$'));
157+
};
158+
})();
159+
}
160+
161+
162+
if (typeof JSON.retrocycle !== 'function') {
163+
JSON.retrocycle = function retrocycle($) {
164+
'use strict';
165+
166+
// Restore an object that was reduced by decycle. Members whose values are
167+
// objects of the form
168+
// {$ref: PATH}
169+
// are replaced with references to the value found by the PATH. This will
170+
// restore cycles. The object will be mutated.
171+
172+
// The eval function is used to locate the values described by a PATH. The
173+
// root object is kept in a $ variable. A regular expression is used to
174+
// assure that the PATH is extremely well formed. The regexp contains nested
175+
// * quantifiers. That has been known to have extremely bad performance
176+
// problems on some browsers for very long strings. A PATH is expected to be
177+
// reasonably short. A PATH is allowed to belong to a very restricted subset of
178+
// Goessner's JSONPath.
179+
180+
// So,
181+
// var s = '[{"$ref":"$"}]';
182+
// return JSON.retrocycle(JSON.parse(s));
183+
// produces an array containing a single element which is the array itself.
184+
185+
var px =
186+
/^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/;
187+
188+
(function rez(value) {
189+
190+
// The rez function walks recursively through the object looking for $ref
191+
// properties. When it finds one that has a value that is a path, then it
192+
// replaces the $ref object with a reference to the value that is found by
193+
// the path.
194+
195+
var i, item, name, path;
196+
197+
if (value && typeof value === 'object') {
198+
if (Object.prototype.toString.apply(value) === '[object Array]') {
199+
for (i = 0; i < value.length; i += 1) {
200+
item = value[i];
201+
if (item && typeof item === 'object') {
202+
path = item.$ref;
203+
if (typeof path === 'string' && px.test(path)) {
204+
value[i] = eval(path);
205+
} else {
206+
rez(item);
207+
}
208+
}
209+
}
210+
} else {
211+
for (name in value) {
212+
if (typeof value[name] === 'object') {
213+
item = value[name];
214+
if (item) {
215+
path = item.$ref;
216+
if (typeof path === 'string' && px.test(path)) {
217+
value[name] = eval(path);
218+
} else {
219+
rez(item);
220+
}
221+
}
222+
}
223+
}
224+
}
225+
}
226+
}($));
227+
return $;
228+
};
229+
}

app/debug/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,19 @@ module.exports = {
7474
},
7575

7676
debugOut: function(data) {
77-
var msg = data.msg;
77+
var msg = data.msg;//.replace(/\\"/g, '"');
7878
var style = data.style;
7979
var line = data.num;
8080
var type = data.type;
81-
if (typeof msg === 'object') msg = JSON.stringify(msg);
82-
else msg = '' + msg;
81+
// if (typeof msg === 'object') msg = JSON.stringify(msg);
82+
// else msg = '' + msg;
8383
if (msg === 'Uncaught ReferenceError: require is not defined') return false;
8484
if (style) {
8585
msg = msg.replace(/%c/g, '');
8686
msg = msg.replace('[', '');
8787
msg = msg.replace(']', '');
8888
}
8989
msg = AutoLinker.link(msg);
90-
// console.log(data);
9190
$('#debug').append('<div class="'+type+'" style="'+(style ? style : '')+'">' + (line ? line + ': ' : '') + msg + '</div>');
9291
$('#debug').scrollTop($('#debug')[0].scrollHeight);
9392
}

app/debug/style.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
div {
99
margin: 5px;
10+
white-space: pre;
1011
a {
1112
color: #fff;
13+
background-color: #333;
1214
}
1315
}
1416
}

gulpfile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ var latestDir = Path.join(Path.join(builderOptions.buildDir, 'latest'));
5656

5757
var jsPath = ['./app/*.js', './app/**/*.js', './app/**/*.html', './public/index.html'];
5858
var cssPath = './app/**/*.scss';
59-
var debugClientPath = './app/debug/client.js';
59+
var debugClientPath = ['./app/debug/client.js', './app/debug/cycle.js'];
6060

6161

6262
gulp.task('browserify', function() {
@@ -74,7 +74,7 @@ gulp.task('browserify', function() {
7474
});
7575

7676
gulp.task('injected-js', function(){
77-
return gulp.src([debugClientPath])
77+
return gulp.src(debugClientPath)
7878
.pipe(concat('debug-console.js'))
7979
.pipe(gulp.dest('./public/js/'));
8080
});

0 commit comments

Comments
 (0)