-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathstringify.js
More file actions
109 lines (92 loc) · 2.98 KB
/
stringify.js
File metadata and controls
109 lines (92 loc) · 2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
* Stringify.
* Inspect native browser objects and functions.
*/
const stringify = (function () {
const sortci = function(a, b) {
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
};
let htmlEntities = function (str) {
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
};
/**
* Recursively stringify an object. Keeps track of which objects it has
* visited to avoid hitting circular references, and a buffer for indentation.
* Goes 2 levels deep.
*/
return function stringify(o, visited, buffer) {
let i, vi, type = '', parts = [], circular = false;
buffer = buffer || '';
visited = visited || [];
// Get out fast with primitives that don't like toString
if (o === null) {
return 'null';
}
if (typeof o === 'undefined') {
return 'undefined';
}
// Determine the type
try {
type = ({}).toString.call(o);
} catch (e) { // only happens when typeof is protected (...randomly)
type = '[object Object]';
}
// Handle the primitive types
if (type == '[object Number]') {
return ''+o;
}
if (type == '[object Boolean]') {
return o ? 'true' : 'false';
}
if (type == '[object Function]') {
return o.toString().split('\n ').join('\n' + buffer);
}
if (type == '[object String]') {
return '"' + htmlEntities(o.replace(/"/g, '\\"')) + '"';
}
// Check for circular references
for (vi = 0; vi < visited.length; vi++) {
if (o === visited[vi]) {
// Notify the user that a circular object was found and, if available,
// show the object's outerHTML (for body and elements)
return '[circular ' + type.slice(1) +
('outerHTML' in o ? ' :\n' + htmlEntities(o.outerHTML).split('\n').join('\n' + buffer) : '')
}
}
// Remember that we visited this object
visited.push(o);
// Stringify each member of the array
if (type == '[object Array]') {
for (i = 0; i < o.length; i++) {
parts.push(stringify(o[i], visited));
}
return '[' + parts.join(', ') + ']';
}
// Fake array – very tricksy, get out quickly
if (type.match(/Array/)) {
return type;
}
var typeStr = type + ' ',
newBuffer = buffer + ' ';
// Dive down if we're less than 2 levels deep
if (buffer.length / 2 < 2) {
var names = [];
// Some objects don't like 'in', so just skip them
try {
for (i in o) {
names.push(i);
}
} catch (e) {}
names.sort(sortci);
for (i = 0; i < names.length; i++) {
try {
parts.push(newBuffer + names[i] + ': ' + stringify(o[names[i]], visited, newBuffer));
} catch (e) {}
}
}
// If nothing was gathered, return empty object
if (!parts.length) return typeStr + '{ ... }';
// Return the indented object with new lines
return typeStr + '{\n' + parts.join(',\n') + '\n' + buffer + '}';
};
}());