|
| 1 | +// prettier-ignore |
| 2 | +// Prettier disabled due to trailing comma not working in IE10/11 |
| 3 | +(function( |
| 4 | + _window, |
| 5 | + _document, |
| 6 | + _script, |
| 7 | + _onerror, |
| 8 | + _onunhandledrejection, |
| 9 | + _namespace, |
| 10 | + _publicKey, |
| 11 | + _sdkBundleUrl, |
| 12 | + _config |
| 13 | +) { |
| 14 | + var lazy = true; |
| 15 | + var forceLoad = false; |
| 16 | + |
| 17 | + for (var i = 0; i < document.scripts.length; i++) { |
| 18 | + if (document.scripts[i].src.indexOf(_publicKey) > -1) { |
| 19 | + lazy = !(document.scripts[i].getAttribute('data-lazy') === 'no'); |
| 20 | + break; |
| 21 | + } |
| 22 | + } |
| 23 | + |
| 24 | + var injected = false; |
| 25 | + var onLoadCallbacks = []; |
| 26 | + |
| 27 | + // Create a namespace and attach function that will store captured exception |
| 28 | + // Because functions are also objects, we can attach the queue itself straight to it and save some bytes |
| 29 | + var queue = function(content) { |
| 30 | + // content.e = error |
| 31 | + // content.p = promise rejection |
| 32 | + // content.f = function call the Sentry |
| 33 | + if ( |
| 34 | + ('e' in content || |
| 35 | + 'p' in content || |
| 36 | + (content.f && content.f.indexOf('capture') > -1) || |
| 37 | + (content.f && content.f.indexOf('showReportDialog') > -1)) && |
| 38 | + lazy |
| 39 | + ) { |
| 40 | + // We only want to lazy inject/load the sdk bundle if |
| 41 | + // an error or promise rejection occured |
| 42 | + // OR someone called `capture...` on the SDK |
| 43 | + injectSdk(onLoadCallbacks); |
| 44 | + } |
| 45 | + queue.data.push(content); |
| 46 | + }; |
| 47 | + queue.data = []; |
| 48 | + |
| 49 | + function injectSdk(callbacks) { |
| 50 | + if (injected) { |
| 51 | + return; |
| 52 | + } |
| 53 | + injected = true; |
| 54 | + |
| 55 | + // Create a `script` tag with provided SDK `url` and attach it just before the first, already existing `script` tag |
| 56 | + // Scripts that are dynamically created and added to the document are async by default, |
| 57 | + // they don't block rendering and execute as soon as they download, meaning they could |
| 58 | + // come out in the wrong order. Because of that we don't need async=1 as GA does. |
| 59 | + // it was probably(?) a legacy behavior that they left to not modify few years old snippet |
| 60 | + // https://www.html5rocks.com/en/tutorials/speed/script-loading/ |
| 61 | + var _currentScriptTag = _document.scripts[0]; |
| 62 | + var _newScriptTag = _document.createElement(_script); |
| 63 | + _newScriptTag.src = _sdkBundleUrl; |
| 64 | + _newScriptTag.setAttribute('crossorigin', 'anonymous'); |
| 65 | + |
| 66 | + // Once our SDK is loaded |
| 67 | + _newScriptTag.addEventListener('load', function() { |
| 68 | + try { |
| 69 | + // Restore onerror/onunhandledrejection handlers |
| 70 | + _window[_onerror] = _oldOnerror; |
| 71 | + _window[_onunhandledrejection] = _oldOnunhandledrejection; |
| 72 | + |
| 73 | + // Add loader as SDK source |
| 74 | + _window.SENTRY_SDK_SOURCE = 'loader'; |
| 75 | + |
| 76 | + var SDK = _window[_namespace]; |
| 77 | + |
| 78 | + var oldInit = SDK.init; |
| 79 | + |
| 80 | + // Configure it using provided DSN and config object |
| 81 | + SDK.init = function(options) { |
| 82 | + var target = _config; |
| 83 | + for (var key in options) { |
| 84 | + if (Object.prototype.hasOwnProperty.call(options, key)) { |
| 85 | + target[key] = options[key]; |
| 86 | + } |
| 87 | + } |
| 88 | + oldInit(target); |
| 89 | + }; |
| 90 | + |
| 91 | + sdkLoaded(callbacks, SDK); |
| 92 | + } catch (o_O) { |
| 93 | + console.error(o_O); |
| 94 | + } |
| 95 | + }); |
| 96 | + |
| 97 | + _currentScriptTag.parentNode.insertBefore(_newScriptTag, _currentScriptTag); |
| 98 | + } |
| 99 | + |
| 100 | + function sdkLoaded(callbacks, SDK) { |
| 101 | + try { |
| 102 | + var data = queue.data; |
| 103 | + |
| 104 | + // We have to make sure to call all callbacks first |
| 105 | + for (var i = 0; i < callbacks.length; i++) { |
| 106 | + if (typeof callbacks[i] === 'function') { |
| 107 | + callbacks[i](); |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + var initAlreadyCalled = false; |
| 112 | + var __sentry = _window['__SENTRY__']; |
| 113 | + // If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked |
| 114 | + if (!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient()) { |
| 115 | + initAlreadyCalled = true; |
| 116 | + } |
| 117 | + |
| 118 | + // We want to replay all calls to Sentry and also make sure that `init` is called if it wasn't already |
| 119 | + // We replay all calls to `Sentry.*` now |
| 120 | + var calledSentry = false; |
| 121 | + for (var i = 0; i < data.length; i++) { |
| 122 | + if (data[i].f) { |
| 123 | + calledSentry = true; |
| 124 | + var call = data[i]; |
| 125 | + if (initAlreadyCalled === false && call.f !== 'init') { |
| 126 | + // First call always has to be init, this is a conveniece for the user so call to init is optional |
| 127 | + SDK.init(); |
| 128 | + } |
| 129 | + initAlreadyCalled = true; |
| 130 | + SDK[call.f].apply(SDK, call.a); |
| 131 | + } |
| 132 | + } |
| 133 | + if (initAlreadyCalled === false && calledSentry === false) { |
| 134 | + // Sentry has never been called but we need Sentry.init() so call it |
| 135 | + SDK.init(); |
| 136 | + } |
| 137 | + |
| 138 | + // Because we installed the SDK, at this point we have an access to TraceKit's handler, |
| 139 | + // which can take care of browser differences (eg. missing exception argument in onerror) |
| 140 | + var tracekitErrorHandler = _window[_onerror]; |
| 141 | + var tracekitUnhandledRejectionHandler = _window[_onunhandledrejection]; |
| 142 | + |
| 143 | + // And now capture all previously caught exceptions |
| 144 | + for (var i = 0; i < data.length; i++) { |
| 145 | + if ('e' in data[i] && tracekitErrorHandler) { |
| 146 | + tracekitErrorHandler.apply(_window, data[i].e); |
| 147 | + } else if ('p' in data[i] && tracekitUnhandledRejectionHandler) { |
| 148 | + tracekitUnhandledRejectionHandler.apply(_window, [data[i].p]); |
| 149 | + } |
| 150 | + } |
| 151 | + } catch (o_O) { |
| 152 | + console.error(o_O); |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + // We make sure we do not overwrite window.Sentry since there could be already integrations in there |
| 157 | + _window[_namespace] = _window[_namespace] || {}; |
| 158 | + |
| 159 | + _window[_namespace].onLoad = function (callback) { |
| 160 | + onLoadCallbacks.push(callback); |
| 161 | + if (lazy && !forceLoad) { |
| 162 | + return; |
| 163 | + } |
| 164 | + injectSdk(onLoadCallbacks); |
| 165 | + }; |
| 166 | + |
| 167 | + _window[_namespace].forceLoad = function() { |
| 168 | + forceLoad = true; |
| 169 | + if (lazy) { |
| 170 | + setTimeout(function() { |
| 171 | + injectSdk(onLoadCallbacks); |
| 172 | + }); |
| 173 | + } |
| 174 | + }; |
| 175 | + |
| 176 | + |
| 177 | + [ |
| 178 | + 'init', |
| 179 | + 'addBreadcrumb', |
| 180 | + 'captureMessage', |
| 181 | + 'captureException', |
| 182 | + 'captureEvent', |
| 183 | + 'configureScope', |
| 184 | + 'withScope', |
| 185 | + 'showReportDialog' |
| 186 | + ].forEach(function(f) { |
| 187 | + _window[_namespace][f] = function() { |
| 188 | + queue({ f: f, a: arguments }); |
| 189 | + }; |
| 190 | + }); |
| 191 | + |
| 192 | + // Store reference to the old `onerror` handler and override it with our own function |
| 193 | + // that will just push exceptions to the queue and call through old handler if we found one |
| 194 | + var _oldOnerror = _window[_onerror]; |
| 195 | + _window[_onerror] = function(message, source, lineno, colno, exception) { |
| 196 | + // Use keys as "data type" to save some characters" |
| 197 | + queue({ |
| 198 | + e: [].slice.call(arguments) |
| 199 | + }); |
| 200 | + |
| 201 | + if (_oldOnerror) _oldOnerror.apply(_window, arguments); |
| 202 | + }; |
| 203 | + |
| 204 | + // Do the same store/queue/call operations for `onunhandledrejection` event |
| 205 | + var _oldOnunhandledrejection = _window[_onunhandledrejection]; |
| 206 | + _window[_onunhandledrejection] = function(e) { |
| 207 | + queue({ |
| 208 | + p: 'reason' in e ? e.reason : 'detail' in e && 'reason' in e.detail ? e.detail.reason : e |
| 209 | + }); |
| 210 | + if (_oldOnunhandledrejection) _oldOnunhandledrejection.apply(_window, arguments); |
| 211 | + }; |
| 212 | + |
| 213 | + if (!lazy) { |
| 214 | + setTimeout(function () { |
| 215 | + injectSdk(onLoadCallbacks); |
| 216 | + }); |
| 217 | + } |
| 218 | +})(window, document, 'script', 'onerror', 'onunhandledrejection', 'Sentry', 'loader.js', '/base/artifacts/sdk.js', { |
| 219 | + dsn: 'https://[email protected]/1' |
| 220 | +}); |
0 commit comments