1 | var port = chrome.runtime.connect(),
|
---|
2 | collapsers,
|
---|
3 | options,
|
---|
4 | jsonObject,
|
---|
5 | jsonSelector;
|
---|
6 |
|
---|
7 | function displayError(error, loc, offset) {
|
---|
8 | var link = document.createElement('link'),
|
---|
9 | pre = document.body.firstChild.firstChild,
|
---|
10 | text = pre.textContent.substring(offset),
|
---|
11 | start = 0,
|
---|
12 | ranges = [],
|
---|
13 | idx = 0,
|
---|
14 | end,
|
---|
15 | range = document.createRange(),
|
---|
16 | imgError = document.createElement('img'),
|
---|
17 | content = document.createElement('div'),
|
---|
18 | errorPosition = document.createElement('span'),
|
---|
19 | container = document.createElement('div'),
|
---|
20 | closeButton = document.createElement('div');
|
---|
21 | link.rel = 'stylesheet';
|
---|
22 | link.type = 'text/css';
|
---|
23 | link.href = chrome.runtime.getURL('content_error.css');
|
---|
24 | document.head.appendChild(link);
|
---|
25 | while (idx != -1) {
|
---|
26 | idx = text.indexOf('\n', start);
|
---|
27 | ranges.push(start);
|
---|
28 | start = idx + 1;
|
---|
29 | }
|
---|
30 | start = ranges[loc.first_line - 1] + loc.first_column + offset;
|
---|
31 | end = ranges[loc.last_line - 1] + loc.last_column + offset;
|
---|
32 | range.setStart(pre, start);
|
---|
33 | if (start == end - 1) range.setEnd(pre, start);
|
---|
34 | else range.setEnd(pre, end);
|
---|
35 | errorPosition.className = 'error-position';
|
---|
36 | errorPosition.id = 'error-position';
|
---|
37 | range.surroundContents(errorPosition);
|
---|
38 | imgError.src = chrome.runtime.getURL('error.gif');
|
---|
39 | errorPosition.insertBefore(imgError, errorPosition.firstChild);
|
---|
40 | content.className = 'content';
|
---|
41 | closeButton.className = 'close-error';
|
---|
42 | closeButton.onclick = function() {
|
---|
43 | content.parentElement.removeChild(content);
|
---|
44 | };
|
---|
45 | content.textContent = error;
|
---|
46 | content.appendChild(closeButton);
|
---|
47 | container.className = 'container';
|
---|
48 | container.appendChild(content);
|
---|
49 | errorPosition.parentNode.insertBefore(container, errorPosition.nextSibling);
|
---|
50 | location.hash = 'error-position';
|
---|
51 | history.replaceState({}, '', '#');
|
---|
52 | }
|
---|
53 |
|
---|
54 | function displayUI(theme, html) {
|
---|
55 | var statusElement,
|
---|
56 | toolboxElement,
|
---|
57 | expandElement,
|
---|
58 | reduceElement,
|
---|
59 | viewSourceElement,
|
---|
60 | optionsElement,
|
---|
61 | content = '';
|
---|
62 | content +=
|
---|
63 | '<link rel="stylesheet" type="text/css" href="' +
|
---|
64 | chrome.runtime.getURL('jsonview-core.css') +
|
---|
65 | '">';
|
---|
66 | content += '<style>' + theme.replace(/<\/\s*style/g, '') + '</style>';
|
---|
67 | content += html;
|
---|
68 | document.body.innerHTML = content;
|
---|
69 | collapsers = document.querySelectorAll('#json .collapsible .collapsible');
|
---|
70 | statusElement = document.createElement('div');
|
---|
71 | statusElement.className = 'status';
|
---|
72 | copyPathElement = document.createElement('div');
|
---|
73 | copyPathElement.className = 'copy-path';
|
---|
74 | statusElement.appendChild(copyPathElement);
|
---|
75 | document.body.appendChild(statusElement);
|
---|
76 | toolboxElement = document.createElement('div');
|
---|
77 | toolboxElement.className = 'toolbox';
|
---|
78 | expandElement = document.createElement('span');
|
---|
79 | expandElement.title = 'expand all';
|
---|
80 | expandElement.innerText = '+';
|
---|
81 | reduceElement = document.createElement('span');
|
---|
82 | reduceElement.title = 'reduce all';
|
---|
83 | reduceElement.innerText = '-';
|
---|
84 | viewSourceElement = document.createElement('a');
|
---|
85 | viewSourceElement.innerText = 'View source';
|
---|
86 | viewSourceElement.target = '_blank';
|
---|
87 | viewSourceElement.href = 'view-source:' + location.href;
|
---|
88 | optionsElement = document.createElement('img');
|
---|
89 | optionsElement.title = 'options';
|
---|
90 | optionsElement.src = chrome.runtime.getURL('options.png');
|
---|
91 | toolboxElement.appendChild(expandElement);
|
---|
92 | toolboxElement.appendChild(reduceElement);
|
---|
93 | toolboxElement.style.padding = '2px';
|
---|
94 | toolboxElement.style.borderRightWidth = '1px';
|
---|
95 | toolboxElement.style.borderBottomRightRadius = '4px';
|
---|
96 | toolboxElement.style.marginRight = '1.25em';
|
---|
97 | // toolboxElement.appendChild(viewSourceElement);
|
---|
98 | // toolboxElement.appendChild(optionsElement);
|
---|
99 | document.body.appendChild(toolboxElement);
|
---|
100 | document.body.addEventListener('click', ontoggle, false);
|
---|
101 | document.body.addEventListener('mouseover', onmouseMove, false);
|
---|
102 | document.body.addEventListener('click', onmouseClick, false);
|
---|
103 | document.body.addEventListener('contextmenu', onContextMenu, false);
|
---|
104 | expandElement.addEventListener('click', onexpand, false);
|
---|
105 | reduceElement.addEventListener('click', onreduce, false);
|
---|
106 | optionsElement.addEventListener(
|
---|
107 | 'click',
|
---|
108 | function(ev) {
|
---|
109 | if (ev.isTrusted === false) return;
|
---|
110 | window.open(chrome.runtime.getURL('options.html'));
|
---|
111 | },
|
---|
112 | false
|
---|
113 | );
|
---|
114 | copyPathElement.addEventListener(
|
---|
115 | 'click',
|
---|
116 | function(ev) {
|
---|
117 | if (ev.isTrusted === false) return;
|
---|
118 | port.postMessage({
|
---|
119 | copyPropertyPath: true,
|
---|
120 | path: statusElement.innerText
|
---|
121 | });
|
---|
122 | },
|
---|
123 | false
|
---|
124 | );
|
---|
125 | }
|
---|
126 |
|
---|
127 | function extractData(rawText) {
|
---|
128 | var tokens,
|
---|
129 | text = rawText.trim();
|
---|
130 |
|
---|
131 | function test(text) {
|
---|
132 | return (
|
---|
133 | (text.charAt(0) == '[' && text.charAt(text.length - 1) == ']') ||
|
---|
134 | (text.charAt(0) == '{' && text.charAt(text.length - 1) == '}')
|
---|
135 | );
|
---|
136 | }
|
---|
137 |
|
---|
138 | if (test(text))
|
---|
139 | return {
|
---|
140 | text: rawText,
|
---|
141 | offset: 0
|
---|
142 | };
|
---|
143 | tokens = text.match(/^([^\s\(]*)\s*\(([\s\S]*)\)\s*;?$/);
|
---|
144 | if (tokens && tokens[1] && tokens[2]) {
|
---|
145 | if (test(tokens[2].trim()))
|
---|
146 | return {
|
---|
147 | fnName: tokens[1],
|
---|
148 | text: tokens[2],
|
---|
149 | offset: rawText.indexOf(tokens[2])
|
---|
150 | };
|
---|
151 | }
|
---|
152 | }
|
---|
153 |
|
---|
154 | function processData(data) {
|
---|
155 | var xhr, jsonText;
|
---|
156 |
|
---|
157 | function formatToHTML(fnName, offset) {
|
---|
158 | if (!jsonText) return;
|
---|
159 | port.postMessage({
|
---|
160 | jsonToHTML: true,
|
---|
161 | json: jsonText,
|
---|
162 | fnName: fnName,
|
---|
163 | offset: offset
|
---|
164 | });
|
---|
165 | try {
|
---|
166 | jsonObject = JSON.parse(jsonText);
|
---|
167 | } catch (e) {}
|
---|
168 | }
|
---|
169 |
|
---|
170 | if (window == top || options.injectInFrame)
|
---|
171 | if (options.safeMethod) {
|
---|
172 | xhr = new XMLHttpRequest();
|
---|
173 | xhr.onreadystatechange = function() {
|
---|
174 | if (this.readyState == 4) {
|
---|
175 | data = extractData(this.responseText);
|
---|
176 | if (data) {
|
---|
177 | jsonText = data.text;
|
---|
178 | formatToHTML(data.fnName, data.offset);
|
---|
179 | }
|
---|
180 | }
|
---|
181 | };
|
---|
182 | xhr.open('GET', document.location.href, true);
|
---|
183 | xhr.send(null);
|
---|
184 | } else if (data) {
|
---|
185 | jsonText = data.text;
|
---|
186 | formatToHTML(data.fnName, data.offset);
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | function ontoggle(event) {
|
---|
191 | var collapsed,
|
---|
192 | target = event.target;
|
---|
193 | if (event.target.className == 'collapser') {
|
---|
194 | collapsed = target.parentNode.getElementsByClassName('collapsible')[0];
|
---|
195 | if (collapsed.parentNode.classList.contains('collapsed'))
|
---|
196 | collapsed.parentNode.classList.remove('collapsed');
|
---|
197 | else collapsed.parentNode.classList.add('collapsed');
|
---|
198 | }
|
---|
199 | }
|
---|
200 |
|
---|
201 | function onexpand() {
|
---|
202 | Array.prototype.forEach.call(collapsers, function(collapsed) {
|
---|
203 | if (collapsed.parentNode.classList.contains('collapsed'))
|
---|
204 | collapsed.parentNode.classList.remove('collapsed');
|
---|
205 | });
|
---|
206 | }
|
---|
207 |
|
---|
208 | function onreduce() {
|
---|
209 | Array.prototype.forEach.call(collapsers, function(collapsed) {
|
---|
210 | if (!collapsed.parentNode.classList.contains('collapsed'))
|
---|
211 | collapsed.parentNode.classList.add('collapsed');
|
---|
212 | });
|
---|
213 | }
|
---|
214 |
|
---|
215 | function getParentLI(element) {
|
---|
216 | if (element.tagName != 'LI')
|
---|
217 | while (element && element.tagName != 'LI') element = element.parentNode;
|
---|
218 | if (element && element.tagName == 'LI') return element;
|
---|
219 | }
|
---|
220 |
|
---|
221 | var onmouseMove = (function() {
|
---|
222 | var hoveredLI;
|
---|
223 |
|
---|
224 | function onmouseOut() {
|
---|
225 | var statusElement = document.querySelector('.status');
|
---|
226 | if (hoveredLI) {
|
---|
227 | hoveredLI.firstChild.classList.remove('hovered');
|
---|
228 | hoveredLI = null;
|
---|
229 | statusElement.innerText = '';
|
---|
230 | jsonSelector = [];
|
---|
231 | }
|
---|
232 | }
|
---|
233 |
|
---|
234 | return function(event) {
|
---|
235 | if (event.isTrusted === false) return;
|
---|
236 | var str = '',
|
---|
237 | statusElement = document.querySelector('.status');
|
---|
238 | element = getParentLI(event.target);
|
---|
239 | if (element) {
|
---|
240 | jsonSelector = [];
|
---|
241 | if (hoveredLI) hoveredLI.firstChild.classList.remove('hovered');
|
---|
242 | hoveredLI = element;
|
---|
243 | element.firstChild.classList.add('hovered');
|
---|
244 | do {
|
---|
245 | if (element.parentNode.classList.contains('array')) {
|
---|
246 | var index = [].indexOf.call(element.parentNode.children, element);
|
---|
247 | str = '[' + index + ']' + str;
|
---|
248 | jsonSelector.unshift(index);
|
---|
249 | }
|
---|
250 | if (element.parentNode.classList.contains('obj')) {
|
---|
251 | var key = element.firstChild.firstChild.innerText;
|
---|
252 | str = '.' + key + str;
|
---|
253 | jsonSelector.unshift(key);
|
---|
254 | }
|
---|
255 | element = element.parentNode.parentNode.parentNode;
|
---|
256 | } while (element.tagName == 'LI');
|
---|
257 | if (str.charAt(0) == '.') str = str.substring(1);
|
---|
258 | statusElement.innerText = str;
|
---|
259 | return;
|
---|
260 | }
|
---|
261 | onmouseOut();
|
---|
262 | };
|
---|
263 | })();
|
---|
264 |
|
---|
265 | var selectedLI;
|
---|
266 |
|
---|
267 | function onmouseClick() {
|
---|
268 | if (selectedLI) selectedLI.firstChild.classList.remove('selected');
|
---|
269 | selectedLI = getParentLI(event.target);
|
---|
270 | if (selectedLI) {
|
---|
271 | selectedLI.firstChild.classList.add('selected');
|
---|
272 | }
|
---|
273 | }
|
---|
274 |
|
---|
275 | function onContextMenu(ev) {
|
---|
276 | if (ev.isTrusted === false) return;
|
---|
277 | var currentLI,
|
---|
278 | statusElement,
|
---|
279 | selection = '',
|
---|
280 | i,
|
---|
281 | value;
|
---|
282 | currentLI = getParentLI(event.target);
|
---|
283 | statusElement = document.querySelector('.status');
|
---|
284 | if (currentLI) {
|
---|
285 | var value = jsonObject;
|
---|
286 | jsonSelector.forEach(function(idx) {
|
---|
287 | value = value[idx];
|
---|
288 | });
|
---|
289 | port.postMessage({
|
---|
290 | copyPropertyPath: true,
|
---|
291 | path: statusElement.innerText,
|
---|
292 | value: typeof value == 'object' ? JSON.stringify(value) : value
|
---|
293 | });
|
---|
294 | }
|
---|
295 | }
|
---|
296 |
|
---|
297 | function init(data) {
|
---|
298 | port.onMessage.addListener(function(msg) {
|
---|
299 | if (msg.oninit) {
|
---|
300 | options = msg.options;
|
---|
301 | processData(data);
|
---|
302 | }
|
---|
303 | if (msg.onjsonToHTML)
|
---|
304 | if (msg.html) {
|
---|
305 | displayUI(msg.theme, msg.html);
|
---|
306 | } else if (msg.json)
|
---|
307 | port.postMessage({
|
---|
308 | getError: true,
|
---|
309 | json: json,
|
---|
310 | fnName: fnName
|
---|
311 | });
|
---|
312 | if (msg.ongetError) {
|
---|
313 | displayError(msg.error, msg.loc, msg.offset);
|
---|
314 | }
|
---|
315 | });
|
---|
316 | port.postMessage({
|
---|
317 | init: true
|
---|
318 | });
|
---|
319 | }
|
---|
320 |
|
---|
321 | function load() {
|
---|
322 | var child, data;
|
---|
323 | if (
|
---|
324 | document.body &&
|
---|
325 | ((document.body.childNodes[0] &&
|
---|
326 | document.body.childNodes[0].tagName == 'PRE') ||
|
---|
327 | document.body.children.length == 0)
|
---|
328 | ) {
|
---|
329 | child = document.body.children.length
|
---|
330 | ? document.body.childNodes[0]
|
---|
331 | : document.body;
|
---|
332 | data = extractData(child.innerText);
|
---|
333 | if (data) init(data);
|
---|
334 | }
|
---|
335 | }
|
---|
336 |
|
---|
337 | load();
|
---|