UNPKG

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