Completed
Push — master ( e139e8...9e2f61 )
by Dongxin
37s
created

mjsonviewer.js (1 issue)

Severity
1
//////////////////////////////////////////////////////////////////////////////////////
2
// Copyright © 2017 TangDongxin
3
//
4
// Permission is hereby granted, free of charge, to any person obtaining
5
// a copy of this software and associated documentation files (the "Software"),
6
// to deal in the Software without restriction, including without limitation
7
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
// and/or sell copies of the Software, and to permit persons to whom the
9
// Software is furnished to do so, subject to the following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
//////////////////////////////////////////////////////////////////////////////////////
22
23
// ===========================================
24
// JSON PARSER
25
// ===========================================
26
27
var bgColor, intColor, strColor, keyColor, defaultColor;
28
var fontStyle;
29
var strictOnly, hideDetails;
30
31
function onError(error) {
32
    console.log(error);
33
}
34
35
function onGot(result) {
36
    if (result[0]) {
37
        fontStyle    = result[0].fontStyle    || "Consolas";
38
        bgColor      = result[0].bgColor      || "#FDF6E3";
39
        intColor     = result[0].intColor     || "#657A81";
40
        strColor     = result[0].strColor     || "#2AA198";
41
        keyColor     = result[0].keyColor     || "#B58900";
42
        defaultColor = result[0].defaultColor || "#586E75";
43
44
        strictOnly   = result[0].strictOnly   || false;
45
        hideDetails  = result[0].hideDetails  || false;
46
    } else {
47
        fontStyle    = result.fontStyle       || "Consolas";
48
        bgColor      = result.bgColor         || "#FDF6E3";
49
        intColor     = result.intColor        || "#657A81";
50
        strColor     = result.strColor        || "#2AA198";
51
        keyColor     = result.keyColor        || "#B58900";
52
        defaultColor = result.defaultColor    || "#586E75";
53
54
        strictOnly   = result.strictOnly      || false;
55
        hideDetails  = result.hideDetails     || false;
56
    }
57
58
    var str, jsonpMatch, hovered, tag,
59
        chrome = this.chrome || this.browser,
60
        jsonRe = /^\s*(?:\[\s*(?=-?\d|true|false|null|["[{])[^]*\]|\{\s*"[^]+\})\s*$/,
61
        div = document.createElement("div"),
62
        body = document.body,
63
        first = body && body.firstChild,
64
        mod = /Mac|iPod|iPhone|iPad|Pike/.test(navigator.platform) ? "metaKey" : "ctrlKey",
65
        // rand = Math.random().toString(36).slice(2),
66
        rand = 1;
67
        HOV = "H" + rand,
68
        DIV = "D" + rand,
69
        KEY = "K" + rand,
70
        STR = "S" + rand,
71
        BOOL = "B" + rand,
72
        ERR = "E" + rand,
73
        COLL = "C" + rand;
74
75
    function reconvert(str) {
76
        str = str.replace(/(\\u)(\w{1,4})/gi, function($0) {
77
            return (String.fromCharCode(parseInt((escape($0).replace(/(%5Cu)(\w{1,4})/g, "$2")), 16)));
78
        });
79
        str = str.replace(/(&#x)(\w{1,4});/gi, function($0) {
80
            return String.fromCharCode(parseInt(escape($0).replace(/(%26%23x)(\w{1,4})(%3B)/g, "$2"), 16));
81
        });
82
        str = str.replace(/(&#)(\d{1,6});/gi, function($0) {
83
            return String.fromCharCode(parseInt(escape($0).replace(/(%26%23)(\d{1,6})(%3B)/g, "$2")));
84
        });
85
86
        return str;
87
    }
88
89
    function units(size) {
90
        return size > 1048576 ? (0 | (size / 1048576)) + "MB" :
91
            size > 1024 ? (0 | (size / 1024)) + "KB" :
92
            size + "B";
93
    }
94
95
    function fragment(a, b) {
96
        var frag = document.createDocumentFragment();
97
        frag.appendChild(document.createTextNode(a));
98
        if (b) {
99
            frag.appendChild(div.cloneNode());
100
            frag.appendChild(document.createTextNode(b));
101
        } else {
102
            frag.appendChild(document.createElement("br"));
103
        }
104
        return frag;
105
    }
106
107
    function change(node, query, name, set) {
108
        var list = node.querySelectorAll(query),
109
            i = list.length;
110
        for (; i--;) list[i].classList[set ? "add" : "remove"](name);
111
    }
112
113
    function changeSiblings(node, name, set) {
114
        var tmp, i = 0,
115
            query = [];
116
117
        for (; node && node.tagName === "I";) {
118
            tmp = node.previousElementSibling;
119
            if (tmp && tmp.className == KEY) {
120
                query.unshift(".D" + rand + ">i.I" + rand + "[data-key='" + node.dataset.key + "']");
121
            } else if (query[0]) {
122
                query.unshift(".D" + rand + ">i.I" + rand);
123
            } else {
124
                for (; tmp; tmp = tmp.previousElementSibling)
125
                    if (tmp.tagName === "BR") i++;
126
                query.unshift(".D" + rand + ">" + (i ? "br:nth-of-type(" + i + ")+i.I" + rand : "i.I" + rand + ":first-child"));
127
            }
128
            node = node.parentNode && node.parentNode.previousElementSibling;
129
        }
130
        if (!query[1]) return;
131
        query[0] = ".R" + rand + ">i.I" + rand;
132
        change(document, query.join("+"), name, set);
133
    }
134
135
    function keydown(e) {
136
        if (hovered) {
137
            e.preventDefault();
138
            if (e.altKey) {
139
                changeSiblings(hovered, HOV, 1);
140
            } else if (e[mod]) {
141
                change(hovered.nextSibling, "i.I" + rand, HOV, 1);
142
            }
143
        }
144
    }
145
146
    function init() {
147
        tag = document.createElement("style");
148
        tag.textContent = [
149
            '.R', ',.D', '{font:16px ' + fontStyle + '}' +
150
            '.D', '{margin-left:6px; padding-left:1em; margin-top: 1px; border-left:1px dashed; border-color: #93A1A1;}' +
151
            '.X', '{border:1px solid #ccc; padding:1em}' +
152
            'a.L', '{text-decoration:none}' +
153
            'a.L', ':hover,a.L', ':focus{text-decoration:underline}' +
154
            'i.I', '{cursor:pointer;color:#ccc}' +
155
            'i.H', ',i.I', ':hover{text-shadow: 1px 1px 3px #999; color:#333}' +
156
            'i.I', ':before{content:" ▼ "}' +
157
            'i.C', ':before{content:" ▶ "}' +
158
            'i.I', ':after{content:attr(data-content)}' +
159
            'i.C', '+.D', '{width:1px; height:1px; margin:0; padding:0; border:0; display:inline-block; overflow:hidden}' +
160
            '.S', '{color:' + strColor + '}' + // string
161
            '.K', '{color:' + keyColor + '}' + // key
162
            '.E', '{color:#BCADAD}' + // error
163
            '.B', '{color:' + intColor + '}' + // number and bool
164
            '.E', ',.B', '{font-style: italic}' + // number bold
165
            'h3.E', '{margin:0 0 1em}'
166
        ].join(rand);
167
168
        tag.textContent = tag.textContent + 'body {background: ' + bgColor + '; color:' + defaultColor + ';}';
169
170
        div.classList.add(DIV);
171
        document.head.appendChild(tag);
172
        document.addEventListener("keydown", keydown);
173
        document.addEventListener("keyup", function(e) {
174
            if (hovered) change(document, "." + HOV, HOV);
175
        })
176
        document.addEventListener("mouseover", function(e) {
177
            if (e.target.tagName === "I") {
178
                hovered = e.target;
179
                keydown(e);
180
            }
181
        })
182
        document.addEventListener("mouseout", function(e) {
183
            if (hovered) {
184
                change(document, "." + HOV, HOV);
185
                hovered = null;
186
            }
187
        })
188
    }
189
190
    function draw(str, to, first, box) {
191
        tag || init();
192
193
        var re = /("(?:((?:https?|file):\/\/(?:\\?\S)+?)|(?:\\?.)*?)")\s*(:?)|-?\d+\.?\d*(?:e[+-]?\d+)?|true|false|null|[[\]{},]|(\S[^-[\]{},"\d]*)/gi,
194
            node = div.cloneNode(),
195
            link = document.createElement("a"),
196
            span = document.createElement("span"),
197
            info = document.createElement("i"),
198
            colon = document.createTextNode(": "),
199
            comma = fragment(","),
200
            path = [],
201
            cache = {
202
                "{": fragment("{", "}"),
203
                "[": fragment("[", "]")
204
            };
205
206
        node.className = "R" + rand + (box ? " " + box : "");
207
208
        link.classList.add("L" + rand);
209
        info.classList.add("I" + rand);
210
211
        to.addEventListener("click", function(e) {
212
            var target = e.target,
213
                open = target.classList.contains(COLL);
214
            if (target.tagName == "I") {
215
                if (e.altKey) {
216
                    changeSiblings(target, COLL, !open);
217
                } else if (e[mod]) {
218
                    open = target.nextSibling.querySelector("i");
219
                    if (open) change(target.nextSibling, "i", COLL, !open.classList.contains(COLL));
220
                } else {
221
                    target.classList[open ? "remove" : "add"](COLL);
222
                }
223
            }
224
        }, true);
225
226
        to.replaceChild(box = node, first);
227
        loop(str, re);
228
229
        function loop(str, re) {
230
            str = reconvert(str);
231
            var match, val, tmp, i = 0,
232
                len = str.length;
233
            try {
234
                for (; match = re.exec(str);) {
235
                    val = match[0];
236
                    if (val == "{" || val == "[") {
237
                        path.push(node);
238
                        node.appendChild(cache[val].cloneNode(true));
239
                        node = node.lastChild.previousSibling;
240
                        node.len = 1;
241
                        node.start = re.lastIndex;
242
                    } else if ((val == "}" || val == "]") && node.len) {
243
                        if (node.childNodes.length) {
244
                            tmp = info.cloneNode();
245
                            if (!hideDetails) {
246
                                tmp.dataset.content = node.len + (
247
                                    node.len == 1 ?
248
                                    (val == "]" ? " item, " : " property, ") :
249
                                    (val == "]" ? " items, " : " properties, ")
250
                                ) + units(re.lastIndex - node.start + 1);
251
                            }
252
253
                            if ((val = node.previousElementSibling) && val.className == KEY) {
254
                                tmp.dataset.key = reconvert(val.textContent.slice(1, -1).replace(/'/, "\\'"));
255
                            }
256
                            node.parentNode.insertBefore(tmp, node);
257
                        } else {
258
                            node.parentNode.removeChild(node);
259
                        }
260
                        node = path.pop();
261
                    } else if (val == ",") {
262
                        node.len += 1;
263
                        node.appendChild(comma.cloneNode(true));
264
                    } else {
265
                        if (match[2]) {
266
                            tmp = link.cloneNode();
267
                            tmp.href = match[2].replace(/\\"/g, '"');
268
                        } else {
269
                            tmp = span.cloneNode();
270
                        }
271
                        tmp.textContent = match[1] || val;
272
                        tmp.classList.add(match[3] ? KEY : match[1] ? STR : match[4] ? ERR : BOOL);
273
                        node.appendChild(tmp);
274
                        if (match[3]) {
275
                            node.appendChild(colon.cloneNode());
276
                        }
277
                    }
278
                    if (++i > 1000) {
279
                        document.title = (0 | (100 * re.lastIndex / len)) + "% of " + units(len);
280
                        return setTimeout(function() {
281
                            loop(str, re)
282
                        });
283
                    }
284
                }
285
                document.title = ""
286
                JSON.parse(str)
287
            } catch (e) {
288
                tmp = document.createElement("h3");
289
                tmp.className = ERR;
290
                tmp.textContent = e;
291
                box.insertBefore(tmp, box.firstChild);
292
            }
293
        }
294
    }
295
296
    if (strictOnly) {
297
        // only render when the contentType is json
298
        if (/[+\/]json$/i.test(document.contentType)) {
299
            draw(str, body, first)
300
        }
301
    } else {
302
        // check whether the content is json or like json
303
        if (first &&
304
            (first.tagName == "PRE" &&
305
                first == body.lastElementChild ||
306
                first == body.lastChild &&
307
                first.nodeType == 3) &&
308
            (str = first.textContent) &&
309
            (/[+\/]json$/i.test(document.contentType) ||
310
                (jsonpMatch = /^\s*((?:\/\*\*\/\s*)?([$a-z_][$\w]*)\s*(?:&&\s*\2\s*)?\()([^]+)(\)[\s;]*)$/i.exec(str)) &&
311
                jsonRe.test(jsonpMatch[3]) || jsonRe.test(str))) {
312
            if (jsonpMatch) {
313
                str = jsonpMatch[3]
314
                body.replaceChild(fragment(jsonpMatch[1], jsonpMatch[4]), first)
315
                first = body.lastChild.previousSibling
316
            }
317
            draw(str, body, first)
318
        }
319
    }
320
321
    chrome.runtime.onMessage.addListener(function(req, sender, sendResponse) {
322
        var node,
323
            sel = window.getSelection(),
324
            range = sel.rangeCount && sel.getRangeAt(0),
325
            str = range && range.toString()
326
327
        if (!str) return
328
329
        if (req.op === "formatSelection") {
330
            node = document.createElement("div")
331
            range.deleteContents()
332
            range.insertNode(node)
333
            sel.removeAllRanges()
334
            draw(str, node.parentNode, node, "X" + rand)
335
        }
336
    })
337
}
338
339
function formatJson(json, options) {
340
    var reg = null,
341
        formatted = '',
342
        pad = 0,
343
        PADDING = '    ';
344
    options = options || {};
345
    options.newlineAfterColonIfBeforeBraceOrBracket = (options.newlineAfterColonIfBeforeBraceOrBracket === true) ? true : false;
346
    options.spaceAfterColon = (options.spaceAfterColon === false) ? false : true;
347
    if (typeof json !== 'string') {
348
        json = JSON.stringify(json);
349
    } else {
350
        json = JSON.parse(json);
351
        json = JSON.stringify(json);
352
    }
353
    reg = /([\{\}])/g;
354
    json = json.replace(reg, '\r\n$1\r\n');
355
    reg = /([\[\]])/g;
356
    json = json.replace(reg, '\r\n$1\r\n');
357
    reg = /(\,)/g;
358
    json = json.replace(reg, '$1\r\n');
359
    reg = /(\r\n\r\n)/g;
360
    json = json.replace(reg, '\r\n');
361
    reg = /\r\n\,/g;
362
    json = json.replace(reg, ',');
363
    if (!options.newlineAfterColonIfBeforeBraceOrBracket) {
364
        reg = /\:\r\n\{/g;
365
        json = json.replace(reg, ':{');
366
        reg = /\:\r\n\[/g;
367
        json = json.replace(reg, ':[');
368
    }
369
    if (options.spaceAfterColon) {
370
        reg = /\:/g;
371
        json = json.replace(reg, ':');
372
    }(json.split('\r\n')).forEach(function (node, index) {
373
        var i = 0,
374
            indent = 0,
375
            padding = '';
376
        if (node.match(/\{$/) || node.match(/\[$/)) {
377
            indent = 1;
378
        } else if (node.match(/\}/) || node.match(/\]/)) {
379
            if (pad !== 0) {
380
                pad -= 1;
381
            }
382
        } else {
383
            indent = 0;
384
        }
385
        for (i = 0; i < pad; i++) {
386
            padding += PADDING;
387
        }
388
        formatted += padding + node + '\r\n';
389
        pad += indent;
390
    }
391
    );
392
    return formatted;
393
};
394
395
function isJSON(str) {
396
    if (typeof str == 'string') {
397
        try {
398
            var obj = JSON.parse(str);
399
            if (str.indexOf('{') > - 1) {
400
                return true;
401
            } else {
402
                return false;
403
            }
404
        } catch (e) {
405
            return false;
406
        }
407
    }
408
    return false;
409
}
410
411
function onParse() {
412
    var items = document.getElementsByClassName('S1');
413
    for (var i = 0; i < items.length; i++) {
414
        try {
415
            var json = (eval(items[i].innerHTML));
416
            if (isJSON(json)) {
417
                var formated = formatJson(json);
418
                console.log(formated);
0 ignored issues
show
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
419
                items[i].onclick = function() {
420
                    alert(formated);
421
                }
422
            }
423
        } catch (e) {}
424
    }
425
}
426
427
var getting = browser.storage.local.get();
428
getting.then(onGot, onError).then(onParse, onError);
429