diff options
author | Fabio Berger <me@fabioberger.com> | 2018-07-19 23:48:06 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-07-19 23:48:06 +0800 |
commit | d8898cf9a30cc349868afcf2b78e6369e57aa726 (patch) | |
tree | f96d1aa76c7e5aa9e3311d5cdbd0d31c8ec8d7fb /packages/website | |
parent | 1aaf633df883f62fad890b2d87a2dc89067821c5 (diff) | |
parent | 3de88d5345c7a4549bc69e2ca28f0601f5d42189 (diff) | |
download | dexon-0x-contracts-d8898cf9a30cc349868afcf2b78e6369e57aa726.tar.gz dexon-0x-contracts-d8898cf9a30cc349868afcf2b78e6369e57aa726.tar.zst dexon-0x-contracts-d8898cf9a30cc349868afcf2b78e6369e57aa726.zip |
merge v2-prototype
Diffstat (limited to 'packages/website')
60 files changed, 496 insertions, 843 deletions
diff --git a/packages/website/md/docs/sol_cov/usage.md b/packages/website/md/docs/sol_cov/usage.md index 433cfad96..c747005fb 100644 --- a/packages/website/md/docs/sol_cov/usage.md +++ b/packages/website/md/docs/sol_cov/usage.md @@ -8,7 +8,7 @@ In order to use `CoverageSubprovider` with your favorite framework you need to p ### Sol-compiler -If you are generating your artifacts with [@0xproject/sol-compiler](LINK) you can use the `SolCompilerArtifactsAdapter` we've implemented for you. +If you are generating your artifacts with [@0xproject/sol-compiler](https://0xproject.com/docs/sol-compiler) you can use the `SolCompilerArtifactsAdapter` we've implemented for you. ```typescript import { SolCompilerArtifactsAdapter } from '@0xproject/sol-cov'; @@ -19,7 +19,7 @@ const artifactsAdapter = new SolCompilerArtifactsAdapter(artifactsPath, contract ### Truffle -If your project is using [Truffle](LINK), we've written a `TruffleArtifactsAdapter`for you. +If your project is using [Truffle](https://truffleframework.com/), we've written a `TruffleArtifactsAdapter`for you. ```typescript import { TruffleArtifactAdapter } from '@0xproject/sol-cov'; diff --git a/packages/website/package.json b/packages/website/package.json index a3ca047fa..4cefa19a9 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/website", - "version": "0.0.38", + "version": "0.0.39", "engines": { "node": ">=6.12" }, @@ -11,22 +11,25 @@ "clean": "shx rm -f public/bundle*", "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", "watch_without_deps": "webpack-dev-server --content-base public --https", - "deploy_dogfood": "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", - "deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", - "deploy_live": "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers" + "deploy_dogfood": + "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", + "deploy_staging": + "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", + "deploy_live": + "DEPLOY_ROLLBAR_SOURCEMAPS=true npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --exclude *.map.js" }, "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { "@0xproject/contract-wrappers": "^0.0.5", - "@0xproject/order-utils": "^0.0.8", - "@0xproject/react-docs": "^0.0.15", - "@0xproject/react-shared": "^0.2.2", - "@0xproject/subproviders": "^0.10.5", + "@0xproject/order-utils": "^0.0.9", + "@0xproject/react-docs": "^0.0.16", + "@0xproject/react-shared": "^0.2.3", + "@0xproject/subproviders": "^0.10.6", "@0xproject/types": "^0.8.1", - "@0xproject/typescript-typings": "^0.4.2", - "@0xproject/utils": "^0.7.2", - "@0xproject/web3-wrapper": "^0.7.2", + "@0xproject/typescript-typings": "^0.4.3", + "@0xproject/utils": "^0.7.3", + "@0xproject/web3-wrapper": "^0.7.3", "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2", @@ -46,7 +49,6 @@ "react-copy-to-clipboard": "^4.2.3", "react-document-title": "^2.0.3", "react-dom": "15.6.1", - "react-ga": "^2.4.1", "react-popper": "^1.0.0-beta.6", "react-redux": "^5.0.3", "react-router-dom": "^4.1.1", @@ -60,7 +62,6 @@ "thenby": "^1.2.3", "truffle-contract": "2.0.1", "web3-provider-engine": "14.0.6", - "whatwg-fetch": "^2.0.3", "xml-js": "^1.6.4" }, "devDependencies": { @@ -92,10 +93,11 @@ "less-loader": "^2.2.3", "make-promises-safe": "^1.1.0", "raw-loader": "^0.5.1", + "rollbar-sourcemap-webpack-plugin": "^2.3.0", "shx": "^0.2.2", "source-map-loader": "^0.1.6", "style-loader": "0.13.x", - "tslint": "5.8.0", + "tslint": "5.11.0", "tslint-config-0xproject": "^0.0.2", "typescript": "2.7.1", "uglifyjs-webpack-plugin": "^1.2.5", diff --git a/packages/website/public/images/team/peter.jpg b/packages/website/public/images/team/peter.jpg Binary files differnew file mode 100644 index 000000000..fa976cb8d --- /dev/null +++ b/packages/website/public/images/team/peter.jpg diff --git a/packages/website/public/images/token_icons/eos.png b/packages/website/public/images/token_icons/EOS.png Binary files differindex 622df61bc..622df61bc 100644 --- a/packages/website/public/images/token_icons/eos.png +++ b/packages/website/public/images/token_icons/EOS.png diff --git a/packages/website/public/images/token_icons/qtum.png b/packages/website/public/images/token_icons/QTUM.png Binary files differindex b7f09dfd2..b7f09dfd2 100644 --- a/packages/website/public/images/token_icons/qtum.png +++ b/packages/website/public/images/token_icons/QTUM.png diff --git a/packages/website/public/images/token_icons/taas.png b/packages/website/public/images/token_icons/TAAS.png Binary files differindex 266e3690b..266e3690b 100644 --- a/packages/website/public/images/token_icons/taas.png +++ b/packages/website/public/images/token_icons/TAAS.png diff --git a/packages/website/public/images/token_icons/wings.png b/packages/website/public/images/token_icons/WINGS.png Binary files differindex c0a9ce527..c0a9ce527 100644 --- a/packages/website/public/images/token_icons/wings.png +++ b/packages/website/public/images/token_icons/WINGS.png diff --git a/packages/website/public/index.html b/packages/website/public/index.html index 060f2c3c2..a8a61f8ad 100644 --- a/packages/website/public/index.html +++ b/packages/website/public/index.html @@ -23,6 +23,12 @@ </head> <body style="margin: 0px; min-width: 355px;"> + <!-- Heap SDK --> + <script type="text/javascript"> + window.heap = window.heap || [], heap.load = function (e, t) { window.heap.appid = e, window.heap.config = t = t || {}; var r = t.forceSSL || "https:" === document.location.protocol, a = document.createElement("script"); a.type = "text/javascript", a.async = !0, a.src = (r ? "https:" : "http:") + "//cdn.heapanalytics.com/js/heap-" + e + ".js"; var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(a, n); for (var o = function (e) { return function () { heap.push([e].concat(Array.prototype.slice.call(arguments, 0))) } }, p = ["addEventProperties", "addUserProperties", "clearEventProperties", "identify", "resetIdentity", "removeEventProperty", "setEventProperties", "track", "unsetEventProperty"], c = 0; c < p.length; c++)heap[p[c]] = o(p[c]) }; + heap.load("410099666"); + </script> + <!-- End Heap SDK --> <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-98720122-1"></script> <script> diff --git a/packages/website/public/js/rollbar.umd.min.js b/packages/website/public/js/rollbar.umd.min.js new file mode 100644 index 000000000..2734bb8be --- /dev/null +++ b/packages/website/public/js/rollbar.umd.min.js @@ -0,0 +1,2 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.rollbar=e():t.rollbar=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){t.exports=r(1)},function(t,e,r){"use strict";var n=r(2),o="undefined"!=typeof window&&window._rollbarConfig,i=o&&o.globalAlias||"Rollbar",a="undefined"!=typeof window&&window[i]&&"function"==typeof window[i].shimId&&void 0!==window[i].shimId();if("undefined"==typeof window||window._rollbarStartTime||(window._rollbarStartTime=(new Date).getTime()),!a&&o){var s=new n(o);window[i]=s}else"undefined"!=typeof window?(window.rollbar=n,window._rollbarDidLoad=!0):"undefined"!=typeof self&&(self.rollbar=n,self._rollbarDidLoad=!0);t.exports=n},function(t,e,r){"use strict";function n(t,e){this.options=c.merge(x,t);var r=new l(this.options,h,d);this.client=e||new u(this.options,r,p,"browser");var n="undefined"!=typeof window&&window||"undefined"!=typeof self&&self,o="undefined"!=typeof document&&document;i(this.client.notifier),a(this.client.queue),(this.options.captureUncaught||this.options.handleUncaughtExceptions)&&(f.captureUncaughtExceptions(n,this),f.wrapGlobals(n,this)),(this.options.captureUnhandledRejections||this.options.handleUnhandledRejections)&&f.captureUnhandledRejections(n,this),this.instrumenter=new w(this.options,this.client.telemeter,this,n,o),this.instrumenter.instrument()}function o(t){var e="Rollbar is not initialized";p.error(e),t&&t(new Error(e))}function i(t){t.addTransform(m.handleItemWithError).addTransform(m.ensureItemHasSomethingToSay).addTransform(m.addBaseInfo).addTransform(m.addRequestInfo(window)).addTransform(m.addClientInfo(window)).addTransform(m.addPluginInfo(window)).addTransform(m.addBody).addTransform(g.addMessageWithError).addTransform(g.addTelemetryData).addTransform(g.addConfigToPayload).addTransform(m.scrubPayload).addTransform(g.userTransform(p)).addTransform(g.itemToPayload)}function a(t){t.addPredicate(y.checkLevel).addPredicate(v.checkIgnore).addPredicate(y.userCheckIgnore(p)).addPredicate(y.urlIsNotBlacklisted(p)).addPredicate(y.urlIsWhitelisted(p)).addPredicate(y.messageIsIgnored(p))}function s(t){for(var e=0,r=t.length;e<r;++e)if(c.isFunction(t[e]))return t[e]}var u=r(3),c=r(6),l=r(11),p=r(13),f=r(16),h=r(17),d=r(19),m=r(20),g=r(24),v=r(25),y=r(26),b=r(21),w=r(27),_=null;n.init=function(t,e){return _?_.global(t).configure(t):_=new n(t,e)},n.prototype.global=function(t){return this.client.global(t),this},n.global=function(t){return _?_.global(t):void o()},n.prototype.configure=function(t,e){var r=this.options,n={};return e&&(n={payload:e}),this.options=c.merge(r,t,n),this.client.configure(t,e),this.instrumenter.configure(t),this},n.configure=function(t,e){return _?_.configure(t,e):void o()},n.prototype.lastError=function(){return this.client.lastError},n.lastError=function(){return _?_.lastError():void o()},n.prototype.log=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.log(t),{uuid:e}},n.log=function(){if(_)return _.log.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.debug=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.debug(t),{uuid:e}},n.debug=function(){if(_)return _.debug.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.info=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.info(t),{uuid:e}},n.info=function(){if(_)return _.info.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.warn=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.warn(t),{uuid:e}},n.warn=function(){if(_)return _.warn.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.warning=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.warning(t),{uuid:e}},n.warning=function(){if(_)return _.warning.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.error=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.error(t),{uuid:e}},n.error=function(){if(_)return _.error.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.critical=function(){var t=this._createItem(arguments),e=t.uuid;return this.client.critical(t),{uuid:e}},n.critical=function(){if(_)return _.critical.apply(_,arguments);var t=s(arguments);o(t)},n.prototype.handleUncaughtException=function(t,e,r,n,o,i){var a,s=c.makeUnhandledStackInfo(t,e,r,n,o,"onerror","uncaught exception",b);c.isError(o)?(a=this._createItem([t,o,i]),a._unhandledStackInfo=s):c.isError(e)?(a=this._createItem([t,e,i]),a._unhandledStackInfo=s):(a=this._createItem([t,i]),a.stackInfo=s),a.level=this.options.uncaughtErrorLevel,a._isUncaught=!0,this.client.log(a)},n.prototype.handleUnhandledRejection=function(t,e){var r="unhandled rejection was null or undefined!";r=t?t.message||String(t):r;var n,o=t&&t._rollbarContext||e&&e._rollbarContext;c.isError(t)?n=this._createItem([r,t,o]):(n=this._createItem([r,t,o]),n.stackInfo=c.makeUnhandledStackInfo(r,"",0,0,null,"unhandledrejection","",b)),n.level=this.options.uncaughtErrorLevel,n._isUncaught=!0,n._originalArgs=n._originalArgs||[],n._originalArgs.push(e),this.client.log(n)},n.prototype.wrap=function(t,e,r){try{var n;if(n=c.isFunction(e)?e:function(){return e||{}},!c.isFunction(t))return t;if(t._isWrap)return t;if(!t._rollbar_wrapped&&(t._rollbar_wrapped=function(){r&&c.isFunction(r)&&r.apply(this,arguments);try{return t.apply(this,arguments)}catch(r){var e=r;throw e&&(c.isType(e,"string")&&(e=new String(e)),e._rollbarContext=n()||{},e._rollbarContext._wrappedSource=t.toString(),window._rollbarWrappedError=e),e}},t._rollbar_wrapped._isWrap=!0,t.hasOwnProperty))for(var o in t)t.hasOwnProperty(o)&&(t._rollbar_wrapped[o]=t[o]);return t._rollbar_wrapped}catch(e){return t}},n.wrap=function(t,e){return _?_.wrap(t,e):void o()},n.prototype.captureEvent=function(t,e){return this.client.captureEvent(t,e)},n.captureEvent=function(t,e){return _?_.captureEvent(t,e):void o()},n.prototype.captureDomContentLoaded=function(t,e){return e||(e=new Date),this.client.captureDomContentLoaded(e)},n.prototype.captureLoad=function(t,e){return e||(e=new Date),this.client.captureLoad(e)},n.prototype._createItem=function(t){return c.createItem(t,p,this)};var x={version:"2.4.2",scrubFields:["pw","pass","passwd","password","secret","confirm_password","confirmPassword","password_confirmation","passwordConfirmation","access_token","accessToken","secret_key","secretKey","secretToken","cc-number","card number","cardnumber","cardnum","ccnum","ccnumber","cc num","creditcardnumber","credit card number","newcreditcardnumber","new credit card","creditcardno","credit card no","card#","card #","cc-csc","cvc2","cvv2","ccv2","security code","card verification","name on credit card","name on card","nameoncard","cardholder","card holder","name des karteninhabers","card type","cardtype","cc type","cctype","payment type","expiration date","expirationdate","expdate","cc-exp"],logLevel:"debug",reportLevel:"debug",uncaughtErrorLevel:"error",endpoint:"api.rollbar.com/api/1/item/",verbose:!1,enabled:!0,sendConfig:!1,includeItemsInTelemetry:!0,captureIp:!0};t.exports=n},function(t,e,r){"use strict";function n(t,e,r,o){this.options=c.merge(t),this.logger=r,n.rateLimiter.configureGlobal(this.options),n.rateLimiter.setPlatformOptions(o,this.options),this.queue=new a(n.rateLimiter,e,r,this.options),this.notifier=new s(this.queue,this.options),this.telemeter=new u(this.options),this.lastError=null,this.lastErrorHash="none"}function o(t){var e=t.message||"",r=(t.err||{}).stack||String(t.err);return e+"::"+r}var i=r(4),a=r(5),s=r(9),u=r(10),c=r(6),l={maxItems:0,itemsPerMinute:60};n.rateLimiter=new i(l),n.prototype.global=function(t){return n.rateLimiter.configureGlobal(t),this},n.prototype.configure=function(t,e){this.notifier&&this.notifier.configure(t),this.telemeter&&this.telemeter.configure(t);var r=this.options,n={};return e&&(n={payload:e}),this.options=c.merge(r,t,n),this.global(this.options),this},n.prototype.log=function(t){var e=this._defaultLogLevel();return this._log(e,t)},n.prototype.debug=function(t){this._log("debug",t)},n.prototype.info=function(t){this._log("info",t)},n.prototype.warn=function(t){this._log("warning",t)},n.prototype.warning=function(t){this._log("warning",t)},n.prototype.error=function(t){this._log("error",t)},n.prototype.critical=function(t){this._log("critical",t)},n.prototype.wait=function(t){this.queue.wait(t)},n.prototype.captureEvent=function(t,e){return this.telemeter.captureEvent(t,e)},n.prototype.captureDomContentLoaded=function(t){return this.telemeter.captureDomContentLoaded(t)},n.prototype.captureLoad=function(t){return this.telemeter.captureLoad(t)},n.prototype._log=function(t,e){var r;if(e.callback&&(r=e.callback,delete e.callback),this._sameAsLastError(e)){if(r){var n=new Error("ignored identical item");n.item=e,r(n)}}else try{e.level=e.level||t,this.telemeter._captureRollbarItem(e),e.telemetryEvents=this.telemeter.copyEvents(),this.notifier.log(e,r)}catch(t){this.logger.error(t)}},n.prototype._defaultLogLevel=function(){return this.options.logLevel||"debug"},n.prototype._sameAsLastError=function(t){if(!t._isUncaught)return!1;var e=o(t);return this.lastErrorHash===e||(this.lastError=t.err,this.lastErrorHash=e,!1)},t.exports=n},function(t,e){"use strict";function r(t){this.startTime=(new Date).getTime(),this.counter=0,this.perMinCounter=0,this.platform=null,this.platformOptions={},this.configureGlobal(t)}function n(t,e,r){return!t.ignoreRateLimit&&e>=1&&r>e}function o(t,e,r,n,o,a,s){var u=null;return r&&(r=new Error(r)),r||n||(u=i(t,e,o,a,s)),{error:r,shouldSend:n,payload:u}}function i(t,e,r,n,o){var i,a=e.environment||e.payload&&e.payload.environment;i=o?"item per minute limit reached, ignoring errors until timeout":"maxItems has been hit, ignoring errors until reset.";var s={body:{message:{body:i,extra:{maxItems:r,itemsPerMinute:n}}},language:"javascript",environment:a,notifier:{version:e.notifier&&e.notifier.version||e.version}};return"browser"===t?(s.platform="browser",s.framework="browser-js",s.notifier.name="rollbar-browser-js"):"server"===t?(s.framework=e.framework||"node-js",s.notifier.name=e.notifier.name):"react-native"===t&&(s.framework=e.framework||"react-native",s.notifier.name=e.notifier.name),s}r.globalSettings={startTime:(new Date).getTime(),maxItems:void 0,itemsPerMinute:void 0},r.prototype.configureGlobal=function(t){void 0!==t.startTime&&(r.globalSettings.startTime=t.startTime),void 0!==t.maxItems&&(r.globalSettings.maxItems=t.maxItems),void 0!==t.itemsPerMinute&&(r.globalSettings.itemsPerMinute=t.itemsPerMinute)},r.prototype.shouldSend=function(t,e){e=e||(new Date).getTime(),e-this.startTime>=6e4&&(this.startTime=e,this.perMinCounter=0);var i=r.globalSettings.maxItems,a=r.globalSettings.itemsPerMinute;if(n(t,i,this.counter))return o(this.platform,this.platformOptions,i+" max items reached",!1);if(n(t,a,this.perMinCounter))return o(this.platform,this.platformOptions,a+" items per minute reached",!1);this.counter++,this.perMinCounter++;var s=!n(t,i,this.counter),u=s;return s=s&&!n(t,a,this.perMinCounter),o(this.platform,this.platformOptions,null,s,i,a,u)},r.prototype.setPlatformOptions=function(t,e){this.platform=t,this.platformOptions=e},t.exports=r},function(t,e,r){"use strict";function n(t,e,r,n){this.rateLimiter=t,this.api=e,this.logger=r,this.options=n,this.predicates=[],this.pendingItems=[],this.pendingRequests=[],this.retryQueue=[],this.retryHandle=null,this.waitCallback=null,this.waitIntervalID=null}var o=r(6);n.prototype.configure=function(t){this.api&&this.api.configure(t);var e=this.options;return this.options=o.merge(e,t),this},n.prototype.addPredicate=function(t){return o.isFunction(t)&&this.predicates.push(t),this},n.prototype.addPendingItem=function(t){this.pendingItems.push(t)},n.prototype.removePendingItem=function(t){var e=this.pendingItems.indexOf(t);e!==-1&&this.pendingItems.splice(e,1)},n.prototype.addItem=function(t,e,r,n){e&&o.isFunction(e)||(e=function(){});var i=this._applyPredicates(t);if(i.stop)return this.removePendingItem(n),void e(i.err);this._maybeLog(t,r),this.removePendingItem(n),this.pendingRequests.push(t);try{this._makeApiRequest(t,function(r,n){this._dequeuePendingRequest(t),e(r,n)}.bind(this))}catch(r){this._dequeuePendingRequest(t),e(r)}},n.prototype.wait=function(t){o.isFunction(t)&&(this.waitCallback=t,this._maybeCallWait()||(this.waitIntervalID&&(this.waitIntervalID=clearInterval(this.waitIntervalID)),this.waitIntervalID=setInterval(function(){this._maybeCallWait()}.bind(this),500)))},n.prototype._applyPredicates=function(t){for(var e=null,r=0,n=this.predicates.length;r<n;r++)if(e=this.predicates[r](t,this.options),!e||void 0!==e.err)return{stop:!0,err:e.err};return{stop:!1,err:null}},n.prototype._makeApiRequest=function(t,e){var r=this.rateLimiter.shouldSend(t);r.shouldSend?this.api.postItem(t,function(r,n){r?this._maybeRetry(r,t,e):e(r,n)}.bind(this)):r.error?e(r.error):this.api.postItem(r.payload,e)};var i=["ECONNRESET","ENOTFOUND","ESOCKETTIMEDOUT","ETIMEDOUT","ECONNREFUSED","EHOSTUNREACH","EPIPE","EAI_AGAIN"];n.prototype._maybeRetry=function(t,e,r){var n=!1;if(this.options.retryInterval)for(var o=0,a=i.length;o<a;o++)if(t.code===i[o]){n=!0;break}n?this._retryApiRequest(e,r):r(t)},n.prototype._retryApiRequest=function(t,e){this.retryQueue.push({item:t,callback:e}),this.retryHandle||(this.retryHandle=setInterval(function(){for(;this.retryQueue.length;){var t=this.retryQueue.shift();this._makeApiRequest(t.item,t.callback)}}.bind(this),this.options.retryInterval))},n.prototype._dequeuePendingRequest=function(t){var e=this.pendingRequests.indexOf(t);e!==-1&&(this.pendingRequests.splice(e,1),this._maybeCallWait())},n.prototype._maybeLog=function(t,e){if(this.logger&&this.options.verbose){var r=e;if(r=r||o.get(t,"body.trace.exception.message"),r=r||o.get(t,"body.trace_chain.0.exception.message"))return void this.logger.error(r);r=o.get(t,"body.message.body"),r&&this.logger.log(r)}},n.prototype._maybeCallWait=function(){return!(!o.isFunction(this.waitCallback)||0!==this.pendingItems.length||0!==this.pendingRequests.length)&&(this.waitIntervalID&&(this.waitIntervalID=clearInterval(this.waitIntervalID)),this.waitCallback(),!0)},t.exports=n},function(t,e,r){"use strict";function n(){if(!A&&(A=!0,c(JSON)&&(s(JSON.stringify)&&(R.stringify=JSON.stringify),s(JSON.parse)&&(R.parse=JSON.parse)),!a(R.stringify)||!a(R.parse))){var t=r(8);t(R)}}function o(t,e){return e===i(t)}function i(t){var e=typeof t;return"object"!==e?e:t?t instanceof Error?"error":{}.toString.call(t).match(/\s([a-zA-Z]+)/)[1].toLowerCase():"null"}function a(t){return o(t,"function")}function s(t){var e=/[\\^$.*+?()[\]{}|]/g,r=Function.prototype.toString.call(Object.prototype.hasOwnProperty).replace(e,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?"),n=RegExp("^"+r+"$");return u(t)&&n.test(t)}function u(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function c(t){return!o(t,"undefined")}function l(t){var e=i(t);return"object"===e||"array"===e}function p(t){return o(t,"error")}function f(t,e,r){var n,i,a,s=o(t,"object"),u=o(t,"array"),c=[];if(s&&r.indexOf(t)!==-1)return t;if(r.push(t),s)for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&c.push(n);else if(u)for(a=0;a<t.length;++a)c.push(a);var l=s?{}:[];for(a=0;a<c.length;++a)n=c[a],i=t[n],l[n]=e(n,i,r);return 0!=c.length?l:t}function h(){return"********"}function d(){var t=N(),e="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var r=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"===e?r:7&r|8).toString(16)});return e}function m(t){var e=g(t);return e?(""===e.anchor&&(e.source=e.source.replace("#","")),t=e.source.replace("?"+e.query,"")):"(unknown)"}function g(t){if(o(t,"string")){for(var e=D,r=e.parser[e.strictMode?"strict":"loose"].exec(t),n={},i=e.key.length;i--;)n[e.key[i]]=r[i]||"";return n[e.q.name]={},n[e.key[12]].replace(e.q.parser,function(t,r,o){r&&(n[e.q.name][r]=o)}),n}}function v(t,e,r){r=r||{},r.access_token=t;var n,o=[];for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&o.push([n,r[n]].join("="));var i="?"+o.sort().join("&");e=e||{},e.path=e.path||"";var a,s=e.path.indexOf("?"),u=e.path.indexOf("#");s!==-1&&(u===-1||u>s)?(a=e.path,e.path=a.substring(0,s)+i+"&"+a.substring(s+1)):u!==-1?(a=e.path,e.path=a.substring(0,u)+i+a.substring(u)):e.path=e.path+i}function y(t,e){if(e=e||t.protocol,!e&&t.port&&(80===t.port?e="http:":443===t.port&&(e="https:")),e=e||"https:",!t.hostname)return null;var r=e+"//"+t.hostname;return t.port&&(r=r+":"+t.port),t.path&&(r+=t.path),r}function b(t,e){var r,n;try{r=R.stringify(t)}catch(o){if(e&&a(e))try{r=e(t)}catch(t){n=t}else n=o}return{error:n,value:r}}function w(t){var e,r;try{e=R.parse(t)}catch(t){r=t}return{error:r,value:e}}function _(t,e,r,n,o,i,a,s){var u={url:e||"",line:r,column:n};u.func=s.guessFunctionName(u.url,u.line),u.context=s.gatherContext(u.url,u.line);var c=document&&document.location&&document.location.href,l=window&&window.navigator&&window.navigator.userAgent;return{mode:i,message:o?String(o):t||a,url:c,stack:[u],useragent:l}}function x(t,e){return function(r,n){try{e(r,n)}catch(e){t.error(e)}}}function k(t,e,r,n,o){for(var a,s,u,c,l,p,f=[],h=0,m=t.length;h<m;++h){p=t[h];var g=i(p);switch(g){case"undefined":break;case"string":a?f.push(p):a=p;break;case"function":c=x(e,p);break;case"date":f.push(p);break;case"error":case"domexception":s?f.push(p):s=p;break;case"object":case"array":if(p instanceof Error||"undefined"!=typeof DOMException&&p instanceof DOMException){s?f.push(p):s=p;break}if(n&&"object"===g&&!l){for(var v=0,y=n.length;v<y;++v)if(void 0!==p[n[v]]){l=p;break}if(l)break}u?f.push(p):u=p;break;default:if(p instanceof Error||"undefined"!=typeof DOMException&&p instanceof DOMException){s?f.push(p):s=p;break}f.push(p)}}f.length>0&&(u=j(u),u.extraArgs=f);var b={message:a,err:s,custom:u,timestamp:N(),callback:c,uuid:d()};return u&&void 0!==u.level&&(b.level=u.level,delete u.level),n&&l&&(b.request=l),o&&(b.lambdaContext=o),b._originalArgs=t,b}function E(t,e){if(t){var r=e.split("."),n=t;try{for(var o=0,i=r.length;o<i;++o)n=n[r[o]]}catch(t){n=void 0}return n}}function I(t,e,r){if(t){var n=e.split("."),o=n.length;if(!(o<1)){if(1===o)return void(t[n[0]]=r);try{for(var i=t[n[0]]||{},a=i,s=1;s<o-1;s++)i[n[s]]=i[n[s]]||{},i=i[n[s]];i[n[o-1]]=r,t[n[0]]=a}catch(t){return}}}}function T(t,e){function r(t,e,r,n,o,i){return e+h(i)}function n(t){var e;if(o(t,"string"))for(e=0;e<u.length;++e)t=t.replace(u[e],r);return t}function i(t,e){var r;for(r=0;r<s.length;++r)if(s[r].test(t)){e=h(e);break}return e}function a(t,e,r){var s=i(t,e);return s===e?o(e,"object")||o(e,"array")?f(e,a,r):n(s):s}e=e||[];var s=S(e),u=O(e);return f(t,a,[])}function S(t){for(var e,r=[],n=0;n<t.length;++n)e="^\\[?(%5[bB])?"+t[n]+"\\[?(%5[bB])?\\]?(%5[dD])?$",r.push(new RegExp(e,"i"));return r}function O(t){for(var e,r=[],n=0;n<t.length;++n)e="\\[?(%5[bB])?"+t[n]+"\\[?(%5[bB])?\\]?(%5[dD])?",r.push(new RegExp("("+e+"=)([^&\\n]+)","igm"));return r}function L(t){var e,r,n,o=[];for(e=0,r=t.length;e<r;e++){switch(n=t[e],i(n)){case"object":n=b(n),n=n.error||n.value,n.length>500&&(n=n.substr(0,497)+"...");break;case"null":n="null";break;case"undefined":n="undefined";break;case"symbol":n=n.toString()}o.push(n)}return o.join(" ")}function N(){return Date.now?+Date.now():+new Date}function C(t,e){if(t&&t.user_ip&&e!==!0){var r=t.user_ip;if(e)try{var n;if(r.indexOf(".")!==-1)n=r.split("."),n.pop(),n.push("0"),r=n.join(".");else if(r.indexOf(":")!==-1){if(n=r.split(":"),n.length>2){var o=n.slice(0,3),i=o[2].indexOf("/");i!==-1&&(o[2]=o[2].substring(0,i));var a="0000:0000:0000:0000:0000";r=o.concat(a).join(":")}}else r=null}catch(t){r=null}else r=null;t.user_ip=r}}var j=r(7),R={},A=!1;n();var q={debug:0,info:1,warning:2,error:3,critical:4},D={strictMode:!1,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};t.exports={isType:o,typeName:i,isFunction:a,isNativeFunction:s,isIterable:l,isError:p,merge:j,traverse:f,redact:h,uuid4:d,LEVELS:q,sanitizeUrl:m,addParamsAndAccessTokenToPath:v,formatUrl:y,stringify:b,jsonParse:w,makeUnhandledStackInfo:_,createItem:k,get:E,set:I,scrub:T,formatArgsAsString:L,now:N,filterIp:C}},function(t,e){"use strict";function r(){var t,e,n,o,a,s={},u=null,c=arguments.length;for(t=0;t<c;t++)if(u=arguments[t],null!=u)for(a in u)e=s[a],n=u[a],s!==n&&(n&&i(n)?(o=e&&i(e)?e:{},s[a]=r(o,n)):"undefined"!=typeof n&&(s[a]=n));return s}var n=Object.prototype.hasOwnProperty,o=Object.prototype.toString,i=function(t){if(!t||"[object Object]"!==o.call(t))return!1;var e=n.call(t,"constructor"),r=t.constructor&&t.constructor.prototype&&n.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!e&&!r)return!1;var i;for(i in t);return"undefined"==typeof i||n.call(t,i)};t.exports=r},function(t,e){var r=function(t){function e(t){return t<10?"0"+t:t}function r(){return this.valueOf()}function n(t){return i.lastIndex=0,i.test(t)?'"'+t.replace(i,function(t){var e=u[t];return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function o(t,e){var r,i,u,l,p,f=a,h=e[t];switch(h&&"object"==typeof h&&"function"==typeof h.toJSON&&(h=h.toJSON(t)),"function"==typeof c&&(h=c.call(e,t,h)),typeof h){case"string":return n(h);case"number":return isFinite(h)?String(h):"null";case"boolean":case"null":return String(h);case"object":if(!h)return"null";if(a+=s,p=[],"[object Array]"===Object.prototype.toString.apply(h)){for(l=h.length,r=0;r<l;r+=1)p[r]=o(r,h)||"null";return u=0===p.length?"[]":a?"[\n"+a+p.join(",\n"+a)+"\n"+f+"]":"["+p.join(",")+"]",a=f,u}if(c&&"object"==typeof c)for(l=c.length,r=0;r<l;r+=1)"string"==typeof c[r]&&(i=c[r],u=o(i,h),u&&p.push(n(i)+(a?": ":":")+u));else for(i in h)Object.prototype.hasOwnProperty.call(h,i)&&(u=o(i,h),u&&p.push(n(i)+(a?": ":":")+u));return u=0===p.length?"{}":a?"{\n"+a+p.join(",\n"+a)+"\n"+f+"}":"{"+p.join(",")+"}",a=f,u}}var i=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+e(this.getUTCMonth()+1)+"-"+e(this.getUTCDate())+"T"+e(this.getUTCHours())+":"+e(this.getUTCMinutes())+":"+e(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=r,Number.prototype.toJSON=r,String.prototype.toJSON=r);var a,s,u,c;"function"!=typeof t.stringify&&(u={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},t.stringify=function(t,e,r){var n;if(a="",s="","number"==typeof r)for(n=0;n<r;n+=1)s+=" ";else"string"==typeof r&&(s=r);if(c=e,e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return o("",{"":t})}),"function"!=typeof t.parse&&(t.parse=function(){function t(t){return t.replace(/\\(?:u(.{4})|([^u]))/g,function(t,e,r){return e?String.fromCharCode(parseInt(e,16)):a[r]})}var e,r,n,o,i,a={"\\":"\\",'"':'"',"/":"/",t:"\t",n:"\n",r:"\r",f:"\f",b:"\b"},s={go:function(){e="ok"},firstokey:function(){o=i,e="colon"},okey:function(){o=i,e="colon"},ovalue:function(){e="ocomma"},firstavalue:function(){e="acomma"},avalue:function(){e="acomma"}},u={go:function(){e="ok"},ovalue:function(){e="ocomma"},firstavalue:function(){e="acomma"},avalue:function(){e="acomma"}},c={"{":{go:function(){r.push({state:"ok"}),n={},e="firstokey"},ovalue:function(){r.push({container:n,state:"ocomma",key:o}),n={},e="firstokey"},firstavalue:function(){r.push({container:n,state:"acomma"}),n={},e="firstokey"},avalue:function(){r.push({container:n,state:"acomma"}),n={},e="firstokey"}},"}":{firstokey:function(){var t=r.pop();i=n,n=t.container,o=t.key,e=t.state},ocomma:function(){var t=r.pop();n[o]=i,i=n,n=t.container,o=t.key,e=t.state}},"[":{go:function(){r.push({state:"ok"}),n=[],e="firstavalue"},ovalue:function(){r.push({container:n,state:"ocomma",key:o}),n=[],e="firstavalue"},firstavalue:function(){r.push({container:n,state:"acomma"}),n=[],e="firstavalue"},avalue:function(){r.push({container:n,state:"acomma"}),n=[],e="firstavalue"}},"]":{firstavalue:function(){var t=r.pop();i=n,n=t.container,o=t.key,e=t.state},acomma:function(){var t=r.pop();n.push(i),i=n,n=t.container,o=t.key,e=t.state}},":":{colon:function(){if(Object.hasOwnProperty.call(n,o))throw new SyntaxError("Duplicate key '"+o+'"');e="ovalue"}},",":{ocomma:function(){n[o]=i,e="okey"},acomma:function(){n.push(i),e="avalue"}},true:{go:function(){i=!0,e="ok"},ovalue:function(){i=!0,e="ocomma"},firstavalue:function(){i=!0,e="acomma"},avalue:function(){i=!0,e="acomma"}},false:{go:function(){i=!1,e="ok"},ovalue:function(){i=!1,e="ocomma"},firstavalue:function(){i=!1,e="acomma"},avalue:function(){i=!1,e="acomma"}},null:{go:function(){i=null,e="ok"},ovalue:function(){i=null,e="ocomma"},firstavalue:function(){i=null,e="acomma"},avalue:function(){i=null,e="acomma"}}};return function(n,o){var a,l=/^[\u0020\t\n\r]*(?:([,:\[\]{}]|true|false|null)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|"((?:[^\r\n\t\\\"]|\\(?:["\\\/trnfb]|u[0-9a-fA-F]{4}))*)")/;e="go",r=[];try{for(;;){if(a=l.exec(n),!a)break;a[1]?c[a[1]][e]():a[2]?(i=+a[2],u[e]()):(i=t(a[3]),s[e]()),n=n.slice(a[0].length)}}catch(t){e=t}if("ok"!==e||/[^\u0020\t\n\r]/.test(n))throw e instanceof SyntaxError?e:new SyntaxError("JSON");return"function"==typeof o?function t(e,r){var n,a,s=e[r];if(s&&"object"==typeof s)for(n in i)Object.prototype.hasOwnProperty.call(s,n)&&(a=t(s,n),void 0!==a?s[n]=a:delete s[n]);return o.call(e,r,s)}({"":i},""):i}}())};t.exports=r},function(t,e,r){"use strict";function n(t,e){this.queue=t,this.options=e,this.transforms=[]}var o=r(6);n.prototype.configure=function(t){this.queue&&this.queue.configure(t);var e=this.options;return this.options=o.merge(e,t),this},n.prototype.addTransform=function(t){return o.isFunction(t)&&this.transforms.push(t),this},n.prototype.log=function(t,e){if(e&&o.isFunction(e)||(e=function(){}),!this.options.enabled)return e(new Error("Rollbar is not enabled"));this.queue.addPendingItem(t);var r=t.err;this._applyTransforms(t,function(n,o){return n?(this.queue.removePendingItem(t),e(n,null)):void this.queue.addItem(o,e,r,t)}.bind(this))},n.prototype._applyTransforms=function(t,e){var r=-1,n=this.transforms.length,o=this.transforms,i=this.options,a=function(t,s){return t?void e(t,null):(r++,r===n?void e(null,s):void o[r](s,i,a))};a(null,t)},t.exports=n},function(t,e,r){"use strict";function n(t){this.queue=[],this.options=i.merge(t);var e=this.options.maxTelemetryEvents||a;this.maxQueueSize=Math.max(0,Math.min(e,a))}function o(t,e){if(e)return e;var r={error:"error",manual:"info"};return r[t]||"info"}var i=r(6),a=100;n.prototype.configure=function(t){var e=this.options;this.options=i.merge(e,t);var r=this.options.maxTelemetryEvents||a,n=Math.max(0,Math.min(r,a)),o=0;this.maxQueueSize>n&&(o=this.maxQueueSize-n),this.maxQueueSize=n,this.queue.splice(0,o)},n.prototype.copyEvents=function(){return Array.prototype.slice.call(this.queue,0)},n.prototype.capture=function(t,e,r,n,a){var s={level:o(t,r),type:t,timestamp_ms:a||i.now(),body:e,source:"client"};n&&(s.uuid=n);try{if(i.isFunction(this.options.filterTelemetry)&&this.options.filterTelemetry(s))return!1}catch(t){this.options.filterTelemetry=null}return this.push(s),s},n.prototype.captureEvent=function(t,e,r){return this.capture("manual",t,e,r)},n.prototype.captureError=function(t,e,r,n){var o={message:t.message||String(t)};return t.stack&&(o.stack=t.stack),this.capture("error",o,e,r,n)},n.prototype.captureLog=function(t,e,r,n){return this.capture("log",{message:t},e,r,n)},n.prototype.captureNetwork=function(t,e,r,n){e=e||"xhr",t.subtype=t.subtype||e,n&&(t.request=n);var o=this.levelFromStatus(t.status_code);return this.capture("network",t,o,r)},n.prototype.levelFromStatus=function(t){return t>=200&&t<400?"info":0===t||t>=400?"error":"info"},n.prototype.captureDom=function(t,e,r,n,o){var i={subtype:t,element:e};return void 0!==r&&(i.value=r),void 0!==n&&(i.checked=n),this.capture("dom",i,"info",o)},n.prototype.captureNavigation=function(t,e,r){return this.capture("navigation",{from:t,to:e},"info",r)},n.prototype.captureDomContentLoaded=function(t){return this.capture("navigation",{subtype:"DOMContentLoaded"},"info",void 0,t&&t.getTime())},n.prototype.captureLoad=function(t){return this.capture("navigation",{subtype:"load"},"info",void 0,t&&t.getTime())},n.prototype.captureConnectivityChange=function(t,e){return this.captureNetwork({change:t},"connectivity",e)},n.prototype._captureRollbarItem=function(t){if(this.options.includeItemsInTelemetry)return t.err?this.captureError(t.err,t.level,t.uuid,t.timestamp):t.message?this.captureLog(t.message,t.level,t.uuid,t.timestamp):t.custom?this.capture("log",t.custom,t.level,t.uuid,t.timestamp):void 0},n.prototype.push=function(t){this.queue.push(t),this.queue.length>this.maxQueueSize&&this.queue.shift()},t.exports=n},function(t,e,r){"use strict";function n(t,e,r,n){this.options=t,this.transport=e,this.url=r,this.jsonBackup=n,this.accessToken=t.accessToken,this.transportOptions=o(t,r)}function o(t,e){return a.getTransportFromOptions(t,s,e)}var i=r(6),a=r(12),s={hostname:"api.rollbar.com",path:"/api/1/item/",search:null,version:"1",protocol:"https:",port:443};n.prototype.postItem=function(t,e){var r=a.transportOptions(this.transportOptions,"POST"),n=a.buildPayload(this.accessToken,t,this.jsonBackup);this.transport.post(this.accessToken,r,n,e)},n.prototype.configure=function(t){var e=this.oldOptions;return this.options=i.merge(e,t),this.transportOptions=o(this.options,this.url),void 0!==this.options.accessToken&&(this.accessToken=this.options.accessToken),this},t.exports=n},function(t,e,r){"use strict";function n(t,e,r){if(!s.isType(e.context,"string")){var n=s.stringify(e.context,r);n.error?e.context="Error: could not serialize 'context'":e.context=n.value||"",e.context.length>255&&(e.context=e.context.substr(0,255))}return{access_token:t,data:e}}function o(t,e,r){var n=e.hostname,o=e.protocol,i=e.port,a=e.path,s=e.search,u=t.proxy;if(t.endpoint){var c=r.parse(t.endpoint);n=c.hostname,o=c.protocol,i=c.port,a=c.pathname,s=c.search}return{hostname:n,protocol:o,port:i,path:a,search:s,proxy:u}}function i(t,e){var r=t.protocol||"https:",n=t.port||("http:"===r?80:"https:"===r?443:void 0),o=t.hostname,i=t.path;return t.search&&(i+=t.search),t.proxy&&(i=r+"//"+o+i,o=t.proxy.host||t.proxy.hostname,n=t.proxy.port,r=t.proxy.protocol||r),{protocol:r,hostname:o,path:i,port:n,method:e}}function a(t,e){var r=/\/$/.test(t),n=/^\//.test(e);return r&&n?e=e.substring(1):r||n||(e="/"+e),t+e}var s=r(6);t.exports={buildPayload:n,getTransportFromOptions:o,transportOptions:i,appendPathToPath:a}},function(t,e,r){"use strict";function n(){var t=Array.prototype.slice.call(arguments,0);t.unshift("Rollbar:"),a.ieVersion()<=8?console.error(s.formatArgsAsString(t)):console.error.apply(console,t)}function o(){var t=Array.prototype.slice.call(arguments,0);t.unshift("Rollbar:"),a.ieVersion()<=8?console.info(s.formatArgsAsString(t)):console.info.apply(console,t)}function i(){var t=Array.prototype.slice.call(arguments,0);t.unshift("Rollbar:"),a.ieVersion()<=8?console.log(s.formatArgsAsString(t)):console.log.apply(console,t)}r(14);var a=r(15),s=r(6);t.exports={error:n,info:o,log:i}},function(t,e){!function(t){ +"use strict";t.console||(t.console={});for(var e,r,n=t.console,o=function(){},i=["memory"],a="assert,clear,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn".split(",");e=i.pop();)n[e]||(n[e]={});for(;r=a.pop();)n[r]||(n[r]=o)}("undefined"==typeof window?this:window)},function(t,e){"use strict";function r(){var t;if(!document)return t;for(var e=3,r=document.createElement("div"),n=r.getElementsByTagName("i");r.innerHTML="<!--[if gt IE "+ ++e+"]><i></i><![endif]-->",n[0];);return e>4?e:t}var n={ieVersion:r};t.exports=n},function(t,e){"use strict";function r(t,e,r){if(t){var o;"function"==typeof e._rollbarOldOnError?o=e._rollbarOldOnError:t.onerror&&!t.onerror.belongsToShim&&(o=t.onerror,e._rollbarOldOnError=o);var i=function(){var r=Array.prototype.slice.call(arguments,0);n(t,e,o,r)};i.belongsToShim=r,t.onerror=i}}function n(t,e,r,n){t._rollbarWrappedError&&(n[4]||(n[4]=t._rollbarWrappedError),n[5]||(n[5]=t._rollbarWrappedError._rollbarContext),t._rollbarWrappedError=null),e.handleUncaughtException.apply(e,n),r&&r.apply(t,n)}function o(t,e,r){if(t){"function"==typeof t._rollbarURH&&t._rollbarURH.belongsToShim&&t.removeEventListener("unhandledrejection",t._rollbarURH);var n=function(t){var r,n,o;try{r=t.reason}catch(t){r=void 0}try{n=t.promise}catch(t){n="[unhandledrejection] error getting `promise` from event"}try{o=t.detail,!r&&o&&(r=o.reason,n=o.promise)}catch(t){o="[unhandledrejection] error getting `detail` from event"}r||(r="[unhandledrejection] error getting `reason` from event"),e&&e.handleUnhandledRejection&&e.handleUnhandledRejection(r,n)};n.belongsToShim=r,t._rollbarURH=n,t.addEventListener("unhandledrejection",n)}}function i(t,e,r){if(t){var n,o,i="EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split(",");for(n=0;n<i.length;++n)o=i[n],t[o]&&t[o].prototype&&a(e,t[o].prototype,r)}}function a(t,e,r){if(e.hasOwnProperty&&e.hasOwnProperty("addEventListener")){for(var n=e.addEventListener;n._rollbarOldAdd&&n.belongsToShim;)n=n._rollbarOldAdd;var o=function(e,r,o){n.call(this,e,t.wrap(r),o)};o._rollbarOldAdd=n,o.belongsToShim=r,e.addEventListener=o;for(var i=e.removeEventListener;i._rollbarOldRemove&&i.belongsToShim;)i=i._rollbarOldRemove;var a=function(t,e,r){i.call(this,t,e&&e._rollbar_wrapped||e,r)};a._rollbarOldRemove=i,a.belongsToShim=r,e.removeEventListener=a}}t.exports={captureUncaughtExceptions:r,captureUnhandledRejections:o,wrapGlobals:i}},function(t,e,r){"use strict";function n(t,e,r,n,o){n&&l.isFunction(n)||(n=function(){}),l.addParamsAndAccessTokenToPath(t,e,r);var a="GET",s=l.formatUrl(e);i(t,s,a,null,n,o)}function o(t,e,r,n,o){if(n&&l.isFunction(n)||(n=function(){}),!r)return n(new Error("Cannot send empty request"));var a=p.truncate(r);if(a.error)return n(a.error);var s=a.value,u="POST",c=l.formatUrl(e);i(t,c,u,s,n,o)}function i(t,e,r,n,o,i){var p;if(p=i?i():a(),!p)return o(new Error("No way to send a request"));try{try{var h=function(){try{if(h&&4===p.readyState){h=void 0;var t=l.jsonParse(p.responseText);if(s(p))return void o(t.error,t.value);if(u(p)){if(403===p.status){var e=t.value&&t.value.message;f.error(e)}o(new Error(String(p.status)))}else{var r="XHR response had no status code (likely connection failure)";o(c(r))}}}catch(t){var n;n=t&&t.stack?t:new Error(t),o(n)}};p.open(r,e,!0),p.setRequestHeader&&(p.setRequestHeader("Content-Type","application/json"),p.setRequestHeader("X-Rollbar-Access-Token",t)),p.onreadystatechange=h,p.send(n)}catch(t){if("undefined"!=typeof XDomainRequest){if(!window||!window.location)return o(new Error("No window available during request, unknown environment"));"http:"===window.location.href.substring(0,5)&&"https"===e.substring(0,5)&&(e="http"+e.substring(5));var d=new XDomainRequest;d.onprogress=function(){},d.ontimeout=function(){var t="Request timed out",e="ETIMEDOUT";o(c(t,e))},d.onerror=function(){o(new Error("Error during request"))},d.onload=function(){var t=l.jsonParse(d.responseText);o(t.error,t.value)},d.open(r,e,!0),d.send(n)}else o(new Error("Cannot find a method to transport a request"))}}catch(t){o(t)}}function a(){var t,e,r=[function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}],n=r.length;for(e=0;e<n;e++)try{t=r[e]();break}catch(t){}return t}function s(t){return t&&t.status&&200===t.status}function u(t){return t&&l.isType(t.status,"number")&&t.status>=400&&t.status<600}function c(t,e){var r=new Error(t);return r.code=e||"ENOTFOUND",r}var l=r(6),p=r(18),f=r(13);t.exports={get:n,post:o}},function(t,e,r){"use strict";function n(t,e){return[t,f.stringify(t,e)]}function o(t,e){var r=t.length;return r>2*e?t.slice(0,e).concat(t.slice(r-e)):t}function i(t,e,r){r="undefined"==typeof r?30:r;var n,i=t.data.body;if(i.trace_chain)for(var a=i.trace_chain,s=0;s<a.length;s++)n=a[s].frames,n=o(n,r),a[s].frames=n;else i.trace&&(n=i.trace.frames,n=o(n,r),i.trace.frames=n);return[t,f.stringify(t,e)]}function a(t,e){return e&&e.length>t?e.slice(0,t-3).concat("..."):e}function s(t,e,r){function n(e,r,o){switch(f.typeName(r)){case"string":return a(t,r);case"object":case"array":return f.traverse(r,n,o);default:return r}}return e=f.traverse(e,n,[]),[e,f.stringify(e,r)]}function u(t){return t.exception&&(delete t.exception.description,t.exception.message=a(255,t.exception.message)),t.frames=o(t.frames,1),t}function c(t,e){var r=t.data.body;if(r.trace_chain)for(var n=r.trace_chain,o=0;o<n.length;o++)n[o]=u(n[o]);else r.trace&&(r.trace=u(r.trace));return[t,f.stringify(t,e)]}function l(t,e){return t.length>e}function p(t,e,r){r="undefined"==typeof r?524288:r;for(var o,a,u,p=[n,i,s.bind(null,1024),s.bind(null,512),s.bind(null,256),c];o=p.shift();)if(a=o(t,e),t=a[0],u=a[1],u.error||!l(u.value,r))return u;return u}var f=r(6);t.exports={truncate:p,raw:n,truncateFrames:i,truncateStrings:s,maybeTruncateValue:a}},function(t,e){"use strict";function r(t){var e,r,n={protocol:null,auth:null,host:null,path:null,hash:null,href:t,hostname:null,port:null,pathname:null,search:null,query:null};if(e=t.indexOf("//"),e!==-1?(n.protocol=t.substring(0,e),r=e+2):r=0,e=t.indexOf("@",r),e!==-1&&(n.auth=t.substring(r,e),r=e+1),e=t.indexOf("/",r),e===-1){if(e=t.indexOf("?",r),e===-1)return e=t.indexOf("#",r),e===-1?n.host=t.substring(r):(n.host=t.substring(r,e),n.hash=t.substring(e)),n.hostname=n.host.split(":")[0],n.port=n.host.split(":")[1],n.port&&(n.port=parseInt(n.port,10)),n;n.host=t.substring(r,e),n.hostname=n.host.split(":")[0],n.port=n.host.split(":")[1],n.port&&(n.port=parseInt(n.port,10)),r=e}else n.host=t.substring(r,e),n.hostname=n.host.split(":")[0],n.port=n.host.split(":")[1],n.port&&(n.port=parseInt(n.port,10)),r=e;if(e=t.indexOf("#",r),e===-1?n.path=t.substring(r):(n.path=t.substring(r,e),n.hash=t.substring(e)),n.path){var o=n.path.split("?");n.pathname=o[0],n.query=o[1],n.search=n.query?"?"+n.query:null}return n}t.exports={parse:r}},function(t,e,r){"use strict";function n(t,e,r){if(t.data=t.data||{},t.err)try{t.stackInfo=t.err._savedStackTrace||d.parse(t.err)}catch(e){m.error("Error while parsing the error object.",e);try{t.message=t.err.message||t.err.description||t.message||String(t.err)}catch(e){t.message=String(t.err)||String(e)}delete t.err}r(null,t)}function o(t,e,r){t.message||t.stackInfo||t.custom||r(new Error("No message, stack info, or custom data"),null),r(null,t)}function i(t,e,r){var n=e.payload&&e.payload.environment||e.environment;t.data=h.merge(t.data,{environment:n,level:t.level,endpoint:e.endpoint,platform:"browser",framework:"browser-js",language:"javascript",server:{},uuid:t.uuid,notifier:{name:"rollbar-browser-js",version:e.version}}),r(null,t)}function a(t){return function(e,r,n){if(!t||!t.location)return n(null,e);var o="$remote_ip";r.captureIp?r.captureIp!==!0&&(o+="_anonymize"):o=null,h.set(e,"data.request",{url:t.location.href,query_string:t.location.search,user_ip:o}),n(null,e)}}function s(t){return function(e,r,n){if(!t)return n(null,e);var o=t.navigator||{},i=t.screen||{};h.set(e,"data.client",{runtime_ms:e.timestamp-t._rollbarStartTime,timestamp:Math.round(e.timestamp/1e3),javascript:{browser:o.userAgent,language:o.language,cookie_enabled:o.cookieEnabled,screen:{width:i.width,height:i.height}}}),n(null,e)}}function u(t){return function(e,r,n){if(!t||!t.navigator)return n(null,e);for(var o,i=[],a=t.navigator.plugins||[],s=0,u=a.length;s<u;++s)o=a[s],i.push({name:o.name,description:o.description});h.set(e,"data.client.javascript.plugins",i),n(null,e)}}function c(t,e,r){t.stackInfo?p(t,e,r):l(t,e,r)}function l(t,e,r){var n=t.message,o=t.custom;if(!n)if(o){var i=e.scrubFields,a=h.stringify(h.scrub(o,i));n=a.error||a.value||""}else n="";var s={body:n};o&&(s.extra=h.merge(o)),h.set(t,"data.body",{message:s}),r(null,t)}function p(t,e,r){var n=t.data.description,o=t.stackInfo,i=t.custom,a=d.guessErrorClass(o.message),s=o.name||a[0],u=a[1],c={exception:{class:s,message:u}};n&&(c.exception.description=n);var p=o.stack;if(p&&0===p.length&&t._unhandledStackInfo&&t._unhandledStackInfo.stack&&(p=t._unhandledStackInfo.stack),p){0===p.length&&(c.exception.stack=o.rawStack,c.exception.raw=String(o.rawException));var f,m,g,v,y,b,w,_;for(c.frames=[],w=0;w<p.length;++w)f=p[w],m={filename:f.url?h.sanitizeUrl(f.url):"(unknown)",lineno:f.line||null,method:f.func&&"?"!==f.func?f.func:"[anonymous]",colno:f.column},m.method&&m.method.endsWith&&m.method.endsWith("_rollbar_wrapped")||(g=v=y=null,b=f.context?f.context.length:0,b&&(_=Math.floor(b/2),v=f.context.slice(0,_),g=f.context[_],y=f.context.slice(_)),g&&(m.code=g),(v||y)&&(m.context={},v&&v.length&&(m.context.pre=v),y&&y.length&&(m.context.post=y)),f.args&&(m.args=f.args),c.frames.push(m));c.frames.reverse(),i&&(c.extra=h.merge(i)),h.set(t,"data.body",{trace:c}),r(null,t)}else t.message=s+": "+u,l(t,e,r)}function f(t,e,r){var n=e.scrubFields;t.data=h.scrub(t.data,n),r(null,t)}var h=r(6),d=r(21),m=r(13);t.exports={handleItemWithError:n,ensureItemHasSomethingToSay:o,addBaseInfo:i,addRequestInfo:a,addClientInfo:s,addPluginInfo:u,addBody:c,scrubPayload:f}},function(t,e,r){"use strict";function n(){return l}function o(){return null}function i(t){var e={};return e._stackFrame=t,e.url=t.fileName,e.line=t.lineNumber,e.func=t.functionName,e.column=t.columnNumber,e.args=t.args,e.context=o(e.url,e.line),e}function a(t){function e(){var e,r=[];if(t.stack)e=t;else try{throw t}catch(t){e=t}try{r=c.parse(e)}catch(t){r=[]}for(var n=[],o=0;o<r.length;o++)n.push(new i(r[o]));return n}return{stack:e(),message:t.message,name:t.name,rawStack:t.stack,rawException:t}}function s(t){return new a(t)}function u(t){if(!t||!t.match)return["Unknown error. There was no error message to display.",""];var e=t.match(p),r="(unknown)";return e&&(r=e[e.length-1],t=t.replace((e[e.length-2]||"")+r+":",""),t=t.replace(/(^[\s]+|[\s]+$)/g,"")),[r,t]}var c=r(22),l="?",p=new RegExp("^(([a-zA-Z0-9-_$ ]*): *)?(Uncaught )?([a-zA-Z0-9-_$ ]*): ");t.exports={guessFunctionName:n,guessErrorClass:u,gatherContext:o,parse:s,Stack:a,Frame:i}},function(t,e,r){var n,o,i;!function(a,s){"use strict";o=[r(23)],n=s,i="function"==typeof n?n.apply(e,o):n,!(void 0!==i&&(t.exports=i))}(this,function(t){"use strict";function e(t,e,r){if("function"==typeof Array.prototype.map)return t.map(e,r);for(var n=new Array(t.length),o=0;o<t.length;o++)n[o]=e.call(r,t[o]);return n}function r(t,e,r){if("function"==typeof Array.prototype.filter)return t.filter(e,r);for(var n=[],o=0;o<t.length;o++)e.call(r,t[o])&&n.push(t[o]);return n}var n=/(^|@)\S+\:\d+/,o=/^\s*at .*(\S+\:\d+|\(native\))/m,i=/^(eval@)?(\[native code\])?$/;return{parse:function(t){if("undefined"!=typeof t.stacktrace||"undefined"!=typeof t["opera#sourceloc"])return this.parseOpera(t);if(t.stack&&t.stack.match(o))return this.parseV8OrIE(t);if(t.stack)return this.parseFFOrSafari(t);throw new Error("Cannot parse given Error object")},extractLocation:function(t){if(t.indexOf(":")===-1)return[t];var e=t.replace(/[\(\)\s]/g,"").split(":"),r=e.pop(),n=e[e.length-1];if(!isNaN(parseFloat(n))&&isFinite(n)){var o=e.pop();return[e.join(":"),o,r]}return[e.join(":"),r,void 0]},parseV8OrIE:function(n){var i=r(n.stack.split("\n"),function(t){return!!t.match(o)},this);return e(i,function(e){e.indexOf("(eval ")>-1&&(e=e.replace(/eval code/g,"eval").replace(/(\(eval at [^\()]*)|(\)\,.*$)/g,""));var r=e.replace(/^\s+/,"").replace(/\(eval code/g,"(").split(/\s+/).slice(1),n=this.extractLocation(r.pop()),o=r.join(" ")||void 0,i="eval"===n[0]?void 0:n[0];return new t(o,void 0,i,n[1],n[2],e)},this)},parseFFOrSafari:function(n){var o=r(n.stack.split("\n"),function(t){return!t.match(i)},this);return e(o,function(e){if(e.indexOf(" > eval")>-1&&(e=e.replace(/ line (\d+)(?: > eval line \d+)* > eval\:\d+\:\d+/g,":$1")),e.indexOf("@")===-1&&e.indexOf(":")===-1)return new t(e);var r=e.split("@"),n=this.extractLocation(r.pop()),o=r.shift()||void 0;return new t(o,void 0,n[0],n[1],n[2],e)},this)},parseOpera:function(t){return!t.stacktrace||t.message.indexOf("\n")>-1&&t.message.split("\n").length>t.stacktrace.split("\n").length?this.parseOpera9(t):t.stack?this.parseOpera11(t):this.parseOpera10(t)},parseOpera9:function(e){for(var r=/Line (\d+).*script (?:in )?(\S+)/i,n=e.message.split("\n"),o=[],i=2,a=n.length;i<a;i+=2){var s=r.exec(n[i]);s&&o.push(new t(void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera10:function(e){for(var r=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,n=e.stacktrace.split("\n"),o=[],i=0,a=n.length;i<a;i+=2){var s=r.exec(n[i]);s&&o.push(new t(s[3]||void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera11:function(o){var i=r(o.stack.split("\n"),function(t){return!!t.match(n)&&!t.match(/^Error created at/)},this);return e(i,function(e){var r,n=e.split("@"),o=this.extractLocation(n.pop()),i=n.shift()||"",a=i.replace(/<anonymous function(: (\w+))?>/,"$2").replace(/\([^\)]*\)/g,"")||void 0;i.match(/\(([^\)]*)\)/)&&(r=i.replace(/^[^\(]+\(([^\)]*)\)$/,"$1"));var s=void 0===r||"[arguments not available]"===r?void 0:r.split(",");return new t(a,s,o[0],o[1],o[2],e)},this)}}})},function(t,e,r){var n,o,i;!function(r,a){"use strict";o=[],n=a,i="function"==typeof n?n.apply(e,o):n,!(void 0!==i&&(t.exports=i))}(this,function(){"use strict";function t(t){return!isNaN(parseFloat(t))&&isFinite(t)}function e(t,e,r,n,o,i){void 0!==t&&this.setFunctionName(t),void 0!==e&&this.setArgs(e),void 0!==r&&this.setFileName(r),void 0!==n&&this.setLineNumber(n),void 0!==o&&this.setColumnNumber(o),void 0!==i&&this.setSource(i)}return e.prototype={getFunctionName:function(){return this.functionName},setFunctionName:function(t){this.functionName=String(t)},getArgs:function(){return this.args},setArgs:function(t){if("[object Array]"!==Object.prototype.toString.call(t))throw new TypeError("Args must be an Array");this.args=t},getFileName:function(){return this.fileName},setFileName:function(t){this.fileName=String(t)},getLineNumber:function(){return this.lineNumber},setLineNumber:function(e){if(!t(e))throw new TypeError("Line Number must be a Number");this.lineNumber=Number(e)},getColumnNumber:function(){return this.columnNumber},setColumnNumber:function(e){if(!t(e))throw new TypeError("Column Number must be a Number");this.columnNumber=Number(e)},getSource:function(){return this.source},setSource:function(t){this.source=String(t)},toString:function(){var e=this.getFunctionName()||"{anonymous}",r="("+(this.getArgs()||[]).join(",")+")",n=this.getFileName()?"@"+this.getFileName():"",o=t(this.getLineNumber())?":"+this.getLineNumber():"",i=t(this.getColumnNumber())?":"+this.getColumnNumber():"";return e+r+n+o+i}},e})},function(t,e,r){"use strict";function n(t,e,r){var n=e.payload||{};n.body&&delete n.body;var o=u.merge(t.data,n);t._isUncaught&&(o._isUncaught=!0),t._originalArgs&&(o._originalArgs=t._originalArgs),r(null,o)}function o(t,e,r){t.telemetryEvents&&u.set(t,"data.body.telemetry",t.telemetryEvents),r(null,t)}function i(t,e,r){if(!t.message)return void r(null,t);var n="data.body.trace_chain.0",o=u.get(t,n);if(o||(n="data.body.trace",o=u.get(t,n)),o){if(!o.exception||!o.exception.description)return u.set(t,n+".exception.description",t.message),void r(null,t);var i=u.get(t,n+".extra")||{},a=u.merge(i,{message:t.message});u.set(t,n+".extra",a)}r(null,t)}function a(t){return function(e,r,n){var o=u.merge(e);try{u.isFunction(r.transform)&&r.transform(o.data)}catch(o){return r.transform=null,t.error("Error while calling custom transform() function. Removing custom transform().",o),void n(null,e)}n(null,o)}}function s(t,e,r){if(!e.sendConfig)return r(null,t);var n="_rollbarConfig",o=u.get(t,"data.custom")||{};o[n]=e,t.data.custom=o,r(null,t)}var u=r(6);t.exports={itemToPayload:n,addTelemetryData:o,addMessageWithError:i,userTransform:a,addConfigToPayload:s}},function(t,e,r){"use strict";function n(t,e){return!o.get(e,"plugins.jquery.ignoreAjaxErrors")||!o.get(t,"body.message.extra.isAjax")}var o=r(6);t.exports={checkIgnore:n}},function(t,e,r){"use strict";function n(t,e){var r=t.level,n=c.LEVELS[r]||0,o=e.reportLevel,i=c.LEVELS[o]||0;return!(n<i)}function o(t){return function(e,r){var n=!!e._isUncaught;delete e._isUncaught;var o=e._originalArgs;delete e._originalArgs;try{c.isFunction(r.onSendCallback)&&r.onSendCallback(n,o,e)}catch(e){r.onSendCallback=null,t.error("Error while calling onSendCallback, removing",e)}try{if(c.isFunction(r.checkIgnore)&&r.checkIgnore(n,o,e))return!1}catch(e){r.checkIgnore=null,t.error("Error while calling custom checkIgnore(), removing",e)}return!0}}function i(t){return function(e,r){return!s(e,r,"blacklist",t)}}function a(t){return function(e,r){return s(e,r,"whitelist",t)}}function s(t,e,r,n){var o=!1;"blacklist"===r&&(o=!0);var i,a,s,u,l,p,f,h,d,m;try{if(i=o?e.hostBlackList:e.hostWhiteList,f=i&&i.length,a=c.get(t,"body.trace"),!i||0===f)return!o;if(!a||!a.frames||0===a.frames.length)return!o;for(l=a.frames.length,d=0;d<l;d++){if(s=a.frames[d],u=s.filename,!c.isType(u,"string"))return!o;for(m=0;m<f;m++)if(p=i[m],h=new RegExp(p),h.test(u))return!0}}catch(t){o?e.hostBlackList=null:e.hostWhiteList=null;var g=o?"hostBlackList":"hostWhiteList";return n.error("Error while reading your configuration's "+g+" option. Removing custom "+g+".",t),!o}return!1}function u(t){return function(e,r){var n,o,i,a,s,u,l,p,f;try{if(s=!1,i=r.ignoredMessages,!i||0===i.length)return!0;if(l=e.body,p=c.get(l,"trace.exception.message"),f=c.get(l,"message.body"),n=p||f,!n)return!0;for(a=i.length,o=0;o<a&&(u=new RegExp(i[o],"gi"),!(s=u.test(n)));o++);}catch(e){r.ignoredMessages=null,t.error("Error while reading your configuration's ignoredMessages option. Removing custom ignoredMessages.")}return!s}}var c=r(6);t.exports={checkLevel:n,userCheckIgnore:o,urlIsNotBlacklisted:i,urlIsWhitelisted:a,messageIsIgnored:u}},function(t,e,r){"use strict";function n(t,e,r,n,o){var i=t[e];t[e]=r(i),n&&n[o].push([t,e,i])}function o(t,e){for(var r;t[e].length;)r=t[e].shift(),r[0][r[1]]=r[2]}function i(t){if(!t||!t.attributes)return null;for(var e=t.attributes,r=0;r<e.length;++r)if("name"===e[r].key)return e[r].value;return null}function a(t){for(var e=[],r=0;r<t.length;++r)e.push(new RegExp(t[r],"i"));return function(t){var r=i(t);if(!r)return!1;for(var n=0;n<e.length;++n)if(e[n].test(r))return!0;return!1}}function s(t,e,r,n,o){var i=t.autoInstrument;t.enabled===!1||i===!1?this.autoInstrument={}:(u.isType(i,"object")||(i=p),this.autoInstrument=u.merge(p,i)),this.scrubTelemetryInputs=!!t.scrubTelemetryInputs,this.telemetryScrubber=t.telemetryScrubber,this.defaultValueScrubber=a(t.scrubFields),this.telemeter=e,this.rollbar=r,this._window=n||{},this._document=o||{},this.replacements={network:[],log:[],navigation:[],connectivity:[]},this.eventRemovers={dom:[],connectivity:[]},this._location=this._window.location,this._lastHref=this._location&&this._location.href}var u=r(6),c=r(19),l=r(28),p={network:!0,networkResponseHeaders:!1,networkResponseBody:!1,networkRequestBody:!1,log:!0,dom:!0,navigation:!0,connectivity:!0};s.prototype.configure=function(t){var e=t.autoInstrument,r=u.merge(this.autoInstrument);t.enabled===!1||e===!1?this.autoInstrument={}:(u.isType(e,"object")||(e=p),this.autoInstrument=u.merge(p,e)),this.instrument(r),void 0!==t.scrubTelemetryInputs&&(this.scrubTelemetryInputs=!!t.scrubTelemetryInputs),void 0!==t.telemetryScrubber&&(this.telemetryScrubber=t.telemetryScrubber)},s.prototype.instrument=function(t){!this.autoInstrument.network||t&&t.network?!this.autoInstrument.network&&t&&t.network&&this.deinstrumentNetwork():this.instrumentNetwork(),!this.autoInstrument.log||t&&t.log?!this.autoInstrument.log&&t&&t.log&&this.deinstrumentConsole():this.instrumentConsole(),!this.autoInstrument.dom||t&&t.dom?!this.autoInstrument.dom&&t&&t.dom&&this.deinstrumentDom():this.instrumentDom(),!this.autoInstrument.navigation||t&&t.navigation?!this.autoInstrument.navigation&&t&&t.navigation&&this.deinstrumentNavigation():this.instrumentNavigation(),!this.autoInstrument.connectivity||t&&t.connectivity?!this.autoInstrument.connectivity&&t&&t.connectivity&&this.deinstrumentConnectivity():this.instrumentConnectivity()},s.prototype.deinstrumentNetwork=function(){o(this.replacements,"network")},s.prototype.instrumentNetwork=function(){function t(t,r){t in r&&u.isFunction(r[t])&&n(r,t,function(t){return e.rollbar.wrap(t)})}var e=this;if("XMLHttpRequest"in this._window){var r=this._window.XMLHttpRequest.prototype;n(r,"open",function(t){return function(e,r){return u.isType(r,"string")&&(this.__rollbar_xhr={method:e,url:r,status_code:null,start_time_ms:u.now(),end_time_ms:null}),t.apply(this,arguments)}},this.replacements,"network"),n(r,"send",function(r){return function(o){function i(){if(a.__rollbar_xhr&&(1===a.readyState||4===a.readyState)){if(null===a.__rollbar_xhr.status_code){a.__rollbar_xhr.status_code=0;var t=null;e.autoInstrument.networkRequestBody&&(t=o),a.__rollbar_event=e.telemeter.captureNetwork(a.__rollbar_xhr,"xhr",void 0,t)}if(1===a.readyState)a.__rollbar_xhr.start_time_ms=u.now();else{a.__rollbar_xhr.end_time_ms=u.now();var r=null;if(e.autoInstrument.networkResponseHeaders){var n=e.autoInstrument.networkResponseHeaders;r={};try{var i,s;if(n===!0){var c=a.getAllResponseHeaders();if(c){var l,p,f=c.trim().split(/[\r\n]+/);for(s=0;s<f.length;s++)l=f[s].split(": "),i=l.shift(),p=l.join(": "),r[i]=p}}else for(s=0;s<n.length;s++)i=n[s],r[i]=a.getResponseHeader(i)}catch(t){}}var h=null;if(e.autoInstrument.networkResponseBody)try{h=a.responseText}catch(t){}var d=null;(h||r)&&(d={},h&&(d.body=h),r&&(d.headers=r)),d&&(a.__rollbar_xhr.response=d)}try{var m=a.status;m=1223===m?204:m,a.__rollbar_xhr.status_code=m,a.__rollbar_event.level=e.telemeter.levelFromStatus(m)}catch(t){}}}var a=this;return t("onload",a),t("onerror",a),t("onprogress",a),"onreadystatechange"in a&&u.isFunction(a.onreadystatechange)?n(a,"onreadystatechange",function(t){return e.rollbar.wrap(t,void 0,i)}):a.onreadystatechange=i,r.apply(this,arguments)}},this.replacements,"network")}"fetch"in this._window&&n(this._window,"fetch",function(t){return function(r,n){for(var o=new Array(arguments.length),i=0,a=o.length;i<a;i++)o[i]=arguments[i];var s,c=o[0],l="GET";u.isType(c,"string")?s=c:c&&(s=c.url,c.method&&(l=c.method)),o[1]&&o[1].method&&(l=o[1].method);var p={method:l,url:s,status_code:null,start_time_ms:u.now(),end_time_ms:null},f=null;return e.autoInstrument.networkRequestBody&&(o[1]&&o[1].body?f=o[1].body:o[0]&&!u.isType(o[0],"string")&&o[0].body&&(f=o[0].body)),e.telemeter.captureNetwork(p,"fetch",void 0,f),t.apply(this,o).then(function(t){p.end_time_ms=u.now(),p.status_code=t.status;var r=null;if(e.autoInstrument.networkResponseHeaders){var n=e.autoInstrument.networkResponseHeaders;r={};try{if(n===!0);else for(var o=0;o<n.length;o++){var i=n[o];r[i]=t.headers.get(i)}}catch(t){}}var a=null;return r&&(a={headers:r}),a&&(p.response=a),t})}},this.replacements,"network")},s.prototype.deinstrumentConsole=function(){if("console"in this._window&&this._window.console.log)for(var t;this.replacements.log.length;)t=this.replacements.log.shift(),this._window.console[t[0]]=t[1]},s.prototype.instrumentConsole=function(){function t(t){var n=r[t],o=r,i="warn"===t?"warning":t;r[t]=function(){var t=Array.prototype.slice.call(arguments),r=u.formatArgsAsString(t);e.telemeter.captureLog(r,i),n&&Function.prototype.apply.call(n,o,t)},e.replacements.log.push([t,n])}if("console"in this._window&&this._window.console.log)for(var e=this,r=this._window.console,n=["debug","info","warn","error","log"],o=0,i=n.length;o<i;o++)t(n[o])},s.prototype.deinstrumentDom=function(){("addEventListener"in this._window||"attachEvent"in this._window)&&this.removeListeners("dom")},s.prototype.instrumentDom=function(){if("addEventListener"in this._window||"attachEvent"in this._window){var t=this.handleClick.bind(this),e=this.handleBlur.bind(this);this.addListener("dom",this._window,"click","onclick",t,!0),this.addListener("dom",this._window,"blur","onfocusout",e,!0)}},s.prototype.handleClick=function(t){try{var e=l.getElementFromEvent(t,this._document),r=e&&e.tagName,n=l.isDescribedElement(e,"a")||l.isDescribedElement(e,"button");r&&(n||l.isDescribedElement(e,"input",["button","submit"]))?this.captureDomEvent("click",e):l.isDescribedElement(e,"input",["checkbox","radio"])&&this.captureDomEvent("input",e,e.value,e.checked)}catch(t){}},s.prototype.handleBlur=function(t){try{var e=l.getElementFromEvent(t,this._document);e&&e.tagName&&(l.isDescribedElement(e,"textarea")?this.captureDomEvent("input",e,e.value):l.isDescribedElement(e,"select")&&e.options&&e.options.length?this.handleSelectInputChanged(e):l.isDescribedElement(e,"input")&&!l.isDescribedElement(e,"input",["button","submit","hidden","checkbox","radio"])&&this.captureDomEvent("input",e,e.value))}catch(t){}},s.prototype.handleSelectInputChanged=function(t){if(t.multiple)for(var e=0;e<t.options.length;e++)t.options[e].selected&&this.captureDomEvent("input",t,t.options[e].value);else t.selectedIndex>=0&&t.options[t.selectedIndex]&&this.captureDomEvent("input",t,t.options[t.selectedIndex].value)},s.prototype.captureDomEvent=function(t,e,r,n){if(void 0!==r)if(this.scrubTelemetryInputs||"password"===l.getElementType(e))r="[scrubbed]";else{var o=l.describeElement(e);this.telemetryScrubber?this.telemetryScrubber(o)&&(r="[scrubbed]"):this.defaultValueScrubber(o)&&(r="[scrubbed]")}var i=l.elementArrayToString(l.treeToArray(e));this.telemeter.captureDom(t,i,r,n)},s.prototype.deinstrumentNavigation=function(){var t=this._window.chrome,e=t&&t.app&&t.app.runtime,r=!e&&this._window.history&&this._window.history.pushState;r&&o(this.replacements,"navigation")},s.prototype.instrumentNavigation=function(){var t=this._window.chrome,e=t&&t.app&&t.app.runtime,r=!e&&this._window.history&&this._window.history.pushState;if(r){var o=this;n(this._window,"onpopstate",function(t){return function(){var e=o._location.href;o.handleUrlChange(o._lastHref,e),t&&t.apply(this,arguments)}},this.replacements,"navigation"),n(this._window.history,"pushState",function(t){return function(){var e=arguments.length>2?arguments[2]:void 0;return e&&o.handleUrlChange(o._lastHref,e+""),t.apply(this,arguments)}},this.replacements,"navigation")}},s.prototype.handleUrlChange=function(t,e){var r=c.parse(this._location.href),n=c.parse(e),o=c.parse(t);this._lastHref=e,r.protocol===n.protocol&&r.host===n.host&&(e=n.path+(n.hash||"")),r.protocol===o.protocol&&r.host===o.host&&(t=o.path+(o.hash||"")),this.telemeter.captureNavigation(t,e)},s.prototype.deinstrumentConnectivity=function(){("addEventListener"in this._window||"body"in this._document)&&(this._window.addEventListener?this.removeListeners("connectivity"):o(this.replacements,"connectivity"))},s.prototype.instrumentConnectivity=function(){if("addEventListener"in this._window||"body"in this._document)if(this._window.addEventListener)this.addListener("connectivity",this._window,"online",void 0,function(){this.telemeter.captureConnectivityChange("online")}.bind(this),!0),this.addListener("connectivity",this._window,"offline",void 0,function(){this.telemeter.captureConnectivityChange("offline")}.bind(this),!0);else{var t=this;n(this._document.body,"ononline",function(e){return function(){t.telemeter.captureConnectivityChange("online"),e&&e.apply(this,arguments)}},this.replacements,"connectivity"),n(this._document.body,"onoffline",function(e){return function(){t.telemeter.captureConnectivityChange("offline"),e&&e.apply(this,arguments)}},this.replacements,"connectivity")}},s.prototype.addListener=function(t,e,r,n,o,i){e.addEventListener?(e.addEventListener(r,o,i),this.eventRemovers[t].push(function(){e.removeEventListener(r,o,i)})):n&&(e.attachEvent(n,o),this.eventRemovers[t].push(function(){e.detachEvent(n,o)}))},s.prototype.removeListeners=function(t){for(var e;this.eventRemovers[t].length;)(e=this.eventRemovers[t].shift())()},t.exports=s},function(t,e){"use strict";function r(t){return(t.getAttribute("type")||"").toLowerCase()}function n(t,e,n){if(t.tagName.toLowerCase()!==e.toLowerCase())return!1;if(!n)return!0;t=r(t);for(var o=0;o<n.length;o++)if(n[o]===t)return!0;return!1}function o(t,e){return t.target?t.target:e&&e.elementFromPoint?e.elementFromPoint(t.clientX,t.clientY):void 0}function i(t){for(var e,r=5,n=[],o=0;t&&o<r&&(e=u(t),"html"!==e.tagName);o++)n.unshift(e),t=t.parentNode;return n}function a(t){for(var e,r,n=80,o=" > ",i=o.length,a=[],u=0,c=t.length-1;c>=0;c--){if(e=s(t[c]),r=u+a.length*i+e.length,c<t.length-1&&r>=n+3){a.unshift("...");break}a.unshift(e),u+=e.length}return a.join(o)}function s(t){if(!t||!t.tagName)return"";var e=[t.tagName];t.id&&e.push("#"+t.id),t.classes&&e.push("."+t.classes.join("."));for(var r=0;r<t.attributes.length;r++)e.push("["+t.attributes[r].key+'="'+t.attributes[r].value+'"]');return e.join("")}function u(t){if(!t||!t.tagName)return null;var e,r,n,o,i={};i.tagName=t.tagName.toLowerCase(),t.id&&(i.id=t.id),e=t.className,e&&"string"==typeof e&&(i.classes=e.split(/\s+/));var a=["type","name","title","alt"];for(i.attributes=[],o=0;o<a.length;o++)r=a[o],n=t.getAttribute(r),n&&i.attributes.push({key:r,value:n});return i}t.exports={describeElement:u,descriptionToString:s,elementArrayToString:a,treeToArray:i,getElementFromEvent:o,isDescribedElement:n,getElementType:r}}])});
\ No newline at end of file diff --git a/packages/website/public/js/rollbar.umd.nojson.min.js b/packages/website/public/js/rollbar.umd.nojson.min.js deleted file mode 100644 index f2b05d00a..000000000 --- a/packages/website/public/js/rollbar.umd.nojson.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r();else if("function"==typeof define&&define.amd)define([],r);else{var t=r();for(var n in t)("object"==typeof exports?exports:e)[n]=t[n]}}(this,function(){return function(e){function r(n){if(t[n])return t[n].exports;var o=t[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}var t={};return r.m=e,r.c=t,r.p="",r(0)}([function(e,r,t){e.exports=t(1)},function(e,r,t){"use strict";function n(){var e="undefined"==typeof JSON?{}:JSON;o.setupJSON(e)}var o=t(2),i=t(3);n();var a=window._rollbarConfig,s=a&&a.globalAlias||"Rollbar",u=window[s]&&"undefined"!=typeof window[s].shimId;!u&&a?o.wrapper.init(a):(window.Rollbar=o.wrapper,window.RollbarNotifier=i.Notifier),e.exports=o.wrapper},function(e,r,t){"use strict";function n(e,r,t){!t[4]&&window._rollbarWrappedError&&(t[4]=window._rollbarWrappedError,window._rollbarWrappedError=null),e.uncaughtError.apply(e,t),r&&r.apply(window,t)}function o(e,r){if(r.hasOwnProperty&&r.hasOwnProperty("addEventListener")){var t=r.addEventListener;r.addEventListener=function(r,n,o){t.call(this,r,e.wrap(n),o)};var n=r.removeEventListener;r.removeEventListener=function(e,r,t){n.call(this,e,r&&r._wrapped||r,t)}}}var i=t(3),a=t(8),s=i.Notifier;window._rollbarWrappedError=null;var u={};u.init=function(e,r){var t=new s(r);if(t.configure(e),e.captureUncaught){var i;r&&a.isType(r._rollbarOldOnError,"function")?i=r._rollbarOldOnError:window.onerror&&!window.onerror.belongsToShim&&(i=window.onerror),window.onerror=function(){var e=Array.prototype.slice.call(arguments,0);n(t,i,e)};var u,c,l=["EventTarget","Window","Node","ApplicationCache","AudioTrackList","ChannelMergerNode","CryptoOperation","EventSource","FileReader","HTMLUnknownElement","IDBDatabase","IDBRequest","IDBTransaction","KeyOperation","MediaController","MessagePort","ModalWindow","Notification","SVGElementInstance","Screen","TextTrack","TextTrackCue","TextTrackList","WebSocket","WebSocketWorker","Worker","XMLHttpRequest","XMLHttpRequestEventTarget","XMLHttpRequestUpload"];for(u=0;u<l.length;++u)c=l[u],window[c]&&window[c].prototype&&o(t,window[c].prototype)}return e.captureUnhandledRejections&&(r&&a.isType(r._unhandledRejectionHandler,"function")&&window.removeEventListener("unhandledrejection",r._unhandledRejectionHandler),t._unhandledRejectionHandler=function(e){var r=e.reason,n=e.promise,o=e.detail;!r&&o&&(r=o.reason,n=o.promise),t.unhandledRejection(r,n)},window.addEventListener("unhandledrejection",t._unhandledRejectionHandler)),window.Rollbar=t,s.processPayloads(),t},e.exports={wrapper:u,setupJSON:i.setupJSON}},function(e,r,t){"use strict";function n(e){E=e,w.setupJSON(e),v.setupJSON(e)}function o(e,r){return function(){var t=r||this;try{return e.apply(t,arguments)}catch(e){v.consoleError("[Rollbar]:",e)}}}function i(){h||(h=setTimeout(f,1e3))}function a(){return _}function s(e){_=_||this;var r="https://"+s.DEFAULT_ENDPOINT;this.options={enabled:!0,endpoint:r,environment:"production",scrubFields:g([],s.DEFAULT_SCRUB_FIELDS),checkIgnore:null,logLevel:s.DEFAULT_LOG_LEVEL,reportLevel:s.DEFAULT_REPORT_LEVEL,uncaughtErrorLevel:s.DEFAULT_UNCAUGHT_ERROR_LEVEL,payload:{}},this.lastError=null,this.plugins={},this.parentNotifier=e,e&&(e.hasOwnProperty("shimId")?e.notifier=this:this.configure(e.options))}function u(e){window._rollbarPayloadQueue.push(e),i()}function c(e){return o(function(){var r=this._getLogArgs(arguments);return this._log(e||r.level||this.options.logLevel||s.DEFAULT_LOG_LEVEL,r.message,r.err,r.custom,r.callback)})}function l(e,r){e||(e=r?E.stringify(r):"");var t={body:e};return r&&(t.extra=g(!0,{},r)),{message:t}}function p(e,r,t){var n=m.guessErrorClass(r.message),o=r.name||n[0],i=n[1],a={exception:{class:o,message:i}};if(e&&(a.exception.description=e||"uncaught exception"),r.stack){var s,u,c,p,f,d,h,w;for(a.frames=[],h=0;h<r.stack.length;++h)s=r.stack[h],u={filename:s.url?v.sanitizeUrl(s.url):"(unknown)",lineno:s.line||null,method:s.func&&"?"!==s.func?s.func:"[anonymous]",colno:s.column},c=p=f=null,d=s.context?s.context.length:0,d&&(w=Math.floor(d/2),p=s.context.slice(0,w),c=s.context[w],f=s.context.slice(w)),c&&(u.code=c),(p||f)&&(u.context={},p&&p.length&&(u.context.pre=p),f&&f.length&&(u.context.post=f)),s.args&&(u.args=s.args),a.frames.push(u);return a.frames.reverse(),t&&(a.extra=g(!0,{},t)),{trace:a}}return l(o+": "+i,t)}function f(){var e;try{for(;e=window._rollbarPayloadQueue.shift();)d(e)}finally{h=void 0}}function d(e){var r=e.endpointUrl,t=e.accessToken,n=e.payload,o=e.callback||function(){},i=(new Date).getTime();i-L>=6e4&&(L=i,R=0);var a=window._globalRollbarOptions.maxItems,c=window._globalRollbarOptions.itemsPerMinute,l=function(){return!n.ignoreRateLimit&&a>=1&&T>=a},p=function(){return!n.ignoreRateLimit&&c>=1&&R>=c};return l()?void o(new Error(a+" max items reached")):p()?void o(new Error(c+" items per minute reached")):(T++,R++,l()&&_._log(_.options.uncaughtErrorLevel,"maxItems has been hit. Ignoring errors for the remainder of the current page load.",null,{maxItems:a},null,!1,!0),n.ignoreRateLimit&&delete n.ignoreRateLimit,void y.post(r,t,n,function(r,t){return r?(r instanceof b&&(e.callback=function(){},setTimeout(function(){u(e)},s.RETRY_DELAY)),o(r)):o(null,t)}))}var h,g=t(4),m=t(5),v=t(8),w=t(11),y=w.XHR,b=w.ConnectionError,E=null;s.NOTIFIER_VERSION="1.9.4",s.DEFAULT_ENDPOINT="api.rollbar.com/api/1/",s.DEFAULT_SCRUB_FIELDS=["pw","pass","passwd","password","secret","confirm_password","confirmPassword","password_confirmation","passwordConfirmation","access_token","accessToken","secret_key","secretKey","secretToken"],s.DEFAULT_LOG_LEVEL="debug",s.DEFAULT_REPORT_LEVEL="debug",s.DEFAULT_UNCAUGHT_ERROR_LEVEL="error",s.DEFAULT_ITEMS_PER_MIN=60,s.DEFAULT_MAX_ITEMS=0,s.LEVELS={debug:0,info:1,warning:2,error:3,critical:4},s.RETRY_DELAY=1e4,window._rollbarPayloadQueue=window._rollbarPayloadQueue||[],window._globalRollbarOptions={startTime:(new Date).getTime(),maxItems:s.DEFAULT_MAX_ITEMS,itemsPerMinute:s.DEFAULT_ITEMS_PER_MIN};var _,x=s.prototype;x._getLogArgs=function(e){for(var r,t,n,i,a,u,c=this.options.logLevel||s.DEFAULT_LOG_LEVEL,l=[],p=0;p<e.length;++p)u=e[p],a=v.typeName(u),"string"===a?r?l.push(u):r=u:"function"===a?i=o(u,this):"date"===a?l.push(u):"error"===a||u instanceof Error||"undefined"!=typeof DOMException&&u instanceof DOMException?t?l.push(u):t=u:"object"!==a&&"array"!==a||(n?l.push(u):n=u);return l.length&&(n=n||{},n.extraArgs=l),{level:c,message:r,err:t,custom:n,callback:i}},x._route=function(e){var r=this.options.endpoint,t=/\/$/.test(r),n=/^\//.test(e);return t&&n?e=e.substring(1):t||n||(e="/"+e),r+e},x._processShimQueue=function(e){for(var r,t,n,o,i,a,u,c={};t=e.shift();)r=t.shim,n=t.method,o=t.args,i=r.parentShim,u=c[r.shimId],u||(i?(a=c[i.shimId],u=new s(a)):u=this,c[r.shimId]=u),u[n]&&v.isType(u[n],"function")&&u[n].apply(u,o)},x._buildPayload=function(e,r,t,n,o){var i=this.options.accessToken,a=this.options.environment,u=g(!0,{},this.options.payload),c=v.uuid4();if(void 0===s.LEVELS[r])throw new Error("Invalid level");if(!t&&!n&&!o)throw new Error("No message, stack info or custom data");var l={environment:a,endpoint:this.options.endpoint,uuid:c,level:r,platform:"browser",framework:"browser-js",language:"javascript",body:this._buildBody(t,n,o),request:{url:window.location.href,query_string:window.location.search,user_ip:"$remote_ip"},client:{runtime_ms:e.getTime()-window._globalRollbarOptions.startTime,timestamp:Math.round(e.getTime()/1e3),javascript:{browser:window.navigator.userAgent,language:window.navigator.language,cookie_enabled:window.navigator.cookieEnabled,screen:{width:window.screen.width,height:window.screen.height},plugins:this._getBrowserPlugins()}},server:{},notifier:{name:"rollbar-browser-js",version:s.NOTIFIER_VERSION}};u.body&&delete u.body;var p={access_token:i,data:g(!0,l,u)};return this._scrub(p.data),p},x._buildBody=function(e,r,t){var n;return n=r?p(e,r,t):l(e,t)},x._getBrowserPlugins=function(){if(!this._browserPlugins){var e,r,t=window.navigator.plugins||[],n=t.length,o=[];for(r=0;r<n;++r)e=t[r],o.push({name:e.name,description:e.description});this._browserPlugins=o}return this._browserPlugins},x._scrub=function(e){function r(e,r,t,n,o,i){return r+v.redact(i)}function t(e){var t;if(v.isType(e,"string"))for(t=0;t<s.length;++t)e=e.replace(s[t],r);return e}function n(e,r){var t;for(t=0;t<a.length;++t)if(a[t].test(e)){r=v.redact(r);break}return r}function o(e,r){var o=n(e,r);return o===r?t(o):o}var i=this.options.scrubFields,a=this._getScrubFieldRegexs(i),s=this._getScrubQueryParamRegexs(i);return v.traverse(e,o),e},x._getScrubFieldRegexs=function(e){for(var r,t=[],n=0;n<e.length;++n)r="\\[?(%5[bB])?"+e[n]+"\\[?(%5[bB])?\\]?(%5[dD])?",t.push(new RegExp(r,"i"));return t},x._getScrubQueryParamRegexs=function(e){for(var r,t=[],n=0;n<e.length;++n)r="\\[?(%5[bB])?"+e[n]+"\\[?(%5[bB])?\\]?(%5[dD])?",t.push(new RegExp("("+r+"=)([^&\\n]+)","igm"));return t},x._urlIsWhitelisted=function(e){var r,t,n,o,i,a,s,u,c,l;try{if(r=this.options.hostWhiteList,t=e&&e.data&&e.data.body&&e.data.body.trace,!r||0===r.length)return!0;if(!t)return!0;for(s=r.length,i=t.frames.length,c=0;c<i;c++){if(n=t.frames[c],o=n.filename,!v.isType(o,"string"))return!0;for(l=0;l<s;l++)if(a=r[l],u=new RegExp(a),u.test(o))return!0}}catch(e){return this.configure({hostWhiteList:null}),v.consoleError("[Rollbar]: Error while reading your configuration's hostWhiteList option. Removing custom hostWhiteList.",e),!0}return!1},x._messageIsIgnored=function(e){var r,t,n,o,i,a,s,u,c;try{if(i=!1,n=this.options.ignoredMessages,!n||0===n.length)return!1;if(s=e&&e.data&&e.data.body,u=s&&s.trace&&s.trace.exception&&s.trace.exception.message,c=s&&s.message&&s.message.body,r=u||c,!r)return!1;for(o=n.length,t=0;t<o&&(a=new RegExp(n[t],"gi"),!(i=a.test(r)));t++);}catch(e){this.configure({ignoredMessages:null}),v.consoleError("[Rollbar]: Error while reading your configuration's ignoredMessages option. Removing custom ignoredMessages.")}return i},x._enqueuePayload=function(e,r,t,n){var o={callback:n,accessToken:this.options.accessToken,endpointUrl:this._route("item/"),payload:e},i=function(){if(n){var e="This item was not sent to Rollbar because it was ignored. This can happen if a custom checkIgnore() function was used or if the item's level was less than the notifier' reportLevel. See https://rollbar.com/docs/notifier/rollbar.js/configuration for more details.";n(null,{err:0,result:{id:null,uuid:null,message:e}})}};if(this._internalCheckIgnore(r,t,e))return void i();try{if(v.isType(this.options.checkIgnore,"function")&&this.options.checkIgnore(r,t,e))return void i()}catch(e){this.configure({checkIgnore:null}),v.consoleError("[Rollbar]: Error while calling custom checkIgnore() function. Removing custom checkIgnore().",e)}if(this._urlIsWhitelisted(e)&&!this._messageIsIgnored(e)){if(this.options.verbose){if(e.data&&e.data.body&&e.data.body.trace){var a=e.data.body.trace,s=a.exception.message;v.consoleError("[Rollbar]: ",s)}v.consoleInfo("[Rollbar]: ",o)}v.isType(this.options.logFunction,"function")&&this.options.logFunction(o);try{v.isType(this.options.transform,"function")&&this.options.transform(e)}catch(e){this.configure({transform:null}),v.consoleError("[Rollbar]: Error while calling custom transform() function. Removing custom transform().",e)}this.options.enabled&&u(o)}},x._internalCheckIgnore=function(e,r,t){var n=r[0],o=s.LEVELS[n]||0,i=s.LEVELS[this.options.reportLevel]||0;if(o<i)return!0;var a=this.options?this.options.plugins:{};if(a&&a.jquery&&a.jquery.ignoreAjaxErrors)try{return!!t.data.body.message.extra.isAjax}catch(e){return!1}return!1},x._log=function(e,r,t,n,o,i,a){var s=null;if(t)try{if(s=t._savedStackTrace?t._savedStackTrace:m.parse(t),t===this.lastError)return;this.lastError=t}catch(e){v.consoleError("[Rollbar]: Error while parsing the error object.",e),r=t.message||t.description||r||String(t),t=null}var u=this._buildPayload(new Date,e,r,s,n);return a&&(u.ignoreRateLimit=!0),this._enqueuePayload(u,!!i,[e,r,t,n],o),{uuid:u.data.uuid}},x.log=c(),x.debug=c("debug"),x.info=c("info"),x.warn=c("warning"),x.warning=c("warning"),x.error=c("error"),x.critical=c("critical"),x.uncaughtError=o(function(e,r,t,n,o,i){if(i=i||null,o&&v.isType(o,"error"))return void this._log(this.options.uncaughtErrorLevel,e,o,i,null,!0);if(r&&v.isType(r,"error"))return void this._log(this.options.uncaughtErrorLevel,e,r,i,null,!0);var a={url:r||"",line:t};a.func=m.guessFunctionName(a.url,a.line),a.context=m.gatherContext(a.url,a.line);var s={mode:"onerror",message:o?String(o):e||"uncaught exception",url:document.location.href,stack:[a],useragent:navigator.userAgent},u=this._buildPayload(new Date,this.options.uncaughtErrorLevel,e,s,i);this._enqueuePayload(u,!0,[this.options.uncaughtErrorLevel,e,r,t,n,o])}),x.unhandledRejection=o(function(e,r){var t,n;if(e?(t=e.message||String(e),n=e._rollbarContext):t="unhandled rejection was null or undefined!",n=n||r._rollbarContext||null,e&&v.isType(e,"error"))return void this._log(this.options.uncaughtErrorLevel,t,e,n,null,!0);var o={url:"",line:0};o.func=m.guessFunctionName(o.url,o.line),o.context=m.gatherContext(o.url,o.line);var i={mode:"unhandledrejection",message:t,url:document.location.href,stack:[o],useragent:navigator.userAgent},a=this._buildPayload(new Date,this.options.uncaughtErrorLevel,t,i,n);this._enqueuePayload(a,!0,[this.options.uncaughtErrorLevel,t,o.url,o.line,0,e,r])}),x.global=o(function(e){e=e||{};var r={startTime:e.startTime,maxItems:e.maxItems,itemsPerMinute:e.itemsPerMinute};g(!0,window._globalRollbarOptions,r),void 0!==e.maxItems&&(T=0),void 0!==e.itemsPerMinute&&(R=0)}),x.configure=o(function(e,r){var t=g(!0,{},e);g(!r,this.options,t),this.global(t)}),x.scope=o(function(e){var r=new s(this);return g(!0,r.options.payload,e),r}),x.wrap=function(e,r){try{var t;if(t=v.isType(r,"function")?r:function(){return r||{}},!v.isType(e,"function"))return e;if(e._isWrap)return e;if(!e._wrapped){e._wrapped=function(){try{return e.apply(this,arguments)}catch(r){throw"string"==typeof r&&(r=new String(r)),r.stack||(r._savedStackTrace=m.parse(r)),r._rollbarContext=t()||{},r._rollbarContext._wrappedSource=e.toString(),window._rollbarWrappedError=r,r}},e._wrapped._isWrap=!0;for(var n in e)e.hasOwnProperty(n)&&(e._wrapped[n]=e[n])}return e._wrapped}catch(r){return e}},x.loadFull=function(){v.consoleError("[Rollbar]: Unexpected Rollbar.loadFull() called on a Notifier instance")},s.processPayloads=function(e){return e?void f():void i()};var L=(new Date).getTime(),T=0,R=0;e.exports={Notifier:s,setupJSON:n,topLevelNotifier:a}},function(e,r){"use strict";var t=Object.prototype.hasOwnProperty,n=Object.prototype.toString,o=function(e){return"function"==typeof Array.isArray?Array.isArray(e):"[object Array]"===n.call(e)},i=function(e){if(!e||"[object Object]"!==n.call(e))return!1;var r=t.call(e,"constructor"),o=e.constructor&&e.constructor.prototype&&t.call(e.constructor.prototype,"isPrototypeOf");if(e.constructor&&!r&&!o)return!1;var i;for(i in e);return"undefined"==typeof i||t.call(e,i)};e.exports=function e(){var r,t,n,a,s,u,c=arguments[0],l=1,p=arguments.length,f=!1;for("boolean"==typeof c?(f=c,c=arguments[1]||{},l=2):("object"!=typeof c&&"function"!=typeof c||null==c)&&(c={});l<p;++l)if(r=arguments[l],null!=r)for(t in r)n=c[t],a=r[t],c!==a&&(f&&a&&(i(a)||(s=o(a)))?(s?(s=!1,u=n&&o(n)?n:[]):u=n&&i(n)?n:{},c[t]=e(f,u,a)):"undefined"!=typeof a&&(c[t]=a));return c}},function(e,r,t){"use strict";function n(){return l}function o(){return null}function i(e){var r={};return r._stackFrame=e,r.url=e.fileName,r.line=e.lineNumber,r.func=e.functionName,r.column=e.columnNumber,r.args=e.args,r.context=o(r.url,r.line),r}function a(e){function r(){var r=[];try{r=c.parse(e)}catch(e){r=[]}for(var t=[],n=0;n<r.length;n++)t.push(new i(r[n]));return t}return{stack:r(),message:e.message,name:e.name}}function s(e){return new a(e)}function u(e){if(!e)return["Unknown error. There was no error message to display.",""];var r=e.match(p),t="(unknown)";return r&&(t=r[r.length-1],e=e.replace((r[r.length-2]||"")+t+":",""),e=e.replace(/(^[\s]+|[\s]+$)/g,"")),[t,e]}var c=t(6),l="?",p=new RegExp("^(([a-zA-Z0-9-_$ ]*): *)?(Uncaught )?([a-zA-Z0-9-_$ ]*): ");e.exports={guessFunctionName:n,guessErrorClass:u,gatherContext:o,parse:s,Stack:a,Frame:i}},function(e,r,t){var n,o,i;!function(a,s){"use strict";o=[t(7)],n=s,i="function"==typeof n?n.apply(r,o):n,!(void 0!==i&&(e.exports=i))}(this,function(e){"use strict";function r(e,r,t){if("function"==typeof Array.prototype.map)return e.map(r,t);for(var n=new Array(e.length),o=0;o<e.length;o++)n[o]=r.call(t,e[o]);return n}function t(e,r,t){if("function"==typeof Array.prototype.filter)return e.filter(r,t);for(var n=[],o=0;o<e.length;o++)r.call(t,e[o])&&n.push(e[o]);return n}var n=/(^|@)\S+\:\d+/,o=/^\s*at .*(\S+\:\d+|\(native\))/m,i=/^(eval@)?(\[native code\])?$/;return{parse:function(e){if("undefined"!=typeof e.stacktrace||"undefined"!=typeof e["opera#sourceloc"])return this.parseOpera(e);if(e.stack&&e.stack.match(o))return this.parseV8OrIE(e);if(e.stack)return this.parseFFOrSafari(e);throw new Error("Cannot parse given Error object")},extractLocation:function(e){if(e.indexOf(":")===-1)return[e];var r=e.replace(/[\(\)\s]/g,"").split(":"),t=r.pop(),n=r[r.length-1];if(!isNaN(parseFloat(n))&&isFinite(n)){var o=r.pop();return[r.join(":"),o,t]}return[r.join(":"),t,void 0]},parseV8OrIE:function(n){var i=t(n.stack.split("\n"),function(e){return!!e.match(o)},this);return r(i,function(r){r.indexOf("(eval ")>-1&&(r=r.replace(/eval code/g,"eval").replace(/(\(eval at [^\()]*)|(\)\,.*$)/g,""));var t=r.replace(/^\s+/,"").replace(/\(eval code/g,"(").split(/\s+/).slice(1),n=this.extractLocation(t.pop()),o=t.join(" ")||void 0,i="eval"===n[0]?void 0:n[0];return new e(o,void 0,i,n[1],n[2],r)},this)},parseFFOrSafari:function(n){var o=t(n.stack.split("\n"),function(e){return!e.match(i)},this);return r(o,function(r){if(r.indexOf(" > eval")>-1&&(r=r.replace(/ line (\d+)(?: > eval line \d+)* > eval\:\d+\:\d+/g,":$1")),r.indexOf("@")===-1&&r.indexOf(":")===-1)return new e(r);var t=r.split("@"),n=this.extractLocation(t.pop()),o=t.shift()||void 0;return new e(o,void 0,n[0],n[1],n[2],r)},this)},parseOpera:function(e){return!e.stacktrace||e.message.indexOf("\n")>-1&&e.message.split("\n").length>e.stacktrace.split("\n").length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(r){for(var t=/Line (\d+).*script (?:in )?(\S+)/i,n=r.message.split("\n"),o=[],i=2,a=n.length;i<a;i+=2){var s=t.exec(n[i]);s&&o.push(new e(void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera10:function(r){for(var t=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,n=r.stacktrace.split("\n"),o=[],i=0,a=n.length;i<a;i+=2){var s=t.exec(n[i]);s&&o.push(new e(s[3]||void 0,void 0,s[2],s[1],void 0,n[i]))}return o},parseOpera11:function(o){var i=t(o.stack.split("\n"),function(e){return!!e.match(n)&&!e.match(/^Error created at/)},this);return r(i,function(r){var t,n=r.split("@"),o=this.extractLocation(n.pop()),i=n.shift()||"",a=i.replace(/<anonymous function(: (\w+))?>/,"$2").replace(/\([^\)]*\)/g,"")||void 0;i.match(/\(([^\)]*)\)/)&&(t=i.replace(/^[^\(]+\(([^\)]*)\)$/,"$1"));var s=void 0===t||"[arguments not available]"===t?void 0:t.split(",");return new e(a,s,o[0],o[1],o[2],r)},this)}}})},function(e,r,t){var n,o,i;!function(t,a){"use strict";o=[],n=a,i="function"==typeof n?n.apply(r,o):n,!(void 0!==i&&(e.exports=i))}(this,function(){"use strict";function e(e){return!isNaN(parseFloat(e))&&isFinite(e)}function r(e,r,t,n,o,i){void 0!==e&&this.setFunctionName(e),void 0!==r&&this.setArgs(r),void 0!==t&&this.setFileName(t),void 0!==n&&this.setLineNumber(n),void 0!==o&&this.setColumnNumber(o),void 0!==i&&this.setSource(i)}return r.prototype={getFunctionName:function(){return this.functionName},setFunctionName:function(e){this.functionName=String(e)},getArgs:function(){return this.args},setArgs:function(e){if("[object Array]"!==Object.prototype.toString.call(e))throw new TypeError("Args must be an Array");this.args=e},getFileName:function(){return this.fileName},setFileName:function(e){this.fileName=String(e)},getLineNumber:function(){return this.lineNumber},setLineNumber:function(r){if(!e(r))throw new TypeError("Line Number must be a Number");this.lineNumber=Number(r)},getColumnNumber:function(){return this.columnNumber},setColumnNumber:function(r){if(!e(r))throw new TypeError("Column Number must be a Number");this.columnNumber=Number(r)},getSource:function(){return this.source},setSource:function(e){this.source=String(e)},toString:function(){var r=this.getFunctionName()||"{anonymous}",t="("+(this.getArgs()||[]).join(",")+")",n=this.getFileName()?"@"+this.getFileName():"",o=e(this.getLineNumber())?":"+this.getLineNumber():"",i=e(this.getColumnNumber())?":"+this.getColumnNumber():"";return r+t+n+o+i}},r})},function(e,r,t){"use strict";function n(e){v=e}function o(e){return{}.toString.call(e).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function i(e,r){return o(e)===r}function a(e){if(!i(e,"string"))throw new Error("received invalid input");for(var r=w,t=r.parser[r.strictMode?"strict":"loose"].exec(e),n={},o=14;o--;)n[r.key[o]]=t[o]||"";return n[r.q.name]={},n[r.key[12]].replace(r.q.parser,function(e,t,o){t&&(n[r.q.name][t]=o)}),n}function s(e){var r=a(e);return""===r.anchor&&(r.source=r.source.replace("#","")),e=r.source.replace("?"+r.query,"")}function u(e,r){var t,n,o,a=i(e,"object"),s=i(e,"array"),c=[];if(a)for(t in e)e.hasOwnProperty(t)&&c.push(t);else if(s)for(o=0;o<e.length;++o)c.push(o);for(o=0;o<c.length;++o)t=c[o],n=e[t],a=i(n,"object"),s=i(n,"array"),a||s?e[t]=u(n,r):e[t]=r(t,n);return e}function c(e){return e=String(e),new Array(e.length+1).join("*")}function l(){var e=(new Date).getTime(),r="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(r){var t=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"===r?t:7&t|8).toString(16)});return r}function p(e){return"function"!=typeof Object.create?function(e){var r=function(){};return function(e){if(null!==e&&e!==Object(e))throw TypeError("Argument must be an object, or null");r.prototype=e||{};var t=new r;return r.prototype=null,null===e&&(t.__proto__=null),t}}()(e):Object.create(e)}function f(){for(var e=[],r=0;r<arguments.length;r++){var t=arguments[r];"object"==typeof t?(t=v.stringify(t),t.length>500&&(t=t.substr(0,500)+"...")):"undefined"==typeof t&&(t="undefined"),e.push(t)}return e.join(" ")}function d(){m.ieVersion()<=8?console.error(f.apply(null,arguments)):console.error.apply(null,arguments)}function h(){m.ieVersion()<=8?console.info(f.apply(null,arguments)):console.info.apply(null,arguments)}function g(){m.ieVersion()<=8?console.log(f.apply(null,arguments)):console.log.apply(null,arguments)}t(9);var m=t(10),v=null,w={strictMode:!1,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}},y={setupJSON:n,isType:i,parseUri:a,parseUriOptions:w,redact:c,sanitizeUrl:s,traverse:u,typeName:o,uuid4:l,objectCreate:p,consoleError:d,consoleInfo:h,consoleLog:g};e.exports=y},function(e,r){!function(e){"use strict";e.console||(e.console={});for(var r,t,n=e.console,o=function(){},i=["memory"],a="assert,clear,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn".split(",");r=i.pop();)n[r]||(n[r]={});for(;t=a.pop();)n[t]||(n[t]=o)}("undefined"==typeof window?this:window)},function(e,r){"use strict";function t(){for(var e,r=3,t=document.createElement("div"),n=t.getElementsByTagName("i");t.innerHTML="<!--[if gt IE "+ ++r+"]><i></i><![endif]-->",n[0];);return r>4?r:e}var n={ieVersion:t};e.exports=n},function(e,r,t){"use strict";function n(e){a=e}function o(e){this.name="Connection Error",this.message=e,this.stack=(new Error).stack}var i=t(8),a=null;o.prototype=i.objectCreate(Error.prototype),o.prototype.constructor=o;var s={XMLHttpFactories:[function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}],createXMLHTTPObject:function(){var e,r=!1,t=s.XMLHttpFactories,n=t.length;for(e=0;e<n;e++)try{r=t[e]();break}catch(e){}return r},post:function(e,r,t,n){if(!i.isType(t,"object"))throw new Error("Expected an object to POST");t=a.stringify(t),n=n||function(){};var u=s.createXMLHTTPObject();if(u)try{try{var c=function(){try{if(c&&4===u.readyState){c=void 0;var e=a.parse(u.responseText);200===u.status?n(null,e):i.isType(u.status,"number")&&u.status>=400&&u.status<600?(403==u.status&&i.consoleError("[Rollbar]:"+e.message),n(new Error(String(u.status)))):n(new o("XHR response had no status code (likely connection failure)"))}}catch(e){var r;r=e&&e.stack?e:new Error(e),n(r)}};u.open("POST",e,!0),u.setRequestHeader&&(u.setRequestHeader("Content-Type","application/json"),u.setRequestHeader("X-Rollbar-Access-Token",r)),u.onreadystatechange=c,u.send(t)}catch(r){if("undefined"!=typeof XDomainRequest){"http:"===window.location.href.substring(0,5)&&"https"===e.substring(0,5)&&(e="http"+e.substring(5));var l=function(){n(new o("Request timed out"))},p=function(){n(new Error("Error during request"))},f=function(){n(null,a.parse(u.responseText))};u=new XDomainRequest,u.onprogress=function(){},u.ontimeout=l,u.onerror=p,u.onload=f,u.open("POST",e,!0),u.send(t)}}}catch(e){n(e)}}};e.exports={XHR:s,setupJSON:n,ConnectionError:o}}])}); diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index fde134b18..88461f8a9 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -15,8 +15,9 @@ import { ledgerEthereumBrowserClientFactoryAsync, LedgerSubprovider, RedundantSubprovider, + RPCSubprovider, SignerSubprovider, - Subprovider, + Web3ProviderEngine, } from '@0xproject/subproviders'; import { BlockParam, @@ -60,9 +61,7 @@ import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; import { utils } from 'ts/utils/utils'; -import ProviderEngine = require('web3-provider-engine'); import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); -import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); import * as MintableArtifacts from '../contracts/Mintable.json'; @@ -84,11 +83,11 @@ export class Blockchain { public networkId: number; public nodeVersion: string; private _contractWrappers: ContractWrappers; - private _dispatcher: Dispatcher; + private readonly _dispatcher: Dispatcher; private _web3Wrapper?: Web3Wrapper; private _blockchainWatcher?: BlockchainWatcher; private _injectedProviderObservable?: InjectedProviderObservable; - private _injectedProviderUpdateHandler: (update: InjectedProviderUpdate) => Promise<void>; + private readonly _injectedProviderUpdateHandler: (update: InjectedProviderUpdate) => Promise<void>; private _userAddressIfExists: string; private _ledgerSubprovider: LedgerSubprovider; private _defaultGasPrice: BigNumber; @@ -126,7 +125,11 @@ export class Blockchain { let networkIdIfExists: number; if (!_.isUndefined(injectedWeb3IfExists)) { try { - networkIdIfExists = _.parseInt(await promisify<string>(injectedWeb3IfExists.version.getNetwork)()); + networkIdIfExists = _.parseInt( + await promisify<string>( + injectedWeb3IfExists.version.getNetwork.bind(injectedWeb3IfExists.version), + )(), + ); } catch (err) { // Ignore error and proceed with networkId undefined } @@ -148,7 +151,7 @@ export class Blockchain { if (!isU2FSupported) { throw new Error('Cannot update providerType to LEDGER without U2F support'); } - const provider = new ProviderEngine(); + const provider = new Web3ProviderEngine(); const ledgerWalletConfigs = { networkId: networkIdIfExists, ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, @@ -157,25 +160,21 @@ export class Blockchain { provider.addProvider(ledgerSubprovider); provider.addProvider(new FilterSubprovider()); const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists], publicNodeUrl => { - return new RpcSubprovider({ - rpcUrl: publicNodeUrl, - }); + return new RPCSubprovider(publicNodeUrl); }); - provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); + provider.addProvider(new RedundantSubprovider(rpcSubproviders)); provider.start(); return [provider, ledgerSubprovider]; } else if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { // We catch all requests involving a users account and send it to the injectedWeb3 // instance. All other requests go to the public hosted node. - const provider = new ProviderEngine(); + const provider = new Web3ProviderEngine(); provider.addProvider(new SignerSubprovider(injectedWeb3.currentProvider)); provider.addProvider(new FilterSubprovider()); const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => { - return new RpcSubprovider({ - rpcUrl: publicNodeUrl, - }); + return new RPCSubprovider(publicNodeUrl); }); - provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); + provider.addProvider(new RedundantSubprovider(rpcSubproviders)); provider.start(); return [provider, undefined]; } else if (doesInjectedWeb3Exist) { @@ -185,15 +184,13 @@ export class Blockchain { // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 // injected into their browser. - const provider = new ProviderEngine(); + const provider = new Web3ProviderEngine(); provider.addProvider(new FilterSubprovider()); const networkId = constants.NETWORK_ID_MAINNET; const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => { - return new RpcSubprovider({ - rpcUrl: publicNodeUrl, - }); + return new RPCSubprovider(publicNodeUrl); }); - provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); + provider.addProvider(new RedundantSubprovider(rpcSubproviders)); provider.start(); return [provider, undefined]; } @@ -537,7 +534,9 @@ export class Blockchain { } public destroy(): void { this._blockchainWatcher.destroy(); - this._injectedProviderObservable.unsubscribe(this._injectedProviderUpdateHandler); + if (this._injectedProviderObservable) { + this._injectedProviderObservable.unsubscribe(this._injectedProviderUpdateHandler); + } this._stopWatchingExchangeLogFillEvents(); this._stopWatchingGasPrice(); } @@ -569,7 +568,7 @@ export class Blockchain { configs.DEFAULT_TRACKED_TOKEN_SYMBOLS, )}) not found in tokenRegistry: ${JSON.stringify(tokenRegistryTokens)}`, ); - await errorReporter.reportAsync(err); + errorReporter.report(err); return; } if (_.isEmpty(trackedTokensByAddress)) { @@ -680,8 +679,7 @@ export class Blockchain { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error // to rollbar and stop watching when one occurs - // tslint:disable-next-line:no-floating-promises - errorReporter.reportAsync(err); // fire and forget + errorReporter.report(err); // fire and forget return; } else { const decodedLog = decodedLogEvent.log; @@ -767,9 +765,13 @@ export class Blockchain { this._contractWrappers.exchange.unsubscribeAll(); } private async _getTokenRegistryTokensByAddressAsync(): Promise<TokenByAddress> { - utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); - const tokenRegistryTokens = await this._contractWrappers.tokenRegistry.getTokensAsync(); - + let tokenRegistryTokens; + if (this.networkId === constants.NETWORK_ID_MAINNET) { + tokenRegistryTokens = await backendClient.getTokenInfosAsync(); + } else { + utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); + tokenRegistryTokens = await this._contractWrappers.tokenRegistry.getTokensAsync(); + } const tokenByAddress: TokenByAddress = {}; _.each(tokenRegistryTokens, (t: ZeroExToken) => { // HACK: For now we have a hard-coded list of iconUrls for the dummyTokens @@ -789,7 +791,7 @@ export class Blockchain { return tokenByAddress; } private async _onPageLoadInitFireAndForgetAsync(): Promise<void> { - await utils.onPageLoadAsync(); // wait for page to load + await utils.onPageLoadPromise; // wait for page to load const networkIdIfExists = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync(); this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists : constants.NETWORK_ID_MAINNET; const injectedWeb3IfExists = Blockchain._getInjectedWeb3(); @@ -909,7 +911,7 @@ export class Blockchain { if (_.includes(errMsg, 'not been deployed to detected network')) { throw new Error(BlockchainCallErrs.ContractDoesNotExist); } else { - await errorReporter.reportAsync(err); + errorReporter.report(err); throw new Error(BlockchainCallErrs.UnhandledError); } } diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts index 4b23aa98a..39ed8b08b 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -4,9 +4,9 @@ import * as _ from 'lodash'; import { Dispatcher } from 'ts/redux/dispatcher'; export class BlockchainWatcher { - private _dispatcher: Dispatcher; - private _web3Wrapper: Web3Wrapper; - private _shouldPollUserAddress: boolean; + private readonly _dispatcher: Dispatcher; + private readonly _web3Wrapper: Web3Wrapper; + private readonly _shouldPollUserAddress: boolean; private _watchBalanceIntervalId: NodeJS.Timer; private _prevUserEtherBalanceInWei?: BigNumber; private _prevUserAddressIfExists: string; diff --git a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx index ce86df856..3ebab03ef 100644 --- a/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx +++ b/packages/website/ts/components/dialogs/u2f_not_supported_dialog.tsx @@ -14,9 +14,9 @@ export const U2fNotSupportedDialog = (props: U2fNotSupportedDialogProps) => { <Dialog title="U2F Not Supported" titleStyle={{ fontWeight: 100 }} - actions={[<FlatButton key="u2fNo" label="Ok" onTouchTap={props.onToggleDialog.bind(this)} />]} + actions={[<FlatButton key="u2fNo" label="Ok" onTouchTap={props.onToggleDialog} />]} open={props.isOpen} - onRequestClose={props.onToggleDialog.bind(this)} + onRequestClose={props.onToggleDialog} autoScrollBodyContent={true} > <div className="pt2" style={{ color: colors.grey700 }}> diff --git a/packages/website/ts/components/eth_weth_conversion_button.tsx b/packages/website/ts/components/eth_weth_conversion_button.tsx index 4b91a2ebd..b0091a1c1 100644 --- a/packages/website/ts/components/eth_weth_conversion_button.tsx +++ b/packages/website/ts/components/eth_weth_conversion_button.tsx @@ -37,7 +37,7 @@ export class EthWethConversionButton extends React.Component< > { public static defaultProps: Partial<EthWethConversionButtonProps> = { isDisabled: false, - onConversionSuccessful: _.noop, + onConversionSuccessful: _.noop.bind(_), }; public constructor(props: EthWethConversionButtonProps) { super(props); @@ -118,7 +118,7 @@ export class EthWethConversionButton extends React.Component< ? 'Failed to wrap your ETH. Please try again.' : 'Failed to unwrap your WETH. Please try again.'; this.props.dispatcher.showFlashMessage(errorMsg); - await errorReporter.reportAsync(err); + errorReporter.report(err); } } this.setState({ diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 03ba1183d..7da2e0870 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -1,5 +1,5 @@ import { getOrderHashHex, isValidSignature } from '@0xproject/order-utils'; -import { colors, constants as sharedConstants } from '@0xproject/react-shared'; +import { colors } from '@0xproject/react-shared'; import { Order as ZeroExOrder } from '@0xproject/types'; import { BigNumber, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; @@ -506,6 +506,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { await this._checkForUntrackedTokensAndAskToAddAsync(); } + private _trackOrderEvent(eventName: string): void { + const parsedOrder = this.state.parsedOrder; + analytics.trackOrderEvent(eventName, parsedOrder); + } private async _onFillOrderClickFireAndForgetAsync(): Promise<void> { if (this.props.blockchainErr !== BlockchainErrs.NoError || _.isEmpty(this.props.userAddress)) { this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); @@ -552,14 +556,12 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { }); return; } - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - const eventLabel = `${parsedOrder.metadata.takerToken.symbol}-${networkName}`; try { const orderFilledAmount: BigNumber = await this.props.blockchain.fillOrderAsync( signedOrder, this.props.orderFillAmount, ); - analytics.logEvent('Portal', 'Fill Order Success', eventLabel, parsedOrder.signedOrder.takerTokenAmount); + this._trackOrderEvent('Fill Order Success'); // After fill completes, let's force fetch the token balances this.props.dispatcher.forceTokenStateRefetch(); this.setState({ @@ -573,7 +575,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { this.setState({ isFilling: false, }); - analytics.logEvent('Portal', 'Fill Order Failure', eventLabel, parsedOrder.signedOrder.takerTokenAmount); + this._trackOrderEvent('Fill Order Failure'); const errMsg = `${err}`; if (utils.didUserDenyWeb3Request(errMsg)) { return; @@ -583,7 +585,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { this.setState({ globalErrMsg, }); - await errorReporter.reportAsync(err); + errorReporter.report(err); return; } } @@ -628,8 +630,6 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { }); return; } - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - const eventLabel = `${parsedOrder.metadata.makerToken.symbol}-${networkName}`; try { await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount); this.setState({ @@ -638,7 +638,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { globalErrMsg: '', unavailableTakerAmount: takerTokenAmount, }); - analytics.logEvent('Portal', 'Cancel Order Success', eventLabel, parsedOrder.signedOrder.makerTokenAmount); + this._trackOrderEvent('Cancel Order Success'); return; } catch (err) { this.setState({ @@ -648,13 +648,13 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { if (utils.didUserDenyWeb3Request(errMsg)) { return; } - analytics.logEvent('Portal', 'Cancel Order Failure', eventLabel, parsedOrder.signedOrder.makerTokenAmount); + this._trackOrderEvent('Cancel Order Failure'); globalErrMsg = 'Failed to cancel order, please refresh and try again'; logUtils.log(`${err}`); this.setState({ globalErrMsg, }); - await errorReporter.reportAsync(err); + errorReporter.report(err); return; } } diff --git a/packages/website/ts/components/fill_warning_dialog.tsx b/packages/website/ts/components/fill_warning_dialog.tsx index 83095b1d3..45c492221 100644 --- a/packages/website/ts/components/fill_warning_dialog.tsx +++ b/packages/website/ts/components/fill_warning_dialog.tsx @@ -18,16 +18,16 @@ export const FillWarningDialog = (props: FillWarningDialogProps) => { <FlatButton key="fillWarningCancel" label="Cancel" - onTouchTap={props.onToggleDialog.bind(this, didCancel)} + onTouchTap={() => props.onToggleDialog(didCancel)} // tslint:disable-line:jsx-no-lambda />, <FlatButton key="fillWarningContinue" label="Fill Order" - onTouchTap={props.onToggleDialog.bind(this, !didCancel)} + onTouchTap={() => props.onToggleDialog(!didCancel)} // tslint:disable-line:jsx-no-lambda />, ]} open={props.isOpen} - onRequestClose={props.onToggleDialog.bind(this)} + onRequestClose={() => props.onToggleDialog(didCancel)} // tslint:disable-line:jsx-no-lambda autoScrollBodyContent={true} modal={true} > diff --git a/packages/website/ts/components/forms/subscribe_form.tsx b/packages/website/ts/components/forms/subscribe_form.tsx index 8ef58328e..761db7517 100644 --- a/packages/website/ts/components/forms/subscribe_form.tsx +++ b/packages/website/ts/components/forms/subscribe_form.tsx @@ -6,6 +6,7 @@ import { Button } from 'ts/components/ui/button'; import { Container } from 'ts/components/ui/container'; import { Input } from 'ts/components/ui/input'; import { Text } from 'ts/components/ui/text'; +import { analytics } from 'ts/utils/analytics'; import { backendClient } from 'ts/utils/backend_client'; export interface SubscribeFormProps {} @@ -112,6 +113,9 @@ export class SubscribeForm extends React.Component<SubscribeFormProps, Subscribe try { const response = await backendClient.subscribeToNewsletterAsync(this.state.emailText); const status = response.status === 200 ? SubscribeFormStatus.Success : SubscribeFormStatus.Error; + if (status === SubscribeFormStatus.Success) { + analytics.identify(this.state.emailText, 'email'); + } this.setState({ status, emailText: '' }); } catch (error) { this._setStatus(SubscribeFormStatus.Error); diff --git a/packages/website/ts/components/generate_order/asset_picker.tsx b/packages/website/ts/components/generate_order/asset_picker.tsx index 5eada37b6..2dca3483f 100644 --- a/packages/website/ts/components/generate_order/asset_picker.tsx +++ b/packages/website/ts/components/generate_order/asset_picker.tsx @@ -46,7 +46,7 @@ export class AssetPicker extends React.Component<AssetPickerProps, AssetPickerSt public static defaultProps: Partial<AssetPickerProps> = { tokenVisibility: TokenVisibility.ALL, }; - private _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs }; + private readonly _dialogConfigsByAssetView: { [assetView: string]: DialogConfigs }; constructor(props: AssetPickerProps) { super(props); this.state = { diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx index d26b5c3fa..72efab033 100644 --- a/packages/website/ts/components/generate_order/generate_order_form.tsx +++ b/packages/website/ts/components/generate_order/generate_order_form.tsx @@ -1,6 +1,6 @@ import { generatePseudoRandomSalt, getOrderHashHex } from '@0xproject/order-utils'; -import { colors, constants as sharedConstants } from '@0xproject/react-shared'; -import { ECSignature, Order } from '@0xproject/types'; +import { colors } from '@0xproject/react-shared'; +import { ECSignature, Order as ZeroExOrder } from '@0xproject/types'; import { BigNumber, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import Dialog from 'material-ui/Dialog'; @@ -20,7 +20,7 @@ import { SwapIcon } from 'ts/components/ui/swap_icon'; import { Dispatcher } from 'ts/redux/dispatcher'; import { portalOrderSchema } from 'ts/schemas/portal_order_schema'; import { validator } from 'ts/schemas/validator'; -import { AlertTypes, BlockchainErrs, HashData, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types'; +import { AlertTypes, BlockchainErrs, HashData, Order, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types'; import { analytics } from 'ts/utils/analytics'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; @@ -254,7 +254,8 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G userAddressIfExists, debitToken.address, ); - const receiveAmount = this.props.sideToAssetToken[Side.Receive].amount; + const receiveToken = this.props.sideToAssetToken[Side.Receive]; + const receiveAmount = receiveToken.amount; if ( !_.isUndefined(debitToken.amount) && !_.isUndefined(receiveAmount) && @@ -264,24 +265,28 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G debitBalance.gte(debitToken.amount) && debitAllowance.gte(debitToken.amount) ) { - const didSignSuccessfully = await this._signTransactionAsync(); - if (didSignSuccessfully) { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - const eventLabel = `${this.props.tokenByAddress[debitToken.address].symbol}-${networkName}`; - analytics.logEvent('Portal', 'Sign Order Success', eventLabel, debitToken.amount.toNumber()); + const signedOrder = await this._signTransactionAsync(); + const doesSignedOrderExist = !_.isUndefined(signedOrder); + if (doesSignedOrderExist) { + analytics.trackOrderEvent('Sign Order Success', signedOrder); this.setState({ globalErrMsg: '', shouldShowIncompleteErrs: false, }); } - return didSignSuccessfully; + return doesSignedOrderExist; } else { let globalErrMsg = 'You must fix the above errors in order to generate a valid order'; if (this.props.userAddress === '') { globalErrMsg = 'You must enable wallet communication'; this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); } - analytics.logEvent('Portal', 'Sign Order Failure', globalErrMsg); + analytics.track('Sign Order Failure', { + makerTokenAmount: debitToken.amount.toString(), + makerToken: this.props.tokenByAddress[debitToken.address].symbol, + takerTokenAmount: receiveToken.amount.toString(), + takerToken: this.props.tokenByAddress[receiveToken.address].symbol, + }); this.setState({ globalErrMsg, shouldShowIncompleteErrs: true, @@ -289,7 +294,7 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G return false; } } - private async _signTransactionAsync(): Promise<boolean> { + private async _signTransactionAsync(): Promise<Order | undefined> { this.setState({ signingState: SigningState.SIGNING, }); @@ -299,11 +304,11 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G this.setState({ signingState: SigningState.UNSIGNED, }); - return false; + return undefined; } const hashData = this.props.hashData; - const zeroExOrder: Order = { + const zeroExOrder: ZeroExOrder = { exchangeContractAddress: exchangeContractAddr, expirationUnixTimestampSec: hashData.orderExpiryTimestamp, feeRecipient: hashData.feeRecipientAddress, @@ -320,9 +325,10 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G const orderHash = getOrderHashHex(zeroExOrder); let globalErrMsg = ''; + let order; try { const ecSignature = await this.props.blockchain.signOrderHashAsync(orderHash); - const order = utils.generateOrder( + order = utils.generateOrder( exchangeContractAddr, this.props.sideToAssetToken, hashData.orderExpiryTimestamp, @@ -349,14 +355,14 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G globalErrMsg = 'An unexpected error occured. Please try refreshing the page'; logUtils.log(`Unexpected error occured: ${err}`); logUtils.log(err.stack); - await errorReporter.reportAsync(err); + errorReporter.report(err); } } this.setState({ signingState: globalErrMsg === '' ? SigningState.SIGNED : SigningState.UNSIGNED, globalErrMsg, }); - return globalErrMsg === ''; + return order; } private _updateOrderAddress(address?: string): void { if (!_.isUndefined(address)) { diff --git a/packages/website/ts/components/inputs/allowance_toggle.tsx b/packages/website/ts/components/inputs/allowance_toggle.tsx index 0d5995696..05dce134a 100644 --- a/packages/website/ts/components/inputs/allowance_toggle.tsx +++ b/packages/website/ts/components/inputs/allowance_toggle.tsx @@ -1,4 +1,4 @@ -import { constants as sharedConstants, Styles } from '@0xproject/react-shared'; +import { Styles } from '@0xproject/react-shared'; import { BigNumber, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import Toggle from 'material-ui/Toggle'; @@ -57,7 +57,7 @@ const styles: Styles = { export class AllowanceToggle extends React.Component<AllowanceToggleProps, AllowanceToggleState> { public static defaultProps = { - onErrorOccurred: _.noop, + onErrorOccurred: _.noop.bind(_), isDisabled: false, }; constructor(props: AllowanceToggleProps) { @@ -111,14 +111,16 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow if (!this._isAllowanceSet()) { newAllowanceAmountInBaseUnits = DEFAULT_ALLOWANCE_AMOUNT_IN_BASE_UNITS; } - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - const eventLabel = `${this.props.token.symbol}-${networkName}`; + const logData = { + tokenSymbol: this.props.token.symbol, + newAllowance: newAllowanceAmountInBaseUnits.toNumber(), + }; try { await this.props.blockchain.setProxyAllowanceAsync(this.props.token, newAllowanceAmountInBaseUnits); - analytics.logEvent('Portal', 'Set Allowance Success', eventLabel, newAllowanceAmountInBaseUnits.toNumber()); + analytics.track('Set Allowances Success', logData); await this.props.refetchTokenStateAsync(); } catch (err) { - analytics.logEvent('Portal', 'Set Allowance Failure', eventLabel, newAllowanceAmountInBaseUnits.toNumber()); + analytics.track('Set Allowance Failure', logData); this.setState({ isSpinnerVisible: false, }); @@ -129,7 +131,7 @@ export class AllowanceToggle extends React.Component<AllowanceToggleProps, Allow logUtils.log(`Unexpected error encountered: ${err}`); logUtils.log(err.stack); this.props.onErrorOccurred(BalanceErrs.allowanceSettingFailed); - await errorReporter.reportAsync(err); + errorReporter.report(err); } } private _isAllowanceSet(): boolean { diff --git a/packages/website/ts/components/inputs/balance_bounded_input.tsx b/packages/website/ts/components/inputs/balance_bounded_input.tsx index f23beb436..eb01e3ea6 100644 --- a/packages/website/ts/components/inputs/balance_bounded_input.tsx +++ b/packages/website/ts/components/inputs/balance_bounded_input.tsx @@ -35,7 +35,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp isDisabled: false, shouldShowErrs: true, hintText: 'amount', - onErrorMsgChange: _.noop, + onErrorMsgChange: _.noop.bind(_), shouldShowUnderline: true, }; constructor(props: BalanceBoundedInputProps) { @@ -125,7 +125,7 @@ export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProp const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount); return errMsg; } - private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop): void { + private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop.bind(_)): void { const errorMsg = this._validate(amount, balance); this.props.onErrorMsgChange(errorMsg); this.setState( diff --git a/packages/website/ts/components/inputs/expiration_input.tsx b/packages/website/ts/components/inputs/expiration_input.tsx index 79dd2f568..5417ce715 100644 --- a/packages/website/ts/components/inputs/expiration_input.tsx +++ b/packages/website/ts/components/inputs/expiration_input.tsx @@ -17,7 +17,7 @@ interface ExpirationInputState { } export class ExpirationInput extends React.Component<ExpirationInputProps, ExpirationInputState> { - private _earliestPickableMoment: moment.Moment; + private readonly _earliestPickableMoment: moment.Moment; constructor(props: ExpirationInputProps) { super(props); // Set the earliest pickable date to today at 00:00, so users can only pick the current or later dates diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx deleted file mode 100644 index c85d97207..000000000 --- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import { colors } from '@0xproject/react-shared'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; -import CircularProgress from 'material-ui/CircularProgress'; -import Paper from 'material-ui/Paper'; -import * as React from 'react'; -import * as DocumentTitle from 'react-document-title'; -import { Route, Switch } from 'react-router-dom'; -import { Blockchain } from 'ts/blockchain'; -import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; -import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; -import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; -import { WrappedEthSectionNoticeDialog } from 'ts/components/dialogs/wrapped_eth_section_notice_dialog'; -import { EthWrappers } from 'ts/components/eth_wrappers'; -import { FillOrder } from 'ts/components/fill_order'; -import { Footer } from 'ts/components/footer'; -import { LegacyPortalMenu } from 'ts/components/legacy_portal/legacy_portal_menu'; -import { TokenBalances } from 'ts/components/token_balances'; -import { TopBar } from 'ts/components/top_bar/top_bar'; -import { TradeHistory } from 'ts/components/trade_history/trade_history'; -import { FlashMessage } from 'ts/components/ui/flash_message'; -import { GenerateOrderForm } from 'ts/containers/generate_order_form'; -import { localStorage } from 'ts/local_storage/local_storage'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, WebsitePaths } from 'ts/types'; -import { constants } from 'ts/utils/constants'; -import { orderParser } from 'ts/utils/order_parser'; -import { Translate } from 'ts/utils/translate'; -import { utils } from 'ts/utils/utils'; - -const THROTTLE_TIMEOUT = 100; - -export interface LegacyPortalProps { - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - dispatcher: Dispatcher; - hashData: HashData; - injectedProviderName: string; - networkId: number; - nodeVersion: string; - orderFillAmount: BigNumber; - providerType: ProviderType; - screenWidth: ScreenWidths; - tokenByAddress: TokenByAddress; - userEtherBalanceInWei?: BigNumber; - userAddress: string; - shouldBlockchainErrDialogBeOpen: boolean; - userSuppliedOrderCache: Order; - location: Location; - flashMessage?: string | React.ReactNode; - lastForceTokenStateRefetch: number; - translate: Translate; -} - -interface LegacyPortalState { - prevNetworkId: number; - prevNodeVersion: string; - prevUserAddress: string; - prevPathname: string; - isDisclaimerDialogOpen: boolean; - isWethNoticeDialogOpen: boolean; - isLedgerDialogOpen: boolean; -} - -export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPortalState> { - private _blockchain: Blockchain; - private _sharedOrderIfExists: Order; - private _throttledScreenWidthUpdate: () => void; - public static hasAlreadyDismissedWethNotice(): boolean { - const didDismissWethNotice = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE); - const hasAlreadyDismissedWethNotice = !_.isUndefined(didDismissWethNotice) && !_.isEmpty(didDismissWethNotice); - return hasAlreadyDismissedWethNotice; - } - constructor(props: LegacyPortalProps) { - super(props); - this._sharedOrderIfExists = orderParser.parse(window.location.search); - this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); - - const isViewingBalances = _.includes(props.location.pathname, `${WebsitePaths.Portal}/balances`); - const hasAlreadyDismissedWethNotice = LegacyPortal.hasAlreadyDismissedWethNotice(); - - const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); - const hasAcceptedDisclaimer = - !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); - this.state = { - prevNetworkId: this.props.networkId, - prevNodeVersion: this.props.nodeVersion, - prevUserAddress: this.props.userAddress, - prevPathname: this.props.location.pathname, - isDisclaimerDialogOpen: !hasAcceptedDisclaimer, - isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, - isLedgerDialogOpen: false, - }; - } - public componentDidMount(): void { - window.addEventListener('resize', this._throttledScreenWidthUpdate); - window.scrollTo(0, 0); - } - public componentWillMount(): void { - this._blockchain = new Blockchain(this.props.dispatcher); - } - public componentWillUnmount(): void { - this._blockchain.destroy(); - window.removeEventListener('resize', this._throttledScreenWidthUpdate); - // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered - // the initialization process always occurs from the same base state. This helps avoid - // initialization inconsistencies (i.e While the portal was unrendered, the user might have - // become disconnected from their backing Ethereum node, changes user accounts, etc...) - this.props.dispatcher.resetState(); - } - public componentWillReceiveProps(nextProps: LegacyPortalProps): void { - if (nextProps.networkId !== this.state.prevNetworkId) { - // tslint:disable-next-line:no-floating-promises - this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); - this.setState({ - prevNetworkId: nextProps.networkId, - }); - } - if (nextProps.userAddress !== this.state.prevUserAddress) { - const newUserAddress = _.isEmpty(nextProps.userAddress) ? undefined : nextProps.userAddress; - // tslint:disable-next-line:no-floating-promises - this._blockchain.userAddressUpdatedFireAndForgetAsync(newUserAddress); - this.setState({ - prevUserAddress: nextProps.userAddress, - }); - } - if (nextProps.nodeVersion !== this.state.prevNodeVersion) { - // tslint:disable-next-line:no-floating-promises - this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); - } - if (nextProps.location.pathname !== this.state.prevPathname) { - const isViewingBalances = _.includes(nextProps.location.pathname, `${WebsitePaths.Portal}/balances`); - const hasAlreadyDismissedWethNotice = LegacyPortal.hasAlreadyDismissedWethNotice(); - this.setState({ - prevPathname: nextProps.location.pathname, - isWethNoticeDialogOpen: !hasAlreadyDismissedWethNotice && isViewingBalances, - }); - } - } - public render(): React.ReactNode { - const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( - this.props.dispatcher, - ); - const portalStyle: React.CSSProperties = { - minHeight: '100vh', - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - }; - const portalMenuContainerStyle: React.CSSProperties = { - overflow: 'hidden', - backgroundColor: colors.darkestGrey, - color: colors.white, - }; - return ( - <div style={portalStyle}> - <DocumentTitle title="0x Portal DApp" /> - <TopBar - userAddress={this.props.userAddress} - networkId={this.props.networkId} - injectedProviderName={this.props.injectedProviderName} - onToggleLedgerDialog={this.onToggleLedgerDialog.bind(this)} - dispatcher={this.props.dispatcher} - providerType={this.props.providerType} - blockchainIsLoaded={this.props.blockchainIsLoaded} - location={this.props.location} - blockchain={this._blockchain} - translate={this.props.translate} - /> - <div id="portal" className="mx-auto max-width-4" style={{ width: '100%' }}> - <Paper className="mb3 mt2"> - <div className="mx-auto flex"> - <div className="col col-2 pr2 pt1 sm-hide xs-hide" style={portalMenuContainerStyle}> - <LegacyPortalMenu menuItemStyle={{ color: colors.white }} /> - </div> - <div className="col col-12 lg-col-10 md-col-10 sm-col sm-col-12"> - <div className="py2" style={{ backgroundColor: colors.grey50 }}> - {this.props.blockchainIsLoaded ? ( - <Switch> - <Route - path={`${WebsitePaths.Portal}/weth`} - render={this._renderEthWrapper.bind(this)} - /> - <Route - path={`${WebsitePaths.Portal}/fill`} - render={this._renderFillOrder.bind(this)} - /> - <Route - path={`${WebsitePaths.Portal}/balances`} - render={this._renderTokenBalances.bind(this)} - /> - <Route - path={`${WebsitePaths.Portal}/trades`} - render={this._renderTradeHistory.bind(this)} - /> - <Route - path={`${WebsitePaths.Home}`} - render={this._renderGenerateOrderForm.bind(this)} - /> - </Switch> - ) : ( - <div className="pt4 sm-px2 sm-pt2 sm-m1" style={{ height: 500 }}> - <div - className="relative sm-px2 sm-pt2 sm-m1" - style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }} - > - <div className="center pb2"> - <CircularProgress size={40} thickness={5} /> - </div> - <div className="center pt2" style={{ paddingBottom: 11 }}> - Loading Portal... - </div> - </div> - </div> - )} - </div> - </div> - </div> - </Paper> - <BlockchainErrDialog - blockchain={this._blockchain} - blockchainErr={this.props.blockchainErr} - isOpen={this.props.shouldBlockchainErrDialogBeOpen} - userAddress={this.props.userAddress} - toggleDialogFn={updateShouldBlockchainErrDialogBeOpen} - networkId={this.props.networkId} - /> - <WrappedEthSectionNoticeDialog - isOpen={this.state.isWethNoticeDialogOpen} - onToggleDialog={this._onWethNoticeAccepted.bind(this)} - /> - <PortalDisclaimerDialog - isOpen={this.state.isDisclaimerDialogOpen} - onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)} - /> - <FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} /> - <LedgerConfigDialog - providerType={this.props.providerType} - networkId={this.props.networkId} - blockchain={this._blockchain} - dispatcher={this.props.dispatcher} - toggleDialogFn={this.onToggleLedgerDialog.bind(this)} - isOpen={this.state.isLedgerDialogOpen} - /> - </div> - <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} /> - </div> - ); - } - public onToggleLedgerDialog(): void { - this.setState({ - isLedgerDialogOpen: !this.state.isLedgerDialogOpen, - }); - } - private _renderEthWrapper(): React.ReactNode { - return ( - <EthWrappers - networkId={this.props.networkId} - blockchain={this._blockchain} - dispatcher={this.props.dispatcher} - tokenByAddress={this.props.tokenByAddress} - userAddress={this.props.userAddress} - userEtherBalanceInWei={this.props.userEtherBalanceInWei} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - /> - ); - } - private _renderTradeHistory(): React.ReactNode { - return ( - <TradeHistory - tokenByAddress={this.props.tokenByAddress} - userAddress={this.props.userAddress} - networkId={this.props.networkId} - /> - ); - } - private _renderTokenBalances(): React.ReactNode { - const trackedTokens = utils.getTrackedTokens(this.props.tokenByAddress); - return ( - <TokenBalances - blockchain={this._blockchain} - blockchainErr={this.props.blockchainErr} - blockchainIsLoaded={this.props.blockchainIsLoaded} - dispatcher={this.props.dispatcher} - screenWidth={this.props.screenWidth} - tokenByAddress={this.props.tokenByAddress} - trackedTokens={trackedTokens} - userAddress={this.props.userAddress} - userEtherBalanceInWei={this.props.userEtherBalanceInWei} - networkId={this.props.networkId} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - /> - ); - } - private _renderFillOrder(_match: any, _location: Location, _history: History): React.ReactNode { - const initialFillOrder = !_.isUndefined(this.props.userSuppliedOrderCache) - ? this.props.userSuppliedOrderCache - : this._sharedOrderIfExists; - return ( - <FillOrder - blockchain={this._blockchain} - blockchainErr={this.props.blockchainErr} - initialOrder={initialFillOrder} - isOrderInUrl={!_.isUndefined(this._sharedOrderIfExists)} - orderFillAmount={this.props.orderFillAmount} - networkId={this.props.networkId} - userAddress={this.props.userAddress} - tokenByAddress={this.props.tokenByAddress} - dispatcher={this.props.dispatcher} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - /> - ); - } - private _renderGenerateOrderForm(_match: any, _location: Location, _history: History): React.ReactNode { - return ( - <GenerateOrderForm - blockchain={this._blockchain} - hashData={this.props.hashData} - dispatcher={this.props.dispatcher} - /> - ); - } - private _onPortalDisclaimerAccepted(): void { - localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); - this.setState({ - isDisclaimerDialogOpen: false, - }); - } - private _onWethNoticeAccepted(): void { - localStorage.setItem(constants.LOCAL_STORAGE_KEY_DISMISS_WETH_NOTICE, 'set'); - this.setState({ - isWethNoticeDialogOpen: false, - }); - } - private _updateScreenWidth(): void { - const newScreenWidth = utils.getScreenWidth(); - this.props.dispatcher.updateScreenWidth(newScreenWidth); - } -} diff --git a/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx b/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx deleted file mode 100644 index 1dd164f8b..000000000 --- a/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import * as _ from 'lodash'; -import * as React from 'react'; -import { MenuItem } from 'ts/components/ui/menu_item'; -import { WebsitePaths } from 'ts/types'; - -export interface LegacyPortalMenuProps { - menuItemStyle: React.CSSProperties; - onClick?: () => void; -} - -interface LegacyPortalMenuState {} - -export class LegacyPortalMenu extends React.Component<LegacyPortalMenuProps, LegacyPortalMenuState> { - public static defaultProps: Partial<LegacyPortalMenuProps> = { - onClick: _.noop, - }; - public render(): React.ReactNode { - return ( - <div> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Generate order', 'zmdi-arrow-right-top')} - </MenuItem> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/fill`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Fill order', 'zmdi-arrow-left-bottom')} - </MenuItem> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/balances`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Balances', 'zmdi-balance-wallet')} - </MenuItem> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/trades`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Trade history', 'zmdi-format-list-bulleted')} - </MenuItem> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/weth`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} - </MenuItem> - </div> - ); - } - private _renderMenuItemWithIcon(title: string, iconName: string): React.ReactNode { - return ( - <div className="flex" style={{ fontWeight: 100 }}> - <div className="pr1 pl2"> - <i style={{ fontSize: 20 }} className={`zmdi ${iconName}`} /> - </div> - <div className="pl1">{title}</div> - </div> - ); - } -} diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 20a8f0a32..f395674a1 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -1,4 +1,3 @@ -import { constants as sharedConstants } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router'; @@ -225,20 +224,24 @@ class PlainPortalOnboardingFlow extends React.Component<PortalOnboardingFlowProp (this.props.stepIndex === 0 && !this.props.isRunning && this.props.blockchainIsLoaded) || (!this.props.isRunning && !this.props.hasBeenClosed && this.props.blockchainIsLoaded) ) { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - analytics.logEvent('Portal', 'Onboarding Started - Automatic', networkName, this.props.stepIndex); + analytics.track('Onboarding Started', { + reason: 'automatic', + stepIndex: this.props.stepIndex, + }); this.props.updateIsRunning(true); } } private _updateOnboardingStep(stepIndex: number): void { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; this.props.updateOnboardingStep(stepIndex); - analytics.logEvent('Portal', 'Update Onboarding Step', networkName, stepIndex); + analytics.track('Update Onboarding Step', { + stepIndex, + }); } private _closeOnboarding(): void { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; this.props.updateIsRunning(false); - analytics.logEvent('Portal', 'Onboarding Closed', networkName, this.props.stepIndex); + analytics.track('Onboarding Closed', { + stepIndex: this.props.stepIndex, + }); } private _renderZrxAllowanceToggle(): React.ReactNode { const zrxToken = utils.getZrxToken(this.props.tokenByAddress); diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx index 35188c024..cf06f10c8 100644 --- a/packages/website/ts/components/order_json.tsx +++ b/packages/website/ts/components/order_json.tsx @@ -1,5 +1,5 @@ import { ECSignature } from '@0xproject/types'; -import { BigNumber, logUtils } from '@0xproject/utils'; +import { BigNumber, fetchAsync, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import Paper from 'material-ui/Paper'; import TextField from 'material-ui/TextField'; @@ -127,7 +127,7 @@ export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> { href: this.state.shareLink, method: 'share', }, - _.noop, + _.noop.bind(_), ); } private _shareViaEmailAsync(): void { @@ -148,13 +148,13 @@ You can see and fill it here: ${this.state.shareLink}`); const bitlyRequestUrl = `${constants.URL_BITLY_API}/v3/shorten?access_token=${ configs.BITLY_ACCESS_TOKEN }&longUrl=${longUrl}`; - const response = await fetch(bitlyRequestUrl); + const response = await fetchAsync(bitlyRequestUrl); const responseBody = await response.text(); const bodyObj = JSON.parse(responseBody); if (response.status !== 200 || bodyObj.status_code !== 200) { // TODO: Show error message in UI logUtils.log(`Unexpected status code: ${response.status} -> ${responseBody}`); - await errorReporter.reportAsync(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`)); + errorReporter.report(new Error(`Bitly returned non-200: ${JSON.stringify(response)}`)); return ''; } return bodyObj.data.url; diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index f983241fa..1790a9678 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -1,4 +1,4 @@ -import { colors, constants as sharedConstants } from '@0xproject/react-shared'; +import { colors } from '@0xproject/react-shared'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as React from 'react'; @@ -110,8 +110,8 @@ const SIDE_PADDING = 20; export class Portal extends React.Component<PortalProps, PortalState> { private _blockchain: Blockchain; - private _sharedOrderIfExists: Order; - private _throttledScreenWidthUpdate: () => void; + private readonly _sharedOrderIfExists: Order; + private readonly _throttledScreenWidthUpdate: () => void; constructor(props: PortalProps) { super(props); this._sharedOrderIfExists = orderParser.parse(window.location.search); @@ -388,10 +388,11 @@ export class Portal extends React.Component<PortalProps, PortalState> { startOnboarding ); } - private _startOnboarding(): void { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - analytics.logEvent('Portal', 'Onboarding Started - Manual', networkName, this.props.portalOnboardingStep); + analytics.track('Onboarding Started', { + reason: 'manual', + stepIndex: this.props.portalOnboardingStep, + }); this.props.dispatcher.updatePortalOnboardingShowing(true); } private _renderWalletSection(): React.ReactNode { diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 431cf145b..193dd237a 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -1,4 +1,4 @@ -import { constants as sharedConstants, Styles } from '@0xproject/react-shared'; +import { Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import { GridTile as PlainGridTile } from 'material-ui/GridList'; import * as React from 'react'; @@ -64,10 +64,10 @@ export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = ( const link = props.relayerInfo.appUrl || props.relayerInfo.url; const topTokens = props.relayerInfo.topTokens; const weeklyTxnVolume = props.relayerInfo.weeklyTxnVolume; - const networkName = sharedConstants.NETWORK_NAME_BY_ID[props.networkId]; - const eventLabel = `${props.relayerInfo.name}-${networkName}`; const onClick = () => { - analytics.logEvent('Portal', 'Relayer Click', eventLabel); + analytics.track('Relayer Click', { + name: props.relayerInfo.name, + }); utils.openUrl(link); }; const headerImageUrl = props.relayerInfo.logoImgUrl; diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx index c48b672e9..f3787bd27 100644 --- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx +++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx @@ -1,9 +1,4 @@ -import { - colors, - constants as sharedConstants, - EtherscanLinkSuffixes, - utils as sharedUtils, -} from '@0xproject/react-shared'; +import { colors, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import * as _ from 'lodash'; import * as React from 'react'; @@ -46,11 +41,11 @@ class TokenLink extends React.Component<TokenLinkProps, TokenLinkState> { }; } public render(): React.ReactNode { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; - const eventLabel = `${this.props.tokenInfo.symbol}-${networkName}`; const onClick = (event: React.MouseEvent<HTMLElement>) => { event.stopPropagation(); - analytics.logEvent('Portal', 'Token Click', eventLabel); + analytics.track('Token Click', { + tokenSymbol: this.props.tokenInfo.symbol, + }); const tokenLink = this._tokenLinkFromToken(this.props.tokenInfo, this.props.networkId); utils.openUrl(tokenLink); }; diff --git a/packages/website/ts/components/send_button.tsx b/packages/website/ts/components/send_button.tsx index 8486dbd8b..ac55d430b 100644 --- a/packages/website/ts/components/send_button.tsx +++ b/packages/website/ts/components/send_button.tsx @@ -80,7 +80,7 @@ export class SendButton extends React.Component<SendButtonProps, SendButtonState logUtils.log(`Unexpected error encountered: ${err}`); logUtils.log(err.stack); this.props.onError(); - await errorReporter.reportAsync(err); + errorReporter.report(err); } } this.setState({ diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 3fae83c00..c8d80702b 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -5,7 +5,7 @@ import { Styles, utils as sharedUtils, } from '@0xproject/react-shared'; -import { BigNumber, errorUtils, logUtils } from '@0xproject/utils'; +import { BigNumber, errorUtils, fetchAsync, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; import Dialog from 'material-ui/Dialog'; @@ -526,7 +526,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala this.setState({ errorType: BalanceErrs.mintingFailed, }); - await errorReporter.reportAsync(err); + errorReporter.report(err); return false; } } @@ -548,7 +548,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala await utils.sleepAsync(ARTIFICIAL_FAUCET_REQUEST_DELAY); const segment = isEtherRequest ? 'ether' : 'zrx'; - const response = await fetch( + const response = await fetchAsync( `${constants.URL_TESTNET_FAUCET}/${segment}/${this.props.userAddress}?networkId=${this.props.networkId}`, ); const responseBody = await response.text(); @@ -561,7 +561,7 @@ export class TokenBalances extends React.Component<TokenBalancesProps, TokenBala this.setState({ errorType, }); - await errorReporter.reportAsync(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`)); + errorReporter.report(new Error(`Faucet returned non-200: ${JSON.stringify(response)}`)); return false; } diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 960e5a824..63ea3475a 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -7,7 +7,6 @@ import MenuItem from 'material-ui/MenuItem'; import * as React from 'react'; import { Link } from 'react-router-dom'; import { Blockchain } from 'ts/blockchain'; -import { LegacyPortalMenu } from 'ts/components/legacy_portal/legacy_portal_menu'; import { DrawerMenu } from 'ts/components/portal/drawer_menu'; import { ProviderDisplay } from 'ts/components/top_bar/provider_display'; import { TopBarMenuItem } from 'ts/components/top_bar/top_bar_menu_item'; @@ -17,7 +16,6 @@ import { Dispatcher } from 'ts/redux/dispatcher'; import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; -import { utils } from 'ts/utils/utils'; export enum TopBarDisplayType { Default, @@ -213,8 +211,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </div> ); const popoverContent = <Menu style={{ color: colors.darkGrey }}>{developerSectionMenuItems}</Menu>; - // TODO : Remove this once we ship portal v2 - const shouldShowPortalV2Drawer = this._isViewingPortal() && utils.shouldShowPortalV2(); return ( <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style, ...{ height } }} @@ -294,11 +290,11 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </div> </div> </Container> - {shouldShowPortalV2Drawer ? this._renderPortalV2Drawer() : this._renderDrawer()} + {this._isViewingPortal() ? this._renderPortalDrawer() : this._renderDrawer()} </div> ); } - private _renderPortalV2Drawer(): React.ReactNode { + private _renderPortalDrawer(): React.ReactNode { return ( <Drawer open={this.state.isDrawerOpen} @@ -326,7 +322,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { onRequestChange={this._onMenuButtonClick.bind(this)} > <div className="clearfix"> - {this._renderPortalMenu()} {this._renderDocsMenu()} {this._renderWiki()} <div className="pl1 py1 mt3" style={{ backgroundColor: colors.lightGrey }}> @@ -478,20 +473,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </div> ); } - private _renderPortalMenu(): React.ReactNode { - if (!this._isViewingPortal()) { - return undefined; - } - - return ( - <div className="lg-hide md-hide"> - <div className="pl1 py1" style={{ backgroundColor: colors.lightGrey }}> - {this.props.translate.get(Key.PortalDApp, Deco.CapWords)} - </div> - <LegacyPortalMenu menuItemStyle={{ color: 'black' }} onClick={this._onMenuButtonClick.bind(this)} /> - </div> - ); - } private _onMenuButtonClick(): void { this.setState({ isDrawerOpen: !this.state.isDrawerOpen, diff --git a/packages/website/ts/components/ui/menu_item.tsx b/packages/website/ts/components/ui/menu_item.tsx index 64c0dc49d..0cb4b387c 100644 --- a/packages/website/ts/components/ui/menu_item.tsx +++ b/packages/website/ts/components/ui/menu_item.tsx @@ -15,7 +15,7 @@ interface MenuItemState { export class MenuItem extends React.Component<MenuItemProps, MenuItemState> { public static defaultProps: Partial<MenuItemProps> = { - onClick: _.noop, + onClick: _.noop.bind(_), className: '', }; public constructor(props: MenuItemProps) { diff --git a/packages/website/ts/components/ui/overlay.tsx b/packages/website/ts/components/ui/overlay.tsx index da26317de..fc7507475 100644 --- a/packages/website/ts/components/ui/overlay.tsx +++ b/packages/website/ts/components/ui/overlay.tsx @@ -26,7 +26,7 @@ export const Overlay: React.StatelessComponent<OverlayProps> = props => ( Overlay.defaultProps = { style: {}, - onClick: _.noop, + onClick: _.noop.bind(_), }; Overlay.displayName = 'Overlay'; diff --git a/packages/website/ts/components/ui/simple_menu.tsx b/packages/website/ts/components/ui/simple_menu.tsx index 74b8ef6ae..8a9349c6d 100644 --- a/packages/website/ts/components/ui/simple_menu.tsx +++ b/packages/website/ts/components/ui/simple_menu.tsx @@ -41,7 +41,7 @@ export const SimpleMenuItem: React.StatelessComponent<SimpleMenuItemProps> = ({ <Text fontSize="14px" fontColor={colors.darkGrey} - onClick={onClick || _.noop} + onClick={onClick || _.noop.bind(_)} hoverColor={colors.mediumBlue} > {displayText} @@ -84,5 +84,5 @@ export interface DifferentWalletSimpleMenuItemProps { export const DifferentWalletSimpleMenuItem: React.StatelessComponent<DifferentWalletSimpleMenuItemProps> = ({ onClick, }) => { - return <SimpleMenuItem displayText="Use a Different Wallet..." onClick={onClick} />; + return <SimpleMenuItem displayText="Use Ledger Wallet..." onClick={onClick} />; }; diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 6c1c495d7..40a8a23ea 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -1,4 +1,4 @@ -import { constants as sharedConstants, EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; +import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { BigNumber, errorUtils } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -204,7 +204,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { <AccountConnection accountState={accountState} injectedProviderName={this.props.injectedProviderName} /> </div> ); - const onClick = _.noop; + const onClick = _.noop.bind(_); const accessory = ( <DropDown activeNode={ @@ -488,19 +488,17 @@ export class Wallet extends React.Component<WalletProps, WalletState> { ); } private _openWrappedEtherActionRow(wrappedEtherDirection: Side): void { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; const action = wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Opened' : 'Wallet - Unwrap WETH Opened'; - analytics.logEvent('Portal', action, networkName); + analytics.track(action); this.setState({ wrappedEtherDirection, }); } private _closeWrappedEtherActionRow(wrappedEtherDirection: Side): void { - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; const action = wrappedEtherDirection === Side.Deposit ? 'Wallet - Wrap ETH Closed' : 'Wallet - Unwrap WETH Closed'; - analytics.logEvent('Portal', action, networkName); + analytics.track(action); this.setState({ wrappedEtherDirection: undefined, }); diff --git a/packages/website/ts/components/wallet/wrap_ether_item.tsx b/packages/website/ts/components/wallet/wrap_ether_item.tsx index 2b4cf93fe..fcab5d1dd 100644 --- a/packages/website/ts/components/wallet/wrap_ether_item.tsx +++ b/packages/website/ts/components/wallet/wrap_ether_item.tsx @@ -1,4 +1,4 @@ -import { constants as sharedConstants, Styles } from '@0xproject/react-shared'; +import { Styles } from '@0xproject/react-shared'; import { BigNumber, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; @@ -188,20 +188,23 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther this.setState({ isEthConversionHappening: true, }); - const networkName = sharedConstants.NETWORK_NAME_BY_ID[this.props.networkId]; + const etherToken = this.props.etherToken; + const amountToConvert = this.state.currentInputAmount; + const ethAmount = Web3Wrapper.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH).toString(); + const tokenAmount = Web3Wrapper.toUnitAmount(amountToConvert, etherToken.decimals).toString(); try { - const etherToken = this.props.etherToken; - const amountToConvert = this.state.currentInputAmount; if (this.props.direction === Side.Deposit) { await this.props.blockchain.convertEthToWrappedEthTokensAsync(etherToken.address, amountToConvert); - const ethAmount = Web3Wrapper.toUnitAmount(amountToConvert, constants.DECIMAL_PLACES_ETH); - this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount.toString()} ETH to WETH`); - analytics.logEvent('Portal', 'Wrap ETH Successfully', networkName); + this.props.dispatcher.showFlashMessage(`Successfully wrapped ${ethAmount} ETH to WETH`); + analytics.track('Wrap ETH Success', { + amount: ethAmount, + }); } else { await this.props.blockchain.convertWrappedEthTokensToEthAsync(etherToken.address, amountToConvert); - const tokenAmount = Web3Wrapper.toUnitAmount(amountToConvert, etherToken.decimals); - this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount.toString()} WETH to ETH`); - analytics.logEvent('Portal', 'Unwrap WETH Successfully', networkName); + this.props.dispatcher.showFlashMessage(`Successfully unwrapped ${tokenAmount} WETH to ETH`); + analytics.track('Unwrap WETH Success', { + amount: tokenAmount, + }); } await this.props.refetchEthTokenStateAsync(); this.props.onConversionSuccessful(); @@ -214,12 +217,16 @@ export class WrapEtherItem extends React.Component<WrapEtherItemProps, WrapEther logUtils.log(err.stack); if (this.props.direction === Side.Deposit) { this.props.dispatcher.showFlashMessage('Failed to wrap your ETH. Please try again.'); - analytics.logEvent('Portal', 'Wrap ETH Failed', networkName); + analytics.track('Wrap ETH Failure', { + amount: ethAmount, + }); } else { this.props.dispatcher.showFlashMessage('Failed to unwrap your WETH. Please try again.'); - analytics.logEvent('Portal', 'Unwrap WETH Failed', networkName); + analytics.track('Unwrap WETH Failed', { + amount: tokenAmount, + }); } - await errorReporter.reportAsync(err); + errorReporter.report(err); } } this.setState({ diff --git a/packages/website/ts/containers/legacy_portal.ts b/packages/website/ts/containers/legacy_portal.ts deleted file mode 100644 index e99f47fb7..000000000 --- a/packages/website/ts/containers/legacy_portal.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; -import * as React from 'react'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; -import { - LegacyPortal as LegacyPortalComponent, - LegacyPortalProps as LegacyPortalComponentProps, -} from 'ts/components/legacy_portal/legacy_portal'; -import { Dispatcher } from 'ts/redux/dispatcher'; -import { State } from 'ts/redux/reducer'; -import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; -import { constants } from 'ts/utils/constants'; -import { Translate } from 'ts/utils/translate'; - -interface ConnectedState { - blockchainErr: BlockchainErrs; - blockchainIsLoaded: boolean; - hashData: HashData; - injectedProviderName: string; - networkId: number; - nodeVersion: string; - orderFillAmount: BigNumber; - providerType: ProviderType; - tokenByAddress: TokenByAddress; - lastForceTokenStateRefetch: number; - userEtherBalanceInWei?: BigNumber; - screenWidth: ScreenWidths; - shouldBlockchainErrDialogBeOpen: boolean; - userAddress: string; - userSuppliedOrderCache: Order; - flashMessage?: string | React.ReactNode; - translate: Translate; -} - -interface ConnectedDispatch { - dispatcher: Dispatcher; -} - -const mapStateToProps = (state: State, _ownProps: LegacyPortalComponentProps): ConnectedState => { - const receiveAssetToken = state.sideToAssetToken[Side.Receive]; - const depositAssetToken = state.sideToAssetToken[Side.Deposit]; - const receiveAddress = !_.isUndefined(receiveAssetToken.address) - ? receiveAssetToken.address - : constants.NULL_ADDRESS; - const depositAddress = !_.isUndefined(depositAssetToken.address) - ? depositAssetToken.address - : constants.NULL_ADDRESS; - const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); - const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); - const hashData = { - depositAmount, - depositTokenContractAddr: depositAddress, - feeRecipientAddress: constants.NULL_ADDRESS, - makerFee: constants.MAKER_FEE, - orderExpiryTimestamp: state.orderExpiryTimestamp, - orderMakerAddress: state.userAddress, - orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, - receiveAmount, - receiveTokenContractAddr: receiveAddress, - takerFee: constants.TAKER_FEE, - orderSalt: state.orderSalt, - }; - return { - blockchainErr: state.blockchainErr, - blockchainIsLoaded: state.blockchainIsLoaded, - hashData, - injectedProviderName: state.injectedProviderName, - networkId: state.networkId, - nodeVersion: state.nodeVersion, - orderFillAmount: state.orderFillAmount, - providerType: state.providerType, - screenWidth: state.screenWidth, - shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, - tokenByAddress: state.tokenByAddress, - lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, - userAddress: state.userAddress, - userEtherBalanceInWei: state.userEtherBalanceInWei, - userSuppliedOrderCache: state.userSuppliedOrderCache, - flashMessage: state.flashMessage, - translate: state.translate, - }; -}; - -const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ - dispatcher: new Dispatcher(dispatch), -}); - -export const LegacyPortal: React.ComponentClass<LegacyPortalComponentProps> = connect( - mapStateToProps, - mapDispatchToProps, -)(LegacyPortalComponent); diff --git a/packages/website/ts/containers/zero_ex_js_documentation.ts b/packages/website/ts/containers/zero_ex_js_documentation.ts index b43a1f645..2fc08e923 100644 --- a/packages/website/ts/containers/zero_ex_js_documentation.ts +++ b/packages/website/ts/containers/zero_ex_js_documentation.ts @@ -183,6 +183,32 @@ const docsInfoConfig: DocsInfoConfig = { 'JSONRPCErrorCallback', 'LogEntryEvent', 'LogEntry', + 'ERC20AssetData', + 'ERC721AssetData', + 'AssetProxyId', + 'WETH9Events', + 'WETH9WithdrawalEventArgs', + 'WETH9ApprovalEventArgs', + 'WETH9EventArgs', + 'WETH9DepositEventArgs', + 'WETH9TransferEventArgs', + 'ERC20TokenTransferEventArgs', + 'ERC20TokenApprovalEventArgs', + 'ERC20TokenEvents', + 'ERC20TokenEventArgs', + 'ERC721TokenApprovalEventArgs', + 'ERC721TokenApprovalForAllEventArgs', + 'ERC721TokenTransferEventArgs', + 'ERC721TokenEvents', + 'ExchangeCancelUpToEventArgs', + 'ExchangeAssetProxyRegisteredEventArgs', + 'ExchangeFillEventArgs', + 'ExchangeCancelEventArgs', + 'ExchangeEventArgs', + 'ContractWrappersConfig', + 'MessagePrefixType', + 'MessagePrefixOpts', + 'OrderInfo', ], typeNameToPrefix: {}, typeNameToExternalLink: { diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index c7ccfdf1f..2a5c5e4f1 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -16,11 +16,9 @@ import { trackedTokenStorage } from 'ts/local_storage/tracked_token_storage'; import { tradeHistoryStorage } from 'ts/local_storage/trade_history_storage'; import { store } from 'ts/redux/store'; import { WebsiteLegacyPaths, WebsitePaths } from 'ts/types'; -import { analytics } from 'ts/utils/analytics'; import { muiTheme } from 'ts/utils/mui_theme'; import { utils } from 'ts/utils/utils'; // Polyfills -import 'whatwg-fetch'; injectTapEventPlugin(); // Check if we've introduced an update that requires us to clear the tradeHistory local storage entries @@ -35,14 +33,9 @@ import 'less/all.less'; // At the same time webpack statically parses for System.import() to determine bundle chunk split points // so each lazy import needs it's own `System.import()` declaration. -// TODO: Remove this once we ship V2 -const LazyPortal = utils.shouldShowPortalV2() - ? createLazyComponent('Portal', async () => - System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'), - ) - : createLazyComponent('LegacyPortal', async () => - System.import<any>(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), - ); +const LazyPortal = createLazyComponent('Portal', async () => + System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'), +); const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () => System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), ); @@ -74,10 +67,6 @@ const LazyEthereumTypesDocumentation = createLazyComponent('Documentation', asyn System.import<any>(/* webpackChunkName: "ethereumTypesDocs" */ 'ts/containers/ethereum_types_documentation'), ); -analytics.init(); -// tslint:disable-next-line:no-floating-promises -analytics.logProviderAsync((window as any).web3); - render( <Router> <div> diff --git a/packages/website/ts/local_storage/local_storage.ts b/packages/website/ts/local_storage/local_storage.ts index 1e3258ce0..13d9ca401 100644 --- a/packages/website/ts/local_storage/local_storage.ts +++ b/packages/website/ts/local_storage/local_storage.ts @@ -5,7 +5,7 @@ export const localStorage = { return !!window.localStorage; }, getItemIfExists(key: string): string { - if (!this.doesExist) { + if (!localStorage.doesExist) { return undefined; } const item = window.localStorage.getItem(key); @@ -15,13 +15,13 @@ export const localStorage = { return item; }, setItem(key: string, value: string): void { - if (!this.doesExist || _.isUndefined(value)) { + if (!localStorage.doesExist || _.isUndefined(value)) { return; } window.localStorage.setItem(key, value); }, removeItem(key: string): void { - if (!this.doesExist) { + if (!localStorage.doesExist) { return; } window.localStorage.removeItem(key); @@ -37,7 +37,7 @@ export const localStorage = { localStorage.setItem(key, JSON.stringify(value)); }, getAllKeys(): string[] { - if (!this.doesExist) { + if (!localStorage.doesExist) { return []; } return _.keys(window.localStorage); diff --git a/packages/website/ts/local_storage/tracked_token_storage.ts b/packages/website/ts/local_storage/tracked_token_storage.ts index f865f8109..b1b579aef 100644 --- a/packages/website/ts/local_storage/tracked_token_storage.ts +++ b/packages/website/ts/local_storage/tracked_token_storage.ts @@ -17,7 +17,7 @@ export const trackedTokenStorage = { localStorage.setItem(TRACKED_TOKENS_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE); }, addTrackedTokenToUser(userAddress: string, networkId: number, token: Token): void { - const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); + const trackedTokensByUserAddress = trackedTokenStorage.getTrackedTokensByUserAddress(); let trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; if (_.isUndefined(trackedTokensByNetworkId)) { trackedTokensByNetworkId = {}; @@ -57,7 +57,7 @@ export const trackedTokenStorage = { return trackedTokensByAddress; }, removeTrackedToken(userAddress: string, networkId: number, tokenAddress: string): void { - const trackedTokensByUserAddress = this.getTrackedTokensByUserAddress(); + const trackedTokensByUserAddress = trackedTokenStorage.getTrackedTokensByUserAddress(); const trackedTokensByNetworkId = trackedTokensByUserAddress[userAddress]; const trackedTokens = trackedTokensByNetworkId[networkId]; const remainingTrackedTokens = _.filter(trackedTokens, (token: Token) => { diff --git a/packages/website/ts/local_storage/trade_history_storage.tsx b/packages/website/ts/local_storage/trade_history_storage.tsx index 2e2f4e64e..a9b042820 100644 --- a/packages/website/ts/local_storage/trade_history_storage.tsx +++ b/packages/website/ts/local_storage/trade_history_storage.tsx @@ -27,31 +27,31 @@ export const tradeHistoryStorage = { localStorage.setItem(FILL_CLEAR_KEY, configs.LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE); }, addFillToUser(userAddress: string, networkId: number, fill: Fill): void { - const fillsByHash = this.getUserFillsByHash(userAddress, networkId); - const fillHash = this._getFillHash(fill); + const fillsByHash = tradeHistoryStorage.getUserFillsByHash(userAddress, networkId); + const fillHash = tradeHistoryStorage._getFillHash(fill); const doesFillExist = !_.isUndefined(fillsByHash[fillHash]); if (doesFillExist) { return; // noop } fillsByHash[fillHash] = fill; const userFillsJSONString = JSON.stringify(fillsByHash); - const userFillsKey = this._getUserFillsKey(userAddress, networkId); + const userFillsKey = tradeHistoryStorage._getUserFillsKey(userAddress, networkId); localStorage.setItem(userFillsKey, userFillsJSONString); }, removeFillFromUser(userAddress: string, networkId: number, fill: Fill): void { - const fillsByHash = this.getUserFillsByHash(userAddress, networkId); - const fillHash = this._getFillHash(fill); + const fillsByHash = tradeHistoryStorage.getUserFillsByHash(userAddress, networkId); + const fillHash = tradeHistoryStorage._getFillHash(fill); const doesFillExist = !_.isUndefined(fillsByHash[fillHash]); if (!doesFillExist) { return; // noop } delete fillsByHash[fillHash]; const userFillsJSONString = JSON.stringify(fillsByHash); - const userFillsKey = this._getUserFillsKey(userAddress, networkId); + const userFillsKey = tradeHistoryStorage._getUserFillsKey(userAddress, networkId); localStorage.setItem(userFillsKey, userFillsJSONString); }, getUserFillsByHash(userAddress: string, networkId: number): { [fillHash: string]: Fill } { - const userFillsKey = this._getUserFillsKey(userAddress, networkId); + const userFillsKey = tradeHistoryStorage._getUserFillsKey(userAddress, networkId); const userFillsJSONString = localStorage.getItemIfExists(userFillsKey); if (_.isEmpty(userFillsJSONString)) { return {}; @@ -66,7 +66,7 @@ export const tradeHistoryStorage = { return userFillsByHash; }, getFillsLatestBlock(userAddress: string, networkId: number): number { - const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId); + const userFillsLatestBlockKey = tradeHistoryStorage._getFillsLatestBlockKey(userAddress, networkId); const blockNumberStr = localStorage.getItemIfExists(userFillsLatestBlockKey); if (_.isEmpty(blockNumberStr)) { return constants.GENESIS_ORDER_BLOCK_BY_NETWORK_ID[networkId]; @@ -75,7 +75,7 @@ export const tradeHistoryStorage = { return blockNumber; }, setFillsLatestBlock(userAddress: string, networkId: number, blockNumber: number): void { - const userFillsLatestBlockKey = this._getFillsLatestBlockKey(userAddress, networkId); + const userFillsLatestBlockKey = tradeHistoryStorage._getFillsLatestBlockKey(userAddress, networkId); localStorage.setItem(userFillsLatestBlockKey, `${blockNumber}`); }, _getUserFillsKey(userAddress: string, networkId: number): string { diff --git a/packages/website/ts/pages/about/about.tsx b/packages/website/ts/pages/about/about.tsx index 5bb5d06a9..33581c938 100644 --- a/packages/website/ts/pages/about/about.tsx +++ b/packages/website/ts/pages/about/about.tsx @@ -99,7 +99,7 @@ const teamRow3: ProfileInfo[] = [ }, { name: 'Jacob Evans', - title: 'Blockchain Engineer', + title: 'Ecosystem Engineer', description: `Previously software engineer at Qantas and RSA Security.`, image: '/images/team/jacob.jpg', linkedIn: 'https://www.linkedin.com/in/dekzter/', @@ -174,14 +174,21 @@ const teamRow6: ProfileInfo[] = [ linkedIn: 'https://www.linkedin.com/in/stephenalexbrowne/', github: 'http://github.com/albrow', }, - // { - // name: 'Chris Kalani', - // title: 'Director of Design', - // description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`, - // image: 'images/team/chris.png', - // linkedIn: 'https://www.linkedin.com/in/chriskalani/', - // github: 'https://github.com/chriskalani', - // }, + { + name: 'Peter Zeitz', + title: 'Research Fellow', + description: `Researching decentralized governance. Previously Assistant Professor of Economics at National University of Singapore Business School. PhD in Economics at UCLA.`, + image: 'images/team/peter.jpg', + linkedIn: 'https://www.linkedin.com/in/peter-z-7b9595163/', + }, + { + name: 'Chris Kalani', + title: 'Director of Design', + description: `Previously founded Wake (acquired by InVision). Early Facebook product designer.`, + image: 'images/team/chris.png', + linkedIn: 'https://www.linkedin.com/in/chriskalani/', + github: 'https://github.com/chriskalani', + }, ]; const advisors: ProfileInfo[] = [ diff --git a/packages/website/ts/pages/jobs/jobs.tsx b/packages/website/ts/pages/jobs/jobs.tsx index 314669ee9..38cefa832 100644 --- a/packages/website/ts/pages/jobs/jobs.tsx +++ b/packages/website/ts/pages/jobs/jobs.tsx @@ -33,7 +33,7 @@ export interface JobsState {} export class Jobs extends React.Component<JobsProps, JobsState> { // TODO: consolidate this small screen scaffolding into one place (its being used in portal and docs as well) - private _throttledScreenWidthUpdate: () => void; + private readonly _throttledScreenWidthUpdate: () => void; public constructor(props: JobsProps) { super(props); this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index b2cf4d979..2a51ee3c0 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -171,7 +171,7 @@ interface LandingState { } export class Landing extends React.Component<LandingProps, LandingState> { - private _throttledScreenWidthUpdate: () => void; + private readonly _throttledScreenWidthUpdate: () => void; constructor(props: LandingProps) { super(props); this.state = { diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index 9659900be..55f532b11 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -205,7 +205,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> { articlesBySection, }, async () => { - await utils.onPageLoadAsync(); + await utils.onPageLoadPromise; const hash = this.props.location.hash.slice(1); sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID); }, diff --git a/packages/website/ts/redux/analyticsMiddleware.ts b/packages/website/ts/redux/analyticsMiddleware.ts new file mode 100644 index 000000000..51d39a5d7 --- /dev/null +++ b/packages/website/ts/redux/analyticsMiddleware.ts @@ -0,0 +1,36 @@ +import { Middleware } from 'redux'; +import { State } from 'ts/redux/reducer'; +import { ActionTypes } from 'ts/types'; +import { analytics } from 'ts/utils/analytics'; + +export const analyticsMiddleware: Middleware = store => next => action => { + const nextAction = next(action); + const nextState = (store.getState() as any) as State; + switch (action.type) { + case ActionTypes.UpdateInjectedProviderName: + analytics.addEventProperties({ + injectedProviderName: nextState.injectedProviderName, + }); + break; + case ActionTypes.UpdateNetworkId: + analytics.addEventProperties({ + networkId: nextState.networkId, + }); + break; + case ActionTypes.UpdateUserAddress: + analytics.addUserProperties({ + ethAddress: nextState.userAddress, + }); + break; + case ActionTypes.UpdateUserEtherBalance: + if (nextState.userEtherBalanceInWei) { + analytics.addUserProperties({ + ethBalance: nextState.userEtherBalanceInWei.toString(), + }); + } + break; + default: + break; + } + return nextAction; +}; diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index e0ce43ae5..db008d319 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -17,7 +17,7 @@ import { } from 'ts/types'; export class Dispatcher { - private _dispatch: Dispatch<State>; + private readonly _dispatch: Dispatch<State>; constructor(dispatch: Dispatch<State>) { this._dispatch = dispatch; } diff --git a/packages/website/ts/redux/store.ts b/packages/website/ts/redux/store.ts index 2672e3f61..006241371 100644 --- a/packages/website/ts/redux/store.ts +++ b/packages/website/ts/redux/store.ts @@ -1,7 +1,8 @@ import * as _ from 'lodash'; -import { createStore, Store as ReduxStore } from 'redux'; -import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly'; +import { applyMiddleware, createStore, Store as ReduxStore } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; import { stateStorage } from 'ts/local_storage/state_storage'; +import { analyticsMiddleware } from 'ts/redux/analyticsMiddleware'; import { reducer, State } from 'ts/redux/reducer'; const ONE_SECOND = 1000; @@ -9,7 +10,7 @@ const ONE_SECOND = 1000; export const store: ReduxStore<State> = createStore( reducer, stateStorage.getPersistedDefaultState(), - devToolsEnhancer({ name: '0x Website Redux Store' }), + composeWithDevTools(applyMiddleware(analyticsMiddleware)), ); store.subscribe( _.throttle(() => { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index e8dc694f6..4d0496f6c 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -244,7 +244,10 @@ export enum BlockchainCallErrs { export enum Environments { DEVELOPMENT = 'DEVELOPMENT', + DOGFOOD = 'DOGFOOD', + STAGING = 'STAGING', PRODUCTION = 'PRODUCTION', + UNKNOWN = 'UNKNOWN', } export type ContractInstance = any; // TODO: add type definition for Contract @@ -516,8 +519,10 @@ export interface OutdatedWrappedEtherByNetworkId { }; } -export interface ItemByAddress<T> { - [address: string]: T; +export type ItemByAddress<T> = ObjectMap<T>; + +export interface ObjectMap<T> { + [key: string]: T; } export type TokenStateByAddress = ItemByAddress<TokenState>; diff --git a/packages/website/ts/utils/analytics.ts b/packages/website/ts/utils/analytics.ts index f4bfa083f..e5a1ddfa4 100644 --- a/packages/website/ts/utils/analytics.ts +++ b/packages/website/ts/utils/analytics.ts @@ -1,27 +1,83 @@ import * as _ from 'lodash'; -import * as ReactGA from 'react-ga'; -import { InjectedWeb3 } from 'ts/types'; -import { configs } from 'ts/utils/configs'; +import { ObjectMap, Order } from 'ts/types'; import { utils } from 'ts/utils/utils'; -export const analytics = { - init(): void { - ReactGA.initialize(configs.GOOGLE_ANALYTICS_ID); - }, - logEvent(category: string, action: string, label: string, value?: any): void { - ReactGA.event({ - category, - action, - label, - value, - }); - }, - async logProviderAsync(web3IfExists: InjectedWeb3): Promise<void> { - await utils.onPageLoadAsync(); - const providerType = - !_.isUndefined(web3IfExists) && !_.isUndefined(web3IfExists.currentProvider) - ? utils.getProviderType(web3IfExists.currentProvider) - : 'NONE'; - ReactGA.ga('set', 'dimension1', providerType); - }, -}; +export interface HeapAnalytics { + loaded: boolean; + identify(id: string, idType: string): void; + track(eventName: string, eventProperties?: ObjectMap<string | number>): void; + resetIdentity(): void; + addUserProperties(properties: ObjectMap<string | number>): void; + addEventProperties(properties: ObjectMap<string | number>): void; + removeEventProperty(property: string): void; + clearEventProperties(): void; +} +export class Analytics { + private _heap: HeapAnalytics; + public static init(): Analytics { + return new Analytics(Analytics.getHeap()); + } + public static getHeap(): HeapAnalytics { + const heap = (window as any).heap; + if (!_.isUndefined(heap)) { + return heap; + } else { + throw new Error('Could not find the Heap SDK on the page.'); + } + } + constructor(heap: HeapAnalytics) { + this._heap = heap; + } + // tslint:disable:no-floating-promises + // HeapAnalytics Wrappers + public identify(id: string, idType: string): void { + this._heapLoadedGuardAsync(() => this._heap.identify(id, idType)); + } + public track(eventName: string, eventProperties?: ObjectMap<string | number>): void { + this._heapLoadedGuardAsync(() => this._heap.track(eventName, eventProperties)); + } + public resetIdentity(): void { + this._heapLoadedGuardAsync(() => this._heap.resetIdentity()); + } + public addUserProperties(properties: ObjectMap<string | number>): void { + this._heapLoadedGuardAsync(() => this._heap.addUserProperties(properties)); + } + public addEventProperties(properties: ObjectMap<string | number>): void { + this._heapLoadedGuardAsync(() => this._heap.addEventProperties(properties)); + } + public removeEventProperty(property: string): void { + this._heapLoadedGuardAsync(() => this._heap.removeEventProperty(property)); + } + public clearEventProperties(): void { + this._heapLoadedGuardAsync(() => this._heap.clearEventProperties()); + } + // tslint:enable:no-floating-promises + // Custom methods + public trackOrderEvent(eventName: string, order: Order): void { + const orderLoggingData = { + takerTokenAmount: order.signedOrder.takerTokenAmount, + makeTokenAmount: order.signedOrder.makerTokenAmount, + takerToken: order.metadata.takerToken.symbol, + makerToken: order.metadata.makerToken.symbol, + }; + this.track(eventName, orderLoggingData); + } + /** + * Heap is not available as a UMD module, and additionally has the strange property of replacing itself with + * a different object once it's loaded. + * Instead of having an await call before every analytics use, we opt to have the awaiting logic in here by + * guarding every API call with the guard below. + */ + private async _heapLoadedGuardAsync(callback: () => void): Promise<void> { + if (this._heap.loaded) { + callback(); + return undefined; + } + await utils.onPageLoadPromise; + // HACK: Reset heap to loaded heap + this._heap = (window as any).heap; + callback(); + } +} + +export const analytics = Analytics.init(); diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts index 835a6ef4d..5164211df 100644 --- a/packages/website/ts/utils/backend_client.ts +++ b/packages/website/ts/utils/backend_client.ts @@ -6,6 +6,7 @@ import { WebsiteBackendJobInfo, WebsiteBackendPriceInfo, WebsiteBackendRelayerInfo, + WebsiteBackendTokenInfo, } from 'ts/types'; import { fetchUtils } from 'ts/utils/fetch_utils'; import { utils } from 'ts/utils/utils'; @@ -14,6 +15,7 @@ const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station'; const JOBS_ENDPOINT = '/jobs'; const PRICES_ENDPOINT = '/prices'; const RELAYERS_ENDPOINT = '/relayers'; +const TOKENS_ENDPOINT = '/tokens'; const WIKI_ENDPOINT = '/wiki'; const SUBSCRIBE_SUBSTACK_NEWSLETTER_ENDPOINT = '/newsletter_subscriber/substack'; @@ -41,6 +43,10 @@ export const backendClient = { const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), RELAYERS_ENDPOINT); return result; }, + async getTokenInfosAsync(): Promise<WebsiteBackendTokenInfo[]> { + const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), TOKENS_ENDPOINT); + return result; + }, async getWikiArticlesBySectionAsync(): Promise<ArticlesBySection> { const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), WIKI_ENDPOINT); return result; diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 97aabd13d..a1c64f9cb 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -1,11 +1,6 @@ -import * as _ from 'lodash'; -import { Environments, OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types'; +import { OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types'; const BASE_URL = window.location.origin; -const isDevelopment = _.includes( - ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'], - BASE_URL, -); const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs'; export const configs = { @@ -19,9 +14,8 @@ export const configs = { DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com', DOMAIN_DOGFOOD: 'dogfood.0xproject.com', - DOMAIN_DEVELOPMENT: '0xproject.localhost:3572', + DOMAINS_DEVELOPMENT: ['0xproject.localhost:3572', 'localhost:3572', '127.0.0.1'], DOMAIN_PRODUCTION: '0xproject.com', - ENVIRONMENT: isDevelopment ? Environments.DEVELOPMENT : Environments.PRODUCTION, GOOGLE_ANALYTICS_ID: 'UA-98720122-1', LAST_LOCAL_STORAGE_FILL_CLEARANCE_DATE: '2017-11-22', LAST_LOCAL_STORAGE_TRACKED_TOKEN_CLEARANCE_DATE: '2018-7-5', diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 4b3443d21..20441cd75 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -33,7 +33,7 @@ export const constants = { PROVIDER_NAME_TOSHI: 'Toshi', PROVIDER_NAME_GENERIC: 'Injected Web3', PROVIDER_NAME_PUBLIC: '0x Public', - ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65', + ROLLBAR_ACCESS_TOKEN: '32c39bfa4bb6440faedc1612a9c13d28', S3_DOC_BUCKET_ROOT: 'https://s3.amazonaws.com/doc-jsons', S3_STAGING_DOC_BUCKET_ROOT: 'https://s3.amazonaws.com/staging-doc-jsons', SUCCESS_STATUS: 200, @@ -58,7 +58,7 @@ export const constants = { PROJECT_URL_MAKER: 'https://makerdao.com', PROJECT_URL_ARAGON: 'https://aragon.one', PROJECT_URL_BLOCKNET: 'https://blocknet.co', - PROJECT_URL_0CEAN: 'http://the0cean.com', + PROJECT_URL_0CEAN: 'https://theocean.trade', PROJECT_URL_IMTOKEN: 'https://tokenlon.token.im/', PROJECT_URL_AUGUR: 'https://augur.net', PROJECT_URL_AUCTUS: 'https://auctus.org', diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts index 7768835fb..1627b9b0c 100644 --- a/packages/website/ts/utils/doc_utils.ts +++ b/packages/website/ts/utils/doc_utils.ts @@ -1,5 +1,5 @@ import { DoxityDocObj, TypeDocNode } from '@0xproject/react-docs'; -import { logUtils } from '@0xproject/utils'; +import { fetchAsync, logUtils } from '@0xproject/utils'; import findVersions = require('find-versions'); import * as _ from 'lodash'; import { S3FileObject, VersionToFilePath } from 'ts/types'; @@ -16,7 +16,7 @@ export const docUtils = { return versionToFilePath; }, async getVersionFileNamesAsync(s3DocJsonRoot: string, folderName: string): Promise<string[]> { - const response = await fetch(s3DocJsonRoot); + const response = await fetchAsync(s3DocJsonRoot); if (response.status !== 200) { // TODO: Show the user an error message when the docs fail to load const errMsg = await response.text(); @@ -73,7 +73,7 @@ export const docUtils = { }, async getJSONDocFileAsync(filePath: string, s3DocJsonRoot: string): Promise<TypeDocNode | DoxityDocObj> { const endpoint = `${s3DocJsonRoot}/${filePath}`; - const response = await fetch(endpoint); + const response = await fetchAsync(endpoint); if (response.status !== 200) { // TODO: Show the user an error message when the docs fail to load const errMsg = await response.text(); diff --git a/packages/website/ts/utils/error_reporter.ts b/packages/website/ts/utils/error_reporter.ts index f875141fe..6008fffed 100644 --- a/packages/website/ts/utils/error_reporter.ts +++ b/packages/website/ts/utils/error_reporter.ts @@ -1,7 +1,7 @@ import { logUtils } from '@0xproject/utils'; -import { Environments } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; +import { utils } from 'ts/utils/utils'; // Suggested way to include Rollbar with Webpack // https://github.com/rollbar/rollbar.js/tree/master/examples/webpack @@ -12,7 +12,15 @@ const rollbarConfig = { itemsPerMinute: 10, maxItems: 500, payload: { - environment: configs.ENVIRONMENT, + environment: utils.getEnvironment(), + client: { + javascript: { + source_map_enabled: true, + // This is only defined in production environments. + code_version: process.env.GIT_SHA, + guess_uncaught_frames: true, + }, + }, }, uncaughtErrorLevel: 'error', hostWhiteList: [configs.DOMAIN_PRODUCTION, configs.DOMAIN_STAGING], @@ -28,25 +36,18 @@ const rollbarConfig = { 'SecurityError (DOM Exception 18)', ], }; -import Rollbar = require('../../public/js/rollbar.umd.nojson.min.js'); +import Rollbar = require('../../public/js/rollbar.umd.min.js'); const rollbar = Rollbar.init(rollbarConfig); export const errorReporter = { - async reportAsync(err: Error): Promise<any> { - if (configs.ENVIRONMENT === Environments.DEVELOPMENT) { + report(err: Error): void { + if (utils.isDevelopment()) { return; // Let's not log development errors to rollbar } - - return new Promise((resolve, _reject) => { - rollbar.error(err, (rollbarErr: Error) => { - if (rollbarErr) { - logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`); - // We never want to reject and cause the app to throw because of rollbar - resolve(); - } else { - resolve(); - } - }); + rollbar.error(err, (rollbarErr: Error) => { + if (rollbarErr) { + logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`); + } }); }, }; diff --git a/packages/website/ts/utils/fetch_utils.ts b/packages/website/ts/utils/fetch_utils.ts index 513f7e479..e9a88b6b3 100644 --- a/packages/website/ts/utils/fetch_utils.ts +++ b/packages/website/ts/utils/fetch_utils.ts @@ -1,4 +1,4 @@ -import { logUtils } from '@0xproject/utils'; +import { fetchAsync, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import * as queryString from 'query-string'; @@ -9,8 +9,7 @@ const logErrorIfPresent = (response: Response, requestedURL: string) => { const errorText = `Error requesting url: ${requestedURL}, ${response.status}: ${response.statusText}`; logUtils.log(errorText); const error = Error(errorText); - // tslint:disable-next-line:no-floating-promises - errorReporter.reportAsync(error); + errorReporter.report(error); throw error; } }; @@ -19,14 +18,14 @@ export const fetchUtils = { async requestAsync(baseUrl: string, path: string, queryParams?: object): Promise<any> { const query = queryStringFromQueryParams(queryParams); const url = `${baseUrl}${path}${query}`; - const response = await fetch(url); + const response = await fetchAsync(url); logErrorIfPresent(response, url); const result = await response.json(); return result; }, async postAsync(baseUrl: string, path: string, body: object): Promise<Response> { const url = `${baseUrl}${path}`; - const response = await fetch(url, { + const response = await fetchAsync(url, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 8c76a7592..e656d5963 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -30,8 +30,6 @@ import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import * as u2f from 'ts/vendor/u2f_api'; -const isDogfood = (): boolean => _.includes(window.location.href, configs.DOMAIN_DOGFOOD); - export const utils = { assert(condition: boolean, message: string): void { if (!condition) { @@ -61,7 +59,7 @@ export const utils = { return moment.unix(unixTimestampSec.toNumber()); }, convertToReadableDateTimeFromUnixTimestamp(unixTimestampSec: BigNumber): string { - const m = this.convertToMomentFromUnixTimestamp(unixTimestampSec); + const m = utils.convertToMomentFromUnixTimestamp(unixTimestampSec); const formattedDate: string = m.format('h:MMa MMMM D YYYY'); return formattedDate; }, @@ -177,18 +175,6 @@ export const utils = { _.includes(errMsg, ledgerDenialErrMsg); return isUserDeniedErrMsg; }, - getCurrentEnvironment(): string { - switch (location.host) { - case configs.DOMAIN_DEVELOPMENT: - return 'development'; - case configs.DOMAIN_STAGING: - return 'staging'; - case configs.DOMAIN_PRODUCTION: - return 'production'; - default: - return 'production'; - } - }, getAddressBeginAndEnd(address: string): string { const truncatedAddress = `${address.substring(0, 6)}...${address.substr(-4)}`; // 0x3d5a...b287 return truncatedAddress; @@ -313,14 +299,13 @@ export const utils = { const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`; return baseUrl; }, - async onPageLoadAsync(): Promise<void> { + onPageLoadPromise: new Promise<void>((resolve, _reject) => { if (document.readyState === 'complete') { - return; // Already loaded + resolve(); + return; } - return new Promise<void>((resolve, _reject) => { - window.onload = () => resolve(); - }); - }, + window.onload = () => resolve(); + }), getProviderType(provider: Provider): Providers | string { const constructorName = provider.constructor.name; let parsedProviderName = constructorName; @@ -346,10 +331,10 @@ export const utils = { return parsedProviderName; }, getBackendBaseUrl(): string { - return isDogfood() ? configs.BACKEND_BASE_STAGING_URL : configs.BACKEND_BASE_PROD_URL; + return utils.isDogfood() ? configs.BACKEND_BASE_STAGING_URL : configs.BACKEND_BASE_PROD_URL; }, isDevelopment(): boolean { - return configs.ENVIRONMENT === Environments.DEVELOPMENT; + return _.includes(configs.DOMAINS_DEVELOPMENT, window.location.origin); }, isStaging(): boolean { return _.includes(window.location.href, configs.DOMAIN_STAGING); @@ -357,14 +342,29 @@ export const utils = { isExternallyInjected(providerType: ProviderType, injectedProviderName: string): boolean { return providerType === ProviderType.Injected && injectedProviderName !== constants.PROVIDER_NAME_PUBLIC; }, - isDogfood, - shouldShowPortalV2(): boolean { - // return this.isDevelopment() || this.isStaging() || this.isDogfood(); - // TODO: Remove this method entirely after launch. - return true; + isDogfood(): boolean { + return _.includes(window.location.href, configs.DOMAIN_DOGFOOD); + }, + isProduction(): boolean { + return _.includes(window.location.href, configs.DOMAIN_PRODUCTION); + }, + getEnvironment(): Environments { + if (utils.isDogfood()) { + return Environments.DOGFOOD; + } + if (utils.isDevelopment()) { + return Environments.DEVELOPMENT; + } + if (utils.isStaging()) { + return Environments.STAGING; + } + if (utils.isProduction()) { + return Environments.PRODUCTION; + } + return Environments.UNKNOWN; }, shouldShowJobsPage(): boolean { - return this.isDevelopment() || this.isStaging() || this.isDogfood(); + return utils.isDevelopment() || utils.isStaging() || utils.isDogfood(); }, getEthToken(tokenByAddress: TokenByAddress): Token { return utils.getTokenBySymbol(constants.ETHER_TOKEN_SYMBOL, tokenByAddress); @@ -379,27 +379,34 @@ export const utils = { }, getTrackedTokens(tokenByAddress: TokenByAddress): Token[] { const allTokens = _.values(tokenByAddress); - const trackedTokens = _.filter(allTokens, t => this.isTokenTracked(t)); + const trackedTokens = _.filter(allTokens, t => utils.isTokenTracked(t)); return trackedTokens; }, getFormattedAmountFromToken(token: Token, tokenState: TokenState): string { return utils.getFormattedAmount(tokenState.balance, token.decimals); }, + format(value: BigNumber, format: string): string { + const formattedAmount = numeral(value).format(format); + if (_.isNaN(formattedAmount)) { + // https://github.com/adamwdraper/Numeral-js/issues/596 + return numeral(new BigNumber(0)).format(format); + } + return formattedAmount; + }, getFormattedAmount(amount: BigNumber, decimals: number): string { const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); // if the unit amount is less than 1, show the natural number of decimal places with a max of 4 // if the unit amount is greater than or equal to 1, show only 2 decimal places - const precision = unitAmount.lt(1) - ? Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces()) - : 2; + const lessThanOnePrecision = Math.min(constants.TOKEN_AMOUNT_DISPLAY_PRECISION, unitAmount.decimalPlaces()); + const greaterThanOnePrecision = 2; + const precision = unitAmount.lt(1) ? lessThanOnePrecision : greaterThanOnePrecision; const format = `0,0.${_.repeat('0', precision)}`; - const formattedAmount = numeral(unitAmount).format(format); - return formattedAmount; + return utils.format(unitAmount, format); }, getUsdValueFormattedAmount(amount: BigNumber, decimals: number, price: BigNumber): string { const unitAmount = Web3Wrapper.toUnitAmount(amount, decimals); const value = unitAmount.mul(price); - return numeral(value).format(constants.NUMERAL_USD_FORMAT); + return utils.format(value, constants.NUMERAL_USD_FORMAT); }, openUrl(url: string): void { window.open(url, '_blank'); diff --git a/packages/website/webpack.config.js b/packages/website/webpack.config.js index b5e9cf6dd..8653196a6 100644 --- a/packages/website/webpack.config.js +++ b/packages/website/webpack.config.js @@ -1,7 +1,51 @@ const path = require('path'); const webpack = require('webpack'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const RollbarSourceMapPlugin = require('rollbar-sourcemap-webpack-plugin'); +const childProcess = require('child_process'); +const GIT_SHA = childProcess + .execSync('git rev-parse HEAD') + .toString() + .trim(); + +const generatePlugins = () => { + let plugins = []; + if (process.env.NODE_ENV === 'production') { + plugins = plugins.concat([ + // Since we do not use moment's locale feature, we exclude them from the bundle. + // This reduces the bundle size by 0.4MB. + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + GIT_SHA: JSON.stringify(GIT_SHA), + }, + }), + // TODO: Revert to webpack bundled version with webpack v4. + // The v3 series bundled version does not support ES6 and + // fails to build. + new UglifyJsPlugin({ + sourceMap: true, + uglifyOptions: { + mangle: { + reserved: ['BigNumber'], + }, + }, + }), + ]); + if (process.env.DEPLOY_ROLLBAR_SOURCEMAPS === 'true') { + plugins = plugins.concat([ + new RollbarSourceMapPlugin({ + accessToken: '32c39bfa4bb6440faedc1612a9c13d28', + version: GIT_SHA, + publicPath: 'https://0xproject.com/', + }), + ]); + } + } + return plugins; +}; module.exports = { entry: ['./ts/index.tsx'], output: { @@ -71,27 +115,5 @@ module.exports = { }, disableHostCheck: true, }, - plugins: - process.env.NODE_ENV === 'production' - ? [ - // Since we do not use moment's locale feature, we exclude them from the bundle. - // This reduces the bundle size by 0.4MB. - new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(process.env.NODE_ENV), - }, - }), - // TODO: Revert to webpack bundled version with webpack v4. - // The v3 series bundled version does not support ES6 and - // fails to build. - new UglifyJsPlugin({ - uglifyOptions: { - mangle: { - reserved: ['BigNumber'], - }, - }, - }), - ] - : [], + plugins: generatePlugins(), }; |