source: extensions/json_view/content.js@ 1

Last change on this file since 1 was 1, checked in by Bart Vastenhouw, 5 years ago

First public release

File size: 9.8 KB
Line 
1var port = chrome.runtime.connect(),
2 collapsers,
3 options,
4 jsonObject,
5 jsonSelector;
6
7function 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
54function 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
127function 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
154function 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
190function 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
201function 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
208function 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
215function 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
221var 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
265var selectedLI;
266
267function 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
275function 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
297function 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
321function 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
337load();
Note: See TracBrowser for help on using the repository browser.