-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
139 lines (128 loc) · 4.57 KB
/
index.js
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* `jsonhtmlify()` takes any type (object, array, string, null, ...) and converts
* it to a DOM structure.
*
* This function does not do any styling, and it does also does not provide any
* click events. See the examples on [github](http://htmlpreview.github.io/?https://github.com/jhthorsen/jsonhtmlify/blob/master/examples/index.html)
* for information on how to do that.
*
* What it does however, is that it adds many classes to the DOM elements, so
* you style and modify it however you like.
*
* @example
* // Import the function
* const {jsonhtmlify} = require('jsonhtmlify');
*
* // <span class="json-boolean">true</span>
* const domNode = jsonhtmlify(true);
*
* // <span class="json-number">42</span>
* const domNode = jsonhtmlify(42);
*
* // <span class="json-string">foo</span>
* const domNode = jsonhtmlify('foo');
*
* // <span class="json-null">null</span>
* const domNode = jsonhtmlify(null);
* const domNode = jsonhtmlify(undefined);
*
* // <div class="json-array is-empty"></div>
* const domNode = jsonhtmlify([]);
*
* // <div class="json-object is-empty"></div>
* const domNode = jsonhtmlify({});
*
* // <div class="json-object has-items">
* // <div class="json-item contains-number">
* // <span class="json-key">age</span><span class="json-number">36</span>
* // </div>
* // <div class="json-item contains-array has-items">
* // <span class="json-key">languages</span><span class="json-type">array[2]</span>
* // <div class="json-array has-items">
* // <div class="json-item contains-string">
* // <span class="json-key">0</span><span class="json-string">norwegian</span>
* // </div>
* // <div class="json-item contains-string">
* // <span class="json-key">1</span><span class="json-string">english</span>
* // </div>
* // </div>
* // </div>
* // </div>
* const domNode = jsonhtmlify({age: 36, languages: ['norwegian', 'english']});
*
* @param {Any} json An array, boolean, null, number, object or string.
* @returns {HTMLElement} A DOM element.
*/
function jsonhtmlify(json) {
let rootEl = document.createElement('div');
const queue = [[json, rootEl]];
const visited = [];
TOPIC:
while (queue.length) {
const [topic, parentEl] = queue.shift();
let topicEl, keyByIndex;
// Figure out the type we are working with
let type = typeof topic; // boolean, number, string, ...
if (topic === null || type == 'undefined') {
type = 'null';
}
else if (Array.isArray(topic)) {
type = 'array';
}
// Convert topic to a DOM element
if (type == 'array') {
keyByIndex = (i) => i;
keyByIndex.len = topic.length;
topicEl = document.createElement('div');
topicEl.className = 'json-array ' + (keyByIndex.len ? 'has-items' : 'is-empty');
}
else if (type == 'object') {
const keys = Object.keys(topic).sort();
keyByIndex = (i) => keys[i];
keyByIndex.len = keys.length;
topicEl = document.createElement('div');
topicEl.className = 'json-object ' + (keyByIndex.len ? 'has-items' : 'is-empty');
}
else {
topicEl = document.createElement('span');
topicEl.className = 'json-' + type;
topicEl.textContent = type == 'null' ? 'null' : type != 'boolean' ? topic : topic ? 'true' : 'false';
}
// Iterate over children of array object
if (keyByIndex) {
// Add "array[...]" or "object[...]" type/description
const typeEl = document.createElement('span');
typeEl.className = 'json-type';
typeEl.textContent = keyByIndex.len ? type + '[' + keyByIndex.len + ']': '{}';
parentEl.appendChild(typeEl);
// Guard against recursive structures
if (visited.indexOf(topic) != -1) {
rootEl.classList.add('has-recursive-items');
topicEl.classList.add('is-seen');
}
else {
// Create child nodes for array or object
for (let i = 0; i < keyByIndex.len; i++) {
const key = keyByIndex(i);
const itemEl = document.createElement('div');
const keyEl = document.createElement('span');
keyEl.className = 'json-key';
keyEl.textContent = key;
itemEl.appendChild(keyEl);
topicEl.appendChild(itemEl);
queue.push([topic[key], itemEl]);
}
visited.push(topic);
}
}
parentEl.className = 'json-item ' + topicEl.className.replace(/^json-/, 'contains-');
parentEl.appendChild(topicEl);
}
return rootEl;
}
// Not very pretty, but seems to work
try {
module.exports = {jsonhtmlify};
} catch(err) {
window.jsonhtmlify = jsonhtmlify;
}