UNPKG

6.84 kBJavaScriptView Raw
1(function() {
2 window.onerror = function(message) {
3 window.parent.postMessage({ type: 'iframe-error', message }, '*');
4 };
5 window.addEventListener('unhandledrejection', err => {
6 window.parent.postMessage(
7 { type: 'iframe-error', message: err.reason.stack },
8 '*'
9 );
10 });
11
12 /**
13 * Stringify.
14 * Inspect native browser objects and functions.
15 */
16 const stringify = (function() {
17 const sortci = function(a, b) {
18 return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
19 };
20
21 const htmlEntities = function(str) {
22 return String(str);
23 // .replace(/&/g, '&amp;')
24 // .replace(/</g, '&lt;')
25 // .replace(/>/g, '&gt;')
26 // .replace(/"/g, '&quot;')
27 };
28
29 /**
30 * Recursively stringify an object. Keeps track of which objects it has
31 * visited to avoid hitting circular references, and a buffer for indentation.
32 * Goes 2 levels deep.
33 */
34 return function stringify(o, visited, buffer) {
35 // eslint-disable-line complexity
36 let i;
37 let vi;
38 let type = '';
39 const parts = [];
40 // const circular = false
41 buffer = buffer || '';
42 visited = visited || [];
43
44 // Get out fast with primitives that don't like toString
45 if (o === null) {
46 return 'null';
47 }
48 if (typeof o === 'undefined') {
49 return 'undefined';
50 }
51
52 // Determine the type
53 try {
54 type = {}.toString.call(o);
55 } catch (err) {
56 // only happens when typeof is protected (...randomly)
57 type = '[object Object]';
58 }
59
60 // Handle the primitive types
61 if (type === '[object Number]') {
62 return String(o);
63 }
64 if (type === '[object Boolean]') {
65 return o ? 'true' : 'false';
66 }
67 if (type === '[object Function]') {
68 return o
69 .toString()
70 .split('\n ')
71 .join('\n' + buffer);
72 }
73 if (type === '[object String]') {
74 return '"' + htmlEntities(o.replace(/"/g, '\\"')) + '"';
75 }
76
77 // Check for circular references
78 for (vi = 0; vi < visited.length; vi++) {
79 if (o === visited[vi]) {
80 // Notify the user that a circular object was found and, if available,
81 // show the object's outerHTML (for body and elements)
82 return (
83 '[circular ' +
84 type.slice(1) +
85 ('outerHTML' in o
86 ? ' :\n' +
87 htmlEntities(o.outerHTML)
88 .split('\n')
89 .join('\n' + buffer)
90 : '')
91 );
92 }
93 }
94
95 // Remember that we visited this object
96 visited.push(o);
97
98 // Stringify each member of the array
99 if (type === '[object Array]') {
100 for (i = 0; i < o.length; i++) {
101 parts.push(stringify(o[i], visited));
102 }
103 return '[' + parts.join(', ') + ']';
104 }
105
106 // Fake array – very tricksy, get out quickly
107 if (type.match(/Array/)) {
108 return type;
109 }
110
111 const typeStr = type + ' ';
112 const newBuffer = buffer + ' ';
113
114 // Dive down if we're less than 2 levels deep
115 if (buffer.length / 2 < 2) {
116 const names = [];
117 // Some objects don't like 'in', so just skip them
118 try {
119 for (i in o) {
120 // eslint-disable-line guard-for-in
121 names.push(i);
122 }
123 } catch (err) {
124 /* empty */
125 }
126
127 names.sort(sortci);
128 for (i = 0; i < names.length; i++) {
129 try {
130 parts.push(
131 newBuffer +
132 names[i] +
133 ': ' +
134 stringify(o[names[i]], visited, newBuffer)
135 );
136 } catch (err) {
137 /* empty */
138 }
139 }
140 }
141
142 // If nothing was gathered, return empty object
143 if (parts.length === 0) return typeStr + '{ ... }';
144
145 // Return the indented object with new lines
146 return typeStr + '{\n' + parts.join(',\n') + '\n' + buffer + '}';
147 };
148 })();
149 /** =========================================================================
150 * Console
151 * Proxy console.logs out to the parent window
152 * ========================================================================== */
153
154 const proxyConsole = (function() {
155 const ProxyConsole = function() {};
156
157 /**
158 * Stringify all of the console objects from an array for proxying
159 */
160 const stringifyArgs = function(args) {
161 const newArgs = [];
162 // TODO this was forEach but when the array is [undefined] it wouldn't
163 // iterate over them
164 let i = 0;
165 const length = args.length;
166 let arg;
167 for (; i < length; i++) {
168 arg = args[i];
169 if (typeof arg === 'undefined') {
170 newArgs.push('undefined');
171 } else {
172 newArgs.push(stringify(arg));
173 }
174 }
175 return newArgs;
176 };
177
178 // Create each of these methods on the proxy, and postMessage up to JS Bin
179 // when one is called.
180 const methods = (ProxyConsole.prototype.methods = [
181 'debug',
182 'clear',
183 'error',
184 'info',
185 'log',
186 'warn',
187 'dir',
188 'props',
189 '_raw',
190 'group',
191 'groupEnd',
192 'dirxml',
193 'table',
194 'trace',
195 'assert',
196 'count',
197 'markTimeline',
198 'profile',
199 'profileEnd',
200 'time',
201 'timeEnd',
202 'timeStamp',
203 'groupCollapsed'
204 ]);
205
206 methods.forEach(method => {
207 // Create console method
208 const originalMethod = console[method];
209 const originalClear = console.clear;
210 ProxyConsole.prototype[method] = function() {
211 // Replace args that can't be sent through postMessage
212 const originalArgs = [].slice.call(arguments);
213 const args = stringifyArgs(originalArgs);
214
215 // Post up with method and the arguments
216 window.parent.postMessage(
217 {
218 type: 'demoground-console',
219 method: method === '_raw' ? originalArgs.shift() : method,
220 args: method === '_raw' ? args.slice(1) : args
221 },
222 '*'
223 );
224
225 // If the browner supports it, use the browser console but ignore _raw,
226 // as _raw should only go to the proxy console.
227 // Ignore clear if it doesn't exist as it's beahviour is different than
228 // log and we let it fallback to jsconsole for the panel and to nothing
229 // for the browser console
230 if (!originalMethod) {
231 method = 'log';
232 }
233
234 if (method !== '_raw') {
235 if (method !== 'clear' || (method === 'clear' && originalClear)) {
236 originalMethod.apply(ProxyConsole, originalArgs);
237 }
238 }
239 };
240 });
241
242 return new ProxyConsole();
243 })();
244
245 window.console = proxyConsole;
246})(); // eslint-disable-line semi