diff --git a/client/webserver/site/dist/entry.js b/client/webserver/site/dist/entry.js index 3cd2bfd8c5..81b037c43b 100644 --- a/client/webserver/site/dist/entry.js +++ b/client/webserver/site/dist/entry.js @@ -1,3 +1,3 @@ /*! For license information please see entry.js.LICENSE.txt */ -(()=>{var e={61:(e,t,n)=>{var r=n(698).default;function a(){"use strict";e.exports=a=function(){return t},e.exports.__esModule=!0,e.exports.default=e.exports;var t={},n=Object.prototype,o=n.hasOwnProperty,s=Object.defineProperty||function(e,t,n){e[t]=n.value},i="function"==typeof Symbol?Symbol:{},c=i.iterator||"@@iterator",l=i.asyncIterator||"@@asyncIterator",u=i.toStringTag||"@@toStringTag";function h(e,t,n){return Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{h({},"")}catch(e){h=function(e,t,n){return e[t]=n}}function d(e,t,n,r){var a=t&&t.prototype instanceof m?t:m,o=Object.create(a.prototype),i=new I(r||[]);return s(o,"_invoke",{value:S(e,n,i)}),o}function p(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(e){return{type:"throw",arg:e}}}t.wrap=d;var f={};function m(){}function v(){}function y(){}var g={};h(g,c,(function(){return this}));var b=Object.getPrototypeOf,w=b&&b(b(R([])));w&&w!==n&&o.call(w,c)&&(g=w);var k=y.prototype=m.prototype=Object.create(g);function x(e){["next","throw","return"].forEach((function(t){h(e,t,(function(e){return this._invoke(t,e)}))}))}function C(e,t){function n(a,s,i,c){var l=p(e[a],e,s);if("throw"!==l.type){var u=l.arg,h=u.value;return h&&"object"==r(h)&&o.call(h,"__await")?t.resolve(h.__await).then((function(e){n("next",e,i,c)}),(function(e){n("throw",e,i,c)})):t.resolve(h).then((function(e){u.value=e,i(u)}),(function(e){return n("throw",e,i,c)}))}c(l.arg)}var a;s(this,"_invoke",{value:function(e,r){function o(){return new t((function(t,a){n(e,r,t,a)}))}return a=a?a.then(o,o):o()}})}function S(e,t,n){var r="suspendedStart";return function(a,o){if("executing"===r)throw new Error("Generator is already running");if("completed"===r){if("throw"===a)throw o;return{value:void 0,done:!0}}for(n.method=a,n.arg=o;;){var s=n.delegate;if(s){var i=E(s,n);if(i){if(i===f)continue;return i}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if("suspendedStart"===r)throw r="completed",n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);r="executing";var c=p(e,t,n);if("normal"===c.type){if(r=n.done?"completed":"suspendedYield",c.arg===f)continue;return{value:c.arg,done:n.done}}"throw"===c.type&&(r="completed",n.method="throw",n.arg=c.arg)}}}function E(e,t){var n=t.method,r=e.iterator[n];if(void 0===r)return t.delegate=null,"throw"===n&&e.iterator.return&&(t.method="return",t.arg=void 0,E(e,t),"throw"===t.method)||"return"!==n&&(t.method="throw",t.arg=new TypeError("The iterator does not provide a '"+n+"' method")),f;var a=p(r,e.iterator,t.arg);if("throw"===a.type)return t.method="throw",t.arg=a.arg,t.delegate=null,f;var o=a.arg;return o?o.done?(t[e.resultName]=o.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,f):o:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,f)}function A(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function F(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function I(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(A,this),this.reset(!0)}function R(e){if(e){var t=e[c];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,r=function t(){for(;++n=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return n("end");if(a.tryLoc<=this.prev){var i=o.call(a,"catchLoc"),c=o.call(a,"finallyLoc");if(i&&c){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&o.call(r,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),F(n),f}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var r=n.completion;if("throw"===r.type){var a=r.arg;F(n)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:R(e),resultName:t,nextLoc:n},"next"===this.method&&(this.arg=void 0),f}},t}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},698:e=>{function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},687:(e,t,n)=>{var r=n(61)();e.exports=r;try{regeneratorRuntime=r}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=r:Function("r","regeneratorRuntime = r")(r)}}},t={};function n(r){var a=t[r];if(void 0!==a)return a.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";function e(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Dt(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=n.left&&e.pageX<=n.right&&e.pageY>=n.top&&e.pageY<=n.bottom}},{key:"layoutMetrics",value:function(e){var t=e.getBoundingClientRect(),n=document.documentElement,r=t.top+n.scrollTop,a=t.left+n.scrollLeft,o=e.offsetWidth,s=e.offsetHeight;return{bodyTop:r,bodyLeft:a,width:o,height:s,centerX:a+o/2,centerY:r+s/2}}},{key:"descendentMetrics",value:function(t,n){var r=e.layoutMetrics(t),a=e.layoutMetrics(n);return{bodyTop:a.bodyTop-r.bodyTop,bodyLeft:a.bodyLeft-r.bodyLeft,width:a.width,height:a.height,centerX:a.centerX-r.bodyLeft,centerY:a.centerY-r.bodyTop}}},{key:"empty",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),a=1;a1?n-1:0),a=1;a"),e),document.createElement("div"))}},{key:"idDescendants",value:function(t){var n,r={},a=Rt(e.applySelector(t,"[id]"));try{for(a.s();!(n=a.n()).done;){var o=n.value;r[o.id]=o}}catch(e){a.e(e)}finally{a.f()}return r}},{key:"formatCoinValue",value:function(e,t){var n=r(_t(e,t),2),a=n[0],o=n[1];return Number.isInteger(a)?Lt.format(a):function(e){return Ut(Wt,2,e)}(o).format(a)}},{key:"formatThreeSigFigs",value:function(e){return e>=1e3?Lt.format(Math.round(e)):Bt.format(e)}},{key:"formatFiveSigFigs",value:function(e,t){return e>=1e4?Lt.format(Math.round(e)):e<1e5?Nt(null!=t?t:8).format(e):Mt.format(e)}},{key:"formatFullPrecision",value:function(e,t){var n=r(_t(e,t),2),a=n[0];return Nt(n[1]).format(a)}},{key:"formatFiatConversion",value:function(e,t,n){if(!t||0===t)return It(gt);var a=r(_t(e,n),1)[0]*t;return Nt(2).format(a)}},{key:"logoPath",value:function(e){return-1===Pt.indexOf(e)&&(e=e.substring(0,1)),"/img/coins/".concat(e,".png")}},{key:"bipSymbol",value:function(e){return Tt[e]}},{key:"logoPathFromID",value:function(t){return e.logoPath(Tt[t])}},{key:"symbolize",value:function(e){var t=e.split("."),n=document.createElement("span");if(n.textContent=t[0].toUpperCase(),1===t.length)return n;var r=document.createElement("span");r.classList.add("token-aware-symbol"),r.appendChild(n);var a=document.createElement("sup");return a.textContent=t[1].toUpperCase(),r.appendChild(a),r}},{key:"shortSymbol",value:function(e){return e.split(".")[0].toUpperCase()}},{key:"cleanTemplates",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n0||u>0)&&u++,e>0&&(l+="".concat(e," ").concat(t," ")),u>=2},d=r(Zt(c,Gt),2);if(t=d[0],c=d[1],h(t,"y"))return l;var p=r(Zt(c,Xt),2);if(n=p[0],c=p[1],h(n,"mo"))return l;var f=r(Zt(c,Qt),2);if(a=f[0],c=f[1],h(a,"d"))return l;var m=r(Zt(c,Yt),2);if(o=m[0],c=m[1],h(o,"h"))return l;var v=r(Zt(c,Jt),2);if(s=v[0],c=v[1],h(s,"m"))return l;var y=r(Zt(c,1e3),2);return i=y[0],c=y[1],h(i,"s"),l||"0 s"}},{key:"disableMouseWheel",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n=0?n:31536e7,o=a?jt[a]:jt.linear,s=(new Date).getTime(),i=n===e.Forever?Number.MAX_SAFE_INTEGER:s+n,c=i-s,l=1e3/30,u=s,this.endAnimation=!1;case 8:if(!(ue.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(document.cookie.split(";"));try{for(n.s();!(t=n.n()).done;){var a=r(t.value.split("="),2),o=a[0],s=a[1];if(o.trim()===e)return s}}catch(e){n.e(e)}finally{n.f()}return null}},{key:"removeCookie",value:function(e){document.cookie="".concat(e,"=;expires=Thu, 01 Jan 1970 00:00:01 GMT;")}},{key:"isDark",value:function(){return document.cookie.split(";").filter((function(t){return t.includes("".concat(e.darkModeCK,"=1"))})).length}},{key:"passwordIsCached",value:function(){return!!this.getCookie(e.pwKeyCK)}},{key:"storeLocal",value:function(e,t){window.localStorage.setItem(e,JSON.stringify(t))}},{key:"fetchLocal",value:function(e){var t=window.localStorage.getItem(e);return null!==t?JSON.parse(t):null}},{key:"removeLocal",value:function(e){window.localStorage.removeItem(e)}}]),e}();function tn(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function nn(e,t){return nn=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},nn(e,t)}function rn(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&nn(e,t)}function an(e,t){if(t&&("object"===i(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return tn(e)}function on(e){return on=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},on(e)}h(en,"darkModeCK","darkMode"),h(en,"authCK","dexauth"),h(en,"pwKeyCK","sessionkey"),h(en,"popupsLK","popups"),h(en,"loggersLK","loggers"),h(en,"recordersLK","recorders"),h(en,"lastMarketLK","selectedMarket"),h(en,"depthZoomLK","depthZoom"),h(en,"lastMMMarketLK","mmMarket"),h(en,"optionsExpansionLK","mmOptsExpand"),h(en,"leftMarketDockLK","leftmarketdock"),h(en,"selectedAssetLK","selectedasset"),h(en,"notificationsLK","notifications"),h(en,"orderDisclaimerAckedLK","ordAck"),null===en.getCookie(en.darkModeCK)&&en.setCookie(en.darkModeCK,"1"),null===en.fetchLocal(en.popupsLK)&&en.storeLocal(en.popupsLK,"1"),null===en.fetchLocal(en.leftMarketDockLK)&&en.storeLocal(en.leftMarketDockLK,"1");var sn,cn,ln,un,hn,dn,pn,fn=function(){function e(){s(this,e)}return u(e,[{key:"unload",value:function(){}}]),e}();function mn(e,t,n){return vn.apply(this,arguments)}function vn(){return(vn=o(w().mark((function e(t,n,r){var a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,window.fetch(n,{method:t,headers:new window.Headers({"content-type":"application/json"}),body:r});case 3:if(200===(a=e.sent).status){e.next=6;break}throw a;case 6:return e.next=8,a.json();case 8:return(o=e.sent).requestSuccessful=!0,e.abrupt("return",o);case 13:return e.prev=13,e.t0=e.catch(0),e.t0.requestSuccessful=!1,e.next=18,e.t0.text();case 18:return e.t0.msg=e.sent,e.abrupt("return",e.t0);case 20:case"end":return e.stop()}}),e,null,[[0,13]])})))).apply(this,arguments)}function yn(e,t){return gn.apply(this,arguments)}function gn(){return(gn=o(w().mark((function e(t,n){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",mn("POST",t,JSON.stringify(n)));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function bn(e){return wn.apply(this,arguments)}function wn(){return(wn=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",mn("GET",t));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function kn(n){return function(t){if(Array.isArray(t))return e(t)}(n)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(n)||t(n)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function xn(){return un}function Cn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=on(e);if(t){var a=on(this).constructor;n=Reflect.construct(r,arguments,a)}else n=r.apply(this,arguments);return an(this,n)}}function Sn(e){var t=[e.booleanOptTmpl,e.rangeOptTmpl,e.orderOptTmpl];dn=t[0],pn=t[1],hn=t[2]}!function(e){e[e.walletErr=0]="walletErr",e[e.walletAuthErr=1]="walletAuthErr",e[e.walletBalanceErr=2]="walletBalanceErr",e[e.dupeDEXErr=3]="dupeDEXErr",e[e.assetSupportErr=4]="assetSupportErr",e[e.registerErr=5]="registerErr",e[e.signatureErr=6]="signatureErr",e[e.zeroFeeErr=7]="zeroFeeErr",e[e.feeMismatchErr=8]="feeMismatchErr",e[e.feeSendErr=9]="feeSendErr",e[e.passwordErr=10]="passwordErr",e[e.emptyHostErr=11]="emptyHostErr",e[e.connectionErr=12]="connectionErr",e[e.acctKeyErr=13]="acctKeyErr",e[e.unknownOrderErr=14]="unknownOrderErr",e[e.orderParamsErr=15]="orderParamsErr",e[e.dbErr=16]="dbErr",e[e.authErr=17]="authErr",e[e.connectWalletErr=18]="connectWalletErr",e[e.missingWalletErr=19]="missingWalletErr",e[e.encryptionErr=20]="encryptionErr",e[e.decodeErr=21]="decodeErr",e[e.accountVerificationErr=22]="accountVerificationErr",e[e.accountProofErr=23]="accountProofErr",e[e.parseKeyErr=24]="parseKeyErr",e[e.marketErr=25]="marketErr",e[e.addressParseErr=26]="addressParseErr",e[e.addrErr=27]="addrErr",e[e.fileReadErr=28]="fileReadErr",e[e.unknownDEXErr=29]="unknownDEXErr",e[e.accountRetrieveErr=30]="accountRetrieveErr",e[e.accountDisableErr=31]="accountDisableErr",e[e.suspendedAcctErr=32]="suspendedAcctErr",e[e.existenceCheckErr=33]="existenceCheckErr",e[e.createWalletErr=34]="createWalletErr",e[e.activeOrdersErr=35]="activeOrdersErr",e[e.newAddrErr=36]="newAddrErr"}(sn||(sn={})),function(e){e[e.Disconnected=0]="Disconnected",e[e.Connected=1]="Connected",e[e.InvalidCert=2]="InvalidCert"}(cn||(cn={})),function(e){e[e.WalletDefault=0]="WalletDefault",e[e.UserAdded=1]="UserAdded",e[e.Discovered=2]="Discovered"}(ln||(ln={}));var En=new Intl.NumberFormat(navigator.languages,{minimumSignificantDigits:3,maximumSignificantDigits:3}),An=u((function e(t,n,r){var a=this;s(this,e),h(this,"opt",void 0),h(this,"node",void 0),h(this,"tmpl",void 0),h(this,"on",void 0),this.opt=t;var o=this.node=hn.cloneNode(!0),i=this.tmpl=zt.parseTemplate(o);i.optName.textContent=t.displayname,i.tooltip.dataset.tooltip=t.description,n?i.chainIcon.src=zt.logoPath(n):zt.hide(i.chainIcon),this.on=!1,zt.bind(o,"click",(function(){a.on||(a.on=!0,o.classList.add("selected"),r.enable())})),zt.bind(i.toggle,"click",(function(e){a.on&&(e.stopPropagation(),a.on=!1,o.classList.remove("selected"),r.disable())}))})),Fn=function(e){rn(n,e);var t=Cn(n);function n(e,r,a,o){var i;if(s(this,n),h(tn(i=t.call(this,e,r,{enable:function(){return i.enable()},disable:function(){return i.disable()}})),"control",void 0),h(tn(i),"changed",void 0),h(tn(i),"dict",void 0),i.dict=a,i.changed=function(){return o()},void 0===e.boolean)throw Error("not a boolean opt");var c=e.boolean,l=i.control=dn.cloneNode(!0);return i.tmpl.controls.appendChild(l),zt.parseTemplate(l).reason.textContent=c.reason,i.on=void 0!==a[e.key]?a[e.key]:e.default,i.on&&i.node.classList.add("selected"),i}return u(n,[{key:"store",value:function(){this.on===this.opt.default?delete this.dict[this.opt.key]:this.dict[this.opt.key]=this.on,this.changed()}},{key:"enable",value:function(){this.store()}},{key:"disable",value:function(){this.store()}}]),n}(An),In=function(e){rn(n,e);var t=Cn(n);function n(e,r,a,o){var i;if(s(this,n),h(tn(i=t.call(this,e,r,{enable:function(){return i.enable()},disable:function(){return i.disable()}})),"handler",void 0),h(tn(i),"x",void 0),h(tn(i),"changed",void 0),h(tn(i),"dict",void 0),i.dict=a,i.changed=o,void 0===e.xyRange)throw Error("not an xy range opt");var c=e.xyRange,l=a[e.key];return i.on=void 0!==l,i.on?(i.node.classList.add("selected"),i.x=l):i.x=e.default,i.handler=new Rn(c,i.x,(function(e){i.x=e,i.dict[i.opt.key]=e}),(function(){i.changed()}),(function(){i.node.classList.add("selected")})),i.tmpl.controls.appendChild(i.handler.control),i}return u(n,[{key:"enable",value:function(){this.dict[this.opt.key]=this.x,this.changed()}},{key:"disable",value:function(){delete this.dict[this.opt.key],this.changed()}},{key:"setValue",value:function(e){this.handler.setValue(e),this.on=!0,this.node.classList.add("selected")}}]),n}(An),Rn=function(){function e(t,n,r,a,o,i){var c=this;s(this,e),h(this,"control",void 0),h(this,"cfg",void 0),h(this,"tmpl",void 0),h(this,"x",void 0),h(this,"scrollingX",void 0),h(this,"y",void 0),h(this,"r",void 0),h(this,"roundY",void 0),h(this,"updated",void 0),h(this,"changed",void 0),h(this,"selected",void 0),h(this,"setConfig",void 0);var l=this.control=pn.cloneNode(!0),u=this.tmpl=zt.parseTemplate(l);this.roundY=Boolean(i),this.cfg=t,this.changed=a,this.selected=o,this.updated=r;var d=u.slider,p=u.handle,f=t.end.x-t.start.x,m=t.end.y-t.start.y,v=function(e){return(e-t.start.x)/f},y=function(e){f=e.end.x-e.start.x,m=e.end.y-e.start.y,t=c.cfg=e,u.rangeLblStart.textContent=t.start.label,u.rangeLblEnd.textContent=t.end.label,u.xUnit.textContent=t.xUnit,u.yUnit.textContent=t.yUnit,c.y=c.r*m+t.start.y,c.r=(c.y-t.start.y)/m,c.scrollingX=c.r*f+t.start.x};y(t),this.setConfig=function(e){y(e),c.accept(c.scrollingX)},this.r=v(n),this.scrollingX=this.x=n,this.y=this.r*m+t.start.y;var g=function e(n){if("change"===n.type||n.target!==u.xInput){var r=u.xInput.value;if(r){var a=parseFloat(r);isNaN(a)||(c.scrollingX=Dn(a,t.start.x,t.end.x),c.r=v(c.scrollingX),c.y=c.r*m+t.start.y,c.accept(c.scrollingX))}zt.hide(u.xInput),zt.show(u.x),zt.unbind(document,"click",e),c.changed()}};zt.bind(u.x,"click",(function(e){zt.hide(u.x),zt.show(u.xInput),u.xInput.focus(),u.xInput.value=En.format(c.scrollingX),zt.bind(document,"click",g),e.stopPropagation()})),zt.bind(u.xInput,"change",g);var b=function e(n){if("change"===n.type||n.target!==u.yInput){var r=u.yInput.value;if(r){var a=parseFloat(r);isNaN(a)||(c.y=Dn(a,t.start.y,t.end.y),c.r=(c.y-t.start.y)/m,c.scrollingX=t.start.x+c.r*f,c.accept(c.scrollingX))}zt.hide(u.yInput),zt.show(u.y),zt.unbind(document,"click",e),c.changed()}};zt.bind(u.y,"click",(function(e){zt.hide(u.y),zt.show(u.yInput),u.yInput.focus(),u.yInput.value=En.format(c.y),zt.bind(document,"click",b),e.stopPropagation()})),zt.bind(u.yInput,"change",b),zt.bind(p,"mousedown",(function(e){if(0===e.button){e.preventDefault(),c.selected();var n=e.pageX,r=d.clientWidth-p.offsetWidth,a=v(c.scrollingX)*r,o=function(e){e.preventDefault(),c.r=function(e){return Math.max(Math.min(a+(e.pageX-n),r),0)}(e)/r,c.scrollingX=c.r*f+t.start.x,c.y=c.r*m+t.start.y,c.accept(c.scrollingX)};zt.bind(document,"mousemove",o),zt.bind(document,"mouseup",(function e(t){o(t),zt.unbind(document,"mousemove",o),zt.unbind(document,"mouseup",e),c.changed()}))}})),this.accept(this.scrollingX,!0)}return u(e,[{key:"accept",value:function(e,t){var n=this.tmpl;this.roundY&&(this.y=Math.round(this.y)),n.x.textContent=En.format(e),n.y.textContent=En.format(this.y),this.roundY&&(n.y.textContent="".concat(this.y)),n.handle.style.left="calc(".concat(100*this.r,"% - ").concat(14*this.r,"px)"),this.x=e,this.scrollingX=e,t||this.updated(e,this.y)}},{key:"setValue",value:function(e){var t=this.cfg;this.r=(e-t.start.x)/(t.end.x-t.start.x),this.y=t.start.y+this.r*(t.end.y-t.start.y),this.accept(e,!0)}}]),e}(),Dn=function(e,t,n){return en?n:e};function On(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(e.matches);try{for(n.s();!(t=n.n()).done;)if(t.value.active)return!0}catch(e){n.e(e)}finally{n.f()}return!1}function Wn(e){if(!e.id)return It(te);var t=Mn(e);switch(e.status){case 0:return It($);case 1:return It(ee);case 2:return e.cancelling?It(K):t?"".concat(It(H),"/").concat(It(ne)):It(H);case 3:return t?It(ne):0===e.filled&&3!==e.type?It(re):It(j);case 4:return t?"".concat(It(ae),"/").concat(It(ne)):It(ae);case 5:return t?"".concat(It(oe),"/").concat(It(ne)):It(oe)}return It($)}function qn(e){if(!e.matches)return 0;var t=Bn(e)?function(e){return e.qty*e.rate/Tn}:function(e){return e.qty};return e.matches.reduce((function(e,n){return n.isCancel?e:e+t(n)}),0)}function Nn(e){if(!e.matches)return 0;var t=Bn(e)?function(e){return e.qty*e.rate/Tn}:function(e){return e.qty};return e.matches.reduce((function(e,n){return n.isCancel?e:0===n.side&&n.status>=3||1===n.side&&n.status>=4?e+t(n):e}),0)}function Un(e,t){return e*t/Tn}function _n(e){return It(tt,{status:It(e)})}function zn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=on(e);if(t){var a=on(this).constructor;n=Reflect.construct(r,arguments,a)}else n=r.apply(this,arguments);return an(this,n)}}function Vn(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return jn(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?jn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function jn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=c&&i=i&&s=h&&v.push([C.rate,k]),!C.epoch)for(w+=C.qty,m.push([C.rate,w]),b.buyBase+=C.qty,b.buyQuote+=C.qty*C.rate;d.length&&ur(d[0].rate,C.rate);){var S=d.shift();S&&f.push({rate:S.rate,qty:C.epoch?k:w,sell:C.sell,active:S.active})}}var E=m.length?or(m)[1]:0;m.push([h,E]);var A=v.length?or(v)[1]:0;v.push([h,A]),k=w=0;for(var F=0;Fu||e=P},N=e.theme.sellLine;Pthis.data.candles.length)return;this.numToShow=this.zoomLevels[t+1]}this.draw()}},{key:"render",value:function(){var e=this,t=this.data;if(t&&this.visible&&0!==this.canvas.width){var n=t.ms,r=this.mousePos,a=t.candles||[],o=Math.min(this.numToShow,a.length),s=a.slice(a.length-o);if(this.clear(),0!==o){var i,c=function(e){return dr(e.endStamp,n)},l=function(e){return c(e)+n},u=function(e){return c(e)+.2*n},h=.6*n,d=s[0],p=s[o-1],f=[d.highRate,d.lowRate,d.matchVolume],m=f[0],v=f[1],y=f[2],g=Vn(s);try{for(g.s();!(i=g.n()).done;){var b=i.value;b.highRate>m&&(m=b.highRate),b.lowRatey&&(y=b.matchVolume)}}catch(e){g.e(e)}finally{g.f()}var w=this.market.ratestep,k=new tr(c(d),l(p),v,m);v===m&&(k.y.min-=w,k.y.max+=w),this.dataExtents=k;var x=this.rateConversionFactor;this.doYLabels(this.candleRegion,w,this.market.quotesymbol,(function(e){return lr(e/x)})),this.candleRegion.extents.x.min=this.yRegion.extents.x.max,this.volumeRegion.extents.x.min=this.yRegion.extents.x.max;var C=function(e,t,n,r){var a=e[0],o=e[e.length-1],s=dr(a.endStamp,t),i=dr(o.endStamp,t)+t,c=i-s,l=Math.min(e.length,n/100),u=dr(c/l,t);if(0===u)return console.error("zero tick",t,c,l),{lbls:[]};var h=s,d=(new Date).getTimezoneOffset(),p=function(e){return(e-=6e4*d)-e%864e5},f=p(s),m=0;p(a.endStamp)===p(o.endStamp)&&(f=0);var v,y=[];for(v=t<864e5?function(e,t){return p(t)!==f?"".concat(ar[e.getMonth()]).concat(e.getDate()," ").concat(e.getHours(),":").concat(String(e.getMinutes()).padStart(2,"0")):"".concat(e.getHours(),":").concat(String(e.getMinutes()).padStart(2,"0"))}:function(e){var t=e.getFullYear();return t!==m?"".concat(ar[e.getMonth()]).concat(e.getDate()," '").concat(String(t).slice(2,4)):"".concat(ar[e.getMonth()]).concat(e.getDate())};h<=i;){var g=new Date(h);y.push({val:h,txt:v(g,h)}),f=p(h),m=g.getFullYear(),h+=u}return{lbls:y}}(s,n,this.plotRegion.width());this.plotXLabels(C,c(d),l(p),[]),this.drawFrame();var S=null;if(r&&(this.plotRegion.plot(new tr(k.x.min,k.x.max,0,1),(function(t,a){var o,i=dr(a.unx(r.x),n),l=Vn(s);try{for(l.s();!(o=l.n()).done;){var u=o.value;if(c(u)===i){S=u,t.fillStyle=e.theme.gridLines,t.fillRect(a.x(c(u)),a.y(0),a.w(n),a.h(1));break}}}catch(e){l.e(e)}finally{l.f()}})),S)){var E=this.xRegion.extents.y;this.xRegion.plot(new tr(k.x.min,k.x.max,E.min,E.max),(function(t,n){if(S){e.applyLabelStyle();var r="".concat(new Date(c(S)).toLocaleString()," - ").concat(new Date(l(S)).toLocaleString()),a=t.measureText(r).width+50,o=n.x((c(S)+l(S))/2),s=o-a/2,i=e.xRegion.extents.x;si.max&&(s=i.max-a),o=s+a/2;var u=E.min+(e.xRegion.height()-16)/2;t.fillStyle=e.theme.legendFill,t.strokeStyle=e.theme.gridBorder;var h=[s-25,u-2,a+50,20];t.fillRect.apply(t,h),t.strokeRect.apply(t,h),e.applyLabelStyle(),t.fillText(r,o,e.xRegion.extents.midY,a)}}))}var A=new tr(c(d),l(p),0,y);this.volumeRegion.plot(A,(function(t,n){t.fillStyle=e.theme.gridBorder;var r,a=Vn(s);try{for(a.s();!(r=a.n()).done;){var o=r.value;t.fillRect(n.x(u(o)),n.y(0),n.w(h),n.h(o.matchVolume))}}catch(e){a.e(e)}finally{a.f()}})),this.candleRegion.plot(k,(function(t,n){t.lineWidth=1;var r,a=Vn(s);try{for(a.s();!(r=a.n()).done;){var o=r.value,i=o.startRate>o.endRate,c=[n.x(u(o)),n.y(o.startRate),n.w(h),n.h(o.endRate-o.startRate)],l=c[0],d=c[1],p=c[2],f=c[3],m=[n.y(o.highRate),n.y(o.lowRate),p/2+l],v=m[0],y=m[1],g=m[2];t.strokeStyle=i?e.theme.sellLine:e.theme.buyLine,t.fillStyle=i?e.theme.sellFill:e.theme.buyFill,t.beginPath(),t.moveTo(g,v),t.lineTo(g,y),t.stroke(),t.fillRect(l,d,p,f),t.strokeRect(l,d,p,f)}}catch(e){a.e(e)}finally{a.f()}})),this.reporters.mouse(S)}}else this.renderScheduled=!0}},{key:"setCandles",value:function(e,t,n,r){if(this.data=e,e.candles){this.market=t;var a=[r.conventional.conversionFactor,n.conventional.conversionFactor],o=a[0],s=a[1];this.rateConversionFactor=Tn*o/s;var i=25;this.zoomLevels=[];for(var c=Math.max(e.candles.length,1e3);i150&&(a=150),o>100&&(o=100);var s=(n-a)/2,i=(r-o)/2;if(e.message){this.fontSize=ir(.15*o,10,14),this.applyLabelStyle(this.fontSize);var c=.5*this.fontSize,l=this.fontSize/2+c;i-=l,this.msgRegion=new nr(this.ctx,new tr(0,n,i+o,i+o+2*l))}this.region=new nr(this.ctx,new tr(s,s+a,i,i+o))}},{key:"drawValues",value:function(e){var t=this;if(this.region){this.clear();var n=function(e){return"hsl(".concat(e,", 35%, 50%)")},r=this.region,a=this.msgRegion,o=this.canvas,s=o.width,i=o.height,c=this.opts,l=c.backgroundColor,u=c.message,h=this.colorShift,d=this.ctx;l&&(d.fillStyle=!0===l?window.getComputedStyle(document.body,null).getPropertyValue("background-color"):l,d.fillRect(0,0,s,i)),r.plot(new tr(0,1,-1,1),(function(t,r){t.lineWidth=4,t.lineCap="round";var a=h+(new Date).getTime()%2e3/2e3*360,o=t.createLinearGradient(r.x(0),0,r.x(1),0);o.addColorStop(0,n(a)),t.strokeStyle=o,t.beginPath(),t.moveTo(r.x(0),r.y(e[0]));for(var s=1;sn.x.min&&tn.y.min}},{key:"translator",value:function(e){var t=this.extents,n=e.x.min,r=e.y.min,a=e.yRange,o=e.xRange,s=t.x.min,i=t.x.max-s,c=t.y.max,l=c-t.y.min,u=i/o,h=l/a;return{x:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return(e-n)*u+s})),y:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return c-(e-r)*h})),unx:function(e){return(e-s)/u+n},uny:function(e){return r-(e-c)/h},w:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return e/o*i})),h:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return-e/a*l})),dataCoords:function(){}}}},{key:"clear",value:function(){var e=this.extents;this.context.clearRect(e.x.min,e.y.min,e.xRange,e.yRange)}},{key:"plot",value:function(e,t,n){var r=this.context,a=this.extents;r.save(),n||(r.beginPath(),r.rect(a.x.min,a.y.min,a.xRange,a.yRange),r.clip());var o=this.translator(e),s=e.yRange,i=a.xRange/e.xRange,c=a.yRange/s,l=e.x.min,u=e.y.min,h=a.x.min+l-l*i,d=-a.y.min-(s-u)*c;o.dataCoords=function(e){r.save(),r.transform(1,0,0,-1,-l,u),r.transform(i,0,0,c,h,d),e(),r.restore()},t(this.context,o),r.restore()}}]),e}();function rr(e,t,n,r,a,o,s,i){i=i||lr;var c=t/a,l=r-n;if(c<1||l<=0)return{lbls:[]};for(var u=l/c,h=u+o-u%o,d=n+h-n%h,p=Math.max(Math.abs(r),Math.abs(n)),f=Math.round(Math.log10(p/h))+2,m=[],v=0;dv&&(v=g),{widest:v,lbls:m}}var ar=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function or(e){return e[e.length-1]}function sr(e,t,n,r,a,o){e.beginPath(),e.moveTo(t,n),e.lineTo(r,a),o||e.stroke()}function ir(e,t,n){return en?n:e}var cr={minimumSignificantDigits:4,maximumSignificantDigits:5};function lr(e){return e.toLocaleString("en-us",cr)}function ur(e,t){return hr(e,t,1e-8)}function hr(e,t,n){return Math.abs(e-t)=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function fr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1)){e.next=34;break}zt.show(r),u=pr(l),e.prev=16,d=w().mark((function e(){var t,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=h.value,(a=n.walletTabTmpl.cloneNode(!0)).dataset.tooltip=t.description,a.textContent=t.tab,r.appendChild(a),zt.bind(a,"click",(function(){var e,n=pr(zt.kids(r));try{for(n.s();!(e=n.n()).done;)e.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}a.classList.add("selected"),p.update(t)}));case 6:case"end":return e.stop()}}),e)})),u.s();case 19:if((h=u.n()).done){e.next=23;break}return e.delegateYield(d(),"t0",21);case 21:e.next=19;break;case 23:e.next=28;break;case 25:e.prev=25,e.t1=e.catch(16),u.e(e.t1);case 28:return e.prev=28,u.f(),e.finish(28);case 31:xn().bindTooltips(r),r.firstChild.classList.add("selected");case 34:return e.next=36,this.update(this.current.selectedDef);case 36:if(!s.walletCreationPending){e.next=39;break}return e.next=39,this.runParentSync();case 39:case"end":return e.stop()}}),e,this,[[16,25,28,31]])}))),function(e){return a.apply(this,arguments)})},{key:"parseAsset",value:function(e){if(this.current&&this.current.asset.id===e)return!1;var t=xn().assets[e],n=t.token;if(!n){if(!t.info)throw Error("this non-token asset has no wallet info!");return this.current={asset:t,winfo:t.info,selectedDef:t.info.availablewallets[0]},!0}var r=xn().user.assets[n.parentID];if(r.wallet)return this.current={asset:t,winfo:n,selectedDef:n.definition},!0;if(!r.info)throw Error("this parent has no wallet info!");return this.current={asset:t,parentAsset:r,winfo:n,selectedDef:r.info.availablewallets[0]},!0}},{key:"update",value:(r=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u,h,d,p,f,m,v,y,g,b;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.page,this.current.selectedDef=t,r=en.passwordIsCached()||this.pwCache&&this.pwCache.pw,zt.hide(n.auth,n.oneBttnBox,n.newWalletPassBox),(a=t.configopts||[]).map((function(e){return e.isBirthdayConfig&&xn().seedGenTime>0&&(e.default=Tr(new Date)),e})),o=!1,s=pr(a),e.prev=8,s.s();case 10:if((i=s.n()).done){e.next=17;break}if(!i.value.required){e.next=15;break}return o=!0,e.abrupt("break",17);case 15:e.next=10;break;case 17:e.next=22;break;case 19:e.prev=19,e.t0=e.catch(8),s.e(e.t0);case 22:return e.prev=22,s.f(),e.finish(22);case 25:if(c=!o&&(t.seeded||Boolean(this.current.asset.token)),r&&c?zt.show(n.oneBttnBox):c?(zt.show(n.auth),n.newWalletPass.value="",n.submitAdd.textContent=It(he)):(zt.show(n.auth),t.noauth||zt.show(n.newWalletPassBox),n.submitAdd.textContent=It(ue)),l=this.current,u=l.asset,h=l.parentAsset,d=l.winfo,h){p=JSON.parse(JSON.stringify(a)),f=pr(p);try{for(f.s();!(m=f.n()).done;)m.value.regAsset=h.id}catch(e){f.e(e)}finally{f.f()}if((v=d.definition.configopts||[]).length>0){y=JSON.parse(JSON.stringify(v)),g=pr(y);try{for(g.s();!(b=g.n()).done;)b.value.regAsset=u.id}catch(e){g.e(e)}finally{g.f()}p.push.apply(p,kn(y))}this.subform.update(p,!1)}else this.subform.update(a,!1);return this.subform.dynamicOpts.children.length||this.subform.defaultSettings.children.length?zt.show(n.walletSettingsHeader):zt.hide(n.walletSettingsHeader),t.seeded||Boolean(this.current.asset.token)?zt.hide(this.subform.fileSelector):zt.show(this.subform.fileSelector),this.refresh(),e.next=34,this.loadDefaults();case 34:case"end":return e.stop()}}),e,this,[[8,19,22,25]])}))),function(e){return r.apply(this,arguments)})},{key:"setError",value:(n=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.page.newWalletErr.textContent=t,zt.show(this.page.newWalletErr);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"loadDefaults",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.current,n=t.asset,r=t.parentAsset,(a=t.selectedDef).configpath){e.next=3;break}return e.abrupt("return");case 3:if(o=n.id,!r){e.next=8;break}if(!a.seeded){e.next=7;break}return e.abrupt("return");case 7:o=r.id;case 8:return s=xn().loading(this.form),e.next=11,yn("/api/defaultwalletcfg",{assetID:o,type:a.type});case 11:if(i=e.sent,s(),xn().checkResponse(i)){e.next=16;break}return this.setError(i.msg),e.abrupt("return");case 16:this.subform.setLoadedConfig(i.config);case 17:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),vr=0,yr=function(){function e(t,n){var r=this;s(this,e),h(this,"form",void 0),h(this,"configElements",void 0),h(this,"configOpts",void 0),h(this,"sectionize",void 0),h(this,"allSettings",void 0),h(this,"dynamicOpts",void 0),h(this,"textInputTmpl",void 0),h(this,"dateInputTmpl",void 0),h(this,"checkboxTmpl",void 0),h(this,"repeatableTmpl",void 0),h(this,"fileSelector",void 0),h(this,"fileInput",void 0),h(this,"errMsg",void 0),h(this,"showOther",void 0),h(this,"showIcon",void 0),h(this,"hideIcon",void 0),h(this,"showHideMsg",void 0),h(this,"otherSettings",void 0),h(this,"loadedSettingsMsg",void 0),h(this,"loadedSettings",void 0),h(this,"defaultSettingsMsg",void 0),h(this,"defaultSettings",void 0),h(this,"assetHasActiveOrders",void 0),this.form=t,this.configElements=[],this.configOpts=[],this.sectionize=n,this.allSettings=zt.tmplElement(t,"allSettings"),this.dynamicOpts=zt.tmplElement(t,"dynamicOpts"),this.textInputTmpl=zt.tmplElement(t,"textInput"),this.textInputTmpl.remove(),this.dateInputTmpl=zt.tmplElement(t,"dateInput"),this.dateInputTmpl.remove(),this.checkboxTmpl=zt.tmplElement(t,"checkbox"),this.checkboxTmpl.remove(),this.repeatableTmpl=zt.tmplElement(t,"repeatableInput"),this.repeatableTmpl.remove(),this.fileSelector=zt.tmplElement(t,"fileSelector"),this.fileInput=zt.tmplElement(t,"fileInput"),this.errMsg=zt.tmplElement(t,"errMsg"),this.showOther=zt.tmplElement(t,"showOther"),this.showIcon=zt.tmplElement(t,"showIcon"),this.hideIcon=zt.tmplElement(t,"hideIcon"),this.showHideMsg=zt.tmplElement(t,"showHideMsg"),this.otherSettings=zt.tmplElement(t,"otherSettings"),this.loadedSettingsMsg=zt.tmplElement(t,"loadedSettingsMsg"),this.loadedSettings=zt.tmplElement(t,"loadedSettings"),this.defaultSettingsMsg=zt.tmplElement(t,"defaultSettingsMsg"),this.defaultSettings=zt.tmplElement(t,"defaultSettings"),n||zt.hide(this.showOther),zt.bind(this.fileSelector,"click",(function(){return r.fileInput.click()})),zt.bind(this.fileInput,"change",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",r.fileInputChanged());case 1:case"end":return e.stop()}}),e)})))),zt.bind(this.showOther,"click",(function(){r.setOtherSettingsViz(r.hideIcon.classList.contains("d-hide"))}))}var t;return u(e,[{key:"fileInputChanged",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(zt.hide(this.errMsg),this.fileInput.value){e.next=3;break}return e.abrupt("return");case 3:if((n=this.fileInput.files)&&0!==n.length){e.next=6;break}return e.abrupt("return");case 6:return r=xn().loading(this.form),e.next=9,n[0].text();case 9:if(a=e.sent){e.next=12;break}return e.abrupt("return");case 12:return e.next=14,yn("/api/parseconfig",{configtext:a});case 14:if(o=e.sent,r(),xn().checkResponse(o)){e.next=20;break}return this.errMsg.textContent=o.msg,zt.show(this.errMsg),e.abrupt("return");case 20:if(0!==Object.keys(o.map).length){e.next=22;break}return e.abrupt("return");case 22:(t=this.dynamicOpts).append.apply(t,kn(this.setConfig(o.map))),this.reorder(this.dynamicOpts),s=[this.loadedSettings.children.length,this.defaultSettings.children.length],c=s[1],0===(i=s[0])&&zt.hide(this.loadedSettings,this.loadedSettingsMsg),0===c&&zt.hide(this.defaultSettings,this.defaultSettingsMsg),i+c===0&&zt.hide(this.showOther,this.otherSettings);case 28:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"addOpt",value:function(e,t,n,r){var a,o=this,s="wcfg-"+t.key+(r?String(r):"");t.isboolean?a=this.checkboxTmpl.cloneNode(!0):t.isdate?a=this.dateInputTmpl.cloneNode(!0):t.repeatable?((a=this.repeatableTmpl.cloneNode(!0)).classList.add("repeatable"),zt.bind(zt.tmplElement(a,"add"),"click",(function(){vr++,o.addOpt(e,t,a,vr)}))):a=this.textInputTmpl.cloneNode(!0),this.configElements.push([t,a]);var i=a.querySelector("input");i.dataset.configKey=t.key,i.id=s;var c=zt.safeSelector(a,"label");if(c.htmlFor=s,c.prepend(t.displayname),void 0!==t.regAsset){var l=new window.Image(15,15);l.src=zt.logoPathFromID(t.regAsset||-1),c.prepend(l)}if(n?n.after(a):e.appendChild(a),t.noecho&&(i.type="password",i.autocomplete="off"),t.description&&(c.dataset.tooltip=t.description),t.isboolean)i.checked=t.default;else if(t.isdate){var u=function(e){return e?Pr("now"===e?new Date:new Date(1e3*e)):""};i.max=u(t.max),i.min=u(t.min);var h=t.default?new Date(1e3*t.default):new Date;i.value=Pr(h)}else i.value=null!==t.default?t.default:"";return i.disabled=Boolean(t.disablewhenactive&&this.assetHasActiveOrders),a}},{key:"update",value:function(e,t){if(this.assetHasActiveOrders=t,this.configElements=[],this.configOpts=e||[],zt.empty(this.dynamicOpts,this.defaultSettings,this.loadedSettings),0===this.configOpts.length)return zt.hide(this.form);zt.show(this.form),this.setOtherSettingsViz(!1),zt.hide(this.loadedSettingsMsg,this.loadedSettings,this.defaultSettingsMsg,this.defaultSettings,this.errMsg);var n,r=[],a=pr(this.configOpts);try{for(a.s();!(n=a.n()).done;){var o=n.value;this.sectionize&&null!==o.default?r.push(o):this.addOpt(this.dynamicOpts,o)}}catch(e){a.e(e)}finally{a.f()}if(r.length){var s,i=pr(r);try{for(i.s();!(s=i.n()).done;){var c=s.value;this.addOpt(this.defaultSettings,c)}}catch(e){i.e(e)}finally{i.f()}zt.show(this.showOther,this.defaultSettingsMsg,this.defaultSettings)}else zt.hide(this.showOther);xn().bindTooltips(this.allSettings),this.dynamicOpts.children.length?zt.show(this.dynamicOpts):zt.hide(this.dynamicOpts)}},{key:"setOtherSettingsViz",value:function(e){if(e)return zt.hide(this.showIcon),zt.show(this.hideIcon,this.otherSettings),void(this.showHideMsg.textContent=It(D));zt.hide(this.hideIcon,this.otherSettings),zt.show(this.showIcon),this.showHideMsg.textContent=It(O)}},{key:"setConfig",value:function(e){for(var t,n=[],a={},o=[],s=0,i=kn(this.configElements);s=0&&this.configElements.splice(k,1)}return n}},{key:"setLoadedConfig",value:function(e){var t,n=this.setConfig(e);this.sectionize&&0!==n.length&&((t=this.loadedSettings).append.apply(t,kn(n)),this.reorder(this.loadedSettings),zt.show(this.loadedSettings,this.loadedSettingsMsg),0===this.defaultSettings.children.length&&zt.hide(this.defaultSettings,this.defaultSettingsMsg))}},{key:"map",value:function(e){var t,n={},a=pr(this.configElements);try{for(a.s();!(t=a.n()).done;){var o=r(t.value,2),s=o[0],i=o[1],c=zt.safeSelector(i,"input");if(void 0===s.regAsset||s.regAsset===e)if(s.isboolean&&s.key)n[s.key]=c.checked?"1":"0";else if(s.isdate&&s.key){var l=c.min?Tr(new Date(c.min+"T00:00")):Number.MIN_SAFE_INTEGER,u=c.max?Tr(new Date(c.max+"T00:00")):Number.MAX_SAFE_INTEGER,h=c.value?Tr(new Date(c.value+"T00:00")):0;hu&&(h=u),n[s.key]=""+h}else c.value&&(s.repeatable&&n[s.key]?n[s.key]+=s.repeatable+c.value:n[s.key]=c.value)}}catch(e){a.e(e)}finally{a.f()}return n}},{key:"reorder",value:function(e){var t=this,n={};e.querySelectorAll("input").forEach((function(e){var a=e.dataset.configKey;if(a){var o,s=[],i=pr(t.configElements);try{for(i.s();!(o=i.n()).done;){var c=r(o.value,2),l=c[0],u=c[1];l.key===a&&s.push(u)}}catch(e){i.e(e)}finally{i.f()}n[a]=s}}));var a,o=pr(this.configOpts);try{for(o.s();!(a=o.n()).done;){var s,i=a.value,c=pr(n[i.key]||[]);try{for(c.s();!(s=c.n()).done;){var l=s.value;e.append(l)}}catch(e){c.e(e)}finally{c.f()}}}catch(e){o.e(e)}finally{o.f()}}}]),e}(),gr=function(){function e(t,n,r,a){var o=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"page",void 0),h(this,"xc",void 0),h(this,"certFile",void 0),h(this,"bondAssetID",void 0),h(this,"pwCache",void 0),this.form=t,this.success=n,this.page=zt.parseTemplate(t),this.certFile="",this.pwCache=a,zt.bind(this.page.goBack,"click",(function(){return r()})),zt.bind(this.page.bondStrengthField,"input",(function(){var e=xn().assets[o.bondAssetID];if(e){var t=e.unitInfo,n=o.xc.bondAssets[e.symbol];o.page.bondAmt.textContent=zt.formatCoinValue(o.totalBondAmount(n.amount),t)}})),Or(t,this.page.submit,(function(){return o.submitForm()}))}var t,n;return u(e,[{key:"setExchange",value:function(e,t){this.xc=e,this.certFile=t;var n=this.page;en.passwordIsCached()||this.pwCache&&this.pwCache.pw?zt.hide(n.passBox):zt.show(n.passBox),n.host.textContent=e.host}},{key:"setAsset",value:function(e){var t=xn().assets[e],n=t.unitInfo;this.bondAssetID=t.id;var r=this.page,a=this.xc.bondAssets[t.symbol];r.bondAmt.textContent=zt.formatCoinValue(this.totalBondAmount(a.amount),n),r.bondUnit.textContent=n.conventional.unit.toUpperCase(),r.logo.src=zt.logoPath(t.symbol)}},{key:"totalBondAmount",value:function(e){var t;return+(null!==(t=this.page.bondStrengthField.value)&&void 0!==t?t:1)*e}},{key:"animate",value:(n=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(400,(function(e){t.style.transform="scale(".concat(e,")"),t.style.opacity=String(Math.pow(e,4));var n="".concat(500*(1-e),"px");t.style.top=n,t.style.left=n}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"submitForm",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if((t=this.page).submit.classList.contains("selected")){e.next=3;break}return e.abrupt("return");case 3:if(n=xn().assets[this.bondAssetID]){e.next=8;break}return t.regErr.innerText=It(yt),zt.show(t.regErr),e.abrupt("return");case 8:return zt.hide(t.regErr),r=this.xc.bondAssets[n.wallet.symbol],e.next=12,this.certFile;case 12:return a=e.sent,o=this.xc.host,s=t.appPass.value||(this.pwCache?this.pwCache.pw:""),i={addr:o,cert:a,pass:s,bond:this.totalBondAmount(r.amount),asset:r.id},t.appPass.value="",c=xn().loading(this.form),e.next=20,yn("/api/postbond",i);case 20:if(l=e.sent,c(),xn().checkResponse(l)){e.next=26;break}return t.regErr.textContent=l.msg,zt.show(t.regErr),e.abrupt("return");case 26:this.success();case 27:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),br=function(){function e(t,n){var r=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"xc",void 0),h(this,"page",void 0),h(this,"assetTmpls",void 0),this.form=t,this.success=n,this.page=zt.parseTemplate(t),zt.cleanTemplates(this.page.marketTmpl,this.page.assetTmpl),xn().registerNoteFeeder({createwallet:function(e){"QueuedCreationSuccess"===e.topic&&r.walletCreated(e.assetID)}})}var t;return u(e,[{key:"setExchange",value:function(e){var t=this;this.xc=e,this.assetTmpls={};var n=this.page;zt.empty(n.assets,n.allMarkets);for(var a=function(e){return e.conventional.conversionFactor},o=function(t,r){var o=n.marketTmpl.cloneNode(!0),s=zt.parseTemplate(o),i=e.assets[t.baseid],c=xn().unitInfo(t.baseid,e),l=e.assets[t.quoteid],u=xn().unitInfo(t.quoteid,e);if(0===a(c)||0===a(u))return null;if(void 0!==r){var h=r===t.baseid,d=e.assets[h?t.quoteid:t.baseid].symbol;s.logo.src=zt.logoPath(d)}else{var p=s.logo.cloneNode(!0);s.logo.src=zt.logoPath(i.symbol),p.src=zt.logoPath(l.symbol);var f=s.logo.parentNode;f&&f.insertBefore(p,s.logo.nextSibling)}var m=i.symbol.toUpperCase(),v=l.symbol.toUpperCase();if(s.baseName.replaceWith(zt.symbolize(m)),s.quoteName.replaceWith(zt.symbolize(v)),s.lotSize.textContent=zt.formatCoinValue(t.lotsize,c),s.lotSizeSymbol.replaceWith(zt.symbolize(m)),t.spot){zt.show(s.quoteLotSize);var y=a(u)/a(c),g=t.lotsize*t.spot.rate/Tn*y,b=zt.formatCoinValue(g,u);s.quoteLotSize.textContent="(~".concat(b," ").concat(v,")")}return o},s=function(){var a=r(c[i],2),s=a[0],l=a[1],u=xn().assets[l.id];if(!u)return"continue";var h=u.unitInfo,d=n.assetTmpl.cloneNode(!0);zt.bind(d,"click",(function(){t.success(l.id)}));var p=t.assetTmpls[l.id]=zt.parseTemplate(d);n.assets.appendChild(d),p.logo.src=zt.logoPath(s);var f=zt.formatCoinValue(l.amount,h);p.feeAmt.textContent=String(f),p.feeSymbol.replaceWith(zt.symbolize(u.symbol)),p.confs.textContent=String(l.confs),wr(p.ready,u);for(var m=0,v=0,y=Object.values(e.markets);v0)if(r.totalForBond.textContent=zt.formatCoinValue(2*s.amount+t,a.unitInfo),zt.hide(r.sendEnough),zt.hide(r.txFeeBox,r.sendEnoughForToken,r.txFeeBalanceBox),zt.hide(r.sendEnoughWithEst),a.token){zt.show(r.txFeeBox,r.sendEnoughForToken,r.txFeeBalanceBox);var l=xn().assets[a.token.parentID];r.txFee.textContent=zt.formatCoinValue(t,l.unitInfo),r.parentFees.textContent=zt.formatCoinValue(t,l.unitInfo),r.tokenFees.textContent=zt.formatCoinValue(s.amount,a.unitInfo),i(r.txFeeUnit,l.symbol),i(r.parentUnit,l.symbol),i(r.parentBalUnit,l.symbol),r.parentBal.textContent=l.wallet?zt.formatCoinValue(l.wallet.balance.available,l.unitInfo):"0"}else zt.show(r.sendEnoughWithEst);else zt.show(r.sendEnough);zt.show(e.synced?r.syncCheck:e.syncProgress>=1?r.syncSpinner:r.syncUncheck),zt.show(e.balance.available>=2*s.amount+t?r.balCheck:r.balUncheck),r.progress.textContent=(100*e.syncProgress).toFixed(1),e.synced&&(this.progressed=!0),this.reportBalance(e.assetID)}},{key:"reportWalletState",value:function(e){this.progressed&&this.funded||(e.assetID===this.assetID&&this.reportProgress(e.synced,e.syncProgress),this.reportBalance(e.assetID))}},{key:"reportBalance",value:function(e){if(!this.funded&&-1!==this.assetID&&(e===this.assetID||e===this.parentID)){var t=this.page,n=xn().assets[this.assetID],r=n.wallet.balance.available;if(t.balance.textContent=zt.formatCoinValue(r,n.unitInfo),n.token){var a=xn().assets[n.token.parentID],o=a.wallet.balance.available;if(t.parentBal.textContent=zt.formatCoinValue(o,a.unitInfo),o=.999)return zt.hide(n.syncRemaining),zt.show(n.syncFinishingUp),zt.show(n.syncRemainBox),void(n.syncFinishingUp.textContent=It(bt));var r=this.progressCache;if(r.push({stamp:(new Date).getTime(),progress:t}),!(r.length<2)){for(;r.length>20;)r.shift();var a=[r[0],r[r.length-1]],o=a[0],s=a[1],i=s.progress-o.progress;if(0!==i){zt.hide(n.syncFinishingUp),zt.show(n.syncRemaining),zt.show(n.syncRemainBox);var c=i/(s.stamp-o.stamp),l=(1-s.progress)/c;n.syncRemain.textContent=zt.formatDuration(l)}}}}]),e}(),xr=function(){function e(t,n,r){var a=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"page",void 0),h(this,"currentAsset",void 0),this.page=zt.idDescendants(t),this.form=t,this.pwCache=r||null,this.success=n,Or(t,this.page.submitUnlock,(function(){return a.submit()}))}var t;return u(e,[{key:"refresh",value:function(e){var t=this.page;this.currentAsset=e,t.uwAssetLogo.src=zt.logoPath(e.symbol),t.uwAssetName.textContent=e.name,t.uwAppPass.value="",t.unlockErr.textContent="",zt.hide(t.unlockErr),en.passwordIsCached()||this.pwCache&&this.pwCache.pw?zt.hide(t.uwAppPassBox):zt.show(t.uwAppPassBox)}},{key:"setError",value:function(e){this.page.unlockErr.textContent=e,zt.show(this.page.unlockErr)}},{key:"showErrorOnly",value:function(e){this.setError(e),zt.hide(this.page.uwAppPassBox),zt.hide(this.page.submitUnlockDiv)}},{key:"submit",value:(t=o(w().mark((function e(){var t,n,r,a,o,s;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.uwAppPass.value||(this.pwCache?this.pwCache.pw:""))||en.passwordIsCached()){e.next=6;break}return t.unlockErr.textContent=It(x),zt.show(t.unlockErr),e.abrupt("return");case 6:return r=this.currentAsset.id,zt.hide(this.page.unlockErr),a={assetID:r,pass:n},t.uwAppPass.value="",o=xn().loading(this.form),e.next=13,yn("/api/openwallet",a);case 13:if(s=e.sent,o(),xn().checkResponse(s)){e.next=18;break}return this.setError(s.msg),e.abrupt("return");case 18:this.pwCache&&(this.pwCache.pw=n),this.success(r);case 20:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Cr=function(){function e(t,n){var r=this;s(this,e),h(this,"form",void 0),h(this,"page",void 0),h(this,"order",void 0),h(this,"acceleratedRate",void 0),h(this,"earlyAcceleration",void 0),h(this,"currencyUnit",void 0),h(this,"success",void 0),this.form=t,this.success=n;var a=this.page=zt.idDescendants(t);zt.bind(a.accelerateSubmit,"click",(function(){r.submit()})),zt.bind(a.submitEarlyConfirm,"click",(function(){r.sendAccelerateRequest()}))}var t,n,r,a;return u(e,[{key:"displayEarlyAccelerationMsg",value:function(){var e=this.page;this.earlyAcceleration&&(e.recentAccelerationTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60)),e.recentSwapTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60)),this.earlyAcceleration.wasAcceleration?(zt.show(e.recentAccelerationMsg),zt.hide(e.recentSwapMsg),e.recentAccelerationTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))):(zt.show(e.recentSwapMsg),zt.hide(e.recentAccelerationMsg),e.recentSwapTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))),zt.hide(e.configureAccelerationDiv,e.accelerateErr),zt.show(e.earlyAccelerationDiv))}},{key:"sendAccelerateRequest",value:(a=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.order,n=this.page,r={pw:n.acceleratePass.value,orderID:t.id,newRate:this.acceleratedRate},n.acceleratePass.value="",a=xn().loading(n.accelerateMainDiv),e.next=7,yn("/api/accelerateorder",r);case 7:o=e.sent,a(),xn().checkResponse(o)?(n.accelerateTxID.textContent=o.txID,zt.hide(n.accelerateMainDiv,n.preAccelerateErr,n.accelerateErr),zt.show(n.accelerateMsgDiv,n.accelerateSuccess),this.success()):(n.accelerateErr.textContent=It(lt,{msg:o.msg}),zt.hide(n.earlyAccelerationDiv),zt.show(n.accelerateErr,n.configureAccelerationDiv));case 10:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"submit",value:(r=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.earlyAcceleration?this.displayEarlyAccelerationMsg():this.sendAccelerateRequest();case 1:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"refresh",value:(n=o(w().mark((function e(t){var n,r,a,o,s,i,c=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.order=t,e.next=4,yn("/api/preaccelerate",t.id);case 4:if(r=e.sent,xn().checkResponse(r)){e.next=10;break}return n.preAccelerateErr.textContent=It(lt,{msg:r.msg}),zt.hide(n.accelerateMainDiv,n.accelerateSuccess),zt.show(n.accelerateMsgDiv,n.preAccelerateErr),e.abrupt("return");case 10:zt.hide(n.accelerateMsgDiv,n.preAccelerateErr,n.accelerateErr,n.feeEstimateDiv,n.earlyAccelerationDiv),zt.show(n.accelerateMainDiv,n.accelerateSuccess,n.configureAccelerationDiv),a=r.preAccelerate,this.earlyAcceleration=a.earlyAcceleration,this.currencyUnit=a.suggestedRange.yUnit,n.accelerateAvgFeeRate.textContent="".concat(a.swapRate," ").concat(a.suggestedRange.yUnit),n.accelerateCurrentFeeRate.textContent="".concat(a.suggestedRate," ").concat(a.suggestedRange.yUnit),this.acceleratedRate=a.suggestedRange.start.y,o=function(){},s=function(e,t){c.acceleratedRate=t},i=new Rn(a.suggestedRange,a.suggestedRange.start.x,s,(function(){return c.updateAccelerationEstimate()}),o,!0),zt.empty(n.sliderContainer),n.sliderContainer.appendChild(i.control),this.updateAccelerationEstimate();case 25:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"updateAccelerationEstimate",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=this.order,r={orderID:n.id,newRate:this.acceleratedRate},a=xn().loading(t.sliderContainer),e.next=6,yn("/api/accelerationestimate",r);case 6:if(o=e.sent,a(),xn().checkResponse(o)){e.next=12;break}return t.accelerateErr.textContent=It(ct,{msg:o.msg}),zt.show(t.accelerateErr),e.abrupt("return");case 12:t.feeRateEstimate.textContent="".concat(this.acceleratedRate," ").concat(this.currencyUnit),n.sell?(s=n.baseID,i=n.baseSymbol):(s=n.quoteID,i=n.quoteSymbol),c=xn().unitInfo(s),t.feeEstimate.textContent="".concat(o.fee/c.conventional.conversionFactor," ").concat(i),zt.show(t.feeEstimateDiv);case 17:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Sr=function(){function e(t,n,r,a){var o=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"page",void 0),h(this,"knownExchanges",void 0),h(this,"dexToUpdate",void 0),this.form=t,this.success=n,this.pwCache=r||null;var i=this.page=zt.parseTemplate(t);i.selectedCert.textContent=It(ie),zt.bind(i.skipRegistration,"change",(function(){return o.showOrHidePWBox()})),zt.bind(i.certFile,"change",(function(){return o.onCertFileChange()})),zt.bind(i.removeCert,"click",(function(){return o.clearCertFile()})),zt.bind(i.addCert,"click",(function(){return i.certFile.click()})),zt.bind(i.showCustom,"click",(function(){zt.hide(i.showCustom),zt.show(i.customBox,i.auth)})),this.knownExchanges=Array.from(i.knownXCs.querySelectorAll(".known-exchange"));var c,l=pr(this.knownExchanges);try{var u=function(){var e=c.value;zt.bind(e,"click",(function(){var t,n=e.dataset.host,a=pr(o.knownExchanges);try{for(a.s();!(t=a.n()).done;)t.value.classList.remove("selected")}catch(e){a.e(e)}finally{a.f()}if(o.skipRegistration()||en.passwordIsCached()||r&&r.pw)return o.checkDEX(n);e.classList.add("selected"),i.appPW.focus(),i.addr.value=n}))};for(l.s();!(c=l.n()).done;)u()}catch(e){l.e(e)}finally{l.f()}Or(t,i.submit,(function(){return o.checkDEX()})),a&&(zt.hide(i.addDexHdr,i.skipRegistrationBox),zt.show(i.updateDexHdr),this.dexToUpdate=a),this.refresh()}var t,n,r;return u(e,[{key:"refresh",value:function(){var e=this.page;e.addr.value="",e.appPW.value="",this.clearCertFile(),zt.hide(e.err),0===this.knownExchanges.length||this.dexToUpdate?(zt.show(e.customBox,e.auth),zt.hide(e.showCustom,e.knownXCs,e.pickServerMsg,e.addCustomMsg)):(zt.hide(e.customBox),zt.show(e.showCustom));var t,n=pr(this.knownExchanges);try{for(n.s();!(t=n.n()).done;)t.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}this.showOrHidePWBox()}},{key:"showOrHidePWBox",value:function(){var e=!(en.passwordIsCached()||this.pwCache&&this.pwCache.pw||this.skipRegistration()),t=this.page;e?zt.show(t.appPWBox,t.auth):(zt.hide(t.appPWBox),zt.setVis(zt.isDisplayed(t.customBox),t.auth))}},{key:"skipRegistration",value:function(){var e;return null!==(e=this.page.skipRegistration.checked)&&void 0!==e&&e}},{key:"animate",value:(r=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(550,(function(e){t.style.transform="scale(".concat(.9+.1*e,")"),t.style.opacity=String(Math.pow(e,4))}),"easeOut");case 2:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"checkDEX",value:(n=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,zt.hide(n.err),""!==(t=t||n.addr.value)){e.next=7;break}return n.err.textContent=It(vt),zt.show(n.err),e.abrupt("return");case 7:if(r="",!n.certFile.value){e.next=14;break}if(!(a=n.certFile.files)||!a.length){e.next=14;break}return e.next=13,a[0].text();case 13:r=e.sent;case 14:return o=this.skipRegistration(),s="",o||en.passwordIsCached()||(s=n.appPW.value||(this.pwCache?this.pwCache.pw:"")),this.dexToUpdate?(i="/api/updatedexhost",c={newHost:t,cert:r,pw:s,oldHost:this.dexToUpdate}):(i=o?"/api/adddex":"/api/discoveracct",c={addr:t,cert:r,pass:s}),l=xn().loading(this.form),e.next=21,yn(i,c);case 21:if(u=e.sent,l(),xn().checkResponse(u)){e.next=26;break}return String(u.msg).includes("certificate required")?zt.show(n.needCert):(n.err.textContent=u.msg,zt.show(n.err)),e.abrupt("return");case 26:if(this.dexToUpdate||!(o||u.paid||Object.keys(u.xc.pendingBonds).length>0)){e.next=32;break}return e.next=29,xn().fetchUser();case 29:return e.next=31,xn().loadPage("markets");case 31:return e.abrupt("return");case 32:this.pwCache&&(this.pwCache.pw=s),this.success(u.xc,r);case 34:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"onCertFileChange",value:(t=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.certFile.files)&&n.length){e.next=4;break}return e.abrupt("return");case 4:t.selectedCert.textContent=n[0].name,zt.show(t.removeCert),zt.hide(t.addCert);case 7:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"clearCertFile",value:function(){var e=this.page;e.certFile.value="",e.selectedCert.textContent=It(ie),zt.hide(e.removeCert),zt.show(e.addCert)}}]),e}(),Er=function(){function e(t,n,r,a){var o=this;s(this,e),h(this,"form",void 0),h(this,"addr",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"page",void 0),this.form=t,this.addr=n,this.success=r,this.pwCache=a||null;var i=this.page=zt.parseTemplate(t);i.dexHost.textContent=n,Or(t,i.submit,(function(){return o.checkDEX()})),this.refresh()}var t,n;return u(e,[{key:"refresh",value:function(){var e=this.page;e.appPW.value="",zt.hide(e.err),en.passwordIsCached()||this.pwCache&&this.pwCache.pw?zt.hide(e.appPWBox):zt.show(e.appPWBox)}},{key:"animate",value:(n=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(550,(function(e){t.style.transform="scale(".concat(.9+.1*e,")"),t.style.opacity=String(Math.pow(e,4))}),"easeOut");case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"checkDEX",value:(t=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.err),n="",en.passwordIsCached()||(n=t.appPW.value||(this.pwCache?this.pwCache.pw:"")),r={addr:this.addr,pass:n},a=xn().loading(this.form),e.next=8,yn("/api/discoveracct",r);case 8:if(o=e.sent,a(),xn().checkResponse(o)){e.next=14;break}return t.err.textContent=o.msg,zt.show(t.err),e.abrupt("return");case 14:if(!o.paid){e.next=20;break}return e.next=17,xn().fetchUser();case 17:return e.next=19,xn().loadPage("markets");case 19:return e.abrupt("return");case 20:this.pwCache&&(this.pwCache.pw=n),this.success(o.xc);case 22:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Ar=function(){function e(t,n,r){var a=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"headerTxt",void 0),h(this,"page",void 0),this.success=n,this.form=t,this.pwCache=r||null;var o=this.page=zt.parseTemplate(t);this.headerTxt=o.header.textContent||"",Or(t,o.submit,(function(){a.submit()})),xn().registerNoteFeeder({login:function(e){a.handleLoginNote(e)}})}var t,n;return u(e,[{key:"handleLoginNote",value:function(e){if(""!==e.details){var t=zt.idel(this.form,"loaderMsg");t&&(t.textContent=e.details)}}},{key:"focus",value:function(){this.page.pw.focus()}},{key:"submit",value:(n=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.errMsg),n=t.pw.value||"",t.pw.value="",r=t.rememberPass.checked,""!==n){e.next=9;break}return t.errMsg.textContent=It(k),zt.show(t.errMsg),e.abrupt("return");case 9:return a=xn().loading(this.form),e.next=12,yn("/api/login",{pass:n,rememberPass:r});case 12:if(o=e.sent,a(),xn().checkResponse(o)){e.next=18;break}return t.errMsg.textContent=o.msg,zt.show(t.errMsg),e.abrupt("return");case 18:o.notes&&(o.notes.reverse(),xn().setNotes(o.notes)),this.pwCache&&(this.pwCache.pw=n),this.success();case 21:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"animate",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(550,(function(e){t.style.transform="scale(".concat(.9+.1*e,")"),t.style.opacity=String(Math.pow(e,4))}),"easeOut");case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Fr=function(){function e(t){var n=this;s(this,e),h(this,"form",void 0),h(this,"page",void 0),h(this,"assetID",void 0),this.form=t;var r=this.page=zt.idDescendants(t);zt.bind(r.newDepAddrBttn,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n.newDepositAddress();case 1:case"end":return e.stop()}}),e)})))),zt.bind(r.copyAddressBtn,"click",(function(){n.copyAddress()}))}var t,n,r;return u(e,[{key:"setAsset",value:(r=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.assetID=t,n=this.page,zt.hide(n.depositErr),r=xn().assets[t],n.depositLogo.src=zt.logoPath(r.symbol),a=xn().walletMap[t],n.depositName.textContent=r.name,n.depositAddress.textContent=a.address,n.qrcode.src="/generateqrcode?address=".concat(a.address),0!=(2&a.traits)?zt.show(n.newDepAddrBttn):zt.hide(n.newDepAddrBttn);case 10:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"newDepositAddress",value:(n=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.depositErr),n=xn().loading(this.form),e.next=5,yn("/api/depositaddress",{assetID:this.assetID});case 5:if(r=e.sent,n(),xn().checkResponse(r)){e.next=11;break}return t.depositErr.textContent=r.msg,zt.show(t.depositErr),e.abrupt("return");case 11:t.depositAddress.textContent=r.address,t.qrcode.src="/generateqrcode?address=".concat(r.address);case 13:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"copyAddress",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,navigator.clipboard.writeText(t.depositAddress.textContent||"").then((function(){zt.show(t.copyAlert),setTimeout((function(){zt.hide(t.copyAlert)}),800)})).catch((function(e){console.error("Unable to copy: ",e)}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Ir=300;function Rr(e,t){return Dr.apply(this,arguments)}function Dr(){return(Dr=o(w().mark((function e(t,n){var r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=document.body.offsetWidth/2,e.next=3,zt.animate(Ir,(function(e){t.style.right="".concat(e*r,"px")}),"easeInHard");case 3:return zt.hide(t),t.style.right="0",n.style.right=String(-r),zt.show(n),n.querySelector("input")&&zt.safeSelector(n,"input").focus(),e.next=10,zt.animate(Ir,(function(e){n.style.right="".concat(e*r-r,"px")}),"easeOutHard");case 10:n.style.right="0";case 11:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Or(e,t,n){var r=function(e){e.preventDefault&&e.preventDefault(),n(e)};zt.bind(t,"click",r),zt.bind(e,"submit",r)}function Tr(e){return Math.floor(e.getTime()/1e3)}function Pr(e){return function(e){return new Date(e.getTime()-60*e.getTimezoneOffset()*1e3)}(e).toISOString().split("T")[0]}var Lr=function(e){rn(y,e);var t,n,r,a,i,c,l,d,p,f,m,v=(f=y,m=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(f);if(m){var n=on(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return an(this,e)});function y(e,t){var n;s(this,y),h(tn(n=v.call(this)),"body",void 0),h(tn(n),"pwCache",void 0),h(tn(n),"currentDEX",void 0),h(tn(n),"page",void 0),h(tn(n),"loginForm",void 0),h(tn(n),"dexAddrForm",void 0),h(tn(n),"discoverAcctForm",void 0),h(tn(n),"newWalletForm",void 0),h(tn(n),"regAssetForm",void 0),h(tn(n),"walletWaitForm",void 0),h(tn(n),"confirmRegisterForm",void 0),n.body=e,n.pwCache={pw:""};var r=n.page=zt.idDescendants(e);t.host&&r.dexAddrForm.classList.contains("selected")&&(r.dexAddrForm.classList.remove("selected"),r.discoverAcctForm.classList.add("selected"),r.discoverAcctForm.dataset.host=t.host),e.querySelectorAll(".form-closer").forEach((function(e){return zt.hide(e)})),Or(r.appPWForm,r.appPWSubmit,(function(){return n.setAppPass()})),zt.bind(r.showSeedRestore,"click",(function(){zt.show(r.seedRestore),zt.hide(r.showSeedRestore)})),n.loginForm=new Ar(r.loginForm,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:n.discoverAcctForm?(n.discoverAcctForm.refresh(),Rr(r.loginForm,r.discoverAcctForm)):(n.dexAddrForm.refresh(),Rr(r.loginForm,r.dexAddrForm));case 3:case"end":return e.stop()}}),e)}))),n.pwCache),n.newWalletForm=new mr(r.newWalletForm,(function(e){return n.newWalletCreated(e)}),n.pwCache,(function(){return n.animateRegAsset(r.newWalletForm)})),n.dexAddrForm=new Sr(r.dexAddrForm,function(){var e=o(w().mark((function e(t,a){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n.requestFeepayment(r.dexAddrForm,t,a);case 1:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),n.pwCache);var a=r.discoverAcctForm.dataset.host;a&&(n.discoverAcctForm=new Er(r.discoverAcctForm,a,function(){var e=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n.requestFeepayment(r.discoverAcctForm,t,"");case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),n.pwCache)),n.regAssetForm=new br(r.regAssetForm,function(){var e=o(w().mark((function e(t){var a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n.confirmRegisterForm.setAsset(t),a=xn().assets[t],!(o=a.wallet)){e.next=14;break}if(s=n.currentDEX.bondAssets[a.symbol],!(o.synced&&o.balance.available>s.amount)){e.next=8;break}return n.animateConfirmForm(r.regAssetForm),e.abrupt("return");case 8:return e.next=10,n.getBondsFeeBuffer(t,r.regAssetForm);case 10:return i=e.sent,n.walletWaitForm.setWallet(o,i),Rr(r.regAssetForm,r.walletWait),e.abrupt("return");case 14:n.newWalletForm.setAsset(t),Rr(r.regAssetForm,r.newWalletForm);case 16:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),n.walletWaitForm=new kr(r.walletWait,(function(){n.animateConfirmForm(r.walletWait)}),(function(){n.animateRegAsset(r.walletWait)})),n.confirmRegisterForm=new gr(r.confirmRegForm,(function(){n.registerDEXSuccess()}),(function(){n.animateRegAsset(r.confirmRegForm)}),n.pwCache);var i=zt.safeSelector(r.forms,":scope > form.selected");switch(i.classList.remove("selected"),i){case r.loginForm:n.loginForm.animate();break;case r.dexAddrForm:n.dexAddrForm.animate();break;case r.discoverAcctForm:n.discoverAcctForm.animate()}return zt.show(i),xn().authed()&&n.auth(),n}return u(y,[{key:"unload",value:function(){this.pwCache.pw=""}},{key:"auth",value:(p=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:case"end":return e.stop()}}),e)}))),function(){return p.apply(this,arguments)})},{key:"requestFeepayment",value:(d=o(w().mark((function e(t,n,r){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.currentDEX=n,this.confirmRegisterForm.setExchange(n,r),this.walletWaitForm.setExchange(n),this.regAssetForm.setExchange(n),this.animateRegAsset(t);case 5:case"end":return e.stop()}}),e,this)}))),function(e,t,n){return d.apply(this,arguments)})},{key:"animateRegAsset",value:(l=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:zt.hide(t),this.regAssetForm.animate(),zt.show(this.page.regAssetForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return l.apply(this,arguments)})},{key:"animateConfirmForm",value:(c=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),zt.hide(t),zt.show(this.page.confirmRegForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"getBondsFeeBuffer",value:(i=o(w().mark((function e(t,n){var r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=xn().loading(n),e.next=3,yn("/api/bondsfeebuffer",{assetID:t});case 3:if(a=e.sent,r(),xn().checkResponse(a)){e.next=7;break}return e.abrupt("return",0);case 7:return e.abrupt("return",a.feeBuffer);case 8:case"end":return e.stop()}}),e)}))),function(e,t){return i.apply(this,arguments)})},{key:"setAppPass",value:(a=o(w().mark((function e(){var t,n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.appPWErrMsg),n=t.appPW.value||"",r=t.appPWAgain.value,""!==n){e.next=8;break}return t.appPWErrMsg.textContent=It(k),zt.show(t.appPWErrMsg),e.abrupt("return");case 8:if(n===r){e.next=12;break}return t.appPWErrMsg.textContent=It(G),zt.show(t.appPWErrMsg),e.abrupt("return");case 12:return xn().setNotes([]),t.appPW.value="",t.appPWAgain.value="",a=xn().loading(t.appPWForm),o=t.seedInput.value,s=t.rememberPass.checked,e.next=20,yn("/api/init",{pass:n,seed:o,rememberPass:s});case 20:if(i=e.sent,a(),xn().checkResponse(i)){e.next=26;break}return t.appPWErrMsg.textContent=i.msg,zt.show(t.appPWErrMsg),e.abrupt("return");case 26:return this.pwCache.pw=n,this.auth(),xn().updateMenuItemsDisplay(),this.newWalletForm.refresh(),this.dexAddrForm.refresh(),e.next=33,Rr(t.appPWForm,t.dexAddrForm);case 33:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"getCertFile",value:(r=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t="",!this.dexAddrForm.page.certFile.value){e.next=7;break}if(!(n=this.dexAddrForm.page.certFile.files)||!n.length){e.next=7;break}return e.next=6,n[0].text();case 6:t=e.sent;case 7:return e.abrupt("return",t);case 8:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"registerDEXSuccess",value:(n=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:return e.next=4,xn().loadPage("markets");case 4:case"end":return e.stop()}}),e)}))),function(){return n.apply(this,arguments)})},{key:"newWalletCreated",value:(t=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.regAssetForm.refresh(),e.next=3,xn().fetchUser();case 3:if(n=e.sent){e.next=6;break}return e.abrupt("return");case 6:if(r=this.page,a=n.assets[t],o=a.wallet,s=this.currentDEX.bondAssets[a.symbol].amount,!(o.synced&&o.balance.available>s)){e.next=14;break}return e.next=13,this.animateConfirmForm(r.newWalletForm);case 13:return e.abrupt("return");case 14:return e.next=16,this.getBondsFeeBuffer(t,r.newWalletForm);case 16:return i=e.sent,this.walletWaitForm.setWallet(o,i),e.next=20,Rr(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),y}(fn);var Br=function(e){rn(i,e);var t,n,r,a=(n=i,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(n);if(r){var a=on(this).constructor;e=Reflect.construct(t,arguments,a)}else e=t.apply(this,arguments);return an(this,e)});function i(e){var t;return s(this,i),h(tn(t=a.call(this)),"form",void 0),h(tn(t),"loginForm",void 0),t.form=zt.idel(e,"loginForm"),zt.show(t.form),t.loginForm=new Ar(t.form,(function(){t.loggedIn()})),t.loginForm.focus(),t}return u(i,[{key:"loggedIn",value:(t=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:return e.next=4,xn().loadPage("markets");case 4:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})}]),i}(fn);function Mr(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Wr(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Wr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Wr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n form"),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){t.closePopups()}))})),zt.bind(n.cancelForce,"click",(function(){t.closePopups()})),t.selectedAssetID=-1,zt.cleanTemplates(n.iconSelectTmpl,n.balanceDetailRow,n.recentOrderTmpl),zt.bind(n.createWallet,"click",(function(){return t.showNewWallet(t.selectedAssetID)})),zt.bind(n.connectBttn,"click",(function(){return t.doConnect(t.selectedAssetID)})),zt.bind(n.send,"click",(function(){return t.showSendForm(t.selectedAssetID)})),zt.bind(n.receive,"click",(function(){return t.showDeposit(t.selectedAssetID)})),zt.bind(n.unlockBttn,"click",(function(){return t.openWallet(t.selectedAssetID)})),zt.bind(n.lockBttn,"click",(function(){return t.lock(t.selectedAssetID)})),zt.bind(n.reconfigureBttn,"click",(function(){return t.showReconfig(t.selectedAssetID)})),zt.bind(n.rescanWallet,"click",(function(){return t.rescanWallet(t.selectedAssetID)})),t.newWalletForm=new mr(n.newWalletForm,(function(e){var r={assetName:xn().assets[e].name};t.assetUpdated(e,n.newWalletForm,It(xe,r)),t.sortAssetButtons()})),t.reconfigForm=new yr(n.reconfigInputs,!1),t.unlockForm=new xr(n.unlockWalletForm,(function(e){return t.openWalletSuccess(e,n.unlockWalletForm)})),Or(n.sendForm,n.submitSendForm,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.stepSend();case 1:case"end":return e.stop()}}),e)})))),Or(n.vSendForm,n.vSend,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.send();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.vCancelSend,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.cancelSend();case 1:case"end":return e.stop()}}),e)})))),Or(n.reconfigForm,n.submitReconfig,(function(){return t.reconfig()})),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){return t.closePopups()}))})),zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||t.closePopups()})),t.keyup=function(e){"Escape"===e.key&&zt.isDisplayed(t.page.forms)&&t.closePopups()},zt.bind(document,"keyup",t.keyup),zt.bind(n.downloadLogs,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.downloadLogs();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.exportWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.displayExportWalletAuth();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.recoverWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showRecoverWallet();case 1:case"end":return e.stop()}}),e)})))),Or(n.exportWalletAuth,n.exportWalletAuthSubmit,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.exportWalletAuthSubmit();case 1:case"end":return e.stop()}}),e)})))),Or(n.recoverWalletConfirm,n.recoverWalletSubmit,(function(){t.recoverWallet()})),Or(n.confirmForce,n.confirmForceSubmit,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.confirmForceSubmit();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.disableWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showToggleWalletStatus(!0);case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.enableWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showToggleWalletStatus(!1);case 1:case"end":return e.stop()}}),e)})))),Or(n.toggleWalletStatusConfirm,n.toggleWalletStatusSubmit,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.toggleWalletStatus();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.managePeers,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showManagePeersForm();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.addPeerSubmit,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.submitAddPeer();case 1:case"end":return e.stop()}}),e)})))),t.depositAddrForm=new Fr(n.deposit),zt.bind(n.walletBal,"click",(function(){t.populateMaxSend()})),zt.bind(n.sendAmt,"input",(function(){var e=xn().assets[t.selectedAssetID].unitInfo,r=parseFloat(n.sendAmt.value||"0"),a=e.conventional.conversionFactor;t.showFiatValue(t.selectedAssetID,r*a,n.sendValue)})),zt.bind(n.maxSend,"click",(function(){t.populateMaxSend()})),zt.bind(n.sendAddr,"input",o(w().mark((function e(){var r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=xn().assets[t.selectedAssetID],zt.hide(n.validAddr),n.sendAddr.classList.remove("invalid"),a=n.sendAddr.value||"",r&&""!==a){e.next=6;break}return e.abrupt("return");case 6:return e.next=8,t.validateSendAddress(a,r.id);case 8:e.sent?zt.show(n.validAddr):n.sendAddr.classList.add("invalid");case 10:case"end":return e.stop()}}),e)})))),zt.bind(n.showChangePW,"click",(function(){t.changeWalletPW=!t.changeWalletPW,t.setPWSettingViz(t.changeWalletPW)})),zt.bind(n.changeWalletTypeSelect,"change",(function(){t.changeWalletType()})),zt.bind(n.showChangeType,"click",(function(){zt.isHidden(n.changeWalletType)?(zt.show(n.changeWalletType,n.changeTypeHideIcon),zt.hide(n.changeTypeShowIcon),n.changeTypeMsg.textContent=It(me)):t.showReconfig(t.selectedAssetID,!0)})),xn().registerNoteFeeder({fiatrateupdate:function(e){t.handleRatesNote(e)},balance:function(e){t.handleBalanceNote(e)},walletstate:function(e){t.handleWalletStateNote(e)},walletconfig:function(e){t.handleWalletStateNote(e)},createwallet:function(e){t.handleCreateWalletNote(e)}});var r=t.sortAssetButtons().id,a=en.fetchLocal(en.selectedAssetLK);return a&&(r=Number(a)),t.setSelectedAsset(r),t}return u(V,[{key:"closePopups",value:function(){zt.hide(this.page.forms),this.animation&&this.animation.stop()}},{key:"stepSend",value:(N=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d,p,f,m,v,y,g,b,k,x;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.vSendErr,t.sendErr,t.vSendEstimates,t.txFeeNotAvailable),n=parseInt(t.sendForm.dataset.assetID||""),r=xn().assets[n].token,a=t.subtractCheckBox.checked||!1,o=xn().unitInfo(n).conventional.conversionFactor,s=Math.round(parseFloat(t.sendAmt.value||"")*o),""!==(i=t.sendAddr.value||"")){e.next=10;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:i})));case 10:if(c=xn().assets[n],l=c.wallet,u=c.unitInfo,h=c.symbol,d=0,0==(1024&l.traits)){e.next=36;break}return p={addr:t.sendAddr.value,assetID:n,subtract:a,value:s},f=xn().loading(t.sendForm),e.next=17,yn("/api/txfee",p);case 17:if(m=e.sent,f(),xn().checkResponse(m)){e.next=29;break}return t.txFeeNotAvailable.dataset.tooltip=It(Me,{err:m.msg}),zt.show(t.txFeeNotAvailable),e.next=24,this.validateSendAddress(i,n);case 24:if(e.sent){e.next=27;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:i||""})));case 27:e.next=34;break;case 29:if(!m.ok){e.next=34;break}if(m.validaddress){e.next=32;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:t.sendAddr.value||""})));case 32:d=m.txfee,zt.show(t.vSendEstimates);case 34:e.next=41;break;case 36:return e.next=38,this.validateSendAddress(i,n);case 38:if(e.sent){e.next=41;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:i||""})));case 41:return t.vSendSymbol.textContent=h.toUpperCase(),t.vSendLogo.src=zt.logoPath(h),r?(v=xn().assets[r.parentID],y=v.unitInfo,g=v.symbol,t.vSendFee.textContent=zt.formatFullPrecision(d,y)+" "+g):t.vSendFee.textContent=zt.formatFullPrecision(d,u),this.showFiatValue(n,d,t.vSendFeeFiat),t.vSendDestinationAmt.textContent=zt.formatFullPrecision(s-d,u),t.vTotalSend.textContent=zt.formatFullPrecision(s,u),this.showFiatValue(n,s,t.vTotalSendFiat),t.vSendAddr.textContent=t.sendAddr.value||"",b=l.balance.available-s,t.balanceAfterSend.textContent=zt.formatFullPrecision(b,u),this.showFiatValue(n,b,t.balanceAfterSendFiat),zt.show(t.approxSign),a||(zt.hide(t.approxSign),t.vSendDestinationAmt.textContent=zt.formatFullPrecision(s,u),k=s,r||(k+=d),t.vTotalSend.textContent=zt.formatFullPrecision(k,u),this.showFiatValue(n,k,t.vTotalSendFiat),x=l.balance.available-s,r||(x-=d),x<=0?(t.balanceAfterSend.textContent=zt.formatFullPrecision(0,u),this.showFiatValue(n,0,t.balanceAfterSendFiat)):(t.balanceAfterSend.textContent=zt.formatFullPrecision(x,u),this.showFiatValue(n,x,t.balanceAfterSendFiat))),zt.hide(t.sendForm),e.next=57,this.showForm(t.vSendForm);case 57:case"end":return e.stop()}}),e,this)}))),function(){return N.apply(this,arguments)})},{key:"cancelSend",value:(q=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.vSendForm,t.sendErr),e.next=4,this.showForm(t.sendForm);case 4:case"end":return e.stop()}}),e,this)}))),function(){return q.apply(this,arguments)})},{key:"validateSendAddress",value:(W=o(w().mark((function e(t,n){var r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,yn("/api/validateaddress",{addr:t,assetID:n});case 2:return r=e.sent,e.abrupt("return",xn().checkResponse(r));case 4:case"end":return e.stop()}}),e)}))),function(e,t){return W.apply(this,arguments)})},{key:"setPWSettingViz",value:function(e){if(e)return zt.hide(this.page.showIcon),zt.show(this.page.hideIcon,this.page.changePW),void(this.page.switchPWMsg.textContent=It(Q));zt.hide(this.page.hideIcon,this.page.changePW),zt.show(this.page.showIcon),this.page.switchPWMsg.textContent=It(Y)}},{key:"updateWalletPeersTable",value:(M=o(w().mark((function e(){var t,n,r,a,s,i,c=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.peerSpinner),e.next=4,yn("/api/getwalletpeers",{assetID:this.selectedAssetID});case 4:if(n=e.sent,xn().checkResponse(n)){e.next=9;break}return t.managePeersErr.textContent=n.msg,zt.show(t.managePeersErr),e.abrupt("return");case 9:for(;t.peersTableBody.firstChild;)t.peersTableBody.removeChild(t.peersTableBody.firstChild);(r=n.peers||[]).sort((function(e,t){return e.source-t.source})),a=It(ze),s=It(Ve),i=It(je),r.forEach((function(e){var n,r=t.peerTableRow.cloneNode(!0),l=zt.parseTemplate(r);switch(l.addr.textContent=e.addr,e.source){case ln.WalletDefault:l.source.textContent=a;break;case ln.UserAdded:l.source.textContent=s;break;case ln.Discovered:l.source.textContent=i}if(n=e.connected?c.page.connectedIconTmpl.cloneNode(!0):c.page.disconnectedIconTmpl.cloneNode(!0),l.connected.appendChild(n),e.source===ln.UserAdded){var u=c.page.removeIconTmpl.cloneNode(!0);zt.bind(u,"click",o(w().mark((function n(){var r;return w().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return zt.hide(t.managePeersErr),n.next=3,yn("/api/removewalletpeer",{assetID:c.selectedAssetID,addr:e.addr});case 3:if(r=n.sent,xn().checkResponse(r)){n.next=8;break}return t.managePeersErr.textContent=r.msg,zt.show(t.managePeersErr),n.abrupt("return");case 8:c.spinUntilPeersUpdate();case 9:case"end":return n.stop()}}),n)})))),l.remove.appendChild(u)}t.peersTableBody.appendChild(r)}));case 16:case"end":return e.stop()}}),e,this)}))),function(){return M.apply(this,arguments)})},{key:"showManagePeersForm",value:(B=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,e.next=3,this.updateWalletPeersTable();case 3:zt.hide(t.managePeersErr),this.showForm(t.managePeersForm);case 5:case"end":return e.stop()}}),e,this)}))),function(){return B.apply(this,arguments)})},{key:"submitAddPeer",value:(L=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.managePeersErr),e.next=4,yn("/api/addwalletpeer",{assetID:this.selectedAssetID,addr:t.addPeerInput.value});case 4:if(n=e.sent,xn().checkResponse(n)){e.next=9;break}return t.managePeersErr.textContent=n.msg,zt.show(t.managePeersErr),e.abrupt("return");case 9:this.spinUntilPeersUpdate(),t.addPeerInput.value="";case 11:case"end":return e.stop()}}),e,this)}))),function(){return L.apply(this,arguments)})},{key:"spinUntilPeersUpdate",value:(P=o(w().mark((function e(){var t,n=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,zt.show(t.peerSpinner),setTimeout((function(){zt.isDisplayed(t.peerSpinner)&&n.updateWalletPeersTable()}),1e4);case 3:case"end":return e.stop()}}),e,this)}))),function(){return P.apply(this,arguments)})},{key:"showToggleWalletStatus",value:function(e){var t=this.page;zt.hide(t.toggleWalletStatusErr,t.walletStatusDisable,t.disableWalletMsg,t.walletStatusEnable,t.enableWalletMsg),e?zt.show(t.walletStatusDisable,t.disableWalletMsg):zt.show(t.walletStatusEnable,t.enableWalletMsg),this.showForm(t.toggleWalletStatusConfirm)}},{key:"toggleWalletStatus",value:(T=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.toggleWalletStatusErr),n=xn().assets[this.selectedAssetID],r=!n.wallet.disabled,a={assetID:this.selectedAssetID,disable:r},o={assetName:n.name},s=xn().loading(t.toggleWalletStatusConfirm),e.next=10,yn("/api/togglewalletstatus",a);case 10:if(i=e.sent,s(),xn().checkResponse(i)){e.next=16;break}return i.code===sn.activeOrdersErr?t.toggleWalletStatusErr.textContent=It(Ie,o):t.toggleWalletStatusErr.textContent=i.msg,zt.show(t.toggleWalletStatusErr),e.abrupt("return");case 16:c=It(Ae,o),r||(c=It(Fe,o)),this.assetUpdated(this.selectedAssetID,t.toggleWalletStatusConfirm,c);case 19:case"end":return e.stop()}}),e,this)}))),function(){return T.apply(this,arguments)})},{key:"showBox",value:(O=o(w().mark((function e(t,n){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t.style.opacity="0",zt.show(t),n&&n.focus(),e.next=5,zt.animate(300,(function(e){t.style.opacity="".concat(e)}),"easeOut");case 5:t.style.opacity="1",this.displayed=t;case 7:case"end":return e.stop()}}),e,this)}))),function(e,t){return O.apply(this,arguments)})},{key:"showForm",value:(D=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return zt.hide(e)})),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(300,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return D.apply(this,arguments)})},{key:"showSuccess",value:(R=o(w().mark((function e(t){var n,a,o,s,i,c,l,u,h,d=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).successMessage.textContent=t,this.currentForm=n.checkmarkForm,this.forms.forEach((function(e){return zt.hide(e)})),zt.show(n.forms,n.checkmarkForm),n.checkmarkForm.style.right="0",n.checkmark.style.fontSize="0px",a=en.isDark()?[223,226,225]:[51,51,51],o=r(a,3),s=o[0],i=o[1],c=o[2],l=16-s,u=163-i,h=16-c,this.animation=new Vt(1200,(function(e){n.checkmark.style.fontSize="".concat(80*e,"px"),n.checkmark.style.color="rgb(".concat(s+e*l,", ").concat(i+e*u,", ").concat(c+e*h,")")}),"easeOutElastic",(function(){d.animation=new Vt(1500,(function(){}),"",(function(){d.currentForm===n.checkmarkForm&&d.closePopups()}))}));case 11:case"end":return e.stop()}}),e,this)}))),function(e){return R.apply(this,arguments)})},{key:"showNewWallet",value:(I=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=n.newWalletForm,this.newWalletForm.setAsset(t),a=this.newWalletForm.loadDefaults(),e.next=6,this.showForm(r);case 6:return e.next=8,a;case 8:case"end":return e.stop()}}),e,this)}))),function(e){return I.apply(this,arguments)})},{key:"sortAssetButtons",value:function(){var e=this,t=this.page;this.assetButtons={},zt.empty(t.assetSelect);var n=kn(Object.values(xn().assets));n.sort((function(e,t){return e.wallet&&!t.wallet?-1:!e.wallet&&t.wallet?1:e.symbol.localeCompare(t.symbol)}));var r,a=Mr(n);try{var o=function(){var n=r.value,a=t.iconSelectTmpl.cloneNode(!0);t.assetSelect.appendChild(a);var o=zt.parseTemplate(a);e.assetButtons[n.id]={tmpl:o,bttn:a},e.updateAssetButton(n.id),zt.bind(a,"click",(function(){e.setSelectedAsset(n.id),en.storeLocal(en.selectedAssetLK,String(n.id))}))};for(a.s();!(r=a.n()).done;)o()}catch(e){a.e(e)}finally{a.f()}return t.assetSelect.classList.remove("invisible"),n[0]}},{key:"updateAssetButton",value:function(e){var t,n=xn().assets[e],r=this.assetButtons[e],a=r.bttn,o=r.tmpl;if(zt.hide(o.fiat,o.noWallet),a.classList.add("nowallet"),(t=o.img).src||(t.src=zt.logoPath(n.symbol)),o.name.textContent=n.name,n.wallet){a.classList.remove("nowallet");var s=n.wallet.balance,i=n.unitInfo,c=s.available+s.locked+s.immature;o.balance.textContent=zt.formatCoinValue(c,i);var l=xn().fiatRatesMap[n.id];l&&(zt.show(o.fiat),o.fiat.textContent=zt.formatFiatConversion(c,l,i))}else zt.show(o.noWallet)}},{key:"setSelectedAsset",value:function(e){var t,n=Mr(this.page.assetSelect.children);try{for(n.s();!(t=n.n()).done;)t.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}this.assetButtons[e].bttn.classList.add("selected"),this.selectedAssetID=e,this.updateDisplayedAsset(e),this.showAvailableMarkets(e),this.showRecentActivity(e)}},{key:"updateDisplayedAsset",value:function(e){if(e===this.selectedAssetID){var t,n=xn().assets[e],r=n.symbol,a=n.wallet,o=n.name,s=this.page,i=Mr(document.querySelectorAll("[data-asset-name]"));try{for(i.s();!(t=i.n()).done;)t.value.textContent=o}catch(e){i.e(e)}finally{i.f()}if(s.assetLogo.src=zt.logoPath(r),zt.hide(s.balanceBox,s.fiatBalanceBox,s.createWalletBox,s.walletDetails,s.sendReceive,s.connectBttnBox,s.statusLocked,s.statusReady,s.statusOff,s.unlockBttnBox,s.lockBttnBox,s.connectBttnBox,s.peerCountBox,s.syncProgressBox,s.statusDisabled),a){this.updateDisplayedAssetBalance();var c=xn().walletDefinition(e,a.type);s.walletType.textContent=c.tab;var l=function(e){var t=xn().assets[e];if(t.token){var n=t.token.definition.configopts;return n&&n.length>0}if(!t.info)throw Error("this asset isn't an asset, I guess");var r=t.info.availablewallets,a=r[0].configopts;return r.length>1||a&&a.length>0}(e);zt.setVis(l,s.passwordWrapper),a.disabled?zt.show(s.statusDisabled):a.running?(zt.show(s.sendReceive,s.peerCountBox,s.syncProgressBox),s.peerCount.textContent=String(a.peerCount),s.syncProgress.textContent="".concat((100*a.syncProgress).toFixed(1),"%"),a.open?(zt.show(s.statusReady),!xn().haveActiveOrders(e)&&a.encrypted&&zt.show(s.lockBttnBox)):zt.show(s.statusLocked,s.unlockBttnBox)):zt.show(s.statusOff,s.connectBttnBox)}else zt.show(s.createWalletBox);s.walletDetailsBox.classList.remove("invisible")}}},{key:"updateDisplayedAssetBalance",value:function(){var e=this.page,t=xn().assets[this.selectedAssetID],n=t.wallet,a=t.unitInfo,o=t.symbol,s=t.id,i=n.balance;zt.show(e.balanceBox,e.walletDetails);var c=i.locked+i.contractlocked+i.bondlocked,l=i.available+c+i.immature;e.balance.textContent=zt.formatCoinValue(l,a),zt.empty(e.balanceUnit),e.balanceUnit.appendChild(zt.symbolize(o));var u=xn().fiatRatesMap[s];u&&(zt.show(e.fiatBalanceBox),e.fiatBalance.textContent=zt.formatFiatConversion(l,u,a));var h=!1;zt.empty(e.balanceDetailBox);var d=function(t,n){var r=e.balanceDetailRow.cloneNode(!0);h&&(r.classList.add("first-other"),h=!1),e.balanceDetailBox.appendChild(r);var o=zt.parseTemplate(r);o.category.textContent=t,o.subBalance.textContent=zt.formatCoinValue(n,a)};d("Available",i.available),d("Locked",c),d("Immature",i.immature);var p=Object.entries(i.other||{});p.sort((function(e,t){return e[0].localeCompare(t[0])})),h=!0,i.contractlocked>0&&d("Swapping (locked)",i.contractlocked),i.bondlocked>0&&d("Bonded (locked)",i.bondlocked);for(var f=0,m=p;f1){zt.empty(r.changeWalletTypeSelect),zt.show(r.showChangeType,r.changeTypeShowIcon),r.changeTypeMsg.textContent=It(fe),i=Mr(s);try{for(i.s();!(c=i.n()).done;)l=c.value,u=document.createElement("option"),l.type===o.type&&(u.selected=!0),u.value=u.textContent=l.type,r.changeWalletTypeSelect.appendChild(u)}catch(e){i.e(e)}finally{i.f()}}else zt.hide(r.showChangeType);return h=xn().walletMap[t],zt.setVis(4&h.traits,r.downloadLogs),zt.setVis(32&h.traits,r.recoverWallet),zt.setVis(256&h.traits,r.exportWallet),zt.setVis(1&h.traits,r.rescanWallet),zt.setVis(2048&h.traits,r.managePeers),zt.setVis(0&h.traits,r.otherActionsLabel),h.disabled?zt.show(r.enableWallet):zt.show(r.disableWallet),r.recfgAssetLogo.src=zt.logoPath(a.symbol),r.recfgAssetName.textContent=a.name,n||this.showForm(r.reconfigForm),d=xn().loading(r.reconfigForm),e.next=24,yn("/api/walletsettings",{assetID:t});case 24:if(p=e.sent,d(),xn().checkResponse(p)){e.next=29;break}return zt.showFormError(r.reconfigErr,p.msg),e.abrupt("return");case 29:f=xn().haveActiveOrders(t),this.reconfigForm.update(o.configopts||[],f),this.reconfigForm.setConfig(p.map),this.updateDisplayedReconfigFields(o);case 33:case"end":return e.stop()}}),e,this)}))),function(e,t){return C.apply(this,arguments)})},{key:"changeWalletType",value:function(){var e=this.page.changeWalletTypeSelect.value||"",t=xn().walletDefinition(this.selectedAssetID,e);this.reconfigForm.update(t.configopts||[],!1),this.updateDisplayedReconfigFields(t)}},{key:"updateDisplayedReconfigFields",value:function(e){e.seeded||"token"===e.type?(zt.hide(this.page.showChangePW,this.reconfigForm.fileSelector),this.changeWalletPW=!1,this.setPWSettingViz(!1)):zt.show(this.page.showChangePW,this.reconfigForm.fileSelector)}},{key:"showDeposit",value:(b=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.depositAddrForm.setAsset(t),this.showForm(this.page.deposit);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return b.apply(this,arguments)})},{key:"showSendForm",value:(g=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u,h,d,p,f,m,v,y;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,r=n.sendForm,a=xn().assets[t],o=a.wallet,s=a.name,i=a.unitInfo,c=a.symbol,l=a.token,zt.hide(n.toggleSubtract),n.subtractCheckBox.checked=!1,(u=0!=(64&o.traits))&&zt.show(n.toggleSubtract),zt.hide(n.validAddr,n.sendErr,n.maxSendDisplay),n.sendAddr.classList.remove("invalid"),n.sendAddr.value="",n.sendAmt.value="",this.showFiatValue(t,0,n.sendValue),n.walletBal.textContent=zt.formatFullPrecision(o.balance.available,i),n.sendLogo.src=zt.logoPath(c),n.sendName.textContent=s,!(o.balance.available>0&&0!=(1024&o.traits))){e.next=23;break}return h={assetID:t,subtract:u,value:o.balance.available},d=xn().loading(this.body),e.next=20,yn("/api/txfee",h);case 20:p=e.sent,d(),xn().checkResponse(p)&&(f=o.balance.available,l||(f-=p.txfee)<0&&(f=0),this.maxSend=f,n.maxSend.textContent=zt.formatFullPrecision(f,i),this.showFiatValue(t,f,n.maxSendFiat),l?(m=xn().assets[l.parentID],v=m.unitInfo,y=m.symbol,n.maxSendFee.textContent=zt.formatFullPrecision(p.txfee,v)+" "+y,this.showFiatValue(l.parentID,p.txfee,n.maxSendFeeFiat)):(n.maxSendFee.textContent=zt.formatFullPrecision(p.txfee,i),this.showFiatValue(t,p.txfee,n.maxSendFeeFiat)),zt.show(n.maxSendDisplay));case 23:this.showFiatValue(t,0,n.sendValue),n.walletBal.textContent=zt.formatFullPrecision(o.balance.available,i),n.sendLogo.src=zt.logoPath(o.symbol),n.sendName.textContent=s,r.dataset.assetID=String(t),this.showForm(r);case 29:case"end":return e.stop()}}),e,this)}))),function(e){return g.apply(this,arguments)})},{key:"doConnect",value:(y=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=xn().loading(this.body),e.next=3,yn("/api/connectwallet",{assetID:t});case 3:if(r=e.sent,n(),xn().checkResponse(r)){e.next=7;break}return e.abrupt("return");case 7:this.updateDisplayedAsset(t);case 8:case"end":return e.stop()}}),e,this)}))),function(e){return y.apply(this,arguments)})},{key:"openWalletSuccess",value:(v=o(w().mark((function e(t,n){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.assetUpdated(t,n,It(Ce));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t){return v.apply(this,arguments)})},{key:"assetUpdated",value:function(e,t,n){e===this.selectedAssetID&&(this.updateDisplayedAsset(e),t&&Object.is(this.currentForm,t)&&(n?this.showSuccess(n):this.closePopups()))}},{key:"populateMaxSend",value:(m=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,n=xn().assets[this.selectedAssetID]){e.next=4;break}return e.abrupt("return");case 4:0==(64&n.wallet.traits)?(t.sendAmt.value=String(this.maxSend/n.unitInfo.conventional.conversionFactor),this.showFiatValue(n.id,this.maxSend,t.sendValue),t.subtractCheckBox.checked=!1):(r=n.wallet.balance.available,t.sendAmt.value=String(r/n.unitInfo.conventional.conversionFactor),this.showFiatValue(n.id,r,t.sendValue),t.subtractCheckBox.checked=!0);case 5:case"end":return e.stop()}}),e,this)}))),function(){return m.apply(this,arguments)})},{key:"send",value:(f=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=this.page,o=parseInt(null!==(t=a.sendForm.dataset.assetID)&&void 0!==t?t:""),s=null!==(n=a.subtractCheckBox.checked)&&void 0!==n&&n,i=xn().unitInfo(o).conventional.conversionFactor,c=a.vSendPw.value||"",a.vSendPw.value="",""!==c){e.next=9;break}return zt.showFormError(a.vSendErr,It(k)),e.abrupt("return");case 9:return l={assetID:o,address:a.sendAddr.value,subtract:s,value:Math.round(parseFloat(null!==(r=a.sendAmt.value)&&void 0!==r?r:"")*i),pw:c},u=xn().loading(a.vSendForm),e.next=13,yn("/api/send",l);case 13:if(h=e.sent,u(),xn().checkResponse(h)){e.next=18;break}return zt.showFormError(a.vSendErr,h.msg),e.abrupt("return");case 18:d=xn().assets[o].name,this.assetUpdated(o,a.vSendForm,It(be,{assetName:d}));case 20:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"reconfig",value:(p=o(w().mark((function e(){var t,n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,r=this.selectedAssetID,zt.hide(n.reconfigErr),n.appPW.value||en.passwordIsCached()){e.next=6;break}return zt.showFormError(n.reconfigErr,It(x)),e.abrupt("return");case 6:return a=xn().currentWalletDefinition(r).type,zt.isHidden(n.changeWalletType)||(a=n.changeWalletTypeSelect.value||""),o=xn().loading(n.reconfigForm),s={assetID:r,config:this.reconfigForm.map(r),appPW:null!==(t=n.appPW.value)&&void 0!==t?t:"",walletType:a},this.changeWalletPW&&(s.newWalletPW=n.newPW.value),e.next=13,yn("/api/reconfigurewallet",s);case 13:if(i=e.sent,n.appPW.value="",n.newPW.value="",o(),xn().checkResponse(i)){e.next=20;break}return zt.showFormError(n.reconfigErr,i.msg),e.abrupt("return");case 20:this.assetUpdated(r,n.reconfigForm,It(we));case 21:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"lock",value:(d=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=xn().loading(n.newWalletForm),e.next=4,yn("/api/closewallet",{assetID:t});case 4:if(a=e.sent,r(),xn().checkResponse(a)){e.next=8;break}return e.abrupt("return");case 8:this.updateDisplayedAsset(t);case 9:case"end":return e.stop()}}),e,this)}))),function(e){return d.apply(this,arguments)})},{key:"downloadLogs",value:(l=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(t=new URLSearchParams("")).append("assetid","".concat(this.selectedAssetID)),(n=new URL(window.location.href)).search=t.toString(),n.pathname="/wallets/logfile",window.open(n.toString());case 6:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"displayExportWalletAuth",value:(c=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,zt.hide(t.exportWalletErr),t.exportWalletPW.value="",this.showForm(t.exportWalletAuth);case 4:case"end":return e.stop()}}),e,this)}))),function(){return c.apply(this,arguments)})},{key:"exportWalletAuthSubmit",value:(i=o(w().mark((function e(){var t,n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n={assetID:this.selectedAssetID,pass:t.exportWalletPW.value},r=xn().loading(t.forms),e.next=6,yn("/api/restorewalletinfo",n);case 6:a=e.sent,r(),xn().checkResponse(a)?(t.exportWalletPW.value="",this.displayRestoreWalletInfo(a.restorationinfo)):zt.showFormError(t.exportWalletErr,a.msg);case 9:case"end":return e.stop()}}),e,this)}))),function(){return i.apply(this,arguments)})},{key:"displayRestoreWalletInfo",value:(a=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.page,zt.empty(n.restoreInfoCardsList),r=Mr(t);try{for(r.s();!(a=r.n()).done;)o=a.value,s=this.restoreInfoCard.cloneNode(!0),(i=zt.parseTemplate(s)).name.textContent=o.target,i.seed.textContent=o.seed,i.seedName.textContent="".concat(o.seedName,":"),i.instructions.textContent=o.instructions,n.restoreInfoCardsList.appendChild(s)}catch(e){r.e(e)}finally{r.f()}this.showForm(n.restoreWalletInfo);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return a.apply(this,arguments)})},{key:"recoverWallet",value:(n=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.recoverWalletErr),n={assetID:this.selectedAssetID,appPW:t.recoverWalletPW.value},t.recoverWalletPW.value="",r="/api/recoverwallet",a=xn().loading(t.forms),e.next=8,yn(r,n);case 8:o=e.sent,a(),o.code===sn.activeOrdersErr?(this.forceUrl=r,this.forceReq=n,this.showConfirmForce()):xn().checkResponse(o)?this.closePopups():zt.showFormError(t.recoverWalletErr,o.msg);case 11:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"confirmForceSubmit",value:(t=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.forceReq.force=!0,n=xn().loading(t.forms),e.next=5,yn(this.forceUrl,this.forceReq);case 5:r=e.sent,n(),xn().checkResponse(r)?this.closePopups():zt.showFormError(t.confirmForceErr,r.msg);case 8:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleBalanceNote",value:function(e){this.updateAssetButton(e.assetID),e.assetID===this.selectedAssetID&&this.updateDisplayedAssetBalance()}},{key:"handleRatesNote",value:function(e){this.updateAssetButton(this.selectedAssetID),e.fiatRates[this.selectedAssetID]&&this.updateDisplayedAssetBalance()}},{key:"showFiatValue",value:function(e,t,n){var r=xn().fiatRatesMap[e];r?(n.textContent=zt.formatFiatConversion(t,r,xn().unitInfo(e)),zt.show(n.parentElement)):zt.hide(n.parentElement)}},{key:"handleWalletStateNote",value:function(e){this.updateAssetButton(e.wallet.assetID),this.assetUpdated(e.wallet.assetID),"WalletPeersUpdate"===e.topic&&e.wallet.assetID===this.selectedAssetID&&zt.isDisplayed(this.page.managePeersForm)&&this.updateWalletPeersTable()}},{key:"handleCreateWalletNote",value:function(e){this.updateAssetButton(e.assetID),this.assetUpdated(e.assetID)}},{key:"unload",value:function(){zt.unbind(document,"keyup",this.keyup)}}]),V}(fn),Nr=new Intl.NumberFormat(navigator.languages,{maximumSignificantDigits:4}),Ur=new Intl.NumberFormat(navigator.languages,{minimumFractionDigits:1,maximumFractionDigits:1});function _r(e){return e>=1e3||Math.round(e)===e?Ur.format(e):Nr.format(e)}function zr(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var Vr=["bonds"];var jr=function(e){rn(k,e);var t,n,r,a,i,c,l,d,p,f,m,v,y,g,b=(y=k,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(y);if(g){var n=on(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return an(this,e)});function k(e){var t;s(this,k),h(tn(t=b.call(this)),"body",void 0),h(tn(t),"currentDEX",void 0),h(tn(t),"page",void 0),h(tn(t),"forms",void 0),h(tn(t),"fiatRateSources",void 0),h(tn(t),"regAssetForm",void 0),h(tn(t),"confirmRegisterForm",void 0),h(tn(t),"newWalletForm",void 0),h(tn(t),"walletWaitForm",void 0),h(tn(t),"dexAddrForm",void 0),h(tn(t),"currentForm",void 0),h(tn(t),"pwCache",void 0),h(tn(t),"keyup",void 0),t.body=e;var n=t.page=zt.idDescendants(e);t.forms=zt.applySelector(n.forms,":scope > form"),t.fiatRateSources=zt.applySelector(n.fiatRateSources,"input[type=checkbox]"),zt.bind(n.darkMode,"click",(function(){en.setCookie(en.darkModeCK,n.darkMode.checked?"1":"0"),n.darkMode.checked?document.body.classList.add("dark"):document.body.classList.remove("dark")})),n.showPokes.checked="1"===en.fetchLocal(en.popupsLK),zt.bind(n.showPokes,"click",(function(){var e=n.showPokes.checked||!1;en.storeLocal(en.popupsLK,e?"1":"0"),xn().showPopups=e})),n.commitHash.textContent=xn().commitHash.substring(0,7),zt.bind(n.addADex,"click",(function(){t.dexAddrForm.refresh(),t.showForm(n.dexAddrForm)})),t.fiatRateSources.forEach((function(e){zt.bind(e,"change",o(w().mark((function t(){var n;return w().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,yn("/api/toggleratesource",{disable:!e.checked,source:e.value});case 2:return n=t.sent,xn().checkResponse(n)||(e.checked=!e.checked),t.next=6,xn().fetchUser();case 6:case"end":return t.stop()}}),t)}))))})),t.regAssetForm=new br(n.regAssetForm,function(){var e=o(w().mark((function e(r){var a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.confirmRegisterForm.setAsset(r),a=xn().assets[r],!(o=a.wallet)){e.next=14;break}if(s=t.currentDEX.bondAssets[a.symbol],!(o.synced&&o.balance.available>s.amount)){e.next=8;break}return t.animateConfirmForm(n.regAssetForm),e.abrupt("return");case 8:return e.next=10,t.getRegistrationTxFeeEstimate(r,n.regAssetForm);case 10:return i=e.sent,t.walletWaitForm.setWallet(o,i),Rr(n.regAssetForm,n.walletWait),e.abrupt("return");case 14:t.newWalletForm.setAsset(r),t.currentForm=n.newWalletForm,Rr(n.regAssetForm,n.newWalletForm);case 17:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),t.confirmRegisterForm=new gr(n.confirmRegForm,(function(){t.registerDEXSuccess()}),(function(){t.animateRegAsset(n.confirmRegForm)}),t.pwCache),t.newWalletForm=new mr(n.newWalletForm,(function(e){return t.newWalletCreated(e)}),t.pwCache,(function(){return t.animateRegAsset(n.newWalletForm)})),t.walletWaitForm=new kr(n.walletWait,(function(){t.animateConfirmForm(n.walletWait)}),(function(){t.animateRegAsset(n.walletWait)})),t.dexAddrForm=new Sr(n.dexAddrForm,function(){var e=o(w().mark((function e(r,a){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.currentDEX=r,t.confirmRegisterForm.setExchange(r,a),t.walletWaitForm.setExchange(r),t.regAssetForm.setExchange(r),t.animateRegAsset(n.dexAddrForm);case 5:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}()),zt.bind(n.importAccount,"click",(function(){return t.prepareAccountImport(n.authorizeAccountImportForm)})),Or(n.authorizeAccountImportForm,n.authorizeImportAccountConfirm,(function(){return t.importAccount()})),zt.bind(n.changeAppPW,"click",(function(){return t.showForm(n.changeAppPWForm)})),Or(n.changeAppPWForm,n.submitNewPW,(function(){return t.changeAppPW()})),zt.bind(n.accountFile,"change",(function(){return t.onAccountFileChange()})),zt.bind(n.removeAccount,"click",(function(){return t.clearAccountFile()})),zt.bind(n.addAccount,"click",(function(){return n.accountFile.click()})),zt.bind(n.exportSeed,"click",(function(){return t.showForm(n.exportSeedAuth)})),Or(n.exportSeedAuth,n.exportSeedSubmit,(function(){return t.submitExportSeedReq()}));var r=function(){zt.hide(n.forms),n.exportSeedPW.value="",n.seedDiv.textContent=""};return zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},zt.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){r()}))})),t}return u(k,[{key:"getRegistrationTxFeeEstimate",value:(v=o(w().mark((function e(t,n){var r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getCertFile();case 2:return r=e.sent,a=xn().loading(n),e.next=6,yn("/api/regtxfee",{addr:this.currentDEX.host,cert:r,asset:t});case 6:if(o=e.sent,a(),xn().checkResponse(o)){e.next=10;break}return e.abrupt("return",0);case 10:return e.abrupt("return",o.txfee);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return v.apply(this,arguments)})},{key:"newWalletCreated",value:(m=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:if(n=e.sent){e.next=5;break}return e.abrupt("return");case 5:if(r=this.page,a=n.assets[t],o=a.wallet,s=this.currentDEX.bondAssets[a.symbol].amount,!(o.synced&&o.balance.available>s)){e.next=13;break}return e.next=12,this.animateConfirmForm(r.newWalletForm);case 12:return e.abrupt("return");case 13:return e.next=15,this.getRegistrationTxFeeEstimate(t,r.newWalletForm);case 15:return i=e.sent,this.walletWaitForm.setWallet(o,i),this.currentForm=r.walletWait,e.next=20,Rr(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return m.apply(this,arguments)})},{key:"onAccountFileChange",value:(f=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.accountFile.files)&&n.length){e.next=4;break}return e.abrupt("return");case 4:t.selectedAccount.textContent=n[0].name,zt.show(t.removeAccount),zt.hide(t.addAccount);case 7:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"clearAccountFile",value:function(){var e=this.page;e.accountFile.value="",e.selectedAccount.textContent=It(ie),zt.hide(e.removeAccount),zt.show(e.addAccount)}},{key:"prepareAccountImport",value:(p=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.page.importAccountErr.textContent="",this.showForm(t);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return p.apply(this,arguments)})},{key:"importAccount",value:(d=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,n=t.importAccountAppPass.value,t.importAccountAppPass.value="",r="",!t.accountFile.value){e.next=12;break}if((a=t.accountFile.files)&&a.length){e.next=9;break}return console.error("importAccount: no file specified"),e.abrupt("return");case 9:return e.next=11,a[0].text();case 11:r=e.sent;case 12:e.prev=12,o=JSON.parse(r),e.next=21;break;case 16:return e.prev=16,e.t0=e.catch(12),t.importAccountErr.textContent=e.t0.message,zt.show(t.importAccountErr),e.abrupt("return");case 21:if(void 0!==o){e.next=25;break}return t.importAccountErr.textContent=It(X),zt.show(t.importAccountErr),e.abrupt("return");case 25:return i=(s=o).bonds,c=void 0===i?[]:i,l=zr(s,Vr),u={pw:n,account:l,bonds:c},h=xn().loading(this.body),e.next=30,yn("/api/importaccount",u);case 30:if(d=e.sent,h(),xn().checkResponse(d)){e.next=36;break}return t.importAccountErr.textContent=d.msg,zt.show(t.importAccountErr),e.abrupt("return");case 36:return e.next=38,xn().fetchUser();case 38:zt.hide(t.forms),window.location.reload();case 40:case"end":return e.stop()}}),e,this,[[12,16]])}))),function(){return d.apply(this,arguments)})},{key:"submitExportSeedReq",value:(l=o(w().mark((function e(){var t,n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.exportSeedPW.value,r=xn().loading(this.body),e.next=5,yn("/api/exportseed",{pass:n});case 5:if(a=e.sent,r(),xn().checkResponse(a)){e.next=11;break}return t.exportAccountErr.textContent=a.msg,zt.show(t.exportSeedE),e.abrupt("return");case 11:t.exportSeedPW.value="",t.seedDiv.textContent=a.seed,this.showForm(t.authorizeSeedDisplay);case 14:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"showForm",value:(c=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return zt.hide(e)})),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(300,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"getCertFile",value:(i=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t="",!this.dexAddrForm.page.certFile.value){e.next=7;break}if(!(n=this.dexAddrForm.page.certFile.files)||!n.length){e.next=7;break}return e.next=6,n[0].text();case 6:t=e.sent;case 7:return e.abrupt("return",t);case 8:case"end":return e.stop()}}),e,this)}))),function(){return i.apply(this,arguments)})},{key:"registerDEXSuccess",value:(a=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.forms),e.next=4,xn().fetchUser();case 4:window.location.reload();case 5:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"changeAppPW",value:(r=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.changePWErrMsg),n=function(){t.appPW.value="",t.newAppPW.value="",t.confirmNewPW.value=""},t.appPW.value&&t.newAppPW.value&&t.confirmNewPW.value){e.next=8;break}return t.changePWErrMsg.textContent=It(x),zt.show(t.changePWErrMsg),n(),e.abrupt("return");case 8:if(t.newAppPW.value===t.confirmNewPW.value){e.next=13;break}return t.changePWErrMsg.textContent=It(G),zt.show(t.changePWErrMsg),n(),e.abrupt("return");case 13:return r=xn().loading(t.changeAppPW),a={appPW:t.appPW.value,newAppPW:t.newAppPW.value},n(),e.next=18,yn("/api/changeapppass",a);case 18:if(o=e.sent,r(),xn().checkResponse(o)){e.next=24;break}return t.changePWErrMsg.textContent=o.msg,zt.show(t.changePWErrMsg),e.abrupt("return");case 24:zt.hide(t.forms);case 25:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"unload",value:function(){zt.unbind(document,"keyup",this.keyup)}},{key:"animateRegAsset",value:(n=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:zt.hide(t),n=this.page.regAssetForm,this.currentForm=n,this.regAssetForm.animate(),zt.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"animateConfirmForm",value:(t=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),n=this.page.confirmRegForm,this.currentForm=n,zt.hide(t),zt.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),k}(fn);function Hr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(e);try{for(r.s();!(t=r.n()).done;){var a=t.value;if(!a.epoch)return a;n||(n=a)}}catch(e){r.e(e)}finally{r.f()}return n}},{key:"bestGapBuy",value:function(){return this.bestGapOrder(this.buys)}},{key:"bestGapSell",value:function(){return this.bestGapOrder(this.sells)}}]),e}();function Gr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nthis.maxQlength-1;)this.queue.shift();this.queue.push([e,t])}}},{key:"close",value:function(e){window.log("ws","close, reason:",e,this.handlers),this.handlers={},this.connection&&this.connection.close()}},{key:"connect",value:function(e,t){var n=this;this.uri=e,this.reloader=t;var a=0;!function o(){window.log("ws","connecting to ".concat(e));var s=n.connection=new window.WebSocket(e);if(s){var i=setTimeout((function(){s&&s.close()}),500);s.onmessage=function(e){var t=JSON.parse(e.data);Xr(t.route,t.payload,n.handlers)},s.onclose=function(e){window.log("ws","onclose"),clearTimeout(i),s=n.connection=null,Xr("close",null,n.handlers),a++;var t=Math.min(Math.pow(1.25,a),10);console.error("websocket disconnected (".concat(e.code,"), trying again in ").concat(t.toFixed(1)," seconds")),setTimeout((function(){o()}),1e3*t)},s.onopen=function(){window.log("ws","onopen"),clearTimeout(i),a>0&&(a=0,t()),Xr("open",null,n.handlers);var e=n.queue;n.queue=[];var o,s=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Gr(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Gr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(e);try{for(s.s();!(o=s.n()).done;){var c=r(o.value,2),l=c[0],u=c[1];n.request(l,u)}}catch(e){s.e(e)}finally{s.f()}},s.onerror=function(e){window.log("ws","onerror:",e),Xr("error",e,n.handlers)}}}()}}]),e}());function Jr(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Zr(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0}},{key:"setCurrMarketPrice",value:function(){var e=this.market;if(e){var t=xn().exchanges[e.dex.host],n=t.markets[e.cfg.name];if(n.spot){var r,a=$r(this.stats);try{for(a.s();!(r=a.n()).done;){var o=r.value,s=t.assets[n.baseid].unitInfo.conventional.conversionFactor;o.tmpl.volume.textContent=Fa(n.spot.vol24/s),Ia(o.tmpl,t,n)}}catch(e){a.e(e)}finally{a.f()}this.page.obPrice.textContent=zt.formatFullPrecision(n.spot.rate/this.market.rateConversionFactor),this.page.obPrice.classList.remove("sellcolor","buycolor"),this.page.obPrice.classList.add(n.spot.change24>=0?"buycolor":"sellcolor"),zt.setVis(n.spot.change24>=0,this.page.obUp),zt.setVis(n.spot.change24<0,this.page.obDown)}}}},{key:"setMarketDetails",value:function(){if(this.market){var e,t=$r(this.stats);try{for(t.s();!(e=t.n()).done;){var n=e.value;n.tmpl.baseIcon.src=zt.logoPath(this.market.cfg.basesymbol),n.tmpl.quoteIcon.src=zt.logoPath(this.market.cfg.quotesymbol),zt.empty(n.tmpl.baseSymbol,n.tmpl.quoteSymbol),n.tmpl.baseSymbol.appendChild(zt.symbolize(this.market.cfg.basesymbol)),n.tmpl.quoteSymbol.appendChild(zt.symbolize(this.market.cfg.quotesymbol))}}catch(e){t.e(e)}finally{t.f()}}}},{key:"setHighLow",value:function(){var e,t=null===(e=this.market)||void 0===e?void 0:e.candleCaches[da];if(t){for(var n=0,r=0,a=t.candles.length-1;a>=0;a--){var o=t.candles[a];(0===r||o.lowRate>0&&o.lowRaten&&(n=o.highRate)}var s,i=xn().unitInfo(this.market.cfg.quoteid,this.market.dex).conventional.conversionFactor,c=$r(this.stats);try{for(c.s();!(s=c.n()).done;){var l=s.value;l.tmpl.high.textContent=n>0?Fa(n/i):"-",l.tmpl.low.textContent=r>0?Fa(r/i):"-"}}catch(e){c.e(e)}finally{c.f()}}else{var u,h=$r(this.stats);try{for(h.s();!(u=h.n()).done;){var d=u.value;d.tmpl.high.textContent="-",d.tmpl.low.textContent="-"}}catch(e){h.e(e)}finally{h.f()}}}},{key:"assetsAreSupported",value:function(){var e,t,n,r,a=this.market,o=a.base,s=a.quote,i=a.baseCfg,c=a.quoteCfg;if(!o||!s){var l=o?c.symbol:i.symbol;return{isSupported:!1,text:It(L,{asset:l.toUpperCase()})}}var u=o.token?null===(e=xn().assets[o.token.parentID].info)||void 0===e?void 0:e.versions:null===(t=o.info)||void 0===t?void 0:t.versions,h=s.token?null===(n=xn().assets[s.token.parentID].info)||void 0===n?void 0:n.versions:null===(r=s.info)||void 0===r?void 0:r.versions,d="";return u.includes(i.version)?h.includes(c.version)||(d=It(B,{asset:s.symbol.toUpperCase(),version:c.version+""})):d=It(B,{asset:o.symbol.toUpperCase(),version:i.version+""}),{isSupported:u.includes(i.version)&&h.includes(c.version),text:d}}},{key:"setOrderVisibility",value:function(){var e=this.page;this.isLimit()?(zt.show(e.priceBox,e.tifBox,e.qtyBox,e.maxBox),zt.hide(e.mktBuyBox),this.previewQuoteAmt(!0)):(zt.hide(e.tifBox,e.maxBox,e.priceBox),this.isSell()?(zt.hide(e.mktBuyBox),zt.show(e.qtyBox),this.previewQuoteAmt(!0)):(zt.show(e.mktBuyBox),zt.hide(e.qtyBox),this.previewQuoteAmt(!1)))}},{key:"resolveOrderFormVisibility",value:function(){var e=this.page;if(zt.hide(e.orderForm,e.orderTypeBttns),this.assetsAreSupported().isSupported&&!(this.market.dex.tier<1)){var t=this.market,n=t.base,r=t.quote;n&&xn().assets[n.id].wallet&&r&&xn().assets[r.id].wallet&&zt.show(e.orderForm,e.orderTypeBttns)}}},{key:"setLoaderMsgVisibility",value:function(){var e=this.page,t=this.assetsAreSupported(),n=t.isSupported,r=t.text;n?zt.hide(e.loaderMsg):(e.loaderMsg.textContent=r,zt.show(e.loaderMsg),zt.hide(e.notRegistered),zt.hide(e.noWallet))}},{key:"setRegistrationStatusView",value:function(e,t,n){var r=this.page;r.regStatusTitle.textContent=e,r.regStatusConfsDisplay.textContent=t,r.registrationStatus.classList.remove("completed","error","waiting"),r.registrationStatus.classList.add(n)}},{key:"updateRegistrationStatusView",value:function(){var e=this.page,t=this.market.dex;if(e.regStatusDex.textContent=t.host,t.tier>=1)this.setRegistrationStatusView(It(ce),"","completed");else{var n=Object.values(t.pendingBonds).map((function(e){var n=t.bondAssets[e.symbol].confs;return"".concat(e.confs," / ").concat(n)})).join(", ");this.setRegistrationStatusView(It(se),n,"waiting")}}},{key:"setRegistrationStatusVisibility",value:function(){var e=this,t=this.page,n=this.market;if(n&&n.dex&&n.dex.connectionStatus===cn.Connected)if(this.updateRegistrationStatusView(),n.dex.tier>=1){var r=function(){zt.hide(t.registrationStatus,t.bondRequired),e.resolveOrderFormVisibility()};if(zt.isHidden(t.orderForm))return void setTimeout(r,5e3);r()}else n.dex.viewOnly?(t.unregisteredDex.textContent=n.dex.host,zt.show(t.notRegistered)):this.hasPendingBonds()?zt.show(t.registrationStatus):(t.acctTier.textContent="".concat(n.dex.tier),t.dexSettingsLink.href="/dexsettings/".concat(n.dex.host),zt.show(t.bondRequired))}},{key:"setOrderBttnText",value:function(){this.isSell()?this.page.submitBttn.textContent=It(S,{asset:zt.shortSymbol(this.market.baseCfg.symbol)}):this.page.submitBttn.textContent=It(C,{asset:zt.shortSymbol(this.market.baseCfg.symbol)})}},{key:"setCandleDurBttns",value:function(){var e=this,t=this.page,n=this.market;zt.empty(t.durBttnBox);var r,a=$r(n.dex.candleDurs);try{var o=function(){var n=r.value,a=t.durBttnTemplate.cloneNode(!0);a.textContent=n,zt.bind(a,"click",(function(){return e.candleDurationSelected(n)})),t.durBttnBox.appendChild(a)};for(a.s();!(r=a.n()).done;)o()}catch(e){a.e(e)}finally{a.f()}}},{key:"setMarket",value:(x=o(w().mark((function e(t,n,r){var a,o,s,i,c,l,u,h,d,p,f,m,v,y;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=xn().user.exchanges[t],(o=this.page).lotField.value="",o.qtyField.value="",o.rateField.value="",zt.hide(o.notRegistered,o.bondRequired,o.noWallet),a&&a.markets&&a.connectionStatus===cn.Connected){e.next=10;break}return o.chartErrMsg.textContent=It(M),zt.show(o.chartErrMsg),e.abrupt("return");case 10:s=$r(this.stats);try{for(s.s();!(i=s.n()).done;)c=i.value,zt.show(c.row)}catch(e){s.e(e)}finally{s.f()}l=a.assets[n],u=a.assets[r],h=[xn().unitInfo(n,a),xn().unitInfo(r,a)],p=h[1],f=Tn/(d=h[0]).conventional.conversionFactor*p.conventional.conversionFactor,zt.hide(o.maxOrd,o.chartErrMsg),this.maxEstimateTimer&&(window.clearTimeout(this.maxEstimateTimer),this.maxEstimateTimer=null),m=wa(l.symbol,u.symbol),v=xn().assets[n],y=xn().assets[r],this.market={dex:a,sid:m,cfg:a.markets[m],base:v,quote:y,baseUnitInfo:d,quoteUnitInfo:p,maxSell:null,maxBuys:{},maxSellRequested:!1,candleCaches:{},baseCfg:l,quoteCfg:u,rateConversionFactor:f,sellBalance:0,buyBalance:0},zt.setVis(!(v&&y&&v.wallet&&y.wallet),o.noWallet),this.setMarketDetails(),this.setCurrMarketPrice(),this.refreshRecentMatchesTable(),Yr.request("loadmarket",ba(t,n,r)),this.candleDur=da,this.loadCandles(),this.setLoaderMsgVisibility(),this.setRegistrationStatusVisibility(),this.resolveOrderFormVisibility(),this.setOrderBttnText(),this.setCandleDurBttns(),this.previewQuoteAmt(!1);case 35:case"end":return e.stop()}}),e,this)}))),function(e,t,n){return x.apply(this,arguments)})},{key:"reportDepthClick",value:function(e){this.page.rateField.value=String(e),this.rateFieldChanged()}},{key:"reportDepthVolume",value:function(e){var t=this.page,n=this.market,r=n.baseUnitInfo,a=n.quoteUnitInfo;t.sellBookedBase.textContent=zt.formatCoinValue(e.sellBase*r.conventional.conversionFactor,r),t.sellBookedQuote.textContent=zt.formatCoinValue(e.sellQuote*a.conventional.conversionFactor,a),t.buyBookedBase.textContent=zt.formatCoinValue(e.buyBase*r.conventional.conversionFactor,r),t.buyBookedQuote.textContent=zt.formatCoinValue(e.buyQuote*a.conventional.conversionFactor,a)}},{key:"reportDepthMouse",value:function(e){for(;this.hovers.length;)this.hovers.shift().classList.remove("hover");var t=this.page;if(e){zt.show(t.hoverData,t.depthHoverData);for(var n=0,r=Object.values(this.metaOrders);n-1&&(o.classList.add("hover"),this.hovers.push(o))}t.hoverPrice.textContent=zt.formatCoinValue(e.rate),t.hoverVolume.textContent=zt.formatCoinValue(e.depth),t.hoverVolume.style.color=e.dotColor}else zt.hide(t.hoverData,t.depthHoverData)}},{key:"reportDepthZoom",value:function(e){en.storeLocal(en.depthZoomLK,e)}},{key:"reportMouseCandle",value:function(e){var t=this.page;e?(zt.show(t.hoverData,t.candleHoverData),t.candleStart.textContent=zt.formatCoinValue(e.startRate/this.market.rateConversionFactor),t.candleEnd.textContent=zt.formatCoinValue(e.endRate/this.market.rateConversionFactor),t.candleHigh.textContent=zt.formatCoinValue(e.highRate/this.market.rateConversionFactor),t.candleLow.textContent=zt.formatCoinValue(e.lowRate/this.market.rateConversionFactor),t.candleVol.textContent=zt.formatCoinValue(e.matchVolume,this.market.baseUnitInfo)):zt.hide(t.hoverData,t.candleHoverData)}},{key:"parseOrder",value:function(){var e=this.page,t=e.qtyField,n=this.isLimit(),r=this.isSell(),a=this.market,o=a.baseUnitInfo.conventional.conversionFactor;return n||r||(t=e.mktBuyField,o=a.quoteUnitInfo.conventional.conversionFactor),{host:a.dex.host,isLimit:n,sell:r,base:a.base.id,quote:a.quote.id,qty:ka(t.value||"",o),rate:ka(e.rateField.value||"",a.rateConversionFactor),tifnow:e.tifNow.checked||!1,options:{}}}},{key:"previewQuoteAmt",value:function(e){var t=this.page;if(this.market.base&&this.market.quote){var n=this.parseOrder(),r=this.adjustedRate();if(t.orderErr.textContent="",r&&(n.sell?this.preSell():this.preBuy()),this.depthLines.input=[],r&&this.isLimit()&&(this.depthLines.input=[{rate:n.rate/this.market.rateConversionFactor,color:n.sell?this.depthChart.theme.sellLine:this.depthChart.theme.buyLine}]),this.drawChartLines(),!e||!r||!n.qty)return t.orderPreview.textContent="",void this.drawChartLines();var a=xn().assets[n.quote],o=n.qty*n.rate/Tn,s=zt.formatCoinValue(o,this.market.quoteUnitInfo);t.orderPreview.textContent=It(W,{total:s,asset:a.symbol.toUpperCase()}),this.isSell()?this.preSell():this.preBuy()}}},{key:"preSell",value:function(){var e=this,t=this.market,n=xn().assets[t.base.id].wallet;n.balance.available0&&this.book.add(u),this.addTableOrder(u)}}catch(e){l.e(e)}finally{l.f()}if(!this.book)return this.depthChart.clear(),zt.empty(this.page.buyRows),void zt.empty(this.page.sellRows);zt.show(this.page.epochLine),this.loadingAnimations.depth&&this.loadingAnimations.depth.stop(),this.depthChart.canvas.classList.remove("invisible"),this.depthChart.set(this.book,r.lotsize,r.ratestep,a,o),this.recentMatches=null!==(t=e.book.recentMatches)&&void 0!==t?t:[],this.refreshRecentMatchesTable()}},{key:"midGapConventional",value:function(){var e=this.midGap();if(!e)return e;var t=this.market,n=t.baseUnitInfo,r=t.quoteUnitInfo;return e*n.conventional.conversionFactor/r.conventional.conversionFactor}},{key:"midGap",value:function(){var e=this.book;if(e)return e.buys&&e.buys.length?e.sells&&e.sells.length?(e.buys[0].msgRate+e.sells[0].msgRate)/2/Tn:e.buys[0].msgRate/Tn:e.sells&&e.sells.length?e.sells[0].msgRate/Tn:null}},{key:"setMarketBuyOrderEstimate",value:function(){var e=this.market,t=e.cfg.lotsize,n=xn().user.exchanges[e.dex.host].markets[e.sid].buybuffer,r=this.midGapConventional();r&&(this.page.minMktBuy.textContent=zt.formatCoinValue(t*n*r,e.baseUnitInfo))}},{key:"refreshActiveOrders",value:function(){var e=this,t=this.page,n=this.metaOrders,r=this.market;for(var a in n)delete n[a];var o=xn().orders(r.dex.host,wa(r.baseCfg.symbol,r.quoteCfg.symbol));o.sort((function(e,t){return t.submitTime-e.submitTime})),zt.empty(t.userOrders),zt.setVis(null==o?void 0:o.length,t.userOrders),zt.setVis(!(null!=o&&o.length),t.userNoOrders);var s,i=!1,c=$r(o);try{var l=function(){var r=s.value,a=t.userOrderTmpl.cloneNode(!0);t.userOrders.appendChild(a);var o=zt.tmplElement(a,"header"),c=zt.parseTemplate(o),l=zt.tmplElement(a,"details"),u=zt.parseTemplate(l),h={div:a,header:c,details:u,ord:r};r.id&&(n[r.id]=h),r.readyToTick||(o.classList.add("unready-user-order"),i=!0),c.sideLight.classList.add(r.sell?"sell":"buy"),u.side.textContent=c.side.textContent=Pn(r),u.side.classList.add(r.sell?"sellcolor":"buycolor"),c.side.classList.add(r.sell?"sellcolor":"buycolor"),u.qty.textContent=c.qty.textContent=Fa(r.qty/e.market.baseUnitInfo.conventional.conversionFactor),u.rate.textContent=Fa(r.rate/e.market.rateConversionFactor),c.baseSymbol.textContent=r.baseSymbol.toUpperCase(),u.type.textContent=Ln(r),e.updateMetaOrder(h),zt.bind(a,"mouseenter",(function(){e.activeMarkerRate=r.rate,e.setDepthMarkers()})),r.id?(1===r.type&&1===r.tif&&r.status<3&&(zt.show(u.cancelBttn),ta(u.cancelBttn,"click",(function(t){t.stopPropagation(),e.showCancel(a,r.id)}))),ta(u.accelerateBttn,"click",(function(t){t.stopPropagation(),e.showAccelerate(r)})),xn().canAccelerateOrder(r)&&zt.show(u.accelerateBttn),u.link.href="order/".concat(r.id),xn().bindInternalNavigation(a)):(zt.hide(u.accelerateBttn),zt.hide(u.cancelBttn),zt.hide(u.link)),zt.bind(o,"click",(function(){if(zt.isDisplayed(l))return zt.hide(l),c.expander.classList.add("ico-arrowdown"),void c.expander.classList.remove("ico-arrowup");zt.show(l),c.expander.classList.remove("ico-arrowdown"),c.expander.classList.add("ico-arrowup")})),xn().bindTooltips(a)};for(c.s();!(s=c.n()).done;)l()}catch(e){c.e(e)}finally{c.f()}zt.setVis(i,t.unreadyOrdersMsg),this.setDepthMarkers()}},{key:"updateMetaOrder",value:function(e){var t=e.header,n=e.details,r=e.ord;r.status<=2?t.activeLight.classList.add("active"):t.activeLight.classList.remove("active"),n.status.textContent=t.status.textContent=Wn(r),n.age.textContent=zt.timeSince(r.submitTime),n.filled.textContent="".concat((qn(r)/r.qty*100).toFixed(1),"%"),n.settled.textContent="".concat((Nn(r)/r.qty*100).toFixed(1),"%")}},{key:"setDepthMarkers",value:function(){for(var e={buys:[],sells:[]},t=this.market.rateConversionFactor,n=0,r=Object.values(this.metaOrders);n0&&this.book.add(t),this.addTableOrder(t),this.updateTitle(),this.depthChart.draw()}}},{key:"handleUnbookOrderRoute",value:function(e){if(xn().log("book","handleUnbookOrderRoute:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){var t=e.payload;this.book.remove(t.token),this.removeTableOrder(t),this.updateTitle(),this.depthChart.draw()}}},{key:"handleUpdateRemainingRoute",value:function(e){if(xn().log("book","handleUpdateRemainingRoute:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){var t=e.payload;this.book.updateRemaining(t.token,t.qty,t.qtyAtomic),this.updateTableOrder(t),this.depthChart.draw()}}},{key:"handleEpochOrderRoute",value:function(e){if(xn().log("book","handleEpochOrderRoute:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){var t=e.payload;t.msgRate>0&&this.book.add(t),t.qtyAtomic>0&&this.addTableOrder(t),this.depthChart.draw()}}},{key:"handleCandlesRoute",value:function(e){if(this.candlesLoading&&(clearTimeout(this.candlesLoading.timer),this.candlesLoading.loaded(),this.candlesLoading=null),e.host===this.market.dex.host&&e.marketID===this.market.cfg.name){var t=e.payload.dur;this.market.candleCaches[t]=e.payload,this.setHighLow(),this.candleDur===t&&(this.loadingAnimations.candles&&this.loadingAnimations.candles.stop(),this.candleChart.canvas.classList.remove("invisible"),this.candleChart.setCandles(e.payload,this.market.cfg,this.market.baseUnitInfo,this.market.quoteUnitInfo))}}},{key:"handleEpochMatchSummary",value:function(e){this.addRecentMatches(e.payload),this.refreshRecentMatchesTable()}},{key:"handleCandleUpdateRoute",value:function(e){if(e.host===this.market.dex.host){var t=e.payload,n=t.dur,r=t.candle,a=this.market.candleCaches[n];if(a){var o=a.candles;0===o.length?o.push(r):o[o.length-1].startStamp===r.startStamp?o[o.length-1]=r:o.push(r),this.candleDur===n&&this.candleChart.draw()}}}},{key:"showForm",value:(k=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,zt.hide.apply(zt,kn(Array.from(n.forms.children))),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(500,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return k.apply(this,arguments)})},{key:"showOpen",value:(b=o(w().mark((function e(t,n){var r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:r=this.page,this.openAsset=t,this.openFunc=n,this.unlockForm.refresh(xn().assets[t.id]),this.showForm(r.unlockWalletForm),r.uwAppPass.focus();case 6:case"end":return e.stop()}}),e,this)}))),function(e,t){return b.apply(this,arguments)})},{key:"showToggleWalletStatus",value:function(e){var t=this.page;this.openAsset=e,zt.hide(t.toggleWalletStatusErr,t.walletStatusDisable,t.disableWalletMsg),zt.show(t.walletStatusEnable,t.enableWalletMsg),this.showForm(t.toggleWalletStatusConfirm)}},{key:"toggleWalletStatus",value:(g=o(w().mark((function e(){var t,n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.toggleWalletStatusErr),n={assetID:this.openAsset.id,disable:!1},r=xn().loading(t.toggleWalletStatusConfirm),e.next=7,yn("/api/togglewalletstatus",n);case 7:if(a=e.sent,r(),xn().checkResponse(a)){e.next=13;break}return t.toggleWalletStatusErr.textContent=a.msg,zt.show(t.toggleWalletStatusErr),e.abrupt("return");case 13:zt.hide(this.page.forms),this.balanceWgt.updateAsset(this.openAsset.id);case 15:case"end":return e.stop()}}),e,this)}))),function(){return g.apply(this,arguments)})},{key:"showVerify",value:function(){this.preorderCache={};var e,t=this.page,n=this.currentOrder=this.parseOrder(),r=n.sell,a=xn().assets[n.base],o=xn().assets[n.quote],s=r?o:a,i=r?a:o,c=function(e){switch(e.dataset.icon){case"from":if(i.token){var t=xn().assets[i.token.parentID];e.src=zt.logoPath(t.symbol)}else e.src=zt.logoPath(i.symbol);break;case"to":if(s.token){var n=xn().assets[s.token.parentID];e.src=zt.logoPath(n.symbol)}else e.src=zt.logoPath(s.symbol)}},l=$r(zt.applySelector(t.vDetailPane,"[data-icon]"));try{for(l.s();!(e=l.n()).done;)c(e.value)}catch(e){l.e(e)}finally{l.f()}var u,h=$r(zt.applySelector(t.vFeeSummary,"[data-icon]"));try{for(h.s();!(u=h.n()).done;)c(u.value)}catch(e){h.e(e)}finally{h.f()}zt.hide(t.vUnlockPreorder,t.vPreorderErr),zt.show(t.vPreorder),t.vBuySell.textContent=It(r?Se:Ee);var d=It(r?P:T);if(t.vSideSubmit.textContent=d,t.vOrderHost.textContent=n.host,n.isLimit){zt.show(t.verifyLimit),zt.hide(t.verifyMarket);var p="Limit ".concat(d," Order");t.vOrderType.textContent=n.tifnow?p+" (immediate)":p,t.vRate.textContent=zt.formatCoinValue(n.rate/this.market.rateConversionFactor),t.vQty.textContent=zt.formatCoinValue(n.qty,a.unitInfo);var f=n.rate/Tn*n.qty;t.vTotal.textContent=zt.formatCoinValue(f,o.unitInfo),this.showFiatValue(o.id,f,t.vFiatTotal)}else{zt.hide(t.verifyLimit),zt.show(t.verifyMarket),t.vOrderType.textContent="Market ".concat(d," Order");var m=n.sell?this.market.baseUnitInfo:this.market.quoteUnitInfo;t.vmFromTotal.textContent=zt.formatCoinValue(n.qty,m),t.vmFromAsset.textContent=i.symbol.toUpperCase(),this.showFiatValue(i.id,n.qty,t.vmFromTotalFiat);var v=this.midGap();if(v){zt.show(t.vMarketEstimate);var y=n.sell?n.qty*v:n.qty/v;t.vmToTotal.textContent=zt.formatCoinValue(y,s.unitInfo),t.vmToAsset.textContent=s.symbol.toUpperCase(),this.showFiatValue(s.id,y,t.vmTotalFiat)}else zt.hide(t.vMarketEstimate)}r?(t.vHeader.classList.add(ha),t.vHeader.classList.remove(ua),t.vSubmit.classList.add(ha),t.vSubmit.classList.remove(ua)):(t.vHeader.classList.add(ua),t.vHeader.classList.remove(ha),t.vSubmit.classList.add(ua),t.vSubmit.classList.remove(ha)),this.showVerifyForm(),t.vPass.focus(),a.wallet.open&&o.wallet.open?this.preOrder(n):(zt.hide(t.vPreorder),en.passwordIsCached()?this.unlockWalletsForEstimates(""):zt.show(t.vUnlockPreorder))}},{key:"showFiatValue",value:function(e,t,n){if(n){var r=xn().fiatRatesMap[e];n.textContent=zt.formatFiatConversion(t,r,xn().unitInfo(e)),r?zt.show(n.parentElement):zt.hide(n.parentElement)}}},{key:"showVerifyForm",value:(y=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,zt.hide(t.vErr),this.showForm(t.verifyForm);case 3:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"submitEstimateUnlock",value:(v=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page.vUnlockPass.value||"",e.next=3,this.unlockWalletsForEstimates(t);case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e,this)}))),function(){return v.apply(this,arguments)})},{key:"unlockWalletsForEstimates",value:(m=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=xn().loading(n.verifyForm),e.next=4,this.attemptWalletUnlock(t);case 4:if(a=e.sent,r(),!a){e.next=8;break}return e.abrupt("return",this.setPreorderErr(a));case 8:zt.show(n.vPreorder),zt.hide(n.vUnlockPreorder),this.preOrder(this.parseOrder());case 11:case"end":return e.stop()}}),e,this)}))),function(e){return m.apply(this,arguments)})},{key:"attemptWalletUnlock",value:(f=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.market,r=n.base,a=n.quote,o=[],r.wallet.open||o.push(r.id),a.wallet.open||o.push(a.id),s={pass:t,assetID:-1},i=0,c=o;case 6:if(!(i0?E/x:E,F=e.estimate.realisticBestCase/A*100;r.vSwapFeesLowPct.textContent=x<=0?"":"(".concat(c(F),"%)"),r.vSwapFeesLow.textContent=zt.formatCoinValue(e.estimate.realisticBestCase,w);var I=e.estimate.realisticWorstCase/A*100;r.vSwapFeesHighPct.textContent=x<=0?"":"(".concat(c(I),"%)"),r.vSwapFeesHigh.textContent=zt.formatCoinValue(e.estimate.realisticWorstCase,w);var R=e.estimate.maxFees/A*100;r.vSwapFeesMaxPct.textContent=x<=0?"":"(".concat(c(R),"%)"),r.vSwapFeesMax.textContent=zt.formatCoinValue(e.estimate.maxFees,w);var D=this.midGap()||n.rate/i,O=n.sell?E*D:E/D,T=k>0?O/k:O,P=t.estimate.realisticBestCase/T*100;r.vRedeemFeesLowPct.textContent=k<=0?"":"(".concat(c(P),"%)"),r.vRedeemFeesLow.textContent=zt.formatCoinValue(t.estimate.realisticBestCase,b);var L=t.estimate.realisticWorstCase/T*100;r.vRedeemFeesHighPct.textContent=k<=0?"":"(".concat(c(L),"%)"),r.vRedeemFeesHigh.textContent=zt.formatCoinValue(t.estimate.realisticWorstCase,b),l&&u?(zt.show(r.vFeeSummaryPct),zt.hide(r.vFeeSummary),r.vFeeSummaryLow.textContent=c(F+P),r.vFeeSummaryHigh.textContent=c(I+L)):(zt.hide(r.vFeeSummaryPct),zt.show(r.vFeeSummary),r.summarySwapFeesLow.textContent=r.vSwapFeesLow.textContent,r.summarySwapFeesHigh.textContent=r.vSwapFeesHigh.textContent,r.summaryRedeemFeesLow.textContent=r.vRedeemFeesLow.textContent,r.summaryRedeemFeesHigh.textContent=r.vRedeemFeesHigh.textContent)}else zt.hide(r.vPreorderEstimates)}},{key:"submitCancel",value:(l=o(w().mark((function e(){var t,n,r,a,o,s;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=this.cancelData,r=n.order,a={orderID:r.id,pw:t.cancelPass.value},t.cancelPass.value="",o=xn().loading(t.cancelSubmit),e.next=8,yn("/api/cancel",a);case 8:if(s=e.sent,o(),xn().checkResponse(s)){e.next=14;break}return t.cancelErr.textContent=s.msg,zt.show(t.cancelErr),e.abrupt("return");case 14:zt.hide(n.bttn,t.forms),r.cancelling=!0;case 16:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"showCancel",value:function(e,t){var n=this.metaOrders[t].ord,r=this.page,a=n.qty-n.filled,o=Bn(n)?this.market.quote:this.market.base;r.cancelRemain.textContent=zt.formatCoinValue(a,o.unitInfo),r.cancelUnit.textContent=o.symbol.toUpperCase(),zt.hide(r.cancelErr),this.showForm(r.cancelForm),r.cancelPass.focus(),this.cancelData={bttn:zt.tmplElement(e,"cancelBttn"),order:n}}},{key:"showAccelerate",value:function(e){var t=xn().loading(this.main);this.accelerateOrderForm.refresh(e),t(),this.showForm(this.page.accelerateForm)}},{key:"showCreate",value:function(e){var t=this.page;this.currentCreate=e,this.newWalletForm.setAsset(e.id),this.showForm(t.newWalletForm)}},{key:"stepSubmit",value:function(){var e=this.page,t=this.market;if(zt.hide(e.orderErr),this.validateOrder(this.parseOrder())){var n=xn().walletMap[t.base.id],r=xn().walletMap[t.quote.id];return n?r?void this.showVerify():(e.orderErr.textContent=It(V,{asset:t.quote.symbol}),void zt.show(e.orderErr)):(e.orderErr.textContent=It(V,{asset:t.base.symbol}),void zt.show(e.orderErr))}}},{key:"showDeposit",value:(c=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.depositAddrForm.setAsset(t),this.showForm(this.page.deposit);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"handlePriceUpdate",value:function(e){e.host===this.market.dex.host&&e.spots[this.market.cfg.name]&&this.setCurrMarketPrice(),this.marketList.updateSpots(e)}},{key:"handleBondUpdate",value:(i=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if((n=t.dex)===this.market.dex.host){e.next=3;break}return e.abrupt("return");case 3:if("AccountRegistered"!==t.topic){e.next=6;break}return e.next=6,xn().fetchUser();case 6:this.market.dex=xn().exchanges[n],this.setRegistrationStatusVisibility();case 8:case"end":return e.stop()}}),e,this)}))),function(e){return i.apply(this,arguments)})},{key:"handleMatchNote",value:function(e){var t=this.metaOrders[e.orderID];if(!t)return this.refreshActiveOrders();xn().canAccelerateOrder(t.ord)?zt.show(t.details.accelerateBttn):zt.hide(t.details.accelerateBttn)}},{key:"handleOrderNote",value:function(e){var t=e.order,n=this.metaOrders[t.id];if(!n||"AsyncOrderFailure"===e.topic||"OrderLoaded"===e.topic&&t.readyToTick)return this.refreshActiveOrders();var r=n.status;n.ord=t,"MissedCancel"===e.topic&&zt.show(n.details.cancelBttn),t.filled===t.qty&&zt.hide(n.details.cancelBttn),xn().canAccelerateOrder(t)?zt.show(n.details.accelerateBttn):zt.hide(n.details.accelerateBttn),this.updateMetaOrder(n),(1===r&&2===t.status||2===r&&t.status>2)&&this.setDepthMarkers()}},{key:"handleEpochNote",value:function(e){if(xn().log("book","handleEpochNote:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){this.book&&(this.book.setEpoch(e.epoch),this.depthChart.draw()),this.clearOrderTableEpochs();for(var t=0,n=Object.values(this.metaOrders);ta.epoch;switch(!0){case 1===a.type&&1===a.status&&i:var c=0===a.tif?It(j):It(H);o.status.textContent=s.status.textContent=c,a.status=0===a.tif?3:2;break;case 2===a.type&&1===a.status:o.status.textContent=s.status.textContent=It(j),a.status=3}}}}},{key:"recentMatchesSortCompare",value:function(){var e=this;switch(this.recentMatchesSortKey){case"rate":return function(t,n){return e.recentMatchesSortDirection*(t.rate-n.rate)};case"qty":return function(t,n){return e.recentMatchesSortDirection*(t.qty-n.qty)};case"age":return function(t,n){return e.recentMatchesSortDirection*(t.stamp-n.stamp)}}}},{key:"refreshRecentMatchesTable",value:function(){var e=this.page,t=this.recentMatches;if(zt.empty(e.recentMatchesLiveList),t){var n=this.recentMatchesSortCompare();t.sort(n);var r,a=$r(t);try{for(a.s();!(r=a.n()).done;){var o=r.value,s=e.recentMatchesTemplate.cloneNode(!0),i=zt.parseTemplate(s);xn().bindTooltips(s),i.rate.textContent=zt.formatCoinValue(o.rate/this.market.rateConversionFactor),i.qty.textContent=zt.formatCoinValue(o.qty,this.market.baseUnitInfo),i.age.textContent=zt.timeSince(o.stamp),i.age.dataset.sinceStamp=String(o.stamp),s.classList.add(o.sell?"sellcolor":"buycolor"),e.recentMatchesLiveList.append(s)}}catch(e){a.e(e)}finally{a.f()}}}},{key:"addRecentMatches",value:function(e){this.recentMatches=[].concat(kn(e),kn(this.recentMatches)).slice(0,100)}},{key:"setBalanceVisibility",value:function(){var e=this.market;e&&e.dex&&this.balanceWgt.setBalanceVisibility(e.dex.connectionStatus===cn.Connected)}},{key:"handleBalanceNote",value:function(e){this.setBalanceVisibility(),this.preorderCache={};var t=this.market;if(t&&t.dex&&t.dex.connectionStatus===cn.Connected){var n=e.balance.available;switch(e.assetID){case t.baseCfg.id:if(!t.maxSell)break;"number"==typeof t.sellBalance&&t.sellBalance!==n&&(t.maxSell=null),this.isSell()&&this.preSell();break;case t.quoteCfg.id:if(!Object.keys(t.maxBuys).length)break;"number"==typeof t.buyBalance&&t.buyBalance!==n&&(t.maxBuys={}),this.isSell()||this.preBuy()}}}},{key:"submitOrder",value:(a=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.orderErr,t.vErr),n=this.currentOrder,r=t.vPass.value,t.vPass.value="",a={order:Ca(n),pw:r},this.validateOrder(n)){e.next=8;break}return e.abrupt("return");case 8:return t.vSubmit.classList.add("d-hide"),t.vLoader.classList.remove("d-hide"),e.next=12,yn("/api/tradeasync",a);case 12:if(o=e.sent,t.vSubmit.classList.remove("d-hide"),t.vLoader.classList.add("d-hide"),xn().checkResponse(o)){e.next=19;break}return t.vErr.textContent=o.msg,zt.show(t.vErr),e.abrupt("return");case 19:zt.hide(t.forms),this.refreshActiveOrders();case 21:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"createWallet",value:(r=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:if(t=e.sent){e.next=5;break}return e.abrupt("return");case 5:n=t.assets[this.currentCreate.id],zt.hide(this.page.forms),this.balanceWgt.updateAsset(n.id),zt.setVis(!(this.market.base.wallet&&this.market.quote.wallet),this.page.noWallet),this.resolveOrderFormVisibility();case 10:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"walletUnlocked",value:(n=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:zt.hide(this.page.forms),this.balanceWgt.updateAsset(this.openAsset.id);case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"lotChanged",value:function(){var e=this.page,t=parseInt(e.lotField.value||"0");if(t<=0)return e.lotField.value="0",e.qtyField.value="",void this.previewQuoteAmt(!1);var n=this.market.cfg.lotsize;e.lotField.value=String(t),e.qtyField.value=String(t*n/this.market.baseUnitInfo.conventional.conversionFactor),this.previewQuoteAmt(!0)}},{key:"quantityChanged",value:function(e){var t=this.page,n=this.parseOrder();if(n.qty<0)return t.lotField.value="0",t.qtyField.value="",void this.previewQuoteAmt(!1);var r=this.market.cfg.lotsize,a=Math.floor(n.qty/r),o=a*r;t.lotField.value=String(a),(n.isLimit||n.sell)&&(e&&(t.qtyField.value=String(o/this.market.baseUnitInfo.conventional.conversionFactor)),this.previewQuoteAmt(!0))}},{key:"marketBuyChanged",value:function(){var e=this.page,t=ka(e.mktBuyField.value||"",this.market.quoteUnitInfo.conventional.conversionFactor),n=this.midGap();if(!n||!t)return e.mktBuyLots.textContent="0",void(e.mktBuyScore.textContent="0");var r=this.market.cfg.lotsize,a=t/n;e.mktBuyLots.textContent=(a/r).toFixed(1),e.mktBuyScore.textContent=zt.formatCoinValue(a,this.market.baseUnitInfo)}},{key:"rateFieldChanged",value:function(){var e=this.adjustedRate();if(e<=0)return this.depthLines.input=[],this.drawChartLines(),void(this.page.rateField.value="0");var t=this.parseOrder(),n=e/this.market.rateConversionFactor;this.page.rateField.value=String(n),this.depthLines.input=[{rate:n,color:t.sell?this.depthChart.theme.sellLine:this.depthChart.theme.buyLine}],this.drawChartLines(),this.previewQuoteAmt(!0)}},{key:"adjustedRate",value:function(){var e=this.page.rateField.value;if(!e)return NaN;var t=ka(e,this.market.rateConversionFactor);return t-t%this.market.cfg.ratestep}},{key:"loadTable",value:function(){this.loadTableSide(!0),this.loadTableSide(!1)}},{key:"binOrdersByRateAndEpoch",value:function(e){if(!e||!e.length)return[];var t=[],n=[],r=[],a=e[0].msgRate;e[0].epoch?n.push(e[0]):r.push(e[0]);for(var o=1;o0}))}},{key:"loadTableSide",value:function(e){var t=this,n=e?this.book.sells:this.book.buys,r=e?this.page.sellRows:this.page.buyRows;zt.empty(r),n&&n.length&&this.binOrdersByRateAndEpoch(n).forEach((function(e){r.appendChild(t.orderTableRow(e))}))}},{key:"addTableOrder",value:function(e){var t=e.sell?this.page.sellRows:this.page.buyRows,n=t.firstChild;if(0!==e.rate){for(n&&0===n.manager.getRate()&&(n=n.nextSibling);n;){if(0===n.manager.compare(e))return void n.manager.insertOrder(e);if(n.manager.compare(e)>0){var r=this.orderTableRow([e]);return void t.insertBefore(r,n)}n=n.nextSibling}var a=this.orderTableRow([e]);t.appendChild(a)}else{if(0===e.qtyAtomic)return;n&&0===n.manager.getRate()?n.manager.insertOrder(e):(n=this.orderTableRow([e]),t.insertBefore(n,t.firstChild))}}},{key:"removeTableOrder",value:function(e){for(var t=e.token,n=0,r=[this.page.sellRows,this.page.buyRows];n36e5&&!i.disabled?(zt.show(n.expired),i.running&&xn().fetchBalance(o)):zt.hide(n.expired)}else zt.show(n.connect)}else zt.show(n.unsupported)}}},{key:"updateParent",value:function(e){var t=xn().assets[e.parentID],n=t.wallet.balance,r=t.unitInfo;e.parentBal&&(e.parentBal.textContent=zt.formatCoinValue(n.available,r))}},{key:"updateAsset",value:function(e){e===this.base.id?this.updateWallet(this.base):e===this.quote.id&&this.updateWallet(this.quote),e===this.base.parentID&&this.updateParent(this.base),e===this.quote.parentID&&this.updateParent(this.quote)}}]),e}();function ba(e,t,n){return{host:e,base:t,quote:n}}function wa(e,t){return"".concat(e,"_").concat(t)}function ka(e,t){return e?Math.round(parseFloat(e)*t):0}function xa(e,t){e.classList.remove("selected"),t.classList.add("selected")}function Ca(e){for(var t={},n=0,a=Object.entries(e.options);n1?(r.removeAttribute("hidden"),r.innerText=String(t),r.title="quantity is comprised of ".concat(t," orders")):r.setAttribute("hidden","true")}},{key:"insertOrder",value:function(e){this.orderBin.push(e),this.updateQtyNumOrdersEl()}},{key:"updateOrderQty",value:function(e){for(var t=e.token,n=e.qty,r=e.qtyAtomic,a=0;ae.msgRate===e.sell?1:-1:this.isEpoch()?1:-1}}]),e}(),Ea=new Intl.NumberFormat(navigator.languages,{maximumSignificantDigits:4}),Aa=new Intl.NumberFormat(navigator.languages,{minimumFractionDigits:1,maximumFractionDigits:1});function Fa(e){return e>=1e3||Math.round(e)===e?Aa.format(e):Ea.format(e)}function Ia(e,t,n){if(n.spot){e.price.textContent=Fa(xn().conventionalRate(n.baseid,n.quoteid,n.spot.rate,t));var r=n.spot.change24>0?"+":"";e.change.classList.add(n.spot.change24>=0?"buycolor":"sellcolor"),e.change.textContent="".concat(r).concat((100*n.spot.change24).toFixed(1),"%")}}var Ra=[.5,1/4,3/4,1/8,5/8,3/8,7/8];function Da(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Oa(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Oa(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Oa(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0&&(e.checked=!0)}))}};o(n.hostFilter,"hosts"),o(n.assetFilter,"assets"),o(n.statusFilter,"statuses");var i=[],c=function(e,n){var a=e.querySelector(".apply-bttn");i.push(a),zt.bind(a,"click",(function(){t.submitFilter(),i.forEach((function(e){return zt.hide(e)}))})),e.querySelectorAll("input").forEach((function(t){zt.bind(t,"change",(function(){!function(e,t){if(e.length!==t.length)return!1;var n,r=Da(e);try{for(r.s();!(n=r.n()).done;){var a=n.value;if(-1===t.indexOf(a))return!1}}catch(e){r.e(e)}finally{r.f()}return!0}(Na(e),r[n])?zt.show(a):zt.hide(a)}))}))};return c(n.hostFilter,"hosts"),c(n.assetFilter,"assets"),c(n.statusFilter,"statuses"),zt.bind(t.main,"scroll",(function(){t.loading||n.ordersTable.offsetHeight-t.main.offsetHeight-t.main.scrollTop<0&&t.nextPage()})),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){zt.hide(n.forms)}))})),zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||zt.hide(n.forms)})),zt.bind(n.exportOrders,"click",(function(){t.exportOrders()})),n.showArchivedDateField.addEventListener("change",(function(){n.showArchivedDateField.checked?zt.show(n.archivedDateField):zt.hide(n.archivedDateField,n.deleteArchivedRecordsErr)})),zt.bind(n.deleteArchivedRecords,"click",(function(){var e=t.page;e.showArchivedDateField.checked=!1,e.saveMatchesToFile.checked=!1,e.saveOrdersToFile.checked=!1,e.deleteArchivedRecordsErr.textContent="",e.archivedRecordsLocation.textContent="",e.deleteArchivedRecordsMsg.textContent="",zt.hide(e.deleteArchivedResult,e.deleteArchivedRecordsErr,e.deleteArchivedRecordsMsg,e.archivedRecordsLocation,e.archivedDateField),t.showForm(e.deleteArchivedRecordsForm)})),zt.bind(n.deleteArchivedRecordsSubmit,"click",(function(){var e=0;n.showArchivedDateField.checked&&(e=Date.parse(n.olderThan.value||""),isNaN(e)||e<=0)?zt.showFormError(n.deleteArchivedRecordsErr,It(qe)):t.deleteArchivedRecords(e)})),t.submitFilter(),t}return u(p,[{key:"showForm",value:(i=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,zt.hide(n.deleteArchivedRecordsForm),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(500,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0px";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return i.apply(this,arguments)})},{key:"setOrders",value:function(e){zt.empty(this.page.tableBody),this.appendOrders(e)}},{key:"appendOrders",value:function(e){var t,n=this,r=this.page.tableBody,a=Da(e);try{var o=function(){var e,a,o,s=t.value,i=n.orderTmpl.cloneNode(!0),c=function(e,t){zt.tmplElement(i,e).textContent=t},l="".concat(s.baseSymbol.toUpperCase(),"-").concat(s.quoteSymbol.toUpperCase());c("host","".concat(l," @ ").concat(s.host));var u="",h=[xn().unitInfo(s.baseID),xn().unitInfo(s.quoteID)],d=h[0],p=h[1];if(s.sell){var f=[s.baseSymbol,s.quoteSymbol];e=f[0],a=f[1],o=zt.formatCoinValue(s.qty,d),1===s.type&&(u=zt.formatCoinValue(s.qty/Tn*s.rate,p))}else{var m=[s.quoteSymbol,s.baseSymbol];e=m[0],a=m[1],2===s.type?o=zt.formatCoinValue(s.qty,d):(o=zt.formatCoinValue(s.qty/Tn*s.rate,p),u=zt.formatCoinValue(s.qty,d))}c("fromQty",o),zt.tmplElement(i,"fromLogo").src=zt.logoPath(e),c("fromSymbol",e),c("toQty",u),zt.tmplElement(i,"toLogo").src=zt.logoPath(a),c("toSymbol",a),c("type","".concat(Ln(s)," ").concat(Pn(s))),c("rate",zt.formatCoinValue(xn().conventionalRate(s.baseID,s.quoteID,s.rate))),c("status",Wn(s)),c("filled","".concat((qn(s)/s.qty*100).toFixed(1),"%")),c("settled","".concat((Nn(s)/s.qty*100).toFixed(1),"%"));var v=new Date(s.submitTime).toLocaleString();c("time","".concat(zt.timeSince(s.submitTime)," ago, ").concat(v)),zt.tmplElement(i,"link").href="order/".concat(s.id),xn().bindInternalNavigation(i),r.appendChild(i)};for(a.s();!(t=a.n()).done;)o()}catch(e){a.e(e)}finally{a.f()}50===e.length?this.offset=e[e.length-1].id:this.offset=""}},{key:"submitFilter",value:(a=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.offset="",(n=this.filterState).hosts=Na(t.hostFilter),n.assets=Na(t.assetFilter).map((function(e){return parseInt(e)})),n.statuses=Na(t.statusFilter).map((function(e){return parseInt(e)})),e.t0=this,e.next=9,this.fetchOrders();case 9:e.t1=e.sent,e.t0.setOrders.call(e.t0,e.t1);case 11:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"fetchOrders",value:(r=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=xn().loading(this.main),e.next=3,yn("/api/orders",this.currentFilter());case 3:return n=e.sent,t(),e.abrupt("return",n.orders);case 6:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"exportOrders",value:function(){this.offset="";var e=this.currentFilter(),t=new URL(window.location.href),n=new URLSearchParams(""),r=function(t){e[t].forEach((function(e){n.append(t,e)}))};r("hosts"),r("assets"),r("statuses"),t.search=n.toString(),t.pathname="/orders/export",window.open(t.toString())}},{key:"deleteArchivedRecords",value:(n=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=n.saveMatchesToFile.checked||!1,a=n.saveOrdersToFile.checked||!1,o={olderThanMs:t,saveMatchesToFile:r,saveOrdersToFile:a},s=xn().loading(this.main),e.next=7,yn("/api/deletearchivedrecords",o);case 7:if(i=e.sent,s(),xn().checkResponse(i)){e.next=11;break}return e.abrupt("return",zt.showFormError(n.deleteArchivedRecordsErr,i.msg));case 11:i.archivedRecordsDeleted>0?(n.deleteArchivedRecordsMsg.textContent=It(Ue,{nRecords:i.archivedRecordsDeleted}),(r||a)&&(n.archivedRecordsLocation.textContent=It(_e,{path:i.archivedRecordsPath}),zt.show(n.archivedRecordsLocation)),this.submitFilter()):n.deleteArchivedRecordsMsg.textContent=It(Ne),zt.show(n.deleteArchivedResult,n.deleteArchivedRecordsMsg);case 13:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"currentFilter",value:function(){var e=this.filterState;return{hosts:e.hosts,assets:e.assets.map((function(e){return parseInt(e)})),statuses:e.statuses.map((function(e){return parseInt(e)})),n:50,offset:this.offset}}},{key:"nextPage",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(""!==this.offset&&!this.loading){e.next=2;break}return e.abrupt("return");case 2:return this.loading=!0,zt.show(this.page.orderLoader),e.next=6,this.fetchOrders();case 6:t=e.sent,this.loading=!1,zt.hide(this.page.orderLoader),this.appendOrders(t);case 10:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),p}(fn);function Na(e){var t=[];return e.querySelectorAll("input").forEach((function(e){e.checked&&t.push(e.value)})),t}function Ua(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return _a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_a(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function _a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=3}(t)||t.revoked||t.refund?!function(e){return e.status<5&&1===e.side&&e.status>=4}(t)||t.revoked||t.refund?zt.hide(n.makerSwapMsg,n.takerSwapMsg,n.makerRedeemMsg,n.takerRedeemMsg):(n.takerRedeemMsg.textContent=Ha(t.redeem),zt.hide(n.makerSwapMsg,n.takerSwapMsg,n.makerRedeemMsg),zt.show(n.takerRedeemMsg)):(n.makerRedeemMsg.textContent=Ha(t.redeem),zt.hide(n.makerSwapMsg,n.takerSwapMsg,n.takerRedeemMsg),zt.show(n.makerRedeemMsg));else{var a=Ga(t);n.takerSwapMsg.textContent=Ha(a),zt.hide(n.makerSwapMsg,n.makerRedeemMsg,n.takerRedeemMsg),zt.show(n.takerSwapMsg)}else{var o=Ka(t);n.makerSwapMsg.textContent=Ha(o),zt.hide(n.takerSwapMsg,n.makerRedeemMsg,n.takerRedeemMsg),zt.show(n.makerSwapMsg)}zt.setVis(!t.isCancel&&(Ka(t)||!t.revoked),n.makerSwap),zt.setVis(!t.isCancel&&(Ga(t)||!t.revoked),n.takerSwap),zt.setVis(!t.isCancel&&(Xa(t)||!t.revoked),n.makerRedeem),zt.setVis(!t.isCancel&&(Qa(t)||!t.revoked&&t.active||1===t.side&&t.active&&(t.counterRedeem||!t.refund)),n.takerRedeem),zt.setVis(!t.isCancel&&(t.refund||t.revoked&&t.active&&!t.counterRedeem),n.refund)}}},{key:"addNewMatchCard",value:function(e){var t=this.page,n=t.matchCardTmpl.cloneNode(!0);n.dataset.matchID=e.matchID,this.setImmutableMatchCardElements(n,e),this.setMutableMatchCardElements(n,e),t.matchBox.appendChild(n)}},{key:"showMatchCards",value:function(){var e=this,t=this.order;t&&t.matches&&(t.matches.sort((function(e,t){return e.stamp-t.stamp})),t.matches.forEach((function(t){return e.addNewMatchCard(t)})))}},{key:"showCancel",value:function(){var e=this.order,t=this.page,n=e.qty-e.filled,r=Bn(e)?xn().assets[e.quoteID]:xn().assets[e.baseID];t.cancelRemain.textContent=zt.formatCoinValue(n,r.unitInfo),t.cancelUnit.textContent=r.unitInfo.conventional.unit.toUpperCase(),this.showForm(t.cancelForm)}},{key:"showForm",value:(r=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,zt.hide(n.cancelForm,n.accelerateForm),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(500,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0px";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"submitCancel",value:(n=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=this.order,r={orderID:n.id,pw:t.cancelPass.value},t.cancelPass.value="",a=xn().loading(t.cancelForm),e.next=7,yn("/api/cancel",r);case 7:if(o=e.sent,a(),xn().checkResponse(o)){e.next=11;break}return e.abrupt("return");case 11:t.status.textContent=It(K),zt.hide(t.forms),n.cancelling=!0;case 14:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"setAccelerationButtonVis",value:function(){var e=this.order;if(e){var t=this.page;zt.setVis(xn().canAccelerateOrder(e),t.accelerateBttn,t.actionsLabel)}}},{key:"showAccelerateForm",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=xn().loading(this.page.accelerateBttn),this.accelerateOrderForm.refresh(this.order),t(),this.showForm(this.page.accelerateForm);case 4:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleOrderNote",value:function(e){var t=this.page,n=e.order;if(n.id===this.orderID){this.order=n;var r=t.cancelBttn;r&&n.status>2&&zt.hide(r),t.status.textContent=Wn(n);var a,o=Ua(n.matches||[]);try{for(o.s();!(a=o.n()).done;){var s=a.value;this.processMatch(s)}}catch(e){o.e(e)}finally{o.f()}this.setAccelerationButtonVis()}}},{key:"handleMatchNote",value:function(e){e.orderID===this.orderID&&(this.processMatch(e.match),this.setAccelerationButtonVis())}},{key:"processMatch",value:function(e){var t,n=null,r=Ua(zt.applySelector(this.page.matchBox,".match-card"));try{for(r.s();!(t=r.n()).done;){var a=t.value;if(a.dataset.matchID===e.matchID){n=a;break}}}catch(e){r.e(e)}finally{r.f()}n?this.setMutableMatchCardElements(n,e):this.addNewMatchCard(e)}}]),p}(fn);function Ha(e){return e.confs&&0!==e.confs.required?"".concat(e.confs.count," / ").concat(e.confs.required," ").concat(It(pt)):""}function Ka(e){return 0===e.side?e.swap:e.counterSwap}function Ga(e){return 0===e.side?e.counterSwap:e.swap}function Xa(e){return 0===e.side?e.redeem:e.counterRedeem}function Qa(e){return 0===e.side?e.counterRedeem:e.redeem}function Ya(e,t){var n=Za[e];if(n){var r=n[za];r&&(t.classList.remove("plainlink"),t.classList.add("subtlelink"),t.href=r(t.dataset.explorerCoin||""))}}var Ja=(h(Ta={},0,(function(e){if(e.startsWith(Va)){var t=e.substring(Va.length);return"https://etherscan.io/address/".concat(t)}return 42===e.length?"https://etherscan.io/address/".concat(e):"https://etherscan.io/tx/".concat(e)})),h(Ta,1,(function(e){if(e.startsWith(Va)){var t=e.substring(Va.length);return"https://goerli.etherscan.io/address/".concat(t)}return 42===e.length?"https://goerli.etherscan.io/address/".concat(e):"https://goerli.etherscan.io/tx/".concat(e)})),Ta),Za={42:(Pa={},h(Pa,0,(function(e){var t=r(e.split(":"),2),n=t[0],a=t[1];return"https://explorer.dcrdata.org/tx/".concat(n,"/out/").concat(a)})),h(Pa,1,(function(e){var t=r(e.split(":"),2),n=t[0],a=t[1];return"https://testnet.dcrdata.org/tx/".concat(n,"/out/").concat(a)})),Pa),0:(La={},h(La,0,(function(e){return"https://mempool.space/tx/".concat(e.split(":")[0])})),h(La,1,(function(e){return"https://mempool.space/testnet/tx/".concat(e.split(":")[0])})),La),2:(Ba={},h(Ba,0,(function(e){return"https://ltc.bitaps.com/".concat(e.split(":")[0])})),h(Ba,1,(function(e){return"https://sochain.com/tx/LTCTEST/".concat(e.split(":")[0])})),Ba),60:Ja,60001:Ja,3:(Ma={},h(Ma,0,(function(e){return"https://dogeblocks.com/tx/".concat(e.split(":")[0])})),h(Ma,1,(function(e){return"https://blockexplorer.one/dogecoin/testnet/tx/".concat(e.split(":")[0])})),Ma),145:(Wa={},h(Wa,0,(function(e){return"https://bch.loping.net/tx/".concat(e.split(":")[0])})),h(Wa,1,(function(e){return"https://tbch4.loping.net/tx/".concat(e.split(":")[0])})),Wa)};var $a=function(e){rn(g,e);var t,n,a,i,c,l,d,p,f,m,v,y=(m=g,v=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(m);if(v){var n=on(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return an(this,e)});function g(e){var t;s(this,g),h(tn(t=y.call(this)),"body",void 0),h(tn(t),"forms",void 0),h(tn(t),"currentForm",void 0),h(tn(t),"page",void 0),h(tn(t),"host",void 0),h(tn(t),"keyup",void 0),h(tn(t),"dexAddrForm",void 0),t.body=e,t.host=e.dataset.host?e.dataset.host:"";var n=t.page=zt.idDescendants(e);t.forms=zt.applySelector(n.forms,":scope > form"),zt.bind(n.exportDexBtn,"click",(function(){return t.prepareAccountExport(n.authorizeAccountExportForm)})),zt.bind(n.disableAcctBtn,"click",(function(){return t.prepareAccountDisable(n.disableAccountForm)})),zt.bind(n.updateBondOptionsBtn,"click",(function(){return t.prepareUpdateBondOptions()})),zt.bind(n.updateCertBtn,"click",(function(){return n.certFileInput.click()})),zt.bind(n.updateHostBtn,"click",(function(){return t.prepareUpdateHost()})),zt.bind(n.certFileInput,"change",(function(){return t.onCertFileChange()})),t.dexAddrForm=new Sr(n.dexAddrForm,function(){var e=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:window.location.assign("/dexsettings/".concat(t.host));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),void 0,t.host),Or(n.updateBondOptionsForm,n.updateBondOptionsConfirm,(function(){return t.updateBondOptions()})),Or(n.authorizeAccountExportForm,n.authorizeExportAccountConfirm,(function(){return t.exportAccount()})),Or(n.disableAccountForm,n.disableAccountConfirm,(function(){return t.disableAccount()}));var r=function(){zt.hide(n.forms)};return zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},zt.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){r()}))})),xn().registerNoteFeeder({conn:function(){t.setConnectionStatus()}}),t.setConnectionStatus(),t}return u(g,[{key:"unload",value:function(){zt.unbind(document,"keyup",this.keyup)}},{key:"showForm",value:(f=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return zt.hide(e)})),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(300,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return f.apply(this,arguments)})},{key:"exportAccount",value:(p=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.exportAccountAppPass.value,r=t.exportAccountHost.textContent,t.exportAccountAppPass.value="",a={pw:n,host:r},o=xn().loading(this.body),e.next=8,yn("/api/exportaccount",a);case 8:if(s=e.sent,o(),xn().checkResponse(s)){e.next=14;break}return t.exportAccountErr.textContent=s.msg,zt.show(t.exportAccountErr),e.abrupt("return");case 14:s.account.bonds=s.bonds,i=JSON.parse(JSON.stringify(s.account)),(c=document.createElement("a")).setAttribute("download","dcrAccount-"+r+".json"),c.setAttribute("href","data:text/json,"+JSON.stringify(i,null,2)),c.click(),zt.hide(t.forms);case 21:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"disableAccount",value:(d=o(w().mark((function e(){var t,n,r,a,o,s;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.disableAccountAppPW.value,r=t.disableAccountHost.textContent,t.disableAccountAppPW.value="",a={pw:n,host:r},o=xn().loading(this.body),e.next=8,yn("/api/disableaccount",a);case 8:if(s=e.sent,o(),xn().checkResponse(s)){e.next=14;break}return t.disableAccountErr.textContent=s.msg,zt.show(t.disableAccountErr),e.abrupt("return");case 14:zt.hide(t.forms),window.location.assign("/settings");case 16:case"end":return e.stop()}}),e,this)}))),function(){return d.apply(this,arguments)})},{key:"prepareAccountExport",value:(l=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).exportAccountHost.textContent=this.host,n.exportAccountErr.textContent="",en.passwordIsCached()?this.exportAccount():this.showForm(t);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return l.apply(this,arguments)})},{key:"prepareAccountDisable",value:(c=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).disableAccountHost.textContent=this.host,n.disableAccountErr.textContent="",this.showForm(t);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"prepareUpdateBondOptions",value:(i=o(w().mark((function e(){var t,n,a,o,s,i,c,l;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:for(t=this.page,n=xn().user.exchanges[this.host],t.bondTargetTier.setAttribute("placeholder",n.bondOptions.targetTier.toString()),zt.empty(t.bondAssetSelect),a=0,o=Object.entries(n.bondAssets);a=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function ro(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0)this.fetchMaxBuy(Math.round(e.basisPrice/this.currentMarket.atomToConv*Tn)),t.basisPrice.textContent=zt.formatFiveSigFigs(e.basisPrice),zt.show(t.absMaxBox,t.basisPrice),zt.hide(t.manualPriceInput,t.noFiatBox),this.page.gapFactorMax.textContent=zt.formatFiveSigFigs(e.basisPrice);else{t.lotEstQuoteLots.textContent="[no rate]",t.basisPrice.textContent="must set manually",zt.hide(t.basisPrice,t.absMaxBox),zt.show(t.manualPriceInput),t.manualPriceInput.focus();var n=xn().user;0===Object.keys(n.fiatRates).length&&zt.show(t.noFiatBox)}e&&e.breakEvenSpread>0?(zt.show(t.breakEvenGapBox),t.breakEvenGap.textContent=zt.formatFiveSigFigs(e.breakEvenSpread)):zt.hide(t.breakEvenGapBox)}},{key:"updateGapStrategyInputVisibility",value:function(){var e=this.page;switch(e.gapStrategySelect.value){case ao:zt.show(this.gapMultiplierOpt.node),zt.hide(e.absInputBox,e.absMaxBox,this.gapPercentOpt.node);break;case io:case co:zt.show(this.gapPercentOpt.node),zt.hide(e.absInputBox,e.absMaxBox,this.gapMultiplierOpt.node);break;case oo:case so:zt.hide(this.gapMultiplierOpt.node,this.gapPercentOpt.node),zt.show(e.absInputBox,e.absMaxBox)}}},{key:"updateGapStrategyInputs",value:function(e){switch(this.updateGapStrategyInputVisibility(),this.page.gapStrategySelect.value){case ao:e&&e.breakEvenSpread>0?(go.start.y=e.breakEvenSpread,go.end.y=100*e.breakEvenSpread,go.yUnit=this.rateUnit()):(go.start.y=100,go.end.y=1e4,go.yUnit="%"),this.gapMultiplierOpt.handler.setConfig(go);break;case io:case co:e&&e.breakEvenSpread>0?(ko.end.y=e.basisPrice,ko.yUnit=this.rateUnit(),this.gapPercentOpt.handler.setConfig(ko)):(ko.end.y=10,ko.yUnit="%")}}},{key:"setCurrentBasisPrice",value:function(e){if(e>0){var t=this.rateUnit();po.start.y=-.01*e,po.end.y=.01*e,po.yUnit=t,mo.start.y=0,mo.end.y=.01*e,mo.yUnit=t}else po.start.y=-1,po.end.y=1,po.yUnit="%",mo.start.y=0,mo.end.y=1,mo.yUnit="%";this.biasOpt.handler.setConfig(po),this.driftToleranceOpt.handler.setConfig(mo)}},{key:"rateUnit",value:function(){var e=this.currentMarket.quotesymbol.split(".")[0],t=this.currentMarket.basesymbol.split(".")[0];return"".concat(e,"/").concat(t)}},{key:"setMarket",value:(c=o(w().mark((function e(t,n){var r,a,o,s,i,c,l,u,h,d,p,f,m,v;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=this.page,a=t[0],this.currentMarket=a,this.specifiedPrice=0,this.setCurrentReport(null),r.manualPriceInput.value="",en.storeLocal(en.lastMMMarketLK,a),zt.empty(r.baseSelect,r.quoteSelect),r.baseSelect.appendChild(this.assetRow(a.basesymbol)),r.quoteSelect.appendChild(this.assetRow(a.quotesymbol)),this.hideAssetDropdown(),o=function(e,t){zt.setContent(t,zt.symbolize(e.basesymbol),new Text("-"),zt.symbolize(e.quotesymbol),new Text(" @ "),new Text(e.host))},zt.hide(r.marketSelect,r.marketOneChoice),1===t.length)zt.show(r.marketOneChoice),o(a,r.marketOneChoice);else{zt.show(r.marketSelect),zt.empty(r.marketSelect),s=no(t);try{for(s.s();!(i=s.n()).done;)c=i.value,l=document.createElement("option"),r.marketSelect.appendChild(l),l.value="".concat(c.host," ").concat(c.name),o(c,l)}catch(e){s.e(e)}finally{s.f()}}if(!n){e.next=16;break}return e.abrupt("return");case 16:if(zt.setContent(r.lotEstBaseSymbol,zt.symbolize(a.basesymbol)),zt.setContent(r.lotEstQuoteSymbol,zt.symbolize(a.quotesymbol)),u=function(e,t){zt.show(t);var n=zt.parseTemplate(t);return n.assetLogo.src=zt.logoPath(e.symbol),n.assetName.textContent=e.name,!1},zt.hide(r.lotEstQuoteBox,r.lotEstQuoteNoWallet,r.lotEstBaseBox,r.lotEstBaseNoWallet,r.availHeader,r.lotEstimateBox,r.marketInfo,r.oraclesBox,r.lotsBox,r.options,r.advancedBox),h=[xn().assets[a.baseid],xn().assets[a.quoteid]],p=h[1],!(d=h[0]).wallet||!p.wallet){e.next=35;break}return zt.show(r.lotEstQuoteBox,r.lotEstBaseBox,r.availHeader,r.fetchingMarkets,r.lotsBox,r.advancedBox),en.fetchLocal(en.optionsExpansionLK)&&zt.show(r.options),f=xn().loading(r.options),m=this.fetchOracleAndMaxBuy(),v=this.fetchMaxSell(),e.next=29,m;case 29:return e.next=31,v;case 31:return f(),zt.show(r.lotEstimateBox,r.marketInfo,r.oraclesBox),zt.hide(r.fetchingMarkets),e.abrupt("return");case 35:zt.show(r.lotEstimateBox),p.wallet?r.lotEstQuoteLots.textContent="0":u(p,r.lotEstQuoteNoWallet),d.wallet?r.lotEstBaseLots.textContent="0":u(d,r.lotEstBaseNoWallet);case 38:case"end":return e.stop()}}),e,this)}))),function(e,t){return c.apply(this,arguments)})},{key:"fetchMaxSell",value:(i=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.currentMarket,n=this.page,e.next=3,yn("/api/maxsell",{host:t.host,base:t.baseid,quote:t.quoteid});case 3:if(r=e.sent,this.currentMarket===t){e.next=6;break}return e.abrupt("return");case 6:if(xn().checkResponse(r)){e.next=10;break}return n.lotEstBaseLots.textContent="0",console.error(r),e.abrupt("return");case 10:n.lotEstBaseLots.textContent=String(r.maxSell.swap.lots);case 11:case"end":return e.stop()}}),e,this)}))),function(){return i.apply(this,arguments)})},{key:"fetchOracleAndMaxBuy",value:(a=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d,p;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.currentMarket,r=this.page,e.next=3,yn("/api/marketreport",{host:n.host,baseID:n.baseid,quoteID:n.quoteid});case 3:if(a=e.sent,xn().checkResponse(a)){e.next=8;break}return r.lotEstQuoteLots.textContent="0",console.error(a),e.abrupt("return");case 8:if(o=a.report,zt.hide(r.manualPriceBttn),this.setCurrentReport(o),o.oracles&&0!==o.oracles.length){e.next=13;break}return e.abrupt("return");case 13:zt.empty(r.oracles),s=0,i=0,c=no(null!==(t=o.oracles)&&void 0!==t?t:[]);try{for(c.s();!(l=c.n()).done;)u=l.value,h=r.oracleTmpl.cloneNode(!0),r.oracles.appendChild(h),(d=zt.parseTemplate(h)).logo.src="img/"+u.host+".png",d.host.textContent=Fo[u.host],d.volume.textContent=zt.formatThreeSigFigs(u.usdVol),p=(u.bestBuy+u.bestSell)/2,i+=u.usdVol*p,s+=u.usdVol,d.price.textContent=zt.formatThreeSigFigs((u.bestBuy+u.bestSell)/2)}catch(e){c.e(e)}finally{c.f()}r.avgPrice.textContent=zt.formatFiveSigFigs(i/s);case 19:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"fetchMaxBuy",value:(n=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.currentMarket,r=this.page,e.next=3,yn("/api/maxbuy",{host:n.host,base:n.baseid,quote:n.quoteid,rate:null!=t?t:0});case 3:if(a=e.sent,this.currentMarket===n){e.next=6;break}return e.abrupt("return");case 6:r.lotEstQuoteLots.textContent=String(a.maxBuy.swap.lots);case 7:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"setEditProgram",value:function(e){var t=this,n=this.createOpts,r=this.page,a=e.program,o=[xn().assets[a.baseID],xn().assets[a.quoteID]],s=o[0],i=o[1],c=xn().exchanges[a.host].markets["".concat(s.symbol,"_").concat(i.symbol)];this.setMarket([to({host:a.host},c)],!0);for(var l=0,u=Object.values(this.programs);l0?(zt.show(e.programsHeader),zt.hide(e.noProgramsMessage)):(zt.hide(e.programsHeader),zt.show(e.noProgramsMessage))}},{key:"programDiv",value:function(e){var t=this,n=this.page.runningProgramTmpl.cloneNode(!0),r=zt.parseTemplate(n),a=function(){var t=o(w().mark((function t(a,o){var s,i;return w().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return zt.hide(r.startErr),s=xn().loading(n),t.next=4,yn(a,{programID:e.programID,appPW:o});case 4:i=t.sent,s(),xn().checkResponse(i)||(r.startErr.textContent=i.msg,zt.show(r.startErr));case 7:case"end":return t.stop()}}),t)})));return function(e,n){return t.apply(this,arguments)}}();zt.bind(r.pauseBttn,"click",(function(){return a("/api/stopbot")})),zt.bind(r.startBttn,"click",this.authedRoute(function(){var e=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",a("/api/startbot",t));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}())),zt.bind(r.retireBttn,"click",(function(){return a("/api/retirebot")})),zt.bind(r.configureBttn,"click",(function(n){n.stopPropagation(),t.setEditProgram(t.programs[e.programID])}));var s=[xn().assets[e.program.baseID],xn().assets[e.program.quoteID]],i=s[0],c=s[1];return r.base.appendChild(this.assetRow(i.symbol)),r.quote.appendChild(this.assetRow(c.symbol)),r.baseSymbol.textContent=i.symbol.toUpperCase(),r.quoteSymbol.textContent=c.symbol.toUpperCase(),r.host.textContent=e.program.host,this.updateProgramDiv(r,e),this.programs[e.programID]=Object.assign({tmpl:r,div:n},e),n}},{key:"authedRoute",value:function(e){var t=this;return o(w().mark((function n(){return w().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:if(!en.passwordIsCached()){n.next=4;break}return n.next=3,e("");case 3:return n.abrupt("return",n.sent);case 4:return t.pwHandler=e,n.next=7,t.showForm(t.page.pwForm);case 7:case"end":return n.stop()}}),n)})))}},{key:"updateProgramDiv",value:function(e,t){var n=t.program;zt.hide(e.programRunning,e.programPaused),t.running?zt.show(e.programRunning):zt.show(e.programPaused),e.lots.textContent=String(n.lots),e.boost.textContent="".concat((100*n.gapFactor).toFixed(1),"%"),e.driftTolerance.textContent="".concat((100*n.driftTolerance).toFixed(2),"%"),e.oracleWeight.textContent="".concat((100*n.oracleWeighting).toFixed(0),"%"),e.oracleBias.textContent="".concat((100*n.oracleBias).toFixed(1),"%")}},{key:"setMarketSubchoice",value:function(e,t){e===this.currentMarket.host&&t===this.currentMarket.name||this.leaveEditMode(),this.currentMarket=Object.assign({host:e},xn().exchanges[e].markets[t])}},{key:"createOptsUpdated",value:function(){this.createOpts.oracleWeighting?zt.show(this.biasOpt.node):zt.hide(this.biasOpt.node)}},{key:"handleBotNote",value:function(e){var t=this.page,n=e.report;switch(e.topic){case"BotCreated":t.runningPrograms.prepend(this.programDiv(n)),this.setProgramsHeader();break;case"BotRetired":this.programs[n.programID].div.remove(),delete this.programs[n.programID],this.setProgramsHeader();break;default:var r=this.programs[n.programID];Object.assign(r,n),this.updateProgramDiv(r.tmpl,n)}}},{key:"setCreationBase",value:function(e){for(var t=this.currentMarket.quotesymbol,n=So(),r=[],a=0,o=n;a0)return this.setMarket(r);for(var u=0,h=n;u0)return this.setMarket(r);for(var u=0,h=n;u 0 lots"));case 6:i=Object.assign({host:r.host,baseID:r.baseid,quoteID:r.quoteid},this.createOpts,{lots:s,gapStrategy:""}),c=n.gapStrategySelect.value,i.gapStrategy=null!=c?c:"",l={botType:"MakerV0",program:i,programID:0,appPW:t,manualRate:0},e.t0=c,e.next=e.t0===oo||e.t0===so?13:e.t0===io||e.t0===co?22:24;break;case 13:if(0!==(u=parseFloat(n.absInput.value||"0"))){e.next=18;break}return e.abrupt("return",o("gap must be specified for strategy = absolute"));case 18:if(!(null!=a&&a.basisPrice&&u>=a.basisPrice)){e.next=20;break}return e.abrupt("return",o("gap width cannot be > current spot price"));case 20:return i.gapFactor=u,e.abrupt("break",25);case 22:return i.gapFactor=this.gapRanges.gapPercent,e.abrupt("break",25);case 24:i.gapFactor=this.gapRanges.gapMultiplier;case 25:if(h="/api/createbot",null===this.editProgram){e.next=31;break}l.programID=this.editProgram.programID,h="/api/updatebotprogram",e.next=36;break;case 31:if(this.currentReport&&0!==this.currentReport.basisPrice){e.next=36;break}if(0!==this.specifiedPrice){e.next=35;break}return o("price must be set manually"),e.abrupt("return");case 35:l.program.manualRate=this.specifiedPrice;case 36:return d=xn().loading(n.botCreator),e.next=39,yn(h,l);case 39:if(p=e.sent,d(),xn().checkResponse(p)){e.next=45;break}return n.createErr.textContent=p.msg,zt.show(n.createErr),e.abrupt("return");case 45:this.leaveEditMode(),n.lotsInput.value="";case 47:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"newWalletCreated",value:function(e){var t=this.currentMarket;if(e===t.baseid)this.setCreationBase(t.basesymbol);else{if(e!==t.quoteid)return;this.setCreationQuote(t.quotesymbol)}this.closePopups()}}]),m}(fn);function So(){for(var e=[],t=function(e){return Object.values(e.markets).map((function(t){return Object.assign({host:e.host},t)}))},n=0,r=Object.values(xn().user.exchanges);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Oo(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n-1:0),a=1;adocument.body.offsetWidth&&(r=document.body.offsetWidth-t.tooltip.offsetWidth-5),t.tooltip.style.left="".concat(r,"px"),t.tooltip.style.top="".concat(n.bodyTop-t.tooltip.offsetHeight-5,"px")})),Po(e,"mouseleave",(function(){t.tooltip.style.left="-10000px"}))}))}},{key:"attachHeader",value:function(){var e=this;this.header=To(document.body,"header"),this.headerSpace=zt.idel(this.header,"headerSpace"),this.popupNotes=To(document.body,"popupNotes"),this.popupTmpl=zt.tmplElement(this.popupNotes,"note"),this.popupTmpl?this.popupTmpl.remove():console.error("popupTmpl element not found"),this.tooltip=To(document.body,"tooltip");var t=this.page=zt.idDescendants(this.header);t.noteTmpl.removeAttribute("id"),t.noteTmpl.remove(),t.pokeTmpl.removeAttribute("id"),t.pokeTmpl.remove(),t.loader.remove(),zt.show(t.loader),Po(t.noteBell,"click",o(w().mark((function n(){var r,a,o;return w().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:zt.hide(t.pokeList),zt.show(t.noteList),e.ackNotes(),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),e.showDropdown(t.noteBell,t.noteBox),zt.hide(t.noteIndicator),r=Do(e.notes);try{for(r.s();!(a=r.n()).done;)(o=a.value).acked&&o.el.classList.remove("firstview")}catch(e){r.e(e)}finally{r.f()}e.setNoteTimes(t.noteList),e.setNoteTimes(t.pokeList),e.storeNotes();case 12:case"end":return n.stop()}}),n)})))),Po(t.burgerIcon,"click",(function(){zt.hide(t.logoutErr),e.showDropdown(t.burgerIcon,t.profileBox)})),Po(t.innerNoteIcon,"click",(function(){zt.hide(t.noteBox)})),Po(t.innerBurgerIcon,"click",(function(){zt.hide(t.profileBox)})),Po(t.profileSignout,"click",o(w().mark((function t(){return w().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,e.signOut();case 2:return t.abrupt("return",t.sent);case 3:case"end":return t.stop()}}),t)})))),Po(t.pokeCat,"click",(function(){e.setNoteTimes(t.pokeList),t.pokeCat.classList.add("active"),t.noteCat.classList.remove("active"),zt.hide(t.noteList),zt.show(t.pokeList),e.ackNotes()})),Po(t.noteCat,"click",(function(){e.setNoteTimes(t.noteList),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),zt.hide(t.pokeList),zt.show(t.noteList),e.ackNotes()}))}},{key:"showDropdown",value:function(e,t){var n=this,r=e.getBoundingClientRect();zt.hide(this.page.noteBox,this.page.profileBox),zt.show(t),t.style.right="".concat(window.innerWidth-r.left-r.width+5,"px"),t.style.top="".concat(r.top-4,"px"),Po(document,"click",(function e(r){zt.mouseInElement(r,t)||(zt.hide(t),Lo(document,"click",e),t===n.page.noteBox&&zt.isDisplayed(n.page.noteList)&&n.ackNotes())}))}},{key:"ackNotes",value:function(){var e,t=[],n=Do(this.notes);try{for(n.s();!(e=n.n()).done;){var r=e.value;r.acked?r.el.classList.remove("firstview"):(r.acked=!0,r.id&&r.severity>2&&t.push(r.id))}}catch(e){n.e(e)}finally{n.f()}t.length&&Yr.request("acknotes",t),zt.hide(this.page.noteIndicator)}},{key:"setNoteTimes",value:function(e){for(var t=0,n=Array.from(e.children);t0?zt.show(e.marketsMenuEntry):zt.hide(e.marketsMenuEntry)}}},{key:"attachCommon",value:function(e){this.bindInternalNavigation(e)}},{key:"updateBondConfs",value:function(e,t,n,r){var a=this.exchanges[e],o=this.assets[r].symbol;a.pendingBonds[t]={confs:n,assetID:r,symbol:o}}},{key:"updateTier",value:function(e,t){this.exchanges[e].tier=t}},{key:"handleBondNote",value:function(e){switch(e.topic){case"RegUpdate":null!==e.coinID&&this.updateBondConfs(e.dex,e.coinID,e.confirmations,e.asset);break;case"BondConfirmed":null!==e.tier&&this.updateTier(e.dex,e.tier)}}},{key:"setNotes",value:function(e){this.log("notes","setNotes",e),this.notes=[],zt.empty(this.page.noteList);for(var t=0;t=0&&R.splice(D,1):D>=0?R[D]=A.report:R.push(A.report)}}else this.fiatRatesMap=e.fiatRates}},{key:"notify",value:function(e){this.log("notes","notify",e),this.updateUser(e);var t,n=Do(this.noteReceivers);try{for(n.s();!(t=n.n()).done;){var r=t.value[e.type];if(r)try{r(e)}catch(t){console.error("note feeder error:",t.message?t.message:t),console.log(e),console.log(t.stack)}}}catch(e){n.e(e)}finally{n.f()}if(!(e.severity<2)){var a=this.popupTmpl,s=this.popupNotes;if(this.showPopups){var i=a.cloneNode(!0);zt.tmplElement(i,"text").textContent="".concat(e.subject,": ").concat(e.details);var c=zt.tmplElement(i,"indicator");for(2===e.severity?zt.hide(c):Uo(c,e.severity),s.appendChild(i);s.children.length>5;)s.removeChild(s.firstChild);setTimeout(o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,zt.animate(500,(function(e){i.style.opacity=String(1-e)}));case 2:i.remove();case 3:case"end":return e.stop()}}),e)}))),6e3)}2===e.severity?this.prependPokeElement(e):this.prependNoteElement(e)}}},{key:"registerNoteFeeder",value:function(e){this.noteReceivers.push(e)}},{key:"log",value:function(e){for(var t,n=arguments.length,r=new Array(n>1?n-1:0),a=1;a100;)this.pokes.shift();this.prependListElement(this.page.pokeList,a,n)}},{key:"prependNoteElement",value:function(e,t){var n=r(this.makeNote(e),2),a=n[0],o=n[1];for(this.notes.push(o);this.notes.length>100;)this.notes.shift();var s=this.page.noteList;if(this.prependListElement(s,o,a),t||this.storeNotes(),!(0===this.notes.length||zt.isDisplayed(this.page.noteBox)&&zt.isDisplayed(s))){var i=0,c=this.notes.reduce((function(e,t){return t.acked||i++,!t.acked&&t.severity>e?t.severity:e}),0),l=this.page.noteIndicator;Uo(l,c),i?(l.textContent=String(i>99?"".concat(99,"+"):i),zt.show(l)):zt.hide(l)}}},{key:"prependListElement",value:function(e,t,n){for(n.note=t,e.prepend(n);e.children.length>100;)e.removeChild(e.lastChild);this.setNoteTimes(e)}},{key:"makeNote",value:function(e){var t=this.page.noteTmpl.cloneNode(!0);if(e.severity>2){var n=3===e.severity?"good":4===e.severity?"warn":"bad";zt.safeSelector(t,"div.note-indicator").classList.add(n)}return zt.safeSelector(t,"div.note-subject").textContent=e.subject,zt.safeSelector(t,"div.note-details").textContent=e.details,[t,Ro({el:t},e)]}},{key:"makePoke",value:function(e){var t=this.page.pokeTmpl.cloneNode(!0),n=new Date(e.stamp);return zt.tmplElement(t,"dateTime").textContent="".concat(n.toLocaleDateString(),", ").concat(n.toLocaleTimeString()),zt.tmplElement(t,"details").textContent="".concat(e.subject,": ").concat(e.details),[t,Ro({el:t},e)]}},{key:"loading",value:function(e){var t=this.page.loader.cloneNode(!0);return e.appendChild(t),function(){t.remove()}}},{key:"orders",value:function(e,t){var n=[],r=this.user.exchanges[e].markets[t];return r.orders&&(n=n.concat(r.orders)),r.inflight&&(n=n.concat(r.inflight)),n}},{key:"haveActiveOrders",value:function(e){for(var t=0,n=Object.values(this.user.exchanges);t{var e={61:(e,t,n)=>{var r=n(698).default;function a(){"use strict";e.exports=a=function(){return t},e.exports.__esModule=!0,e.exports.default=e.exports;var t={},n=Object.prototype,o=n.hasOwnProperty,s=Object.defineProperty||function(e,t,n){e[t]=n.value},i="function"==typeof Symbol?Symbol:{},c=i.iterator||"@@iterator",l=i.asyncIterator||"@@asyncIterator",u=i.toStringTag||"@@toStringTag";function h(e,t,n){return Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{h({},"")}catch(e){h=function(e,t,n){return e[t]=n}}function d(e,t,n,r){var a=t&&t.prototype instanceof m?t:m,o=Object.create(a.prototype),i=new I(r||[]);return s(o,"_invoke",{value:S(e,n,i)}),o}function p(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(e){return{type:"throw",arg:e}}}t.wrap=d;var f={};function m(){}function v(){}function y(){}var g={};h(g,c,(function(){return this}));var b=Object.getPrototypeOf,w=b&&b(b(R([])));w&&w!==n&&o.call(w,c)&&(g=w);var k=y.prototype=m.prototype=Object.create(g);function x(e){["next","throw","return"].forEach((function(t){h(e,t,(function(e){return this._invoke(t,e)}))}))}function C(e,t){function n(a,s,i,c){var l=p(e[a],e,s);if("throw"!==l.type){var u=l.arg,h=u.value;return h&&"object"==r(h)&&o.call(h,"__await")?t.resolve(h.__await).then((function(e){n("next",e,i,c)}),(function(e){n("throw",e,i,c)})):t.resolve(h).then((function(e){u.value=e,i(u)}),(function(e){return n("throw",e,i,c)}))}c(l.arg)}var a;s(this,"_invoke",{value:function(e,r){function o(){return new t((function(t,a){n(e,r,t,a)}))}return a=a?a.then(o,o):o()}})}function S(e,t,n){var r="suspendedStart";return function(a,o){if("executing"===r)throw new Error("Generator is already running");if("completed"===r){if("throw"===a)throw o;return{value:void 0,done:!0}}for(n.method=a,n.arg=o;;){var s=n.delegate;if(s){var i=E(s,n);if(i){if(i===f)continue;return i}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if("suspendedStart"===r)throw r="completed",n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);r="executing";var c=p(e,t,n);if("normal"===c.type){if(r=n.done?"completed":"suspendedYield",c.arg===f)continue;return{value:c.arg,done:n.done}}"throw"===c.type&&(r="completed",n.method="throw",n.arg=c.arg)}}}function E(e,t){var n=t.method,r=e.iterator[n];if(void 0===r)return t.delegate=null,"throw"===n&&e.iterator.return&&(t.method="return",t.arg=void 0,E(e,t),"throw"===t.method)||"return"!==n&&(t.method="throw",t.arg=new TypeError("The iterator does not provide a '"+n+"' method")),f;var a=p(r,e.iterator,t.arg);if("throw"===a.type)return t.method="throw",t.arg=a.arg,t.delegate=null,f;var o=a.arg;return o?o.done?(t[e.resultName]=o.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,f):o:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,f)}function A(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function F(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function I(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(A,this),this.reset(!0)}function R(e){if(e){var t=e[c];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,r=function t(){for(;++n=0;--r){var a=this.tryEntries[r],s=a.completion;if("root"===a.tryLoc)return n("end");if(a.tryLoc<=this.prev){var i=o.call(a,"catchLoc"),c=o.call(a,"finallyLoc");if(i&&c){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&o.call(r,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),F(n),f}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var r=n.completion;if("throw"===r.type){var a=r.arg;F(n)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:R(e),resultName:t,nextLoc:n},"next"===this.method&&(this.arg=void 0),f}},t}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},698:e=>{function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},687:(e,t,n)=>{var r=n(61)();e.exports=r;try{regeneratorRuntime=r}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=r:Function("r","regeneratorRuntime = r")(r)}}},t={};function n(r){var a=t[r];if(void 0!==a)return a.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";function e(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Dt(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=n.left&&e.pageX<=n.right&&e.pageY>=n.top&&e.pageY<=n.bottom}},{key:"layoutMetrics",value:function(e){var t=e.getBoundingClientRect(),n=document.documentElement,r=t.top+n.scrollTop,a=t.left+n.scrollLeft,o=e.offsetWidth,s=e.offsetHeight;return{bodyTop:r,bodyLeft:a,width:o,height:s,centerX:a+o/2,centerY:r+s/2}}},{key:"descendentMetrics",value:function(t,n){var r=e.layoutMetrics(t),a=e.layoutMetrics(n);return{bodyTop:a.bodyTop-r.bodyTop,bodyLeft:a.bodyLeft-r.bodyLeft,width:a.width,height:a.height,centerX:a.centerX-r.bodyLeft,centerY:a.centerY-r.bodyTop}}},{key:"empty",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),a=1;a1?n-1:0),a=1;a"),e),document.createElement("div"))}},{key:"idDescendants",value:function(t){var n,r={},a=Rt(e.applySelector(t,"[id]"));try{for(a.s();!(n=a.n()).done;){var o=n.value;r[o.id]=o}}catch(e){a.e(e)}finally{a.f()}return r}},{key:"formatCoinValue",value:function(e,t){var n=r(_t(e,t),2),a=n[0],o=n[1];return Number.isInteger(a)?Lt.format(a):function(e){return Ut(Wt,2,e)}(o).format(a)}},{key:"formatThreeSigFigs",value:function(e){return e>=1e3?Lt.format(Math.round(e)):Bt.format(e)}},{key:"formatFiveSigFigs",value:function(e,t){return e>=1e4?Lt.format(Math.round(e)):e<1e5?Nt(null!=t?t:8).format(e):Mt.format(e)}},{key:"formatFullPrecision",value:function(e,t){var n=r(_t(e,t),2),a=n[0];return Nt(n[1]).format(a)}},{key:"formatFiatConversion",value:function(e,t,n){if(!t||0===t)return It(gt);var a=r(_t(e,n),1)[0]*t;return Nt(2).format(a)}},{key:"logoPath",value:function(e){return-1===Pt.indexOf(e)&&(e=e.substring(0,1)),"/img/coins/".concat(e,".png")}},{key:"bipSymbol",value:function(e){return Tt[e]}},{key:"logoPathFromID",value:function(t){return e.logoPath(Tt[t])}},{key:"symbolize",value:function(e){var t=e.split("."),n=document.createElement("span");if(n.textContent=t[0].toUpperCase(),1===t.length)return n;var r=document.createElement("span");r.classList.add("token-aware-symbol"),r.appendChild(n);var a=document.createElement("sup");return a.textContent=t[1].toUpperCase(),r.appendChild(a),r}},{key:"shortSymbol",value:function(e){return e.split(".")[0].toUpperCase()}},{key:"cleanTemplates",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n0||u>0)&&u++,e>0&&(l+="".concat(e," ").concat(t," ")),u>=2},d=r(Zt(c,Gt),2);if(t=d[0],c=d[1],h(t,"y"))return l;var p=r(Zt(c,Xt),2);if(n=p[0],c=p[1],h(n,"mo"))return l;var f=r(Zt(c,Qt),2);if(a=f[0],c=f[1],h(a,"d"))return l;var m=r(Zt(c,Yt),2);if(o=m[0],c=m[1],h(o,"h"))return l;var v=r(Zt(c,Jt),2);if(s=v[0],c=v[1],h(s,"m"))return l;var y=r(Zt(c,1e3),2);return i=y[0],c=y[1],h(i,"s"),l||"0 s"}},{key:"disableMouseWheel",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n=0?n:31536e7,o=a?jt[a]:jt.linear,s=(new Date).getTime(),i=n===e.Forever?Number.MAX_SAFE_INTEGER:s+n,c=i-s,l=1e3/30,u=s,this.endAnimation=!1;case 8:if(!(ue.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(document.cookie.split(";"));try{for(n.s();!(t=n.n()).done;){var a=r(t.value.split("="),2),o=a[0],s=a[1];if(o.trim()===e)return s}}catch(e){n.e(e)}finally{n.f()}return null}},{key:"removeCookie",value:function(e){document.cookie="".concat(e,"=;expires=Thu, 01 Jan 1970 00:00:01 GMT;")}},{key:"isDark",value:function(){return document.cookie.split(";").filter((function(t){return t.includes("".concat(e.darkModeCK,"=1"))})).length}},{key:"passwordIsCached",value:function(){return!!this.getCookie(e.pwKeyCK)}},{key:"storeLocal",value:function(e,t){window.localStorage.setItem(e,JSON.stringify(t))}},{key:"fetchLocal",value:function(e){var t=window.localStorage.getItem(e);return null!==t?JSON.parse(t):null}},{key:"removeLocal",value:function(e){window.localStorage.removeItem(e)}}]),e}();function tn(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function nn(e,t){return nn=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},nn(e,t)}function rn(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&nn(e,t)}function an(e,t){if(t&&("object"===i(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return tn(e)}function on(e){return on=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},on(e)}h(en,"darkModeCK","darkMode"),h(en,"authCK","dexauth"),h(en,"pwKeyCK","sessionkey"),h(en,"popupsLK","popups"),h(en,"loggersLK","loggers"),h(en,"recordersLK","recorders"),h(en,"lastMarketLK","selectedMarket"),h(en,"depthZoomLK","depthZoom"),h(en,"lastMMMarketLK","mmMarket"),h(en,"optionsExpansionLK","mmOptsExpand"),h(en,"leftMarketDockLK","leftmarketdock"),h(en,"selectedAssetLK","selectedasset"),h(en,"notificationsLK","notifications"),h(en,"orderDisclaimerAckedLK","ordAck"),null===en.getCookie(en.darkModeCK)&&en.setCookie(en.darkModeCK,"1"),null===en.fetchLocal(en.popupsLK)&&en.storeLocal(en.popupsLK,"1"),null===en.fetchLocal(en.leftMarketDockLK)&&en.storeLocal(en.leftMarketDockLK,"1");var sn,cn,ln,un,hn,dn,pn,fn=function(){function e(){s(this,e)}return u(e,[{key:"unload",value:function(){}}]),e}();function mn(e,t,n){return vn.apply(this,arguments)}function vn(){return(vn=o(w().mark((function e(t,n,r){var a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,window.fetch(n,{method:t,headers:new window.Headers({"content-type":"application/json"}),body:r});case 3:if(200===(a=e.sent).status){e.next=6;break}throw a;case 6:return e.next=8,a.json();case 8:return(o=e.sent).requestSuccessful=!0,e.abrupt("return",o);case 13:return e.prev=13,e.t0=e.catch(0),e.t0.requestSuccessful=!1,e.next=18,e.t0.text();case 18:return e.t0.msg=e.sent,e.abrupt("return",e.t0);case 20:case"end":return e.stop()}}),e,null,[[0,13]])})))).apply(this,arguments)}function yn(e,t){return gn.apply(this,arguments)}function gn(){return(gn=o(w().mark((function e(t,n){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",mn("POST",t,JSON.stringify(n)));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function bn(e){return wn.apply(this,arguments)}function wn(){return(wn=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",mn("GET",t));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function kn(n){return function(t){if(Array.isArray(t))return e(t)}(n)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(n)||t(n)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function xn(){return un}function Cn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=on(e);if(t){var a=on(this).constructor;n=Reflect.construct(r,arguments,a)}else n=r.apply(this,arguments);return an(this,n)}}function Sn(e){var t=[e.booleanOptTmpl,e.rangeOptTmpl,e.orderOptTmpl];dn=t[0],pn=t[1],hn=t[2]}!function(e){e[e.walletErr=0]="walletErr",e[e.walletAuthErr=1]="walletAuthErr",e[e.walletBalanceErr=2]="walletBalanceErr",e[e.dupeDEXErr=3]="dupeDEXErr",e[e.assetSupportErr=4]="assetSupportErr",e[e.registerErr=5]="registerErr",e[e.signatureErr=6]="signatureErr",e[e.zeroFeeErr=7]="zeroFeeErr",e[e.feeMismatchErr=8]="feeMismatchErr",e[e.feeSendErr=9]="feeSendErr",e[e.passwordErr=10]="passwordErr",e[e.emptyHostErr=11]="emptyHostErr",e[e.connectionErr=12]="connectionErr",e[e.acctKeyErr=13]="acctKeyErr",e[e.unknownOrderErr=14]="unknownOrderErr",e[e.orderParamsErr=15]="orderParamsErr",e[e.dbErr=16]="dbErr",e[e.authErr=17]="authErr",e[e.connectWalletErr=18]="connectWalletErr",e[e.missingWalletErr=19]="missingWalletErr",e[e.encryptionErr=20]="encryptionErr",e[e.decodeErr=21]="decodeErr",e[e.accountVerificationErr=22]="accountVerificationErr",e[e.accountProofErr=23]="accountProofErr",e[e.parseKeyErr=24]="parseKeyErr",e[e.marketErr=25]="marketErr",e[e.addressParseErr=26]="addressParseErr",e[e.addrErr=27]="addrErr",e[e.fileReadErr=28]="fileReadErr",e[e.unknownDEXErr=29]="unknownDEXErr",e[e.accountRetrieveErr=30]="accountRetrieveErr",e[e.accountDisableErr=31]="accountDisableErr",e[e.suspendedAcctErr=32]="suspendedAcctErr",e[e.existenceCheckErr=33]="existenceCheckErr",e[e.createWalletErr=34]="createWalletErr",e[e.activeOrdersErr=35]="activeOrdersErr",e[e.newAddrErr=36]="newAddrErr"}(sn||(sn={})),function(e){e[e.Disconnected=0]="Disconnected",e[e.Connected=1]="Connected",e[e.InvalidCert=2]="InvalidCert"}(cn||(cn={})),function(e){e[e.WalletDefault=0]="WalletDefault",e[e.UserAdded=1]="UserAdded",e[e.Discovered=2]="Discovered"}(ln||(ln={}));var En=new Intl.NumberFormat(navigator.languages,{minimumSignificantDigits:3,maximumSignificantDigits:3}),An=u((function e(t,n,r){var a=this;s(this,e),h(this,"opt",void 0),h(this,"node",void 0),h(this,"tmpl",void 0),h(this,"on",void 0),this.opt=t;var o=this.node=hn.cloneNode(!0),i=this.tmpl=zt.parseTemplate(o);i.optName.textContent=t.displayname,i.tooltip.dataset.tooltip=t.description,n?i.chainIcon.src=zt.logoPath(n):zt.hide(i.chainIcon),this.on=!1,zt.bind(o,"click",(function(){a.on||(a.on=!0,o.classList.add("selected"),r.enable())})),zt.bind(i.toggle,"click",(function(e){a.on&&(e.stopPropagation(),a.on=!1,o.classList.remove("selected"),r.disable())}))})),Fn=function(e){rn(n,e);var t=Cn(n);function n(e,r,a,o){var i;if(s(this,n),h(tn(i=t.call(this,e,r,{enable:function(){return i.enable()},disable:function(){return i.disable()}})),"control",void 0),h(tn(i),"changed",void 0),h(tn(i),"dict",void 0),i.dict=a,i.changed=function(){return o()},void 0===e.boolean)throw Error("not a boolean opt");var c=e.boolean,l=i.control=dn.cloneNode(!0);return i.tmpl.controls.appendChild(l),zt.parseTemplate(l).reason.textContent=c.reason,i.on=void 0!==a[e.key]?a[e.key]:e.default,i.on&&i.node.classList.add("selected"),i}return u(n,[{key:"store",value:function(){this.on===this.opt.default?delete this.dict[this.opt.key]:this.dict[this.opt.key]=this.on,this.changed()}},{key:"enable",value:function(){this.store()}},{key:"disable",value:function(){this.store()}}]),n}(An),In=function(e){rn(n,e);var t=Cn(n);function n(e,r,a,o){var i;if(s(this,n),h(tn(i=t.call(this,e,r,{enable:function(){return i.enable()},disable:function(){return i.disable()}})),"handler",void 0),h(tn(i),"x",void 0),h(tn(i),"changed",void 0),h(tn(i),"dict",void 0),i.dict=a,i.changed=o,void 0===e.xyRange)throw Error("not an xy range opt");var c=e.xyRange,l=a[e.key];return i.on=void 0!==l,i.on?(i.node.classList.add("selected"),i.x=l):i.x=e.default,i.handler=new Rn(c,i.x,(function(e){i.x=e,i.dict[i.opt.key]=e}),(function(){i.changed()}),(function(){i.node.classList.add("selected")})),i.tmpl.controls.appendChild(i.handler.control),i}return u(n,[{key:"enable",value:function(){this.dict[this.opt.key]=this.x,this.changed()}},{key:"disable",value:function(){delete this.dict[this.opt.key],this.changed()}},{key:"setValue",value:function(e){this.handler.setValue(e),this.on=!0,this.node.classList.add("selected")}}]),n}(An),Rn=function(){function e(t,n,r,a,o,i){var c=this;s(this,e),h(this,"control",void 0),h(this,"cfg",void 0),h(this,"tmpl",void 0),h(this,"x",void 0),h(this,"scrollingX",void 0),h(this,"y",void 0),h(this,"r",void 0),h(this,"roundY",void 0),h(this,"updated",void 0),h(this,"changed",void 0),h(this,"selected",void 0),h(this,"setConfig",void 0);var l=this.control=pn.cloneNode(!0),u=this.tmpl=zt.parseTemplate(l);this.roundY=Boolean(i),this.cfg=t,this.changed=a,this.selected=o,this.updated=r;var d=u.slider,p=u.handle,f=t.end.x-t.start.x,m=t.end.y-t.start.y,v=function(e){return(e-t.start.x)/f},y=function(e){f=e.end.x-e.start.x,m=e.end.y-e.start.y,t=c.cfg=e,u.rangeLblStart.textContent=t.start.label,u.rangeLblEnd.textContent=t.end.label,u.xUnit.textContent=t.xUnit,u.yUnit.textContent=t.yUnit,c.y=c.r*m+t.start.y,c.r=(c.y-t.start.y)/m,c.scrollingX=c.r*f+t.start.x};y(t),this.setConfig=function(e){y(e),c.accept(c.scrollingX)},this.r=v(n),this.scrollingX=this.x=n,this.y=this.r*m+t.start.y;var g=function e(n){if("change"===n.type||n.target!==u.xInput){var r=u.xInput.value;if(r){var a=parseFloat(r);isNaN(a)||(c.scrollingX=Dn(a,t.start.x,t.end.x),c.r=v(c.scrollingX),c.y=c.r*m+t.start.y,c.accept(c.scrollingX))}zt.hide(u.xInput),zt.show(u.x),zt.unbind(document,"click",e),c.changed()}};zt.bind(u.x,"click",(function(e){zt.hide(u.x),zt.show(u.xInput),u.xInput.focus(),u.xInput.value=En.format(c.scrollingX),zt.bind(document,"click",g),e.stopPropagation()})),zt.bind(u.xInput,"change",g);var b=function e(n){if("change"===n.type||n.target!==u.yInput){var r=u.yInput.value;if(r){var a=parseFloat(r);isNaN(a)||(c.y=Dn(a,t.start.y,t.end.y),c.r=(c.y-t.start.y)/m,c.scrollingX=t.start.x+c.r*f,c.accept(c.scrollingX))}zt.hide(u.yInput),zt.show(u.y),zt.unbind(document,"click",e),c.changed()}};zt.bind(u.y,"click",(function(e){zt.hide(u.y),zt.show(u.yInput),u.yInput.focus(),u.yInput.value=En.format(c.y),zt.bind(document,"click",b),e.stopPropagation()})),zt.bind(u.yInput,"change",b),zt.bind(p,"mousedown",(function(e){if(0===e.button){e.preventDefault(),c.selected();var n=e.pageX,r=d.clientWidth-p.offsetWidth,a=v(c.scrollingX)*r,o=function(e){e.preventDefault(),c.r=function(e){return Math.max(Math.min(a+(e.pageX-n),r),0)}(e)/r,c.scrollingX=c.r*f+t.start.x,c.y=c.r*m+t.start.y,c.accept(c.scrollingX)};zt.bind(document,"mousemove",o),zt.bind(document,"mouseup",(function e(t){o(t),zt.unbind(document,"mousemove",o),zt.unbind(document,"mouseup",e),c.changed()}))}})),this.accept(this.scrollingX,!0)}return u(e,[{key:"accept",value:function(e,t){var n=this.tmpl;this.roundY&&(this.y=Math.round(this.y)),n.x.textContent=En.format(e),n.y.textContent=En.format(this.y),this.roundY&&(n.y.textContent="".concat(this.y)),n.handle.style.left="calc(".concat(100*this.r,"% - ").concat(14*this.r,"px)"),this.x=e,this.scrollingX=e,t||this.updated(e,this.y)}},{key:"setValue",value:function(e){var t=this.cfg;this.r=(e-t.start.x)/(t.end.x-t.start.x),this.y=t.start.y+this.r*(t.end.y-t.start.y),this.accept(e,!0)}}]),e}(),Dn=function(e,t,n){return en?n:e};function On(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(e.matches);try{for(n.s();!(t=n.n()).done;)if(t.value.active)return!0}catch(e){n.e(e)}finally{n.f()}return!1}function Wn(e){if(!e.id)return It(te);var t=Mn(e);switch(e.status){case 0:return It($);case 1:return It(ee);case 2:return e.cancelling?It(K):t?"".concat(It(H),"/").concat(It(ne)):It(H);case 3:return t?It(ne):0===e.filled&&3!==e.type?It(re):It(j);case 4:return t?"".concat(It(ae),"/").concat(It(ne)):It(ae);case 5:return t?"".concat(It(oe),"/").concat(It(ne)):It(oe)}return It($)}function qn(e){if(!e.matches)return 0;var t=Bn(e)?function(e){return e.qty*e.rate/Tn}:function(e){return e.qty};return e.matches.reduce((function(e,n){return n.isCancel?e:e+t(n)}),0)}function Nn(e){if(!e.matches)return 0;var t=Bn(e)?function(e){return e.qty*e.rate/Tn}:function(e){return e.qty};return e.matches.reduce((function(e,n){return n.isCancel?e:0===n.side&&n.status>=3||1===n.side&&n.status>=4?e+t(n):e}),0)}function Un(e,t){return e*t/Tn}function _n(e){return It(tt,{status:It(e)})}function zn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=on(e);if(t){var a=on(this).constructor;n=Reflect.construct(r,arguments,a)}else n=r.apply(this,arguments);return an(this,n)}}function Vn(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return jn(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?jn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function jn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=c&&i=i&&s=h&&v.push([C.rate,k]),!C.epoch)for(w+=C.qty,m.push([C.rate,w]),b.buyBase+=C.qty,b.buyQuote+=C.qty*C.rate;d.length&&ur(d[0].rate,C.rate);){var S=d.shift();S&&f.push({rate:S.rate,qty:C.epoch?k:w,sell:C.sell,active:S.active})}}var E=m.length?or(m)[1]:0;m.push([h,E]);var A=v.length?or(v)[1]:0;v.push([h,A]),k=w=0;for(var F=0;Fu||e=P},N=e.theme.sellLine;Pthis.data.candles.length)return;this.numToShow=this.zoomLevels[t+1]}this.draw()}},{key:"render",value:function(){var e=this,t=this.data;if(t&&this.visible&&0!==this.canvas.width){var n=t.ms,r=this.mousePos,a=t.candles||[],o=Math.min(this.numToShow,a.length),s=a.slice(a.length-o);if(this.clear(),0!==o){var i,c=function(e){return dr(e.endStamp,n)},l=function(e){return c(e)+n},u=function(e){return c(e)+.2*n},h=.6*n,d=s[0],p=s[o-1],f=[d.highRate,d.lowRate,d.matchVolume],m=f[0],v=f[1],y=f[2],g=Vn(s);try{for(g.s();!(i=g.n()).done;){var b=i.value;b.highRate>m&&(m=b.highRate),b.lowRatey&&(y=b.matchVolume)}}catch(e){g.e(e)}finally{g.f()}var w=this.market.ratestep,k=new tr(c(d),l(p),v,m);v===m&&(k.y.min-=w,k.y.max+=w),this.dataExtents=k;var x=this.rateConversionFactor;this.doYLabels(this.candleRegion,w,this.market.quotesymbol,(function(e){return lr(e/x)})),this.candleRegion.extents.x.min=this.yRegion.extents.x.max,this.volumeRegion.extents.x.min=this.yRegion.extents.x.max;var C=function(e,t,n,r){var a=e[0],o=e[e.length-1],s=dr(a.endStamp,t),i=dr(o.endStamp,t)+t,c=i-s,l=Math.min(e.length,n/100),u=dr(c/l,t);if(0===u)return console.error("zero tick",t,c,l),{lbls:[]};var h=s,d=(new Date).getTimezoneOffset(),p=function(e){return(e-=6e4*d)-e%864e5},f=p(s),m=0;p(a.endStamp)===p(o.endStamp)&&(f=0);var v,y=[];for(v=t<864e5?function(e,t){return p(t)!==f?"".concat(ar[e.getMonth()]).concat(e.getDate()," ").concat(e.getHours(),":").concat(String(e.getMinutes()).padStart(2,"0")):"".concat(e.getHours(),":").concat(String(e.getMinutes()).padStart(2,"0"))}:function(e){var t=e.getFullYear();return t!==m?"".concat(ar[e.getMonth()]).concat(e.getDate()," '").concat(String(t).slice(2,4)):"".concat(ar[e.getMonth()]).concat(e.getDate())};h<=i;){var g=new Date(h);y.push({val:h,txt:v(g,h)}),f=p(h),m=g.getFullYear(),h+=u}return{lbls:y}}(s,n,this.plotRegion.width());this.plotXLabels(C,c(d),l(p),[]),this.drawFrame();var S=null;if(r&&(this.plotRegion.plot(new tr(k.x.min,k.x.max,0,1),(function(t,a){var o,i=dr(a.unx(r.x),n),l=Vn(s);try{for(l.s();!(o=l.n()).done;){var u=o.value;if(c(u)===i){S=u,t.fillStyle=e.theme.gridLines,t.fillRect(a.x(c(u)),a.y(0),a.w(n),a.h(1));break}}}catch(e){l.e(e)}finally{l.f()}})),S)){var E=this.xRegion.extents.y;this.xRegion.plot(new tr(k.x.min,k.x.max,E.min,E.max),(function(t,n){if(S){e.applyLabelStyle();var r="".concat(new Date(c(S)).toLocaleString()," - ").concat(new Date(l(S)).toLocaleString()),a=t.measureText(r).width+50,o=n.x((c(S)+l(S))/2),s=o-a/2,i=e.xRegion.extents.x;si.max&&(s=i.max-a),o=s+a/2;var u=E.min+(e.xRegion.height()-16)/2;t.fillStyle=e.theme.legendFill,t.strokeStyle=e.theme.gridBorder;var h=[s-25,u-2,a+50,20];t.fillRect.apply(t,h),t.strokeRect.apply(t,h),e.applyLabelStyle(),t.fillText(r,o,e.xRegion.extents.midY,a)}}))}var A=new tr(c(d),l(p),0,y);this.volumeRegion.plot(A,(function(t,n){t.fillStyle=e.theme.gridBorder;var r,a=Vn(s);try{for(a.s();!(r=a.n()).done;){var o=r.value;t.fillRect(n.x(u(o)),n.y(0),n.w(h),n.h(o.matchVolume))}}catch(e){a.e(e)}finally{a.f()}})),this.candleRegion.plot(k,(function(t,n){t.lineWidth=1;var r,a=Vn(s);try{for(a.s();!(r=a.n()).done;){var o=r.value,i=o.startRate>o.endRate,c=[n.x(u(o)),n.y(o.startRate),n.w(h),n.h(o.endRate-o.startRate)],l=c[0],d=c[1],p=c[2],f=c[3],m=[n.y(o.highRate),n.y(o.lowRate),p/2+l],v=m[0],y=m[1],g=m[2];t.strokeStyle=i?e.theme.sellLine:e.theme.buyLine,t.fillStyle=i?e.theme.sellFill:e.theme.buyFill,t.beginPath(),t.moveTo(g,v),t.lineTo(g,y),t.stroke(),t.fillRect(l,d,p,f),t.strokeRect(l,d,p,f)}}catch(e){a.e(e)}finally{a.f()}})),this.reporters.mouse(S)}}else this.renderScheduled=!0}},{key:"setCandles",value:function(e,t,n,r){if(this.data=e,e.candles){this.market=t;var a=[r.conventional.conversionFactor,n.conventional.conversionFactor],o=a[0],s=a[1];this.rateConversionFactor=Tn*o/s;var i=25;this.zoomLevels=[];for(var c=Math.max(e.candles.length,1e3);i150&&(a=150),o>100&&(o=100);var s=(n-a)/2,i=(r-o)/2;if(e.message){this.fontSize=ir(.15*o,10,14),this.applyLabelStyle(this.fontSize);var c=.5*this.fontSize,l=this.fontSize/2+c;i-=l,this.msgRegion=new nr(this.ctx,new tr(0,n,i+o,i+o+2*l))}this.region=new nr(this.ctx,new tr(s,s+a,i,i+o))}},{key:"drawValues",value:function(e){var t=this;if(this.region){this.clear();var n=function(e){return"hsl(".concat(e,", 35%, 50%)")},r=this.region,a=this.msgRegion,o=this.canvas,s=o.width,i=o.height,c=this.opts,l=c.backgroundColor,u=c.message,h=this.colorShift,d=this.ctx;l&&(d.fillStyle=!0===l?window.getComputedStyle(document.body,null).getPropertyValue("background-color"):l,d.fillRect(0,0,s,i)),r.plot(new tr(0,1,-1,1),(function(t,r){t.lineWidth=4,t.lineCap="round";var a=h+(new Date).getTime()%2e3/2e3*360,o=t.createLinearGradient(r.x(0),0,r.x(1),0);o.addColorStop(0,n(a)),t.strokeStyle=o,t.beginPath(),t.moveTo(r.x(0),r.y(e[0]));for(var s=1;sn.x.min&&tn.y.min}},{key:"translator",value:function(e){var t=this.extents,n=e.x.min,r=e.y.min,a=e.yRange,o=e.xRange,s=t.x.min,i=t.x.max-s,c=t.y.max,l=c-t.y.min,u=i/o,h=l/a;return{x:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return(e-n)*u+s})),y:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return c-(e-r)*h})),unx:function(e){return(e-s)/u+n},uny:function(e){return r-(e-c)/h},w:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return e/o*i})),h:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return-e/a*l})),dataCoords:function(){}}}},{key:"clear",value:function(){var e=this.extents;this.context.clearRect(e.x.min,e.y.min,e.xRange,e.yRange)}},{key:"plot",value:function(e,t,n){var r=this.context,a=this.extents;r.save(),n||(r.beginPath(),r.rect(a.x.min,a.y.min,a.xRange,a.yRange),r.clip());var o=this.translator(e),s=e.yRange,i=a.xRange/e.xRange,c=a.yRange/s,l=e.x.min,u=e.y.min,h=a.x.min+l-l*i,d=-a.y.min-(s-u)*c;o.dataCoords=function(e){r.save(),r.transform(1,0,0,-1,-l,u),r.transform(i,0,0,c,h,d),e(),r.restore()},t(this.context,o),r.restore()}}]),e}();function rr(e,t,n,r,a,o,s,i){i=i||lr;var c=t/a,l=r-n;if(c<1||l<=0)return{lbls:[]};for(var u=l/c,h=u+o-u%o,d=n+h-n%h,p=Math.max(Math.abs(r),Math.abs(n)),f=Math.round(Math.log10(p/h))+2,m=[],v=0;dv&&(v=g),{widest:v,lbls:m}}var ar=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function or(e){return e[e.length-1]}function sr(e,t,n,r,a,o){e.beginPath(),e.moveTo(t,n),e.lineTo(r,a),o||e.stroke()}function ir(e,t,n){return en?n:e}var cr={minimumSignificantDigits:4,maximumSignificantDigits:5};function lr(e){return e.toLocaleString("en-us",cr)}function ur(e,t){return hr(e,t,1e-8)}function hr(e,t,n){return Math.abs(e-t)=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function fr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1)){e.next=34;break}zt.show(r),u=pr(l),e.prev=16,d=w().mark((function e(){var t,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=h.value,(a=n.walletTabTmpl.cloneNode(!0)).dataset.tooltip=t.description,a.textContent=t.tab,r.appendChild(a),zt.bind(a,"click",(function(){var e,n=pr(zt.kids(r));try{for(n.s();!(e=n.n()).done;)e.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}a.classList.add("selected"),p.update(t)}));case 6:case"end":return e.stop()}}),e)})),u.s();case 19:if((h=u.n()).done){e.next=23;break}return e.delegateYield(d(),"t0",21);case 21:e.next=19;break;case 23:e.next=28;break;case 25:e.prev=25,e.t1=e.catch(16),u.e(e.t1);case 28:return e.prev=28,u.f(),e.finish(28);case 31:xn().bindTooltips(r),r.firstChild.classList.add("selected");case 34:return e.next=36,this.update(this.current.selectedDef);case 36:if(!s.walletCreationPending){e.next=39;break}return e.next=39,this.runParentSync();case 39:case"end":return e.stop()}}),e,this,[[16,25,28,31]])}))),function(e){return a.apply(this,arguments)})},{key:"parseAsset",value:function(e){if(this.current&&this.current.asset.id===e)return!1;var t=xn().assets[e],n=t.token;if(!n){if(!t.info)throw Error("this non-token asset has no wallet info!");return this.current={asset:t,winfo:t.info,selectedDef:t.info.availablewallets[0]},!0}var r=xn().user.assets[n.parentID];if(r.wallet)return this.current={asset:t,winfo:n,selectedDef:n.definition},!0;if(!r.info)throw Error("this parent has no wallet info!");return this.current={asset:t,parentAsset:r,winfo:n,selectedDef:r.info.availablewallets[0]},!0}},{key:"update",value:(r=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u,h,d,p,f,m,v,y,g,b;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.page,this.current.selectedDef=t,r=en.passwordIsCached()||this.pwCache&&this.pwCache.pw,zt.hide(n.auth,n.oneBttnBox,n.newWalletPassBox),(a=t.configopts||[]).map((function(e){return e.isBirthdayConfig&&xn().seedGenTime>0&&(e.default=Tr(new Date)),e})),o=!1,s=pr(a),e.prev=8,s.s();case 10:if((i=s.n()).done){e.next=17;break}if(!i.value.required){e.next=15;break}return o=!0,e.abrupt("break",17);case 15:e.next=10;break;case 17:e.next=22;break;case 19:e.prev=19,e.t0=e.catch(8),s.e(e.t0);case 22:return e.prev=22,s.f(),e.finish(22);case 25:if(c=!o&&(t.seeded||Boolean(this.current.asset.token)),r&&c?zt.show(n.oneBttnBox):c?(zt.show(n.auth),n.newWalletPass.value="",n.submitAdd.textContent=It(he)):(zt.show(n.auth),t.noauth||zt.show(n.newWalletPassBox),n.submitAdd.textContent=It(ue)),l=this.current,u=l.asset,h=l.parentAsset,d=l.winfo,h){p=JSON.parse(JSON.stringify(a)),f=pr(p);try{for(f.s();!(m=f.n()).done;)m.value.regAsset=h.id}catch(e){f.e(e)}finally{f.f()}if((v=d.definition.configopts||[]).length>0){y=JSON.parse(JSON.stringify(v)),g=pr(y);try{for(g.s();!(b=g.n()).done;)b.value.regAsset=u.id}catch(e){g.e(e)}finally{g.f()}p.push.apply(p,kn(y))}this.subform.update(p,!1)}else this.subform.update(a,!1);return this.subform.dynamicOpts.children.length||this.subform.defaultSettings.children.length?zt.show(n.walletSettingsHeader):zt.hide(n.walletSettingsHeader),t.seeded||Boolean(this.current.asset.token)?zt.hide(this.subform.fileSelector):zt.show(this.subform.fileSelector),this.refresh(),e.next=34,this.loadDefaults();case 34:case"end":return e.stop()}}),e,this,[[8,19,22,25]])}))),function(e){return r.apply(this,arguments)})},{key:"setError",value:(n=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.page.newWalletErr.textContent=t,zt.show(this.page.newWalletErr);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"loadDefaults",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.current,n=t.asset,r=t.parentAsset,(a=t.selectedDef).configpath){e.next=3;break}return e.abrupt("return");case 3:if(o=n.id,!r){e.next=8;break}if(!a.seeded){e.next=7;break}return e.abrupt("return");case 7:o=r.id;case 8:return s=xn().loading(this.form),e.next=11,yn("/api/defaultwalletcfg",{assetID:o,type:a.type});case 11:if(i=e.sent,s(),xn().checkResponse(i)){e.next=16;break}return this.setError(i.msg),e.abrupt("return");case 16:this.subform.setLoadedConfig(i.config);case 17:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),vr=0,yr=function(){function e(t,n){var r=this;s(this,e),h(this,"form",void 0),h(this,"configElements",void 0),h(this,"configOpts",void 0),h(this,"sectionize",void 0),h(this,"allSettings",void 0),h(this,"dynamicOpts",void 0),h(this,"textInputTmpl",void 0),h(this,"dateInputTmpl",void 0),h(this,"checkboxTmpl",void 0),h(this,"repeatableTmpl",void 0),h(this,"fileSelector",void 0),h(this,"fileInput",void 0),h(this,"errMsg",void 0),h(this,"showOther",void 0),h(this,"showIcon",void 0),h(this,"hideIcon",void 0),h(this,"showHideMsg",void 0),h(this,"otherSettings",void 0),h(this,"loadedSettingsMsg",void 0),h(this,"loadedSettings",void 0),h(this,"defaultSettingsMsg",void 0),h(this,"defaultSettings",void 0),h(this,"assetHasActiveOrders",void 0),this.form=t,this.configElements=[],this.configOpts=[],this.sectionize=n,this.allSettings=zt.tmplElement(t,"allSettings"),this.dynamicOpts=zt.tmplElement(t,"dynamicOpts"),this.textInputTmpl=zt.tmplElement(t,"textInput"),this.textInputTmpl.remove(),this.dateInputTmpl=zt.tmplElement(t,"dateInput"),this.dateInputTmpl.remove(),this.checkboxTmpl=zt.tmplElement(t,"checkbox"),this.checkboxTmpl.remove(),this.repeatableTmpl=zt.tmplElement(t,"repeatableInput"),this.repeatableTmpl.remove(),this.fileSelector=zt.tmplElement(t,"fileSelector"),this.fileInput=zt.tmplElement(t,"fileInput"),this.errMsg=zt.tmplElement(t,"errMsg"),this.showOther=zt.tmplElement(t,"showOther"),this.showIcon=zt.tmplElement(t,"showIcon"),this.hideIcon=zt.tmplElement(t,"hideIcon"),this.showHideMsg=zt.tmplElement(t,"showHideMsg"),this.otherSettings=zt.tmplElement(t,"otherSettings"),this.loadedSettingsMsg=zt.tmplElement(t,"loadedSettingsMsg"),this.loadedSettings=zt.tmplElement(t,"loadedSettings"),this.defaultSettingsMsg=zt.tmplElement(t,"defaultSettingsMsg"),this.defaultSettings=zt.tmplElement(t,"defaultSettings"),n||zt.hide(this.showOther),zt.bind(this.fileSelector,"click",(function(){return r.fileInput.click()})),zt.bind(this.fileInput,"change",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",r.fileInputChanged());case 1:case"end":return e.stop()}}),e)})))),zt.bind(this.showOther,"click",(function(){r.setOtherSettingsViz(r.hideIcon.classList.contains("d-hide"))}))}var t;return u(e,[{key:"fileInputChanged",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(zt.hide(this.errMsg),this.fileInput.value){e.next=3;break}return e.abrupt("return");case 3:if((n=this.fileInput.files)&&0!==n.length){e.next=6;break}return e.abrupt("return");case 6:return r=xn().loading(this.form),e.next=9,n[0].text();case 9:if(a=e.sent){e.next=12;break}return e.abrupt("return");case 12:return e.next=14,yn("/api/parseconfig",{configtext:a});case 14:if(o=e.sent,r(),xn().checkResponse(o)){e.next=20;break}return this.errMsg.textContent=o.msg,zt.show(this.errMsg),e.abrupt("return");case 20:if(0!==Object.keys(o.map).length){e.next=22;break}return e.abrupt("return");case 22:(t=this.dynamicOpts).append.apply(t,kn(this.setConfig(o.map))),this.reorder(this.dynamicOpts),s=[this.loadedSettings.children.length,this.defaultSettings.children.length],c=s[1],0===(i=s[0])&&zt.hide(this.loadedSettings,this.loadedSettingsMsg),0===c&&zt.hide(this.defaultSettings,this.defaultSettingsMsg),i+c===0&&zt.hide(this.showOther,this.otherSettings);case 28:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"addOpt",value:function(e,t,n,r){var a,o=this,s="wcfg-"+t.key+(r?String(r):"");t.isboolean?a=this.checkboxTmpl.cloneNode(!0):t.isdate?a=this.dateInputTmpl.cloneNode(!0):t.repeatable?((a=this.repeatableTmpl.cloneNode(!0)).classList.add("repeatable"),zt.bind(zt.tmplElement(a,"add"),"click",(function(){vr++,o.addOpt(e,t,a,vr)}))):a=this.textInputTmpl.cloneNode(!0),this.configElements.push([t,a]);var i=a.querySelector("input");i.dataset.configKey=t.key,i.id=s;var c=zt.safeSelector(a,"label");if(c.htmlFor=s,c.prepend(t.displayname),void 0!==t.regAsset){var l=new window.Image(15,15);l.src=zt.logoPathFromID(t.regAsset||-1),c.prepend(l)}if(n?n.after(a):e.appendChild(a),t.noecho&&(i.type="password",i.autocomplete="off"),t.description&&(c.dataset.tooltip=t.description),t.isboolean)i.checked=t.default;else if(t.isdate){var u=function(e){return e?Pr("now"===e?new Date:new Date(1e3*e)):""};i.max=u(t.max),i.min=u(t.min);var h=t.default?new Date(1e3*t.default):new Date;i.value=Pr(h)}else i.value=null!==t.default?t.default:"";return i.disabled=Boolean(t.disablewhenactive&&this.assetHasActiveOrders),a}},{key:"update",value:function(e,t){if(this.assetHasActiveOrders=t,this.configElements=[],this.configOpts=e||[],zt.empty(this.dynamicOpts,this.defaultSettings,this.loadedSettings),0===this.configOpts.length)return zt.hide(this.form);zt.show(this.form),this.setOtherSettingsViz(!1),zt.hide(this.loadedSettingsMsg,this.loadedSettings,this.defaultSettingsMsg,this.defaultSettings,this.errMsg);var n,r=[],a=pr(this.configOpts);try{for(a.s();!(n=a.n()).done;){var o=n.value;this.sectionize&&null!==o.default?r.push(o):this.addOpt(this.dynamicOpts,o)}}catch(e){a.e(e)}finally{a.f()}if(r.length){var s,i=pr(r);try{for(i.s();!(s=i.n()).done;){var c=s.value;this.addOpt(this.defaultSettings,c)}}catch(e){i.e(e)}finally{i.f()}zt.show(this.showOther,this.defaultSettingsMsg,this.defaultSettings)}else zt.hide(this.showOther);xn().bindTooltips(this.allSettings),this.dynamicOpts.children.length?zt.show(this.dynamicOpts):zt.hide(this.dynamicOpts)}},{key:"setOtherSettingsViz",value:function(e){if(e)return zt.hide(this.showIcon),zt.show(this.hideIcon,this.otherSettings),void(this.showHideMsg.textContent=It(D));zt.hide(this.hideIcon,this.otherSettings),zt.show(this.showIcon),this.showHideMsg.textContent=It(O)}},{key:"setConfig",value:function(e){for(var t,n=[],a={},o=[],s=0,i=kn(this.configElements);s=0&&this.configElements.splice(k,1)}return n}},{key:"setLoadedConfig",value:function(e){var t,n=this.setConfig(e);this.sectionize&&0!==n.length&&((t=this.loadedSettings).append.apply(t,kn(n)),this.reorder(this.loadedSettings),zt.show(this.loadedSettings,this.loadedSettingsMsg),0===this.defaultSettings.children.length&&zt.hide(this.defaultSettings,this.defaultSettingsMsg))}},{key:"map",value:function(e){var t,n={},a=pr(this.configElements);try{for(a.s();!(t=a.n()).done;){var o=r(t.value,2),s=o[0],i=o[1],c=zt.safeSelector(i,"input");if(void 0===s.regAsset||s.regAsset===e)if(s.isboolean&&s.key)n[s.key]=c.checked?"1":"0";else if(s.isdate&&s.key){var l=c.min?Tr(new Date(c.min+"T00:00")):Number.MIN_SAFE_INTEGER,u=c.max?Tr(new Date(c.max+"T00:00")):Number.MAX_SAFE_INTEGER,h=c.value?Tr(new Date(c.value+"T00:00")):0;hu&&(h=u),n[s.key]=""+h}else c.value&&(s.repeatable&&n[s.key]?n[s.key]+=s.repeatable+c.value:n[s.key]=c.value)}}catch(e){a.e(e)}finally{a.f()}return n}},{key:"reorder",value:function(e){var t=this,n={};e.querySelectorAll("input").forEach((function(e){var a=e.dataset.configKey;if(a){var o,s=[],i=pr(t.configElements);try{for(i.s();!(o=i.n()).done;){var c=r(o.value,2),l=c[0],u=c[1];l.key===a&&s.push(u)}}catch(e){i.e(e)}finally{i.f()}n[a]=s}}));var a,o=pr(this.configOpts);try{for(o.s();!(a=o.n()).done;){var s,i=a.value,c=pr(n[i.key]||[]);try{for(c.s();!(s=c.n()).done;){var l=s.value;e.append(l)}}catch(e){c.e(e)}finally{c.f()}}}catch(e){o.e(e)}finally{o.f()}}}]),e}(),gr=function(){function e(t,n,r,a){var o=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"page",void 0),h(this,"xc",void 0),h(this,"certFile",void 0),h(this,"bondAssetID",void 0),h(this,"pwCache",void 0),this.form=t,this.success=n,this.page=zt.parseTemplate(t),this.certFile="",this.pwCache=a,zt.bind(this.page.goBack,"click",(function(){return r()})),zt.bind(this.page.bondStrengthField,"input",(function(){var e=xn().assets[o.bondAssetID];if(e){var t=e.unitInfo,n=o.xc.bondAssets[e.symbol];o.page.bondAmt.textContent=zt.formatCoinValue(o.totalBondAmount(n.amount),t)}})),Or(t,this.page.submit,(function(){return o.submitForm()}))}var t,n;return u(e,[{key:"setExchange",value:function(e,t){this.xc=e,this.certFile=t;var n=this.page;en.passwordIsCached()||this.pwCache&&this.pwCache.pw?zt.hide(n.passBox):zt.show(n.passBox),n.host.textContent=e.host}},{key:"setAsset",value:function(e){var t=xn().assets[e],n=t.unitInfo;this.bondAssetID=t.id;var r=this.page,a=this.xc.bondAssets[t.symbol];r.bondAmt.textContent=zt.formatCoinValue(this.totalBondAmount(a.amount),n),r.bondUnit.textContent=n.conventional.unit.toUpperCase(),r.logo.src=zt.logoPath(t.symbol)}},{key:"totalBondAmount",value:function(e){var t;return+(null!==(t=this.page.bondStrengthField.value)&&void 0!==t?t:1)*e}},{key:"animate",value:(n=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(400,(function(e){t.style.transform="scale(".concat(e,")"),t.style.opacity=String(Math.pow(e,4));var n="".concat(500*(1-e),"px");t.style.top=n,t.style.left=n}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"submitForm",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if((t=this.page).submit.classList.contains("selected")){e.next=3;break}return e.abrupt("return");case 3:if(n=xn().assets[this.bondAssetID]){e.next=8;break}return t.regErr.innerText=It(yt),zt.show(t.regErr),e.abrupt("return");case 8:return zt.hide(t.regErr),r=this.xc.bondAssets[n.wallet.symbol],e.next=12,this.certFile;case 12:return a=e.sent,o=this.xc.host,s=t.appPass.value||(this.pwCache?this.pwCache.pw:""),i={addr:o,cert:a,pass:s,bond:this.totalBondAmount(r.amount),asset:r.id},t.appPass.value="",c=xn().loading(this.form),e.next=20,yn("/api/postbond",i);case 20:if(l=e.sent,c(),xn().checkResponse(l)){e.next=26;break}return t.regErr.textContent=l.msg,zt.show(t.regErr),e.abrupt("return");case 26:this.success();case 27:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),br=function(){function e(t,n){var r=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"xc",void 0),h(this,"page",void 0),h(this,"assetTmpls",void 0),this.form=t,this.success=n,this.page=zt.parseTemplate(t),zt.cleanTemplates(this.page.marketTmpl,this.page.assetTmpl),xn().registerNoteFeeder({createwallet:function(e){"QueuedCreationSuccess"===e.topic&&r.walletCreated(e.assetID)}})}var t;return u(e,[{key:"setExchange",value:function(e){var t=this;this.xc=e,this.assetTmpls={};var n=this.page;zt.empty(n.assets,n.allMarkets);for(var a=function(e){return e.conventional.conversionFactor},o=function(t,r){var o=n.marketTmpl.cloneNode(!0),s=zt.parseTemplate(o),i=e.assets[t.baseid],c=xn().unitInfo(t.baseid,e),l=e.assets[t.quoteid],u=xn().unitInfo(t.quoteid,e);if(0===a(c)||0===a(u))return null;if(void 0!==r){var h=r===t.baseid,d=e.assets[h?t.quoteid:t.baseid].symbol;s.logo.src=zt.logoPath(d)}else{var p=s.logo.cloneNode(!0);s.logo.src=zt.logoPath(i.symbol),p.src=zt.logoPath(l.symbol);var f=s.logo.parentNode;f&&f.insertBefore(p,s.logo.nextSibling)}var m=i.symbol.toUpperCase(),v=l.symbol.toUpperCase();if(s.baseName.replaceWith(zt.symbolize(m)),s.quoteName.replaceWith(zt.symbolize(v)),s.lotSize.textContent=zt.formatCoinValue(t.lotsize,c),s.lotSizeSymbol.replaceWith(zt.symbolize(m)),t.spot){zt.show(s.quoteLotSize);var y=a(u)/a(c),g=t.lotsize*t.spot.rate/Tn*y,b=zt.formatCoinValue(g,u);s.quoteLotSize.textContent="(~".concat(b," ").concat(v,")")}return o},s=function(){var a=r(c[i],2),s=a[0],l=a[1],u=xn().assets[l.id];if(!u)return"continue";var h=u.unitInfo,d=n.assetTmpl.cloneNode(!0);zt.bind(d,"click",(function(){t.success(l.id)}));var p=t.assetTmpls[l.id]=zt.parseTemplate(d);n.assets.appendChild(d),p.logo.src=zt.logoPath(s);var f=zt.formatCoinValue(l.amount,h);p.feeAmt.textContent=String(f),p.feeSymbol.replaceWith(zt.symbolize(u.symbol)),p.confs.textContent=String(l.confs),wr(p.ready,u);for(var m=0,v=0,y=Object.values(e.markets);v0)if(r.totalForBond.textContent=zt.formatCoinValue(2*s.amount+t,a.unitInfo),zt.hide(r.sendEnough),zt.hide(r.txFeeBox,r.sendEnoughForToken,r.txFeeBalanceBox),zt.hide(r.sendEnoughWithEst),a.token){zt.show(r.txFeeBox,r.sendEnoughForToken,r.txFeeBalanceBox);var l=xn().assets[a.token.parentID];r.txFee.textContent=zt.formatCoinValue(t,l.unitInfo),r.parentFees.textContent=zt.formatCoinValue(t,l.unitInfo),r.tokenFees.textContent=zt.formatCoinValue(s.amount,a.unitInfo),i(r.txFeeUnit,l.symbol),i(r.parentUnit,l.symbol),i(r.parentBalUnit,l.symbol),r.parentBal.textContent=l.wallet?zt.formatCoinValue(l.wallet.balance.available,l.unitInfo):"0"}else zt.show(r.sendEnoughWithEst);else zt.show(r.sendEnough);zt.show(e.synced?r.syncCheck:e.syncProgress>=1?r.syncSpinner:r.syncUncheck),zt.show(e.balance.available>=2*s.amount+t?r.balCheck:r.balUncheck),r.progress.textContent=(100*e.syncProgress).toFixed(1),e.synced&&(this.progressed=!0),this.reportBalance(e.assetID)}},{key:"reportWalletState",value:function(e){this.progressed&&this.funded||(e.assetID===this.assetID&&this.reportProgress(e.synced,e.syncProgress),this.reportBalance(e.assetID))}},{key:"reportBalance",value:function(e){if(!this.funded&&-1!==this.assetID&&(e===this.assetID||e===this.parentID)){var t=this.page,n=xn().assets[this.assetID],r=n.wallet.balance.available;if(t.balance.textContent=zt.formatCoinValue(r,n.unitInfo),n.token){var a=xn().assets[n.token.parentID],o=a.wallet.balance.available;if(t.parentBal.textContent=zt.formatCoinValue(o,a.unitInfo),o=.999)return zt.hide(n.syncRemaining),zt.show(n.syncFinishingUp),zt.show(n.syncRemainBox),void(n.syncFinishingUp.textContent=It(bt));var r=this.progressCache;if(r.push({stamp:(new Date).getTime(),progress:t}),!(r.length<2)){for(;r.length>20;)r.shift();var a=[r[0],r[r.length-1]],o=a[0],s=a[1],i=s.progress-o.progress;if(0!==i){zt.hide(n.syncFinishingUp),zt.show(n.syncRemaining),zt.show(n.syncRemainBox);var c=i/(s.stamp-o.stamp),l=(1-s.progress)/c;n.syncRemain.textContent=zt.formatDuration(l)}}}}]),e}(),xr=function(){function e(t,n,r){var a=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"page",void 0),h(this,"currentAsset",void 0),this.page=zt.idDescendants(t),this.form=t,this.pwCache=r||null,this.success=n,Or(t,this.page.submitUnlock,(function(){return a.submit()}))}var t;return u(e,[{key:"refresh",value:function(e){var t=this.page;this.currentAsset=e,t.uwAssetLogo.src=zt.logoPath(e.symbol),t.uwAssetName.textContent=e.name,t.uwAppPass.value="",t.unlockErr.textContent="",zt.hide(t.unlockErr),en.passwordIsCached()||this.pwCache&&this.pwCache.pw?zt.hide(t.uwAppPassBox):zt.show(t.uwAppPassBox)}},{key:"setError",value:function(e){this.page.unlockErr.textContent=e,zt.show(this.page.unlockErr)}},{key:"showErrorOnly",value:function(e){this.setError(e),zt.hide(this.page.uwAppPassBox),zt.hide(this.page.submitUnlockDiv)}},{key:"submit",value:(t=o(w().mark((function e(){var t,n,r,a,o,s;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.uwAppPass.value||(this.pwCache?this.pwCache.pw:""))||en.passwordIsCached()){e.next=6;break}return t.unlockErr.textContent=It(x),zt.show(t.unlockErr),e.abrupt("return");case 6:return r=this.currentAsset.id,zt.hide(this.page.unlockErr),a={assetID:r,pass:n},t.uwAppPass.value="",o=xn().loading(this.form),e.next=13,yn("/api/openwallet",a);case 13:if(s=e.sent,o(),xn().checkResponse(s)){e.next=18;break}return this.setError(s.msg),e.abrupt("return");case 18:this.pwCache&&(this.pwCache.pw=n),this.success(r);case 20:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Cr=function(){function e(t,n){var r=this;s(this,e),h(this,"form",void 0),h(this,"page",void 0),h(this,"order",void 0),h(this,"acceleratedRate",void 0),h(this,"earlyAcceleration",void 0),h(this,"currencyUnit",void 0),h(this,"success",void 0),this.form=t,this.success=n;var a=this.page=zt.idDescendants(t);zt.bind(a.accelerateSubmit,"click",(function(){r.submit()})),zt.bind(a.submitEarlyConfirm,"click",(function(){r.sendAccelerateRequest()}))}var t,n,r,a;return u(e,[{key:"displayEarlyAccelerationMsg",value:function(){var e=this.page;this.earlyAcceleration&&(e.recentAccelerationTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60)),e.recentSwapTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60)),this.earlyAcceleration.wasAcceleration?(zt.show(e.recentAccelerationMsg),zt.hide(e.recentSwapMsg),e.recentAccelerationTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))):(zt.show(e.recentSwapMsg),zt.hide(e.recentAccelerationMsg),e.recentSwapTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))),zt.hide(e.configureAccelerationDiv,e.accelerateErr),zt.show(e.earlyAccelerationDiv))}},{key:"sendAccelerateRequest",value:(a=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.order,n=this.page,r={pw:n.acceleratePass.value,orderID:t.id,newRate:this.acceleratedRate},n.acceleratePass.value="",a=xn().loading(n.accelerateMainDiv),e.next=7,yn("/api/accelerateorder",r);case 7:o=e.sent,a(),xn().checkResponse(o)?(n.accelerateTxID.textContent=o.txID,zt.hide(n.accelerateMainDiv,n.preAccelerateErr,n.accelerateErr),zt.show(n.accelerateMsgDiv,n.accelerateSuccess),this.success()):(n.accelerateErr.textContent=It(lt,{msg:o.msg}),zt.hide(n.earlyAccelerationDiv),zt.show(n.accelerateErr,n.configureAccelerationDiv));case 10:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"submit",value:(r=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.earlyAcceleration?this.displayEarlyAccelerationMsg():this.sendAccelerateRequest();case 1:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"refresh",value:(n=o(w().mark((function e(t){var n,r,a,o,s,i,c=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.order=t,e.next=4,yn("/api/preaccelerate",t.id);case 4:if(r=e.sent,xn().checkResponse(r)){e.next=10;break}return n.preAccelerateErr.textContent=It(lt,{msg:r.msg}),zt.hide(n.accelerateMainDiv,n.accelerateSuccess),zt.show(n.accelerateMsgDiv,n.preAccelerateErr),e.abrupt("return");case 10:zt.hide(n.accelerateMsgDiv,n.preAccelerateErr,n.accelerateErr,n.feeEstimateDiv,n.earlyAccelerationDiv),zt.show(n.accelerateMainDiv,n.accelerateSuccess,n.configureAccelerationDiv),a=r.preAccelerate,this.earlyAcceleration=a.earlyAcceleration,this.currencyUnit=a.suggestedRange.yUnit,n.accelerateAvgFeeRate.textContent="".concat(a.swapRate," ").concat(a.suggestedRange.yUnit),n.accelerateCurrentFeeRate.textContent="".concat(a.suggestedRate," ").concat(a.suggestedRange.yUnit),this.acceleratedRate=a.suggestedRange.start.y,o=function(){},s=function(e,t){c.acceleratedRate=t},i=new Rn(a.suggestedRange,a.suggestedRange.start.x,s,(function(){return c.updateAccelerationEstimate()}),o,!0),zt.empty(n.sliderContainer),n.sliderContainer.appendChild(i.control),this.updateAccelerationEstimate();case 25:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"updateAccelerationEstimate",value:(t=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=this.order,r={orderID:n.id,newRate:this.acceleratedRate},a=xn().loading(t.sliderContainer),e.next=6,yn("/api/accelerationestimate",r);case 6:if(o=e.sent,a(),xn().checkResponse(o)){e.next=12;break}return t.accelerateErr.textContent=It(ct,{msg:o.msg}),zt.show(t.accelerateErr),e.abrupt("return");case 12:t.feeRateEstimate.textContent="".concat(this.acceleratedRate," ").concat(this.currencyUnit),n.sell?(s=n.baseID,i=n.baseSymbol):(s=n.quoteID,i=n.quoteSymbol),c=xn().unitInfo(s),t.feeEstimate.textContent="".concat(o.fee/c.conventional.conversionFactor," ").concat(i),zt.show(t.feeEstimateDiv);case 17:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Sr=function(){function e(t,n,r,a){var o=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"page",void 0),h(this,"knownExchanges",void 0),h(this,"dexToUpdate",void 0),this.form=t,this.success=n,this.pwCache=r||null;var i=this.page=zt.parseTemplate(t);i.selectedCert.textContent=It(ie),zt.bind(i.skipRegistration,"change",(function(){return o.showOrHidePWBox()})),zt.bind(i.certFile,"change",(function(){return o.onCertFileChange()})),zt.bind(i.removeCert,"click",(function(){return o.clearCertFile()})),zt.bind(i.addCert,"click",(function(){return i.certFile.click()})),zt.bind(i.showCustom,"click",(function(){zt.hide(i.showCustom),zt.show(i.customBox,i.auth)})),this.knownExchanges=Array.from(i.knownXCs.querySelectorAll(".known-exchange"));var c,l=pr(this.knownExchanges);try{var u=function(){var e=c.value;zt.bind(e,"click",(function(){var t,n=e.dataset.host,a=pr(o.knownExchanges);try{for(a.s();!(t=a.n()).done;)t.value.classList.remove("selected")}catch(e){a.e(e)}finally{a.f()}if(o.skipRegistration()||en.passwordIsCached()||r&&r.pw)return o.checkDEX(n);e.classList.add("selected"),i.appPW.focus(),i.addr.value=n}))};for(l.s();!(c=l.n()).done;)u()}catch(e){l.e(e)}finally{l.f()}Or(t,i.submit,(function(){return o.checkDEX()})),a&&(zt.hide(i.addDexHdr,i.skipRegistrationBox),zt.show(i.updateDexHdr),this.dexToUpdate=a),this.refresh()}var t,n,r;return u(e,[{key:"refresh",value:function(){var e=this.page;e.addr.value="",e.appPW.value="",this.clearCertFile(),zt.hide(e.err),0===this.knownExchanges.length||this.dexToUpdate?(zt.show(e.customBox,e.auth),zt.hide(e.showCustom,e.knownXCs,e.pickServerMsg,e.addCustomMsg)):(zt.hide(e.customBox),zt.show(e.showCustom));var t,n=pr(this.knownExchanges);try{for(n.s();!(t=n.n()).done;)t.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}this.showOrHidePWBox()}},{key:"showOrHidePWBox",value:function(){var e=!(en.passwordIsCached()||this.pwCache&&this.pwCache.pw||this.skipRegistration()),t=this.page;e?zt.show(t.appPWBox,t.auth):(zt.hide(t.appPWBox),zt.setVis(zt.isDisplayed(t.customBox),t.auth))}},{key:"skipRegistration",value:function(){var e;return null!==(e=this.page.skipRegistration.checked)&&void 0!==e&&e}},{key:"animate",value:(r=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(550,(function(e){t.style.transform="scale(".concat(.9+.1*e,")"),t.style.opacity=String(Math.pow(e,4))}),"easeOut");case 2:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"checkDEX",value:(n=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,zt.hide(n.err),""!==(t=t||n.addr.value)){e.next=7;break}return n.err.textContent=It(vt),zt.show(n.err),e.abrupt("return");case 7:if(r="",!n.certFile.value){e.next=14;break}if(!(a=n.certFile.files)||!a.length){e.next=14;break}return e.next=13,a[0].text();case 13:r=e.sent;case 14:return o=this.skipRegistration(),s="",o||en.passwordIsCached()||(s=n.appPW.value||(this.pwCache?this.pwCache.pw:"")),this.dexToUpdate?(i="/api/updatedexhost",c={newHost:t,cert:r,pw:s,oldHost:this.dexToUpdate}):(i=o?"/api/adddex":"/api/discoveracct",c={addr:t,cert:r,pass:s}),l=xn().loading(this.form),e.next=21,yn(i,c);case 21:if(u=e.sent,l(),xn().checkResponse(u)){e.next=26;break}return String(u.msg).includes("certificate required")?zt.show(n.needCert):(n.err.textContent=u.msg,zt.show(n.err)),e.abrupt("return");case 26:if(this.dexToUpdate||!(o||u.paid||Object.keys(u.xc.pendingBonds).length>0)){e.next=32;break}return e.next=29,xn().fetchUser();case 29:return e.next=31,xn().loadPage("markets");case 31:return e.abrupt("return");case 32:this.pwCache&&(this.pwCache.pw=s),this.success(u.xc,r);case 34:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"onCertFileChange",value:(t=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.certFile.files)&&n.length){e.next=4;break}return e.abrupt("return");case 4:t.selectedCert.textContent=n[0].name,zt.show(t.removeCert),zt.hide(t.addCert);case 7:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"clearCertFile",value:function(){var e=this.page;e.certFile.value="",e.selectedCert.textContent=It(ie),zt.hide(e.removeCert),zt.show(e.addCert)}}]),e}(),Er=function(){function e(t,n,r,a){var o=this;s(this,e),h(this,"form",void 0),h(this,"addr",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"page",void 0),this.form=t,this.addr=n,this.success=r,this.pwCache=a||null;var i=this.page=zt.parseTemplate(t);i.dexHost.textContent=n,Or(t,i.submit,(function(){return o.checkDEX()})),this.refresh()}var t,n;return u(e,[{key:"refresh",value:function(){var e=this.page;e.appPW.value="",zt.hide(e.err),en.passwordIsCached()||this.pwCache&&this.pwCache.pw?zt.hide(e.appPWBox):zt.show(e.appPWBox)}},{key:"animate",value:(n=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(550,(function(e){t.style.transform="scale(".concat(.9+.1*e,")"),t.style.opacity=String(Math.pow(e,4))}),"easeOut");case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"checkDEX",value:(t=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.err),n="",en.passwordIsCached()||(n=t.appPW.value||(this.pwCache?this.pwCache.pw:"")),r={addr:this.addr,pass:n},a=xn().loading(this.form),e.next=8,yn("/api/discoveracct",r);case 8:if(o=e.sent,a(),xn().checkResponse(o)){e.next=14;break}return t.err.textContent=o.msg,zt.show(t.err),e.abrupt("return");case 14:if(!o.paid){e.next=20;break}return e.next=17,xn().fetchUser();case 17:return e.next=19,xn().loadPage("markets");case 19:return e.abrupt("return");case 20:this.pwCache&&(this.pwCache.pw=n),this.success(o.xc);case 22:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Ar=function(){function e(t,n,r){var a=this;s(this,e),h(this,"form",void 0),h(this,"success",void 0),h(this,"pwCache",void 0),h(this,"headerTxt",void 0),h(this,"page",void 0),this.success=n,this.form=t,this.pwCache=r||null;var o=this.page=zt.parseTemplate(t);this.headerTxt=o.header.textContent||"",Or(t,o.submit,(function(){a.submit()})),xn().registerNoteFeeder({login:function(e){a.handleLoginNote(e)}})}var t,n;return u(e,[{key:"handleLoginNote",value:function(e){if(""!==e.details){var t=zt.idel(this.form,"loaderMsg");t&&(t.textContent=e.details)}}},{key:"focus",value:function(){this.page.pw.focus()}},{key:"submit",value:(n=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.errMsg),n=t.pw.value||"",t.pw.value="",r=t.rememberPass.checked,""!==n){e.next=9;break}return t.errMsg.textContent=It(k),zt.show(t.errMsg),e.abrupt("return");case 9:return a=xn().loading(this.form),e.next=12,yn("/api/login",{pass:n,rememberPass:r});case 12:if(o=e.sent,a(),xn().checkResponse(o)){e.next=18;break}return t.errMsg.textContent=o.msg,zt.show(t.errMsg),e.abrupt("return");case 18:o.notes&&(o.notes.reverse(),xn().setNotes(o.notes)),this.pwCache&&(this.pwCache.pw=n),this.success();case 21:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"animate",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,zt.animate(550,(function(e){t.style.transform="scale(".concat(.9+.1*e,")"),t.style.opacity=String(Math.pow(e,4))}),"easeOut");case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Fr=function(){function e(t){var n=this;s(this,e),h(this,"form",void 0),h(this,"page",void 0),h(this,"assetID",void 0),this.form=t;var r=this.page=zt.idDescendants(t);zt.bind(r.newDepAddrBttn,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n.newDepositAddress();case 1:case"end":return e.stop()}}),e)})))),zt.bind(r.copyAddressBtn,"click",(function(){n.copyAddress()}))}var t,n,r;return u(e,[{key:"setAsset",value:(r=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.assetID=t,n=this.page,zt.hide(n.depositErr),r=xn().assets[t],n.depositLogo.src=zt.logoPath(r.symbol),a=xn().walletMap[t],n.depositName.textContent=r.name,n.depositAddress.textContent=a.address,n.qrcode.src="/generateqrcode?address=".concat(a.address),0!=(2&a.traits)?zt.show(n.newDepAddrBttn):zt.hide(n.newDepAddrBttn);case 10:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"newDepositAddress",value:(n=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.depositErr),n=xn().loading(this.form),e.next=5,yn("/api/depositaddress",{assetID:this.assetID});case 5:if(r=e.sent,n(),xn().checkResponse(r)){e.next=11;break}return t.depositErr.textContent=r.msg,zt.show(t.depositErr),e.abrupt("return");case 11:t.depositAddress.textContent=r.address,t.qrcode.src="/generateqrcode?address=".concat(r.address);case 13:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"copyAddress",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,navigator.clipboard.writeText(t.depositAddress.textContent||"").then((function(){zt.show(t.copyAlert),setTimeout((function(){zt.hide(t.copyAlert)}),800)})).catch((function(e){console.error("Unable to copy: ",e)}));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Ir=300;function Rr(e,t){return Dr.apply(this,arguments)}function Dr(){return(Dr=o(w().mark((function e(t,n){var r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=document.body.offsetWidth/2,e.next=3,zt.animate(Ir,(function(e){t.style.right="".concat(e*r,"px")}),"easeInHard");case 3:return zt.hide(t),t.style.right="0",n.style.right=String(-r),zt.show(n),n.querySelector("input")&&zt.safeSelector(n,"input").focus(),e.next=10,zt.animate(Ir,(function(e){n.style.right="".concat(e*r-r,"px")}),"easeOutHard");case 10:n.style.right="0";case 11:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Or(e,t,n){var r=function(e){e.preventDefault&&e.preventDefault(),n(e)};zt.bind(t,"click",r),zt.bind(e,"submit",r)}function Tr(e){return Math.floor(e.getTime()/1e3)}function Pr(e){return function(e){return new Date(e.getTime()-60*e.getTimezoneOffset()*1e3)}(e).toISOString().split("T")[0]}var Lr=function(e){rn(y,e);var t,n,r,a,i,c,l,d,p,f,m,v=(f=y,m=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(f);if(m){var n=on(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return an(this,e)});function y(e,t){var n;s(this,y),h(tn(n=v.call(this)),"body",void 0),h(tn(n),"pwCache",void 0),h(tn(n),"currentDEX",void 0),h(tn(n),"page",void 0),h(tn(n),"loginForm",void 0),h(tn(n),"dexAddrForm",void 0),h(tn(n),"discoverAcctForm",void 0),h(tn(n),"newWalletForm",void 0),h(tn(n),"regAssetForm",void 0),h(tn(n),"walletWaitForm",void 0),h(tn(n),"confirmRegisterForm",void 0),n.body=e,n.pwCache={pw:""};var r=n.page=zt.idDescendants(e);t.host&&r.dexAddrForm.classList.contains("selected")&&(r.dexAddrForm.classList.remove("selected"),r.discoverAcctForm.classList.add("selected"),r.discoverAcctForm.dataset.host=t.host),e.querySelectorAll(".form-closer").forEach((function(e){return zt.hide(e)})),Or(r.appPWForm,r.appPWSubmit,(function(){return n.setAppPass()})),zt.bind(r.showSeedRestore,"click",(function(){zt.show(r.seedRestore),zt.hide(r.showSeedRestore)})),n.loginForm=new Ar(r.loginForm,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:n.discoverAcctForm?(n.discoverAcctForm.refresh(),Rr(r.loginForm,r.discoverAcctForm)):(n.dexAddrForm.refresh(),Rr(r.loginForm,r.dexAddrForm));case 3:case"end":return e.stop()}}),e)}))),n.pwCache),n.newWalletForm=new mr(r.newWalletForm,(function(e){return n.newWalletCreated(e)}),n.pwCache,(function(){return n.animateRegAsset(r.newWalletForm)})),n.dexAddrForm=new Sr(r.dexAddrForm,function(){var e=o(w().mark((function e(t,a){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n.requestFeepayment(r.dexAddrForm,t,a);case 1:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),n.pwCache);var a=r.discoverAcctForm.dataset.host;a&&(n.discoverAcctForm=new Er(r.discoverAcctForm,a,function(){var e=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n.requestFeepayment(r.discoverAcctForm,t,"");case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),n.pwCache)),n.regAssetForm=new br(r.regAssetForm,function(){var e=o(w().mark((function e(t){var a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n.confirmRegisterForm.setAsset(t),a=xn().assets[t],!(o=a.wallet)){e.next=14;break}if(s=n.currentDEX.bondAssets[a.symbol],!(o.synced&&o.balance.available>s.amount)){e.next=8;break}return n.animateConfirmForm(r.regAssetForm),e.abrupt("return");case 8:return e.next=10,n.getBondsFeeBuffer(t,r.regAssetForm);case 10:return i=e.sent,n.walletWaitForm.setWallet(o,i),Rr(r.regAssetForm,r.walletWait),e.abrupt("return");case 14:n.newWalletForm.setAsset(t),Rr(r.regAssetForm,r.newWalletForm);case 16:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),n.walletWaitForm=new kr(r.walletWait,(function(){n.animateConfirmForm(r.walletWait)}),(function(){n.animateRegAsset(r.walletWait)})),n.confirmRegisterForm=new gr(r.confirmRegForm,(function(){n.registerDEXSuccess()}),(function(){n.animateRegAsset(r.confirmRegForm)}),n.pwCache);var i=zt.safeSelector(r.forms,":scope > form.selected");switch(i.classList.remove("selected"),i){case r.loginForm:n.loginForm.animate();break;case r.dexAddrForm:n.dexAddrForm.animate();break;case r.discoverAcctForm:n.discoverAcctForm.animate()}return zt.show(i),xn().authed()&&n.auth(),n}return u(y,[{key:"unload",value:function(){this.pwCache.pw=""}},{key:"auth",value:(p=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:case"end":return e.stop()}}),e)}))),function(){return p.apply(this,arguments)})},{key:"requestFeepayment",value:(d=o(w().mark((function e(t,n,r){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.currentDEX=n,this.confirmRegisterForm.setExchange(n,r),this.walletWaitForm.setExchange(n),this.regAssetForm.setExchange(n),this.animateRegAsset(t);case 5:case"end":return e.stop()}}),e,this)}))),function(e,t,n){return d.apply(this,arguments)})},{key:"animateRegAsset",value:(l=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:zt.hide(t),this.regAssetForm.animate(),zt.show(this.page.regAssetForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return l.apply(this,arguments)})},{key:"animateConfirmForm",value:(c=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),zt.hide(t),zt.show(this.page.confirmRegForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"getBondsFeeBuffer",value:(i=o(w().mark((function e(t,n){var r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=xn().loading(n),e.next=3,yn("/api/bondsfeebuffer",{assetID:t});case 3:if(a=e.sent,r(),xn().checkResponse(a)){e.next=7;break}return e.abrupt("return",0);case 7:return e.abrupt("return",a.feeBuffer);case 8:case"end":return e.stop()}}),e)}))),function(e,t){return i.apply(this,arguments)})},{key:"setAppPass",value:(a=o(w().mark((function e(){var t,n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.appPWErrMsg),n=t.appPW.value||"",r=t.appPWAgain.value,""!==n){e.next=8;break}return t.appPWErrMsg.textContent=It(k),zt.show(t.appPWErrMsg),e.abrupt("return");case 8:if(n===r){e.next=12;break}return t.appPWErrMsg.textContent=It(G),zt.show(t.appPWErrMsg),e.abrupt("return");case 12:return xn().setNotes([]),t.appPW.value="",t.appPWAgain.value="",a=xn().loading(t.appPWForm),o=t.seedInput.value,s=t.rememberPass.checked,e.next=20,yn("/api/init",{pass:n,seed:o,rememberPass:s});case 20:if(i=e.sent,a(),xn().checkResponse(i)){e.next=26;break}return t.appPWErrMsg.textContent=i.msg,zt.show(t.appPWErrMsg),e.abrupt("return");case 26:return this.pwCache.pw=n,this.auth(),xn().updateMenuItemsDisplay(),this.newWalletForm.refresh(),this.dexAddrForm.refresh(),e.next=33,Rr(t.appPWForm,t.dexAddrForm);case 33:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"getCertFile",value:(r=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t="",!this.dexAddrForm.page.certFile.value){e.next=7;break}if(!(n=this.dexAddrForm.page.certFile.files)||!n.length){e.next=7;break}return e.next=6,n[0].text();case 6:t=e.sent;case 7:return e.abrupt("return",t);case 8:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"registerDEXSuccess",value:(n=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:return e.next=4,xn().loadPage("markets");case 4:case"end":return e.stop()}}),e)}))),function(){return n.apply(this,arguments)})},{key:"newWalletCreated",value:(t=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.regAssetForm.refresh(),e.next=3,xn().fetchUser();case 3:if(n=e.sent){e.next=6;break}return e.abrupt("return");case 6:if(r=this.page,a=n.assets[t],o=a.wallet,s=this.currentDEX.bondAssets[a.symbol].amount,!(o.synced&&o.balance.available>s)){e.next=14;break}return e.next=13,this.animateConfirmForm(r.newWalletForm);case 13:return e.abrupt("return");case 14:return e.next=16,this.getBondsFeeBuffer(t,r.newWalletForm);case 16:return i=e.sent,this.walletWaitForm.setWallet(o,i),e.next=20,Rr(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),y}(fn);var Br=function(e){rn(i,e);var t,n,r,a=(n=i,r=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(n);if(r){var a=on(this).constructor;e=Reflect.construct(t,arguments,a)}else e=t.apply(this,arguments);return an(this,e)});function i(e){var t;return s(this,i),h(tn(t=a.call(this)),"form",void 0),h(tn(t),"loginForm",void 0),t.form=zt.idel(e,"loginForm"),zt.show(t.form),t.loginForm=new Ar(t.form,(function(){t.loggedIn()})),t.loginForm.focus(),t}return u(i,[{key:"loggedIn",value:(t=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:return e.next=4,xn().loadPage("markets");case 4:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})}]),i}(fn);function Mr(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Wr(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Wr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Wr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n form"),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){t.closePopups()}))})),zt.bind(n.cancelForce,"click",(function(){t.closePopups()})),t.selectedAssetID=-1,zt.cleanTemplates(n.iconSelectTmpl,n.balanceDetailRow,n.recentOrderTmpl),zt.bind(n.createWallet,"click",(function(){return t.showNewWallet(t.selectedAssetID)})),zt.bind(n.connectBttn,"click",(function(){return t.doConnect(t.selectedAssetID)})),zt.bind(n.send,"click",(function(){return t.showSendForm(t.selectedAssetID)})),zt.bind(n.receive,"click",(function(){return t.showDeposit(t.selectedAssetID)})),zt.bind(n.unlockBttn,"click",(function(){return t.openWallet(t.selectedAssetID)})),zt.bind(n.lockBttn,"click",(function(){return t.lock(t.selectedAssetID)})),zt.bind(n.reconfigureBttn,"click",(function(){return t.showReconfig(t.selectedAssetID)})),zt.bind(n.rescanWallet,"click",(function(){return t.rescanWallet(t.selectedAssetID)})),t.newWalletForm=new mr(n.newWalletForm,(function(e){var r={assetName:xn().assets[e].name};t.assetUpdated(e,n.newWalletForm,It(xe,r)),t.sortAssetButtons()})),t.reconfigForm=new yr(n.reconfigInputs,!1),t.unlockForm=new xr(n.unlockWalletForm,(function(e){return t.openWalletSuccess(e,n.unlockWalletForm)})),Or(n.sendForm,n.submitSendForm,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.stepSend();case 1:case"end":return e.stop()}}),e)})))),Or(n.vSendForm,n.vSend,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.send();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.vCancelSend,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.cancelSend();case 1:case"end":return e.stop()}}),e)})))),Or(n.reconfigForm,n.submitReconfig,(function(){return t.reconfig()})),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){return t.closePopups()}))})),zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||t.closePopups()})),t.keyup=function(e){"Escape"===e.key&&zt.isDisplayed(t.page.forms)&&t.closePopups()},zt.bind(document,"keyup",t.keyup),zt.bind(n.downloadLogs,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.downloadLogs();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.exportWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.displayExportWalletAuth();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.recoverWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showRecoverWallet();case 1:case"end":return e.stop()}}),e)})))),Or(n.exportWalletAuth,n.exportWalletAuthSubmit,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.exportWalletAuthSubmit();case 1:case"end":return e.stop()}}),e)})))),Or(n.recoverWalletConfirm,n.recoverWalletSubmit,(function(){t.recoverWallet()})),Or(n.confirmForce,n.confirmForceSubmit,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.confirmForceSubmit();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.disableWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showToggleWalletStatus(!0);case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.enableWallet,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showToggleWalletStatus(!1);case 1:case"end":return e.stop()}}),e)})))),Or(n.toggleWalletStatusConfirm,n.toggleWalletStatusSubmit,o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.toggleWalletStatus();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.managePeers,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.showManagePeersForm();case 1:case"end":return e.stop()}}),e)})))),zt.bind(n.addPeerSubmit,"click",o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.submitAddPeer();case 1:case"end":return e.stop()}}),e)})))),t.depositAddrForm=new Fr(n.deposit),zt.bind(n.walletBal,"click",(function(){t.populateMaxSend()})),zt.bind(n.sendAmt,"input",(function(){var e=xn().assets[t.selectedAssetID].unitInfo,r=parseFloat(n.sendAmt.value||"0"),a=e.conventional.conversionFactor;t.showFiatValue(t.selectedAssetID,r*a,n.sendValue)})),zt.bind(n.maxSend,"click",(function(){t.populateMaxSend()})),zt.bind(n.sendAddr,"input",o(w().mark((function e(){var r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=xn().assets[t.selectedAssetID],zt.hide(n.validAddr),n.sendAddr.classList.remove("invalid"),a=n.sendAddr.value||"",r&&""!==a){e.next=6;break}return e.abrupt("return");case 6:return e.next=8,t.validateSendAddress(a,r.id);case 8:e.sent?zt.show(n.validAddr):n.sendAddr.classList.add("invalid");case 10:case"end":return e.stop()}}),e)})))),zt.bind(n.showChangePW,"click",(function(){t.changeWalletPW=!t.changeWalletPW,t.setPWSettingViz(t.changeWalletPW)})),zt.bind(n.changeWalletTypeSelect,"change",(function(){t.changeWalletType()})),zt.bind(n.showChangeType,"click",(function(){zt.isHidden(n.changeWalletType)?(zt.show(n.changeWalletType,n.changeTypeHideIcon),zt.hide(n.changeTypeShowIcon),n.changeTypeMsg.textContent=It(me)):t.showReconfig(t.selectedAssetID,!0)})),xn().registerNoteFeeder({fiatrateupdate:function(e){t.handleRatesNote(e)},balance:function(e){t.handleBalanceNote(e)},walletstate:function(e){t.handleWalletStateNote(e)},walletconfig:function(e){t.handleWalletStateNote(e)},createwallet:function(e){t.handleCreateWalletNote(e)}});var r=t.sortAssetButtons().id,a=en.fetchLocal(en.selectedAssetLK);return a&&(r=Number(a)),t.setSelectedAsset(r),t}return u(V,[{key:"closePopups",value:function(){zt.hide(this.page.forms),this.animation&&this.animation.stop()}},{key:"stepSend",value:(N=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d,p,f,m,v,y,g,b,k,x;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.vSendErr,t.sendErr,t.vSendEstimates,t.txFeeNotAvailable),n=parseInt(t.sendForm.dataset.assetID||""),r=xn().assets[n].token,a=t.subtractCheckBox.checked||!1,o=xn().unitInfo(n).conventional.conversionFactor,s=Math.round(parseFloat(t.sendAmt.value||"")*o),""!==(i=t.sendAddr.value||"")){e.next=10;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:i})));case 10:if(c=xn().assets[n],l=c.wallet,u=c.unitInfo,h=c.symbol,d=0,0==(1024&l.traits)){e.next=36;break}return p={addr:t.sendAddr.value,assetID:n,subtract:a,value:s},f=xn().loading(t.sendForm),e.next=17,yn("/api/txfee",p);case 17:if(m=e.sent,f(),xn().checkResponse(m)){e.next=29;break}return t.txFeeNotAvailable.dataset.tooltip=It(Me,{err:m.msg}),zt.show(t.txFeeNotAvailable),e.next=24,this.validateSendAddress(i,n);case 24:if(e.sent){e.next=27;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:i||""})));case 27:e.next=34;break;case 29:if(!m.ok){e.next=34;break}if(m.validaddress){e.next=32;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:t.sendAddr.value||""})));case 32:d=m.txfee,zt.show(t.vSendEstimates);case 34:e.next=41;break;case 36:return e.next=38,this.validateSendAddress(i,n);case 38:if(e.sent){e.next=41;break}return e.abrupt("return",zt.showFormError(t.sendErr,It(Be,{address:i||""})));case 41:return t.vSendSymbol.textContent=h.toUpperCase(),t.vSendLogo.src=zt.logoPath(h),r?(v=xn().assets[r.parentID],y=v.unitInfo,g=v.symbol,t.vSendFee.textContent=zt.formatFullPrecision(d,y)+" "+g):t.vSendFee.textContent=zt.formatFullPrecision(d,u),this.showFiatValue(n,d,t.vSendFeeFiat),t.vSendDestinationAmt.textContent=zt.formatFullPrecision(s-d,u),t.vTotalSend.textContent=zt.formatFullPrecision(s,u),this.showFiatValue(n,s,t.vTotalSendFiat),t.vSendAddr.textContent=t.sendAddr.value||"",b=l.balance.available-s,t.balanceAfterSend.textContent=zt.formatFullPrecision(b,u),this.showFiatValue(n,b,t.balanceAfterSendFiat),zt.show(t.approxSign),a||(zt.hide(t.approxSign),t.vSendDestinationAmt.textContent=zt.formatFullPrecision(s,u),k=s,r||(k+=d),t.vTotalSend.textContent=zt.formatFullPrecision(k,u),this.showFiatValue(n,k,t.vTotalSendFiat),x=l.balance.available-s,r||(x-=d),x<=0?(t.balanceAfterSend.textContent=zt.formatFullPrecision(0,u),this.showFiatValue(n,0,t.balanceAfterSendFiat)):(t.balanceAfterSend.textContent=zt.formatFullPrecision(x,u),this.showFiatValue(n,x,t.balanceAfterSendFiat))),zt.hide(t.sendForm),e.next=57,this.showForm(t.vSendForm);case 57:case"end":return e.stop()}}),e,this)}))),function(){return N.apply(this,arguments)})},{key:"cancelSend",value:(q=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.vSendForm,t.sendErr),e.next=4,this.showForm(t.sendForm);case 4:case"end":return e.stop()}}),e,this)}))),function(){return q.apply(this,arguments)})},{key:"validateSendAddress",value:(W=o(w().mark((function e(t,n){var r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,yn("/api/validateaddress",{addr:t,assetID:n});case 2:return r=e.sent,e.abrupt("return",xn().checkResponse(r));case 4:case"end":return e.stop()}}),e)}))),function(e,t){return W.apply(this,arguments)})},{key:"setPWSettingViz",value:function(e){if(e)return zt.hide(this.page.showIcon),zt.show(this.page.hideIcon,this.page.changePW),void(this.page.switchPWMsg.textContent=It(Q));zt.hide(this.page.hideIcon,this.page.changePW),zt.show(this.page.showIcon),this.page.switchPWMsg.textContent=It(Y)}},{key:"updateWalletPeersTable",value:(M=o(w().mark((function e(){var t,n,r,a,s,i,c=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.peerSpinner),e.next=4,yn("/api/getwalletpeers",{assetID:this.selectedAssetID});case 4:if(n=e.sent,xn().checkResponse(n)){e.next=9;break}return t.managePeersErr.textContent=n.msg,zt.show(t.managePeersErr),e.abrupt("return");case 9:for(;t.peersTableBody.firstChild;)t.peersTableBody.removeChild(t.peersTableBody.firstChild);(r=n.peers||[]).sort((function(e,t){return e.source-t.source})),a=It(ze),s=It(Ve),i=It(je),r.forEach((function(e){var n,r=t.peerTableRow.cloneNode(!0),l=zt.parseTemplate(r);switch(l.addr.textContent=e.addr,e.source){case ln.WalletDefault:l.source.textContent=a;break;case ln.UserAdded:l.source.textContent=s;break;case ln.Discovered:l.source.textContent=i}if(n=e.connected?c.page.connectedIconTmpl.cloneNode(!0):c.page.disconnectedIconTmpl.cloneNode(!0),l.connected.appendChild(n),e.source===ln.UserAdded){var u=c.page.removeIconTmpl.cloneNode(!0);zt.bind(u,"click",o(w().mark((function n(){var r;return w().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return zt.hide(t.managePeersErr),n.next=3,yn("/api/removewalletpeer",{assetID:c.selectedAssetID,addr:e.addr});case 3:if(r=n.sent,xn().checkResponse(r)){n.next=8;break}return t.managePeersErr.textContent=r.msg,zt.show(t.managePeersErr),n.abrupt("return");case 8:c.spinUntilPeersUpdate();case 9:case"end":return n.stop()}}),n)})))),l.remove.appendChild(u)}t.peersTableBody.appendChild(r)}));case 16:case"end":return e.stop()}}),e,this)}))),function(){return M.apply(this,arguments)})},{key:"showManagePeersForm",value:(B=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,e.next=3,this.updateWalletPeersTable();case 3:zt.hide(t.managePeersErr),this.showForm(t.managePeersForm);case 5:case"end":return e.stop()}}),e,this)}))),function(){return B.apply(this,arguments)})},{key:"submitAddPeer",value:(L=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.managePeersErr),e.next=4,yn("/api/addwalletpeer",{assetID:this.selectedAssetID,addr:t.addPeerInput.value});case 4:if(n=e.sent,xn().checkResponse(n)){e.next=9;break}return t.managePeersErr.textContent=n.msg,zt.show(t.managePeersErr),e.abrupt("return");case 9:this.spinUntilPeersUpdate(),t.addPeerInput.value="";case 11:case"end":return e.stop()}}),e,this)}))),function(){return L.apply(this,arguments)})},{key:"spinUntilPeersUpdate",value:(P=o(w().mark((function e(){var t,n=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,zt.show(t.peerSpinner),setTimeout((function(){zt.isDisplayed(t.peerSpinner)&&n.updateWalletPeersTable()}),1e4);case 3:case"end":return e.stop()}}),e,this)}))),function(){return P.apply(this,arguments)})},{key:"showToggleWalletStatus",value:function(e){var t=this.page;zt.hide(t.toggleWalletStatusErr,t.walletStatusDisable,t.disableWalletMsg,t.walletStatusEnable,t.enableWalletMsg),e?zt.show(t.walletStatusDisable,t.disableWalletMsg):zt.show(t.walletStatusEnable,t.enableWalletMsg),this.showForm(t.toggleWalletStatusConfirm)}},{key:"toggleWalletStatus",value:(T=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.toggleWalletStatusErr),n=xn().assets[this.selectedAssetID],r=!n.wallet.disabled,a={assetID:this.selectedAssetID,disable:r},o={assetName:n.name},s=xn().loading(t.toggleWalletStatusConfirm),e.next=10,yn("/api/togglewalletstatus",a);case 10:if(i=e.sent,s(),xn().checkResponse(i)){e.next=16;break}return i.code===sn.activeOrdersErr?t.toggleWalletStatusErr.textContent=It(Ie,o):t.toggleWalletStatusErr.textContent=i.msg,zt.show(t.toggleWalletStatusErr),e.abrupt("return");case 16:c=It(Ae,o),r||(c=It(Fe,o)),this.assetUpdated(this.selectedAssetID,t.toggleWalletStatusConfirm,c);case 19:case"end":return e.stop()}}),e,this)}))),function(){return T.apply(this,arguments)})},{key:"showBox",value:(O=o(w().mark((function e(t,n){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t.style.opacity="0",zt.show(t),n&&n.focus(),e.next=5,zt.animate(300,(function(e){t.style.opacity="".concat(e)}),"easeOut");case 5:t.style.opacity="1",this.displayed=t;case 7:case"end":return e.stop()}}),e,this)}))),function(e,t){return O.apply(this,arguments)})},{key:"showForm",value:(D=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return zt.hide(e)})),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(300,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return D.apply(this,arguments)})},{key:"showSuccess",value:(R=o(w().mark((function e(t){var n,a,o,s,i,c,l,u,h,d=this;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).successMessage.textContent=t,this.currentForm=n.checkmarkForm,this.forms.forEach((function(e){return zt.hide(e)})),zt.show(n.forms,n.checkmarkForm),n.checkmarkForm.style.right="0",n.checkmark.style.fontSize="0px",a=en.isDark()?[223,226,225]:[51,51,51],o=r(a,3),s=o[0],i=o[1],c=o[2],l=16-s,u=163-i,h=16-c,this.animation=new Vt(1200,(function(e){n.checkmark.style.fontSize="".concat(80*e,"px"),n.checkmark.style.color="rgb(".concat(s+e*l,", ").concat(i+e*u,", ").concat(c+e*h,")")}),"easeOutElastic",(function(){d.animation=new Vt(1500,(function(){}),"",(function(){d.currentForm===n.checkmarkForm&&d.closePopups()}))}));case 11:case"end":return e.stop()}}),e,this)}))),function(e){return R.apply(this,arguments)})},{key:"showNewWallet",value:(I=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=n.newWalletForm,this.newWalletForm.setAsset(t),a=this.newWalletForm.loadDefaults(),e.next=6,this.showForm(r);case 6:return e.next=8,a;case 8:case"end":return e.stop()}}),e,this)}))),function(e){return I.apply(this,arguments)})},{key:"sortAssetButtons",value:function(){var e=this,t=this.page;this.assetButtons={},zt.empty(t.assetSelect);var n=kn(Object.values(xn().assets));n.sort((function(e,t){return e.wallet&&!t.wallet?-1:!e.wallet&&t.wallet?1:e.symbol.localeCompare(t.symbol)}));var r,a=Mr(n);try{var o=function(){var n=r.value,a=t.iconSelectTmpl.cloneNode(!0);t.assetSelect.appendChild(a);var o=zt.parseTemplate(a);e.assetButtons[n.id]={tmpl:o,bttn:a},e.updateAssetButton(n.id),zt.bind(a,"click",(function(){e.setSelectedAsset(n.id),en.storeLocal(en.selectedAssetLK,String(n.id))}))};for(a.s();!(r=a.n()).done;)o()}catch(e){a.e(e)}finally{a.f()}return t.assetSelect.classList.remove("invisible"),n[0]}},{key:"updateAssetButton",value:function(e){var t,n=xn().assets[e],r=this.assetButtons[e],a=r.bttn,o=r.tmpl;if(zt.hide(o.fiat,o.noWallet),a.classList.add("nowallet"),(t=o.img).src||(t.src=zt.logoPath(n.symbol)),o.name.textContent=n.name,n.wallet){a.classList.remove("nowallet");var s=n.wallet.balance,i=n.unitInfo,c=s.available+s.locked+s.immature;o.balance.textContent=zt.formatCoinValue(c,i);var l=xn().fiatRatesMap[n.id];l&&(zt.show(o.fiat),o.fiat.textContent=zt.formatFiatConversion(c,l,i))}else zt.show(o.noWallet)}},{key:"setSelectedAsset",value:function(e){var t,n=Mr(this.page.assetSelect.children);try{for(n.s();!(t=n.n()).done;)t.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}this.assetButtons[e].bttn.classList.add("selected"),this.selectedAssetID=e,this.updateDisplayedAsset(e),this.showAvailableMarkets(e),this.showRecentActivity(e)}},{key:"updateDisplayedAsset",value:function(e){if(e===this.selectedAssetID){var t,n=xn().assets[e],r=n.symbol,a=n.wallet,o=n.name,s=this.page,i=Mr(document.querySelectorAll("[data-asset-name]"));try{for(i.s();!(t=i.n()).done;)t.value.textContent=o}catch(e){i.e(e)}finally{i.f()}if(s.assetLogo.src=zt.logoPath(r),zt.hide(s.balanceBox,s.fiatBalanceBox,s.createWalletBox,s.walletDetails,s.sendReceive,s.connectBttnBox,s.statusLocked,s.statusReady,s.statusOff,s.unlockBttnBox,s.lockBttnBox,s.connectBttnBox,s.peerCountBox,s.syncProgressBox,s.statusDisabled),a){this.updateDisplayedAssetBalance();var c=xn().walletDefinition(e,a.type);s.walletType.textContent=c.tab;var l=function(e){var t=xn().assets[e];if(t.token){var n=t.token.definition.configopts;return n&&n.length>0}if(!t.info)throw Error("this asset isn't an asset, I guess");var r=t.info.availablewallets,a=r[0].configopts;return r.length>1||a&&a.length>0}(e);zt.setVis(l,s.passwordWrapper),a.disabled?zt.show(s.statusDisabled):a.running?(zt.show(s.sendReceive,s.peerCountBox,s.syncProgressBox),s.peerCount.textContent=String(a.peerCount),s.syncProgress.textContent="".concat((100*a.syncProgress).toFixed(1),"%"),a.open?(zt.show(s.statusReady),!xn().haveActiveOrders(e)&&a.encrypted&&zt.show(s.lockBttnBox)):zt.show(s.statusLocked,s.unlockBttnBox)):zt.show(s.statusOff,s.connectBttnBox)}else zt.show(s.createWalletBox);s.walletDetailsBox.classList.remove("invisible")}}},{key:"updateDisplayedAssetBalance",value:function(){var e=this.page,t=xn().assets[this.selectedAssetID],n=t.wallet,a=t.unitInfo,o=t.symbol,s=t.id,i=n.balance;zt.show(e.balanceBox,e.walletDetails);var c=i.locked+i.contractlocked+i.bondlocked,l=i.available+c+i.immature;e.balance.textContent=zt.formatCoinValue(l,a),zt.empty(e.balanceUnit),e.balanceUnit.appendChild(zt.symbolize(o));var u=xn().fiatRatesMap[s];u&&(zt.show(e.fiatBalanceBox),e.fiatBalance.textContent=zt.formatFiatConversion(l,u,a));var h=!1;zt.empty(e.balanceDetailBox);var d=function(t,n){var r=e.balanceDetailRow.cloneNode(!0);h&&(r.classList.add("first-other"),h=!1),e.balanceDetailBox.appendChild(r);var o=zt.parseTemplate(r);o.category.textContent=t,o.subBalance.textContent=zt.formatCoinValue(n,a)};d("Available",i.available),d("Locked",c),d("Immature",i.immature);var p=Object.entries(i.other||{});p.sort((function(e,t){return e[0].localeCompare(t[0])})),h=!0,i.contractlocked>0&&d("Swapping (locked)",i.contractlocked),i.bondlocked>0&&d("Bonded (locked)",i.bondlocked);for(var f=0,m=p;f1){zt.empty(r.changeWalletTypeSelect),zt.show(r.showChangeType,r.changeTypeShowIcon),r.changeTypeMsg.textContent=It(fe),i=Mr(s);try{for(i.s();!(c=i.n()).done;)l=c.value,u=document.createElement("option"),l.type===o.type&&(u.selected=!0),u.value=u.textContent=l.type,r.changeWalletTypeSelect.appendChild(u)}catch(e){i.e(e)}finally{i.f()}}else zt.hide(r.showChangeType);return h=xn().walletMap[t],zt.setVis(4&h.traits,r.downloadLogs),zt.setVis(32&h.traits,r.recoverWallet),zt.setVis(256&h.traits,r.exportWallet),zt.setVis(1&h.traits,r.rescanWallet),zt.setVis(2048&h.traits,r.managePeers),zt.setVis(0&h.traits,r.otherActionsLabel),h.disabled?zt.show(r.enableWallet):zt.show(r.disableWallet),r.recfgAssetLogo.src=zt.logoPath(a.symbol),r.recfgAssetName.textContent=a.name,n||this.showForm(r.reconfigForm),d=xn().loading(r.reconfigForm),e.next=24,yn("/api/walletsettings",{assetID:t});case 24:if(p=e.sent,d(),xn().checkResponse(p)){e.next=29;break}return zt.showFormError(r.reconfigErr,p.msg),e.abrupt("return");case 29:f=xn().haveActiveOrders(t),this.reconfigForm.update(o.configopts||[],f),this.reconfigForm.setConfig(p.map),this.updateDisplayedReconfigFields(o);case 33:case"end":return e.stop()}}),e,this)}))),function(e,t){return C.apply(this,arguments)})},{key:"changeWalletType",value:function(){var e=this.page.changeWalletTypeSelect.value||"",t=xn().walletDefinition(this.selectedAssetID,e);this.reconfigForm.update(t.configopts||[],!1),this.updateDisplayedReconfigFields(t)}},{key:"updateDisplayedReconfigFields",value:function(e){e.seeded||"token"===e.type?(zt.hide(this.page.showChangePW,this.reconfigForm.fileSelector),this.changeWalletPW=!1,this.setPWSettingViz(!1)):zt.show(this.page.showChangePW,this.reconfigForm.fileSelector)}},{key:"showDeposit",value:(b=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.depositAddrForm.setAsset(t),this.showForm(this.page.deposit);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return b.apply(this,arguments)})},{key:"showSendForm",value:(g=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u,h,d,p,f,m,v,y;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,r=n.sendForm,a=xn().assets[t],o=a.wallet,s=a.name,i=a.unitInfo,c=a.symbol,l=a.token,zt.hide(n.toggleSubtract),n.subtractCheckBox.checked=!1,(u=0!=(64&o.traits))&&zt.show(n.toggleSubtract),zt.hide(n.validAddr,n.sendErr,n.maxSendDisplay),n.sendAddr.classList.remove("invalid"),n.sendAddr.value="",n.sendAmt.value="",this.showFiatValue(t,0,n.sendValue),n.walletBal.textContent=zt.formatFullPrecision(o.balance.available,i),n.sendLogo.src=zt.logoPath(c),n.sendName.textContent=s,!(o.balance.available>0&&0!=(1024&o.traits))){e.next=23;break}return h={assetID:t,subtract:u,value:o.balance.available},d=xn().loading(this.body),e.next=20,yn("/api/txfee",h);case 20:p=e.sent,d(),xn().checkResponse(p)&&(f=o.balance.available,l||(f-=p.txfee)<0&&(f=0),this.maxSend=f,n.maxSend.textContent=zt.formatFullPrecision(f,i),this.showFiatValue(t,f,n.maxSendFiat),l?(m=xn().assets[l.parentID],v=m.unitInfo,y=m.symbol,n.maxSendFee.textContent=zt.formatFullPrecision(p.txfee,v)+" "+y,this.showFiatValue(l.parentID,p.txfee,n.maxSendFeeFiat)):(n.maxSendFee.textContent=zt.formatFullPrecision(p.txfee,i),this.showFiatValue(t,p.txfee,n.maxSendFeeFiat)),zt.show(n.maxSendDisplay));case 23:this.showFiatValue(t,0,n.sendValue),n.walletBal.textContent=zt.formatFullPrecision(o.balance.available,i),n.sendLogo.src=zt.logoPath(o.symbol),n.sendName.textContent=s,r.dataset.assetID=String(t),this.showForm(r);case 29:case"end":return e.stop()}}),e,this)}))),function(e){return g.apply(this,arguments)})},{key:"doConnect",value:(y=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=xn().loading(this.body),e.next=3,yn("/api/connectwallet",{assetID:t});case 3:if(r=e.sent,n(),xn().checkResponse(r)){e.next=7;break}return e.abrupt("return");case 7:this.updateDisplayedAsset(t);case 8:case"end":return e.stop()}}),e,this)}))),function(e){return y.apply(this,arguments)})},{key:"openWalletSuccess",value:(v=o(w().mark((function e(t,n){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.assetUpdated(t,n,It(Ce));case 1:case"end":return e.stop()}}),e,this)}))),function(e,t){return v.apply(this,arguments)})},{key:"assetUpdated",value:function(e,t,n){e===this.selectedAssetID&&(this.updateDisplayedAsset(e),t&&Object.is(this.currentForm,t)&&(n?this.showSuccess(n):this.closePopups()))}},{key:"populateMaxSend",value:(m=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,n=xn().assets[this.selectedAssetID]){e.next=4;break}return e.abrupt("return");case 4:0==(64&n.wallet.traits)?(t.sendAmt.value=String(this.maxSend/n.unitInfo.conventional.conversionFactor),this.showFiatValue(n.id,this.maxSend,t.sendValue),t.subtractCheckBox.checked=!1):(r=n.wallet.balance.available,t.sendAmt.value=String(r/n.unitInfo.conventional.conversionFactor),this.showFiatValue(n.id,r,t.sendValue),t.subtractCheckBox.checked=!0);case 5:case"end":return e.stop()}}),e,this)}))),function(){return m.apply(this,arguments)})},{key:"send",value:(f=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=this.page,o=parseInt(null!==(t=a.sendForm.dataset.assetID)&&void 0!==t?t:""),s=null!==(n=a.subtractCheckBox.checked)&&void 0!==n&&n,i=xn().unitInfo(o).conventional.conversionFactor,c=a.vSendPw.value||"",a.vSendPw.value="",""!==c){e.next=9;break}return zt.showFormError(a.vSendErr,It(k)),e.abrupt("return");case 9:return l={assetID:o,address:a.sendAddr.value,subtract:s,value:Math.round(parseFloat(null!==(r=a.sendAmt.value)&&void 0!==r?r:"")*i),pw:c},u=xn().loading(a.vSendForm),e.next=13,yn("/api/send",l);case 13:if(h=e.sent,u(),xn().checkResponse(h)){e.next=18;break}return zt.showFormError(a.vSendErr,h.msg),e.abrupt("return");case 18:d=xn().assets[o].name,this.assetUpdated(o,a.vSendForm,It(be,{assetName:d}));case 20:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"reconfig",value:(p=o(w().mark((function e(){var t,n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,r=this.selectedAssetID,zt.hide(n.reconfigErr),n.appPW.value||en.passwordIsCached()){e.next=6;break}return zt.showFormError(n.reconfigErr,It(x)),e.abrupt("return");case 6:return a=xn().currentWalletDefinition(r).type,zt.isHidden(n.changeWalletType)||(a=n.changeWalletTypeSelect.value||""),o=xn().loading(n.reconfigForm),s={assetID:r,config:this.reconfigForm.map(r),appPW:null!==(t=n.appPW.value)&&void 0!==t?t:"",walletType:a},this.changeWalletPW&&(s.newWalletPW=n.newPW.value),e.next=13,yn("/api/reconfigurewallet",s);case 13:if(i=e.sent,n.appPW.value="",n.newPW.value="",o(),xn().checkResponse(i)){e.next=20;break}return zt.showFormError(n.reconfigErr,i.msg),e.abrupt("return");case 20:this.assetUpdated(r,n.reconfigForm,It(we));case 21:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"lock",value:(d=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=xn().loading(n.newWalletForm),e.next=4,yn("/api/closewallet",{assetID:t});case 4:if(a=e.sent,r(),xn().checkResponse(a)){e.next=8;break}return e.abrupt("return");case 8:this.updateDisplayedAsset(t);case 9:case"end":return e.stop()}}),e,this)}))),function(e){return d.apply(this,arguments)})},{key:"downloadLogs",value:(l=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(t=new URLSearchParams("")).append("assetid","".concat(this.selectedAssetID)),(n=new URL(window.location.href)).search=t.toString(),n.pathname="/wallets/logfile",window.open(n.toString());case 6:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"displayExportWalletAuth",value:(c=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,zt.hide(t.exportWalletErr),t.exportWalletPW.value="",this.showForm(t.exportWalletAuth);case 4:case"end":return e.stop()}}),e,this)}))),function(){return c.apply(this,arguments)})},{key:"exportWalletAuthSubmit",value:(i=o(w().mark((function e(){var t,n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n={assetID:this.selectedAssetID,pass:t.exportWalletPW.value},r=xn().loading(t.forms),e.next=6,yn("/api/restorewalletinfo",n);case 6:a=e.sent,r(),xn().checkResponse(a)?(t.exportWalletPW.value="",this.displayRestoreWalletInfo(a.restorationinfo)):zt.showFormError(t.exportWalletErr,a.msg);case 9:case"end":return e.stop()}}),e,this)}))),function(){return i.apply(this,arguments)})},{key:"displayRestoreWalletInfo",value:(a=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.page,zt.empty(n.restoreInfoCardsList),r=Mr(t);try{for(r.s();!(a=r.n()).done;)o=a.value,s=this.restoreInfoCard.cloneNode(!0),(i=zt.parseTemplate(s)).name.textContent=o.target,i.seed.textContent=o.seed,i.seedName.textContent="".concat(o.seedName,":"),i.instructions.textContent=o.instructions,n.restoreInfoCardsList.appendChild(s)}catch(e){r.e(e)}finally{r.f()}this.showForm(n.restoreWalletInfo);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return a.apply(this,arguments)})},{key:"recoverWallet",value:(n=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.recoverWalletErr),n={assetID:this.selectedAssetID,appPW:t.recoverWalletPW.value},t.recoverWalletPW.value="",r="/api/recoverwallet",a=xn().loading(t.forms),e.next=8,yn(r,n);case 8:o=e.sent,a(),o.code===sn.activeOrdersErr?(this.forceUrl=r,this.forceReq=n,this.showConfirmForce()):xn().checkResponse(o)?this.closePopups():zt.showFormError(t.recoverWalletErr,o.msg);case 11:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"confirmForceSubmit",value:(t=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.forceReq.force=!0,n=xn().loading(t.forms),e.next=5,yn(this.forceUrl,this.forceReq);case 5:r=e.sent,n(),xn().checkResponse(r)?this.closePopups():zt.showFormError(t.confirmForceErr,r.msg);case 8:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleBalanceNote",value:function(e){this.updateAssetButton(e.assetID),e.assetID===this.selectedAssetID&&this.updateDisplayedAssetBalance()}},{key:"handleRatesNote",value:function(e){this.updateAssetButton(this.selectedAssetID),e.fiatRates[this.selectedAssetID]&&this.updateDisplayedAssetBalance()}},{key:"showFiatValue",value:function(e,t,n){var r=xn().fiatRatesMap[e];r?(n.textContent=zt.formatFiatConversion(t,r,xn().unitInfo(e)),zt.show(n.parentElement)):zt.hide(n.parentElement)}},{key:"handleWalletStateNote",value:function(e){this.updateAssetButton(e.wallet.assetID),this.assetUpdated(e.wallet.assetID),"WalletPeersUpdate"===e.topic&&e.wallet.assetID===this.selectedAssetID&&zt.isDisplayed(this.page.managePeersForm)&&this.updateWalletPeersTable()}},{key:"handleCreateWalletNote",value:function(e){this.updateAssetButton(e.assetID),this.assetUpdated(e.assetID)}},{key:"unload",value:function(){zt.unbind(document,"keyup",this.keyup)}}]),V}(fn),Nr=new Intl.NumberFormat(navigator.languages,{maximumSignificantDigits:4}),Ur=new Intl.NumberFormat(navigator.languages,{minimumFractionDigits:1,maximumFractionDigits:1});function _r(e){return e>=1e3||Math.round(e)===e?Ur.format(e):Nr.format(e)}function zr(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var Vr=["bonds"];var jr=function(e){rn(k,e);var t,n,r,a,i,c,l,d,p,f,m,v,y,g,b=(y=k,g=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(y);if(g){var n=on(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return an(this,e)});function k(e){var t;s(this,k),h(tn(t=b.call(this)),"body",void 0),h(tn(t),"currentDEX",void 0),h(tn(t),"page",void 0),h(tn(t),"forms",void 0),h(tn(t),"fiatRateSources",void 0),h(tn(t),"regAssetForm",void 0),h(tn(t),"confirmRegisterForm",void 0),h(tn(t),"newWalletForm",void 0),h(tn(t),"walletWaitForm",void 0),h(tn(t),"dexAddrForm",void 0),h(tn(t),"currentForm",void 0),h(tn(t),"pwCache",void 0),h(tn(t),"keyup",void 0),t.body=e;var n=t.page=zt.idDescendants(e);t.forms=zt.applySelector(n.forms,":scope > form"),t.fiatRateSources=zt.applySelector(n.fiatRateSources,"input[type=checkbox]"),zt.bind(n.darkMode,"click",(function(){en.setCookie(en.darkModeCK,n.darkMode.checked?"1":"0"),n.darkMode.checked?document.body.classList.add("dark"):document.body.classList.remove("dark")})),n.showPokes.checked="1"===en.fetchLocal(en.popupsLK),zt.bind(n.showPokes,"click",(function(){var e=n.showPokes.checked||!1;en.storeLocal(en.popupsLK,e?"1":"0"),xn().showPopups=e})),n.commitHash.textContent=xn().commitHash.substring(0,7),zt.bind(n.addADex,"click",(function(){t.dexAddrForm.refresh(),t.showForm(n.dexAddrForm)})),t.fiatRateSources.forEach((function(e){zt.bind(e,"change",o(w().mark((function t(){var n;return w().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,yn("/api/toggleratesource",{disable:!e.checked,source:e.value});case 2:return n=t.sent,xn().checkResponse(n)||(e.checked=!e.checked),t.next=6,xn().fetchUser();case 6:case"end":return t.stop()}}),t)}))))})),t.regAssetForm=new br(n.regAssetForm,function(){var e=o(w().mark((function e(r){var a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.confirmRegisterForm.setAsset(r),a=xn().assets[r],!(o=a.wallet)){e.next=14;break}if(s=t.currentDEX.bondAssets[a.symbol],!(o.synced&&o.balance.available>s.amount)){e.next=8;break}return t.animateConfirmForm(n.regAssetForm),e.abrupt("return");case 8:return e.next=10,t.getRegistrationTxFeeEstimate(r,n.regAssetForm);case 10:return i=e.sent,t.walletWaitForm.setWallet(o,i),Rr(n.regAssetForm,n.walletWait),e.abrupt("return");case 14:t.newWalletForm.setAsset(r),t.currentForm=n.newWalletForm,Rr(n.regAssetForm,n.newWalletForm);case 17:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),t.confirmRegisterForm=new gr(n.confirmRegForm,(function(){t.registerDEXSuccess()}),(function(){t.animateRegAsset(n.confirmRegForm)}),t.pwCache),t.newWalletForm=new mr(n.newWalletForm,(function(e){return t.newWalletCreated(e)}),t.pwCache,(function(){return t.animateRegAsset(n.newWalletForm)})),t.walletWaitForm=new kr(n.walletWait,(function(){t.animateConfirmForm(n.walletWait)}),(function(){t.animateRegAsset(n.walletWait)})),t.dexAddrForm=new Sr(n.dexAddrForm,function(){var e=o(w().mark((function e(r,a){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.currentDEX=r,t.confirmRegisterForm.setExchange(r,a),t.walletWaitForm.setExchange(r),t.regAssetForm.setExchange(r),t.animateRegAsset(n.dexAddrForm);case 5:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}()),zt.bind(n.importAccount,"click",(function(){return t.prepareAccountImport(n.authorizeAccountImportForm)})),Or(n.authorizeAccountImportForm,n.authorizeImportAccountConfirm,(function(){return t.importAccount()})),zt.bind(n.changeAppPW,"click",(function(){return t.showForm(n.changeAppPWForm)})),Or(n.changeAppPWForm,n.submitNewPW,(function(){return t.changeAppPW()})),zt.bind(n.accountFile,"change",(function(){return t.onAccountFileChange()})),zt.bind(n.removeAccount,"click",(function(){return t.clearAccountFile()})),zt.bind(n.addAccount,"click",(function(){return n.accountFile.click()})),zt.bind(n.exportSeed,"click",(function(){return t.showForm(n.exportSeedAuth)})),Or(n.exportSeedAuth,n.exportSeedSubmit,(function(){return t.submitExportSeedReq()}));var r=function(){zt.hide(n.forms),n.exportSeedPW.value="",n.seedDiv.textContent=""};return zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},zt.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){r()}))})),t}return u(k,[{key:"getRegistrationTxFeeEstimate",value:(v=o(w().mark((function e(t,n){var r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getCertFile();case 2:return r=e.sent,a=xn().loading(n),e.next=6,yn("/api/regtxfee",{addr:this.currentDEX.host,cert:r,asset:t});case 6:if(o=e.sent,a(),xn().checkResponse(o)){e.next=10;break}return e.abrupt("return",0);case 10:return e.abrupt("return",o.txfee);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return v.apply(this,arguments)})},{key:"newWalletCreated",value:(m=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:if(n=e.sent){e.next=5;break}return e.abrupt("return");case 5:if(r=this.page,a=n.assets[t],o=a.wallet,s=this.currentDEX.bondAssets[a.symbol].amount,!(o.synced&&o.balance.available>s)){e.next=13;break}return e.next=12,this.animateConfirmForm(r.newWalletForm);case 12:return e.abrupt("return");case 13:return e.next=15,this.getRegistrationTxFeeEstimate(t,r.newWalletForm);case 15:return i=e.sent,this.walletWaitForm.setWallet(o,i),this.currentForm=r.walletWait,e.next=20,Rr(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return m.apply(this,arguments)})},{key:"onAccountFileChange",value:(f=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.accountFile.files)&&n.length){e.next=4;break}return e.abrupt("return");case 4:t.selectedAccount.textContent=n[0].name,zt.show(t.removeAccount),zt.hide(t.addAccount);case 7:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"clearAccountFile",value:function(){var e=this.page;e.accountFile.value="",e.selectedAccount.textContent=It(ie),zt.hide(e.removeAccount),zt.show(e.addAccount)}},{key:"prepareAccountImport",value:(p=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.page.importAccountErr.textContent="",this.showForm(t);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return p.apply(this,arguments)})},{key:"importAccount",value:(d=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,n=t.importAccountAppPass.value,t.importAccountAppPass.value="",r="",!t.accountFile.value){e.next=12;break}if((a=t.accountFile.files)&&a.length){e.next=9;break}return console.error("importAccount: no file specified"),e.abrupt("return");case 9:return e.next=11,a[0].text();case 11:r=e.sent;case 12:e.prev=12,o=JSON.parse(r),e.next=21;break;case 16:return e.prev=16,e.t0=e.catch(12),t.importAccountErr.textContent=e.t0.message,zt.show(t.importAccountErr),e.abrupt("return");case 21:if(void 0!==o){e.next=25;break}return t.importAccountErr.textContent=It(X),zt.show(t.importAccountErr),e.abrupt("return");case 25:return i=(s=o).bonds,c=void 0===i?[]:i,l=zr(s,Vr),u={pw:n,account:l,bonds:c},h=xn().loading(this.body),e.next=30,yn("/api/importaccount",u);case 30:if(d=e.sent,h(),xn().checkResponse(d)){e.next=36;break}return t.importAccountErr.textContent=d.msg,zt.show(t.importAccountErr),e.abrupt("return");case 36:return e.next=38,xn().fetchUser();case 38:zt.hide(t.forms),window.location.reload();case 40:case"end":return e.stop()}}),e,this,[[12,16]])}))),function(){return d.apply(this,arguments)})},{key:"submitExportSeedReq",value:(l=o(w().mark((function e(){var t,n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.exportSeedPW.value,r=xn().loading(this.body),e.next=5,yn("/api/exportseed",{pass:n});case 5:if(a=e.sent,r(),xn().checkResponse(a)){e.next=11;break}return t.exportAccountErr.textContent=a.msg,zt.show(t.exportSeedE),e.abrupt("return");case 11:t.exportSeedPW.value="",t.seedDiv.textContent=a.seed,this.showForm(t.authorizeSeedDisplay);case 14:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"showForm",value:(c=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return zt.hide(e)})),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(300,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"getCertFile",value:(i=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t="",!this.dexAddrForm.page.certFile.value){e.next=7;break}if(!(n=this.dexAddrForm.page.certFile.files)||!n.length){e.next=7;break}return e.next=6,n[0].text();case 6:t=e.sent;case 7:return e.abrupt("return",t);case 8:case"end":return e.stop()}}),e,this)}))),function(){return i.apply(this,arguments)})},{key:"registerDEXSuccess",value:(a=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.forms),e.next=4,xn().fetchUser();case 4:window.location.reload();case 5:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"changeAppPW",value:(r=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.changePWErrMsg),n=function(){t.appPW.value="",t.newAppPW.value="",t.confirmNewPW.value=""},t.appPW.value&&t.newAppPW.value&&t.confirmNewPW.value){e.next=8;break}return t.changePWErrMsg.textContent=It(x),zt.show(t.changePWErrMsg),n(),e.abrupt("return");case 8:if(t.newAppPW.value===t.confirmNewPW.value){e.next=13;break}return t.changePWErrMsg.textContent=It(G),zt.show(t.changePWErrMsg),n(),e.abrupt("return");case 13:return r=xn().loading(t.changeAppPW),a={appPW:t.appPW.value,newAppPW:t.newAppPW.value},n(),e.next=18,yn("/api/changeapppass",a);case 18:if(o=e.sent,r(),xn().checkResponse(o)){e.next=24;break}return t.changePWErrMsg.textContent=o.msg,zt.show(t.changePWErrMsg),e.abrupt("return");case 24:zt.hide(t.forms);case 25:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"unload",value:function(){zt.unbind(document,"keyup",this.keyup)}},{key:"animateRegAsset",value:(n=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:zt.hide(t),n=this.page.regAssetForm,this.currentForm=n,this.regAssetForm.animate(),zt.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"animateConfirmForm",value:(t=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),n=this.page.confirmRegForm,this.currentForm=n,zt.hide(t),zt.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),k}(fn);function Hr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(e);try{for(r.s();!(t=r.n()).done;){var a=t.value;if(!a.epoch)return a;n||(n=a)}}catch(e){r.e(e)}finally{r.f()}return n}},{key:"bestGapBuy",value:function(){return this.bestGapOrder(this.buys)}},{key:"bestGapSell",value:function(){return this.bestGapOrder(this.sells)}}]),e}();function Gr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nthis.maxQlength-1;)this.queue.shift();this.queue.push([e,t])}}},{key:"close",value:function(e){window.log("ws","close, reason:",e,this.handlers),this.handlers={},this.connection&&this.connection.close()}},{key:"connect",value:function(e,t){var n=this;this.uri=e,this.reloader=t;var a=0;!function o(){window.log("ws","connecting to ".concat(e));var s=n.connection=new window.WebSocket(e);if(s){var i=setTimeout((function(){s&&s.close()}),500);s.onmessage=function(e){var t=JSON.parse(e.data);Xr(t.route,t.payload,n.handlers)},s.onclose=function(e){window.log("ws","onclose"),clearTimeout(i),s=n.connection=null,Xr("close",null,n.handlers),a++;var t=Math.min(Math.pow(1.25,a),10);console.error("websocket disconnected (".concat(e.code,"), trying again in ").concat(t.toFixed(1)," seconds")),setTimeout((function(){o()}),1e3*t)},s.onopen=function(){window.log("ws","onopen"),clearTimeout(i),a>0&&(a=0,t()),Xr("open",null,n.handlers);var e=n.queue;n.queue=[];var o,s=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Gr(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Gr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}(e);try{for(s.s();!(o=s.n()).done;){var c=r(o.value,2),l=c[0],u=c[1];n.request(l,u)}}catch(e){s.e(e)}finally{s.f()}},s.onerror=function(e){window.log("ws","onerror:",e),Xr("error",e,n.handlers)}}}()}}]),e}());function Jr(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Zr(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0}},{key:"setCurrMarketPrice",value:function(){var e=this.market;if(e){var t=xn().exchanges[e.dex.host],n=t.markets[e.cfg.name];if(n.spot){var r,a=$r(this.stats);try{for(a.s();!(r=a.n()).done;){var o=r.value,s=t.assets[n.baseid].unitInfo.conventional.conversionFactor;o.tmpl.volume.textContent=Fa(n.spot.vol24/s),Ia(o.tmpl,t,n)}}catch(e){a.e(e)}finally{a.f()}this.page.obPrice.textContent=zt.formatFullPrecision(n.spot.rate/this.market.rateConversionFactor),this.page.obPrice.classList.remove("sellcolor","buycolor"),this.page.obPrice.classList.add(n.spot.change24>=0?"buycolor":"sellcolor"),zt.setVis(n.spot.change24>=0,this.page.obUp),zt.setVis(n.spot.change24<0,this.page.obDown)}}}},{key:"setMarketDetails",value:function(){if(this.market){var e,t=$r(this.stats);try{for(t.s();!(e=t.n()).done;){var n=e.value;n.tmpl.baseIcon.src=zt.logoPath(this.market.cfg.basesymbol),n.tmpl.quoteIcon.src=zt.logoPath(this.market.cfg.quotesymbol),zt.empty(n.tmpl.baseSymbol,n.tmpl.quoteSymbol),n.tmpl.baseSymbol.appendChild(zt.symbolize(this.market.cfg.basesymbol)),n.tmpl.quoteSymbol.appendChild(zt.symbolize(this.market.cfg.quotesymbol))}}catch(e){t.e(e)}finally{t.f()}}}},{key:"setHighLow",value:function(){var e,t=null===(e=this.market)||void 0===e?void 0:e.candleCaches[da];if(t){for(var n=0,r=0,a=t.candles.length-1;a>=0;a--){var o=t.candles[a];(0===r||o.lowRate>0&&o.lowRaten&&(n=o.highRate)}var s,i=xn().unitInfo(this.market.cfg.quoteid,this.market.dex).conventional.conversionFactor,c=$r(this.stats);try{for(c.s();!(s=c.n()).done;){var l=s.value;l.tmpl.high.textContent=n>0?Fa(n/i):"-",l.tmpl.low.textContent=r>0?Fa(r/i):"-"}}catch(e){c.e(e)}finally{c.f()}}else{var u,h=$r(this.stats);try{for(h.s();!(u=h.n()).done;){var d=u.value;d.tmpl.high.textContent="-",d.tmpl.low.textContent="-"}}catch(e){h.e(e)}finally{h.f()}}}},{key:"assetsAreSupported",value:function(){var e,t,n,r,a=this.market,o=a.base,s=a.quote,i=a.baseCfg,c=a.quoteCfg;if(!o||!s){var l=o?c.symbol:i.symbol;return{isSupported:!1,text:It(L,{asset:l.toUpperCase()})}}var u=o.token?null===(e=xn().assets[o.token.parentID].info)||void 0===e?void 0:e.versions:null===(t=o.info)||void 0===t?void 0:t.versions,h=s.token?null===(n=xn().assets[s.token.parentID].info)||void 0===n?void 0:n.versions:null===(r=s.info)||void 0===r?void 0:r.versions,d="";return u.includes(i.version)?h.includes(c.version)||(d=It(B,{asset:s.symbol.toUpperCase(),version:c.version+""})):d=It(B,{asset:o.symbol.toUpperCase(),version:i.version+""}),{isSupported:u.includes(i.version)&&h.includes(c.version),text:d}}},{key:"setOrderVisibility",value:function(){var e=this.page;this.isLimit()?(zt.show(e.priceBox,e.tifBox,e.qtyBox,e.maxBox),zt.hide(e.mktBuyBox),this.previewQuoteAmt(!0)):(zt.hide(e.tifBox,e.maxBox,e.priceBox),this.isSell()?(zt.hide(e.mktBuyBox),zt.show(e.qtyBox),this.previewQuoteAmt(!0)):(zt.show(e.mktBuyBox),zt.hide(e.qtyBox),this.previewQuoteAmt(!1)))}},{key:"resolveOrderFormVisibility",value:function(){var e=this.page;if(zt.hide(e.orderForm,e.orderTypeBttns),this.assetsAreSupported().isSupported&&!(this.market.dex.tier<1)){var t=this.market,n=t.base,r=t.quote;n&&xn().assets[n.id].wallet&&r&&xn().assets[r.id].wallet&&zt.show(e.orderForm,e.orderTypeBttns)}}},{key:"setLoaderMsgVisibility",value:function(){var e=this.page,t=this.assetsAreSupported(),n=t.isSupported,r=t.text;n?zt.hide(e.loaderMsg):(e.loaderMsg.textContent=r,zt.show(e.loaderMsg),zt.hide(e.notRegistered),zt.hide(e.noWallet))}},{key:"setRegistrationStatusView",value:function(e,t,n){var r=this.page;r.regStatusTitle.textContent=e,r.regStatusConfsDisplay.textContent=t,r.registrationStatus.classList.remove("completed","error","waiting"),r.registrationStatus.classList.add(n)}},{key:"updateRegistrationStatusView",value:function(){var e=this.page,t=this.market.dex;if(e.regStatusDex.textContent=t.host,t.tier>=1)this.setRegistrationStatusView(It(ce),"","completed");else{var n=Object.values(t.pendingBonds).map((function(e){var n=t.bondAssets[e.symbol].confs;return"".concat(e.confs," / ").concat(n)})).join(", ");this.setRegistrationStatusView(It(se),n,"waiting")}}},{key:"setRegistrationStatusVisibility",value:function(){var e=this,t=this.page,n=this.market;if(n&&n.dex&&n.dex.connectionStatus===cn.Connected)if(this.updateRegistrationStatusView(),n.dex.tier>=1){var r=function(){zt.hide(t.registrationStatus,t.bondRequired),e.resolveOrderFormVisibility()};if(zt.isHidden(t.orderForm))return void setTimeout(r,5e3);r()}else n.dex.viewOnly?(t.unregisteredDex.textContent=n.dex.host,zt.show(t.notRegistered)):this.hasPendingBonds()?zt.show(t.registrationStatus):(t.acctTier.textContent="".concat(n.dex.tier),t.dexSettingsLink.href="/dexsettings/".concat(n.dex.host),zt.show(t.bondRequired))}},{key:"setOrderBttnText",value:function(){this.isSell()?this.page.submitBttn.textContent=It(S,{asset:zt.shortSymbol(this.market.baseCfg.symbol)}):this.page.submitBttn.textContent=It(C,{asset:zt.shortSymbol(this.market.baseCfg.symbol)})}},{key:"setCandleDurBttns",value:function(){var e=this,t=this.page,n=this.market;zt.empty(t.durBttnBox);var r,a=$r(n.dex.candleDurs);try{var o=function(){var n=r.value,a=t.durBttnTemplate.cloneNode(!0);a.textContent=n,zt.bind(a,"click",(function(){return e.candleDurationSelected(n)})),t.durBttnBox.appendChild(a)};for(a.s();!(r=a.n()).done;)o()}catch(e){a.e(e)}finally{a.f()}}},{key:"setMarket",value:(x=o(w().mark((function e(t,n,r){var a,o,s,i,c,l,u,h,d,p,f,m,v,y;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=xn().user.exchanges[t],(o=this.page).lotField.value="",o.qtyField.value="",o.rateField.value="",zt.hide(o.notRegistered,o.bondRequired,o.noWallet),a&&a.markets&&a.connectionStatus===cn.Connected){e.next=10;break}return o.chartErrMsg.textContent=It(M),zt.show(o.chartErrMsg),e.abrupt("return");case 10:s=$r(this.stats);try{for(s.s();!(i=s.n()).done;)c=i.value,zt.show(c.row)}catch(e){s.e(e)}finally{s.f()}l=a.assets[n],u=a.assets[r],h=[xn().unitInfo(n,a),xn().unitInfo(r,a)],p=h[1],f=Tn/(d=h[0]).conventional.conversionFactor*p.conventional.conversionFactor,zt.hide(o.maxOrd,o.chartErrMsg),this.maxEstimateTimer&&(window.clearTimeout(this.maxEstimateTimer),this.maxEstimateTimer=null),m=wa(l.symbol,u.symbol),v=xn().assets[n],y=xn().assets[r],this.market={dex:a,sid:m,cfg:a.markets[m],base:v,quote:y,baseUnitInfo:d,quoteUnitInfo:p,maxSell:null,maxBuys:{},maxSellRequested:!1,candleCaches:{},baseCfg:l,quoteCfg:u,rateConversionFactor:f,sellBalance:0,buyBalance:0},zt.setVis(!(v&&y&&v.wallet&&y.wallet),o.noWallet),this.setMarketDetails(),this.setCurrMarketPrice(),this.refreshRecentMatchesTable(),Yr.request("loadmarket",ba(t,n,r)),this.candleDur=da,this.loadCandles(),this.setLoaderMsgVisibility(),this.setRegistrationStatusVisibility(),this.resolveOrderFormVisibility(),this.setOrderBttnText(),this.setCandleDurBttns(),this.previewQuoteAmt(!1);case 35:case"end":return e.stop()}}),e,this)}))),function(e,t,n){return x.apply(this,arguments)})},{key:"reportDepthClick",value:function(e){this.page.rateField.value=String(e),this.rateFieldChanged()}},{key:"reportDepthVolume",value:function(e){var t=this.page,n=this.market,r=n.baseUnitInfo,a=n.quoteUnitInfo;t.sellBookedBase.textContent=zt.formatCoinValue(e.sellBase*r.conventional.conversionFactor,r),t.sellBookedQuote.textContent=zt.formatCoinValue(e.sellQuote*a.conventional.conversionFactor,a),t.buyBookedBase.textContent=zt.formatCoinValue(e.buyBase*r.conventional.conversionFactor,r),t.buyBookedQuote.textContent=zt.formatCoinValue(e.buyQuote*a.conventional.conversionFactor,a)}},{key:"reportDepthMouse",value:function(e){for(;this.hovers.length;)this.hovers.shift().classList.remove("hover");var t=this.page;if(e){zt.show(t.hoverData,t.depthHoverData);for(var n=0,r=Object.values(this.metaOrders);n-1&&(o.classList.add("hover"),this.hovers.push(o))}t.hoverPrice.textContent=zt.formatCoinValue(e.rate),t.hoverVolume.textContent=zt.formatCoinValue(e.depth),t.hoverVolume.style.color=e.dotColor}else zt.hide(t.hoverData,t.depthHoverData)}},{key:"reportDepthZoom",value:function(e){en.storeLocal(en.depthZoomLK,e)}},{key:"reportMouseCandle",value:function(e){var t=this.page;e?(zt.show(t.hoverData,t.candleHoverData),t.candleStart.textContent=zt.formatCoinValue(e.startRate/this.market.rateConversionFactor),t.candleEnd.textContent=zt.formatCoinValue(e.endRate/this.market.rateConversionFactor),t.candleHigh.textContent=zt.formatCoinValue(e.highRate/this.market.rateConversionFactor),t.candleLow.textContent=zt.formatCoinValue(e.lowRate/this.market.rateConversionFactor),t.candleVol.textContent=zt.formatCoinValue(e.matchVolume,this.market.baseUnitInfo)):zt.hide(t.hoverData,t.candleHoverData)}},{key:"parseOrder",value:function(){var e=this.page,t=e.qtyField,n=this.isLimit(),r=this.isSell(),a=this.market,o=a.baseUnitInfo.conventional.conversionFactor;return n||r||(t=e.mktBuyField,o=a.quoteUnitInfo.conventional.conversionFactor),{host:a.dex.host,isLimit:n,sell:r,base:a.base.id,quote:a.quote.id,qty:ka(t.value||"",o),rate:ka(e.rateField.value||"",a.rateConversionFactor),tifnow:e.tifNow.checked||!1,options:{}}}},{key:"previewQuoteAmt",value:function(e){var t=this.page;if(this.market.base&&this.market.quote){var n=this.parseOrder(),r=this.adjustedRate();if(t.orderErr.textContent="",r&&(n.sell?this.preSell():this.preBuy()),this.depthLines.input=[],r&&this.isLimit()&&(this.depthLines.input=[{rate:n.rate/this.market.rateConversionFactor,color:n.sell?this.depthChart.theme.sellLine:this.depthChart.theme.buyLine}]),this.drawChartLines(),!e||!r||!n.qty)return t.orderPreview.textContent="",void this.drawChartLines();var a=xn().assets[n.quote],o=n.qty*n.rate/Tn,s=zt.formatCoinValue(o,this.market.quoteUnitInfo);t.orderPreview.textContent=It(W,{total:s,asset:a.symbol.toUpperCase()}),this.isSell()?this.preSell():this.preBuy()}}},{key:"preSell",value:function(){var e=this,t=this.market,n=xn().assets[t.base.id].wallet;n.balance.available0&&this.book.add(u),this.addTableOrder(u)}}catch(e){l.e(e)}finally{l.f()}if(!this.book)return this.depthChart.clear(),zt.empty(this.page.buyRows),void zt.empty(this.page.sellRows);zt.show(this.page.epochLine),this.loadingAnimations.depth&&this.loadingAnimations.depth.stop(),this.depthChart.canvas.classList.remove("invisible"),this.depthChart.set(this.book,r.lotsize,r.ratestep,a,o),this.recentMatches=null!==(t=e.book.recentMatches)&&void 0!==t?t:[],this.refreshRecentMatchesTable()}},{key:"midGapConventional",value:function(){var e=this.midGap();if(!e)return e;var t=this.market,n=t.baseUnitInfo,r=t.quoteUnitInfo;return e*n.conventional.conversionFactor/r.conventional.conversionFactor}},{key:"midGap",value:function(){var e=this.book;if(e)return e.buys&&e.buys.length?e.sells&&e.sells.length?(e.buys[0].msgRate+e.sells[0].msgRate)/2/Tn:e.buys[0].msgRate/Tn:e.sells&&e.sells.length?e.sells[0].msgRate/Tn:null}},{key:"setMarketBuyOrderEstimate",value:function(){var e=this.market,t=e.cfg.lotsize,n=xn().user.exchanges[e.dex.host].markets[e.sid].buybuffer,r=this.midGapConventional();r&&(this.page.minMktBuy.textContent=zt.formatCoinValue(t*n*r,e.baseUnitInfo))}},{key:"refreshActiveOrders",value:function(){var e=this,t=this.page,n=this.metaOrders,r=this.market;for(var a in n)delete n[a];var o=xn().orders(r.dex.host,wa(r.baseCfg.symbol,r.quoteCfg.symbol));o.sort((function(e,t){return t.submitTime-e.submitTime})),zt.empty(t.userOrders),zt.setVis(null==o?void 0:o.length,t.userOrders),zt.setVis(!(null!=o&&o.length),t.userNoOrders);var s,i=!1,c=$r(o);try{var l=function(){var r=s.value,a=t.userOrderTmpl.cloneNode(!0);t.userOrders.appendChild(a);var o=zt.tmplElement(a,"header"),c=zt.parseTemplate(o),l=zt.tmplElement(a,"details"),u=zt.parseTemplate(l),h={div:a,header:c,details:u,ord:r};r.id&&(n[r.id]=h),r.readyToTick||(o.classList.add("unready-user-order"),i=!0),c.sideLight.classList.add(r.sell?"sell":"buy"),u.side.textContent=c.side.textContent=Pn(r),u.side.classList.add(r.sell?"sellcolor":"buycolor"),c.side.classList.add(r.sell?"sellcolor":"buycolor"),u.qty.textContent=c.qty.textContent=Fa(r.qty/e.market.baseUnitInfo.conventional.conversionFactor),u.rate.textContent=Fa(r.rate/e.market.rateConversionFactor),c.baseSymbol.textContent=r.baseSymbol.toUpperCase(),u.type.textContent=Ln(r),e.updateMetaOrder(h),zt.bind(a,"mouseenter",(function(){e.activeMarkerRate=r.rate,e.setDepthMarkers()})),r.id?(1===r.type&&1===r.tif&&r.status<3&&(zt.show(u.cancelBttn),ta(u.cancelBttn,"click",(function(t){t.stopPropagation(),e.showCancel(a,r.id)}))),ta(u.accelerateBttn,"click",(function(t){t.stopPropagation(),e.showAccelerate(r)})),xn().canAccelerateOrder(r)&&zt.show(u.accelerateBttn),u.link.href="order/".concat(r.id),xn().bindInternalNavigation(a)):(zt.hide(u.accelerateBttn),zt.hide(u.cancelBttn),zt.hide(u.link)),zt.bind(o,"click",(function(){if(zt.isDisplayed(l))return zt.hide(l),c.expander.classList.add("ico-arrowdown"),void c.expander.classList.remove("ico-arrowup");zt.show(l),c.expander.classList.remove("ico-arrowdown"),c.expander.classList.add("ico-arrowup")})),xn().bindTooltips(a)};for(c.s();!(s=c.n()).done;)l()}catch(e){c.e(e)}finally{c.f()}zt.setVis(i,t.unreadyOrdersMsg),this.setDepthMarkers()}},{key:"updateMetaOrder",value:function(e){var t=e.header,n=e.details,r=e.ord;r.status<=2?t.activeLight.classList.add("active"):t.activeLight.classList.remove("active"),n.status.textContent=t.status.textContent=Wn(r),n.age.textContent=zt.timeSince(r.submitTime),n.filled.textContent="".concat((qn(r)/r.qty*100).toFixed(1),"%"),n.settled.textContent="".concat((Nn(r)/r.qty*100).toFixed(1),"%")}},{key:"setDepthMarkers",value:function(){for(var e={buys:[],sells:[]},t=this.market.rateConversionFactor,n=0,r=Object.values(this.metaOrders);n0&&this.book.add(t),this.addTableOrder(t),this.updateTitle(),this.depthChart.draw()}}},{key:"handleUnbookOrderRoute",value:function(e){if(xn().log("book","handleUnbookOrderRoute:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){var t=e.payload;this.book.remove(t.token),this.removeTableOrder(t),this.updateTitle(),this.depthChart.draw()}}},{key:"handleUpdateRemainingRoute",value:function(e){if(xn().log("book","handleUpdateRemainingRoute:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){var t=e.payload;this.book.updateRemaining(t.token,t.qty,t.qtyAtomic),this.updateTableOrder(t),this.depthChart.draw()}}},{key:"handleEpochOrderRoute",value:function(e){if(xn().log("book","handleEpochOrderRoute:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){var t=e.payload;t.msgRate>0&&this.book.add(t),t.qtyAtomic>0&&this.addTableOrder(t),this.depthChart.draw()}}},{key:"handleCandlesRoute",value:function(e){if(this.candlesLoading&&(clearTimeout(this.candlesLoading.timer),this.candlesLoading.loaded(),this.candlesLoading=null),e.host===this.market.dex.host&&e.marketID===this.market.cfg.name){var t=e.payload.dur;this.market.candleCaches[t]=e.payload,this.setHighLow(),this.candleDur===t&&(this.loadingAnimations.candles&&this.loadingAnimations.candles.stop(),this.candleChart.canvas.classList.remove("invisible"),this.candleChart.setCandles(e.payload,this.market.cfg,this.market.baseUnitInfo,this.market.quoteUnitInfo))}}},{key:"handleEpochMatchSummary",value:function(e){this.addRecentMatches(e.payload),this.refreshRecentMatchesTable()}},{key:"handleCandleUpdateRoute",value:function(e){if(e.host===this.market.dex.host){var t=e.payload,n=t.dur,r=t.candle,a=this.market.candleCaches[n];if(a){var o=a.candles;0===o.length?o.push(r):o[o.length-1].startStamp===r.startStamp?o[o.length-1]=r:o.push(r),this.candleDur===n&&this.candleChart.draw()}}}},{key:"showForm",value:(k=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,zt.hide.apply(zt,kn(Array.from(n.forms.children))),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(500,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return k.apply(this,arguments)})},{key:"showOpen",value:(b=o(w().mark((function e(t,n){var r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:r=this.page,this.openAsset=t,this.openFunc=n,this.unlockForm.refresh(xn().assets[t.id]),this.showForm(r.unlockWalletForm),r.uwAppPass.focus();case 6:case"end":return e.stop()}}),e,this)}))),function(e,t){return b.apply(this,arguments)})},{key:"showToggleWalletStatus",value:function(e){var t=this.page;this.openAsset=e,zt.hide(t.toggleWalletStatusErr,t.walletStatusDisable,t.disableWalletMsg),zt.show(t.walletStatusEnable,t.enableWalletMsg),this.showForm(t.toggleWalletStatusConfirm)}},{key:"toggleWalletStatus",value:(g=o(w().mark((function e(){var t,n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,zt.hide(t.toggleWalletStatusErr),n={assetID:this.openAsset.id,disable:!1},r=xn().loading(t.toggleWalletStatusConfirm),e.next=7,yn("/api/togglewalletstatus",n);case 7:if(a=e.sent,r(),xn().checkResponse(a)){e.next=13;break}return t.toggleWalletStatusErr.textContent=a.msg,zt.show(t.toggleWalletStatusErr),e.abrupt("return");case 13:zt.hide(this.page.forms),this.balanceWgt.updateAsset(this.openAsset.id);case 15:case"end":return e.stop()}}),e,this)}))),function(){return g.apply(this,arguments)})},{key:"showVerify",value:function(){this.preorderCache={};var e,t=this.page,n=this.currentOrder=this.parseOrder(),r=n.sell,a=xn().assets[n.base],o=xn().assets[n.quote],s=r?o:a,i=r?a:o,c=function(e){switch(e.dataset.icon){case"from":if(i.token){var t=xn().assets[i.token.parentID];e.src=zt.logoPath(t.symbol)}else e.src=zt.logoPath(i.symbol);break;case"to":if(s.token){var n=xn().assets[s.token.parentID];e.src=zt.logoPath(n.symbol)}else e.src=zt.logoPath(s.symbol)}},l=$r(zt.applySelector(t.vDetailPane,"[data-icon]"));try{for(l.s();!(e=l.n()).done;)c(e.value)}catch(e){l.e(e)}finally{l.f()}var u,h=$r(zt.applySelector(t.vFeeSummary,"[data-icon]"));try{for(h.s();!(u=h.n()).done;)c(u.value)}catch(e){h.e(e)}finally{h.f()}zt.hide(t.vUnlockPreorder,t.vPreorderErr),zt.show(t.vPreorder),t.vBuySell.textContent=It(r?Se:Ee);var d=It(r?P:T);if(t.vSideSubmit.textContent=d,t.vOrderHost.textContent=n.host,n.isLimit){zt.show(t.verifyLimit),zt.hide(t.verifyMarket);var p="Limit ".concat(d," Order");t.vOrderType.textContent=n.tifnow?p+" (immediate)":p,t.vRate.textContent=zt.formatCoinValue(n.rate/this.market.rateConversionFactor),t.vQty.textContent=zt.formatCoinValue(n.qty,a.unitInfo);var f=n.rate/Tn*n.qty;t.vTotal.textContent=zt.formatCoinValue(f,o.unitInfo),this.showFiatValue(o.id,f,t.vFiatTotal)}else{zt.hide(t.verifyLimit),zt.show(t.verifyMarket),t.vOrderType.textContent="Market ".concat(d," Order");var m=n.sell?this.market.baseUnitInfo:this.market.quoteUnitInfo;t.vmFromTotal.textContent=zt.formatCoinValue(n.qty,m),t.vmFromAsset.textContent=i.symbol.toUpperCase(),this.showFiatValue(i.id,n.qty,t.vmFromTotalFiat);var v=this.midGap();if(v){zt.show(t.vMarketEstimate);var y=n.sell?n.qty*v:n.qty/v;t.vmToTotal.textContent=zt.formatCoinValue(y,s.unitInfo),t.vmToAsset.textContent=s.symbol.toUpperCase(),this.showFiatValue(s.id,y,t.vmTotalFiat)}else zt.hide(t.vMarketEstimate)}r?(t.vHeader.classList.add(ha),t.vHeader.classList.remove(ua),t.vSubmit.classList.add(ha),t.vSubmit.classList.remove(ua)):(t.vHeader.classList.add(ua),t.vHeader.classList.remove(ha),t.vSubmit.classList.add(ua),t.vSubmit.classList.remove(ha)),this.showVerifyForm(),t.vPass.focus(),a.wallet.open&&o.wallet.open?this.preOrder(n):(zt.hide(t.vPreorder),en.passwordIsCached()?this.unlockWalletsForEstimates(""):zt.show(t.vUnlockPreorder))}},{key:"showFiatValue",value:function(e,t,n){if(n){var r=xn().fiatRatesMap[e];n.textContent=zt.formatFiatConversion(t,r,xn().unitInfo(e)),r?zt.show(n.parentElement):zt.hide(n.parentElement)}}},{key:"showVerifyForm",value:(y=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,zt.hide(t.vErr),this.showForm(t.verifyForm);case 3:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"submitEstimateUnlock",value:(v=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page.vUnlockPass.value||"",e.next=3,this.unlockWalletsForEstimates(t);case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e,this)}))),function(){return v.apply(this,arguments)})},{key:"unlockWalletsForEstimates",value:(m=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=xn().loading(n.verifyForm),e.next=4,this.attemptWalletUnlock(t);case 4:if(a=e.sent,r(),!a){e.next=8;break}return e.abrupt("return",this.setPreorderErr(a));case 8:zt.show(n.vPreorder),zt.hide(n.vUnlockPreorder),this.preOrder(this.parseOrder());case 11:case"end":return e.stop()}}),e,this)}))),function(e){return m.apply(this,arguments)})},{key:"attemptWalletUnlock",value:(f=o(w().mark((function e(t){var n,r,a,o,s,i,c,l,u;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.market,r=n.base,a=n.quote,o=[],r.wallet.open||o.push(r.id),a.wallet.open||o.push(a.id),s={pass:t,assetID:-1},i=0,c=o;case 6:if(!(i0?E/x:E,F=e.estimate.realisticBestCase/A*100;r.vSwapFeesLowPct.textContent=x<=0?"":"(".concat(c(F),"%)"),r.vSwapFeesLow.textContent=zt.formatCoinValue(e.estimate.realisticBestCase,w);var I=e.estimate.realisticWorstCase/A*100;r.vSwapFeesHighPct.textContent=x<=0?"":"(".concat(c(I),"%)"),r.vSwapFeesHigh.textContent=zt.formatCoinValue(e.estimate.realisticWorstCase,w);var R=e.estimate.maxFees/A*100;r.vSwapFeesMaxPct.textContent=x<=0?"":"(".concat(c(R),"%)"),r.vSwapFeesMax.textContent=zt.formatCoinValue(e.estimate.maxFees,w);var D=this.midGap()||n.rate/i,O=n.sell?E*D:E/D,T=k>0?O/k:O,P=t.estimate.realisticBestCase/T*100;r.vRedeemFeesLowPct.textContent=k<=0?"":"(".concat(c(P),"%)"),r.vRedeemFeesLow.textContent=zt.formatCoinValue(t.estimate.realisticBestCase,b);var L=t.estimate.realisticWorstCase/T*100;r.vRedeemFeesHighPct.textContent=k<=0?"":"(".concat(c(L),"%)"),r.vRedeemFeesHigh.textContent=zt.formatCoinValue(t.estimate.realisticWorstCase,b),l&&u?(zt.show(r.vFeeSummaryPct),zt.hide(r.vFeeSummary),r.vFeeSummaryLow.textContent=c(F+P),r.vFeeSummaryHigh.textContent=c(I+L)):(zt.hide(r.vFeeSummaryPct),zt.show(r.vFeeSummary),r.summarySwapFeesLow.textContent=r.vSwapFeesLow.textContent,r.summarySwapFeesHigh.textContent=r.vSwapFeesHigh.textContent,r.summaryRedeemFeesLow.textContent=r.vRedeemFeesLow.textContent,r.summaryRedeemFeesHigh.textContent=r.vRedeemFeesHigh.textContent)}else zt.hide(r.vPreorderEstimates)}},{key:"submitCancel",value:(l=o(w().mark((function e(){var t,n,r,a,o,s;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=this.cancelData,r=n.order,a={orderID:r.id,pw:t.cancelPass.value},t.cancelPass.value="",o=xn().loading(t.cancelSubmit),e.next=8,yn("/api/cancel",a);case 8:if(s=e.sent,o(),xn().checkResponse(s)){e.next=14;break}return t.cancelErr.textContent=s.msg,zt.show(t.cancelErr),e.abrupt("return");case 14:zt.hide(n.bttn,t.forms),r.cancelling=!0;case 16:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"showCancel",value:function(e,t){var n=this.metaOrders[t].ord,r=this.page,a=n.qty-n.filled,o=Bn(n)?this.market.quote:this.market.base;r.cancelRemain.textContent=zt.formatCoinValue(a,o.unitInfo),r.cancelUnit.textContent=o.symbol.toUpperCase(),zt.hide(r.cancelErr),this.showForm(r.cancelForm),r.cancelPass.focus(),this.cancelData={bttn:zt.tmplElement(e,"cancelBttn"),order:n}}},{key:"showAccelerate",value:function(e){var t=xn().loading(this.main);this.accelerateOrderForm.refresh(e),t(),this.showForm(this.page.accelerateForm)}},{key:"showCreate",value:function(e){var t=this.page;this.currentCreate=e,this.newWalletForm.setAsset(e.id),this.showForm(t.newWalletForm)}},{key:"stepSubmit",value:function(){var e=this.page,t=this.market;if(zt.hide(e.orderErr),this.validateOrder(this.parseOrder())){var n=xn().walletMap[t.base.id],r=xn().walletMap[t.quote.id];return n?r?void this.showVerify():(e.orderErr.textContent=It(V,{asset:t.quote.symbol}),void zt.show(e.orderErr)):(e.orderErr.textContent=It(V,{asset:t.base.symbol}),void zt.show(e.orderErr))}}},{key:"showDeposit",value:(c=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.depositAddrForm.setAsset(t),this.showForm(this.page.deposit);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"handlePriceUpdate",value:function(e){e.host===this.market.dex.host&&e.spots[this.market.cfg.name]&&this.setCurrMarketPrice(),this.marketList.updateSpots(e)}},{key:"handleBondUpdate",value:(i=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if((n=t.dex)===this.market.dex.host){e.next=3;break}return e.abrupt("return");case 3:if("AccountRegistered"!==t.topic){e.next=6;break}return e.next=6,xn().fetchUser();case 6:this.market.dex=xn().exchanges[n],this.setRegistrationStatusVisibility();case 8:case"end":return e.stop()}}),e,this)}))),function(e){return i.apply(this,arguments)})},{key:"handleMatchNote",value:function(e){var t=this.metaOrders[e.orderID];if(!t)return this.refreshActiveOrders();xn().canAccelerateOrder(t.ord)?zt.show(t.details.accelerateBttn):zt.hide(t.details.accelerateBttn)}},{key:"handleOrderNote",value:function(e){var t=e.order,n=this.metaOrders[t.id];if(!n||"AsyncOrderFailure"===e.topic||"OrderLoaded"===e.topic&&t.readyToTick)return this.refreshActiveOrders();var r=n.status;n.ord=t,"MissedCancel"===e.topic&&zt.show(n.details.cancelBttn),t.filled===t.qty&&zt.hide(n.details.cancelBttn),xn().canAccelerateOrder(t)?zt.show(n.details.accelerateBttn):zt.hide(n.details.accelerateBttn),this.updateMetaOrder(n),(1===r&&2===t.status||2===r&&t.status>2)&&this.setDepthMarkers()}},{key:"handleEpochNote",value:function(e){if(xn().log("book","handleEpochNote:",e),e.host===this.market.dex.host&&e.marketID===this.market.sid){this.book&&(this.book.setEpoch(e.epoch),this.depthChart.draw()),this.clearOrderTableEpochs();for(var t=0,n=Object.values(this.metaOrders);ta.epoch;switch(!0){case 1===a.type&&1===a.status&&i:var c=0===a.tif?It(j):It(H);o.status.textContent=s.status.textContent=c,a.status=0===a.tif?3:2;break;case 2===a.type&&1===a.status:o.status.textContent=s.status.textContent=It(j),a.status=3}}}}},{key:"recentMatchesSortCompare",value:function(){var e=this;switch(this.recentMatchesSortKey){case"rate":return function(t,n){return e.recentMatchesSortDirection*(t.rate-n.rate)};case"qty":return function(t,n){return e.recentMatchesSortDirection*(t.qty-n.qty)};case"age":return function(t,n){return e.recentMatchesSortDirection*(t.stamp-n.stamp)}}}},{key:"refreshRecentMatchesTable",value:function(){var e=this.page,t=this.recentMatches;if(zt.empty(e.recentMatchesLiveList),t){var n=this.recentMatchesSortCompare();t.sort(n);var r,a=$r(t);try{for(a.s();!(r=a.n()).done;){var o=r.value,s=e.recentMatchesTemplate.cloneNode(!0),i=zt.parseTemplate(s);xn().bindTooltips(s),i.rate.textContent=zt.formatCoinValue(o.rate/this.market.rateConversionFactor),i.qty.textContent=zt.formatCoinValue(o.qty,this.market.baseUnitInfo),i.age.textContent=zt.timeSince(o.stamp),i.age.dataset.sinceStamp=String(o.stamp),s.classList.add(o.sell?"sellcolor":"buycolor"),e.recentMatchesLiveList.append(s)}}catch(e){a.e(e)}finally{a.f()}}}},{key:"addRecentMatches",value:function(e){this.recentMatches=[].concat(kn(e),kn(this.recentMatches)).slice(0,100)}},{key:"setBalanceVisibility",value:function(){var e=this.market;e&&e.dex&&this.balanceWgt.setBalanceVisibility(e.dex.connectionStatus===cn.Connected)}},{key:"handleBalanceNote",value:function(e){this.setBalanceVisibility(),this.preorderCache={};var t=this.market;if(t&&t.dex&&t.dex.connectionStatus===cn.Connected){var n=e.balance.available;switch(e.assetID){case t.baseCfg.id:if(!t.maxSell)break;"number"==typeof t.sellBalance&&t.sellBalance!==n&&(t.maxSell=null),this.isSell()&&this.preSell();break;case t.quoteCfg.id:if(!Object.keys(t.maxBuys).length)break;"number"==typeof t.buyBalance&&t.buyBalance!==n&&(t.maxBuys={}),this.isSell()||this.preBuy()}}}},{key:"submitOrder",value:(a=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,zt.hide(t.orderErr,t.vErr),n=this.currentOrder,r=t.vPass.value,t.vPass.value="",a={order:Ca(n),pw:r},this.validateOrder(n)){e.next=8;break}return e.abrupt("return");case 8:return t.vSubmit.classList.add("d-hide"),t.vLoader.classList.remove("d-hide"),e.next=12,yn("/api/tradeasync",a);case 12:if(o=e.sent,t.vSubmit.classList.remove("d-hide"),t.vLoader.classList.add("d-hide"),xn().checkResponse(o)){e.next=19;break}return t.vErr.textContent=o.msg,zt.show(t.vErr),e.abrupt("return");case 19:zt.hide(t.forms),this.refreshActiveOrders();case 21:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"createWallet",value:(r=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,xn().fetchUser();case 2:if(t=e.sent){e.next=5;break}return e.abrupt("return");case 5:n=t.assets[this.currentCreate.id],zt.hide(this.page.forms),this.balanceWgt.updateAsset(n.id),zt.setVis(!(this.market.base.wallet&&this.market.quote.wallet),this.page.noWallet),this.resolveOrderFormVisibility();case 10:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"walletUnlocked",value:(n=o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:zt.hide(this.page.forms),this.balanceWgt.updateAsset(this.openAsset.id);case 2:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"lotChanged",value:function(){var e=this.page,t=parseInt(e.lotField.value||"0");if(t<=0)return e.lotField.value="0",e.qtyField.value="",void this.previewQuoteAmt(!1);var n=this.market.cfg.lotsize;e.lotField.value=String(t),e.qtyField.value=String(t*n/this.market.baseUnitInfo.conventional.conversionFactor),this.previewQuoteAmt(!0)}},{key:"quantityChanged",value:function(e){var t=this.page,n=this.parseOrder();if(n.qty<0)return t.lotField.value="0",t.qtyField.value="",void this.previewQuoteAmt(!1);var r=this.market.cfg.lotsize,a=Math.floor(n.qty/r),o=a*r;t.lotField.value=String(a),(n.isLimit||n.sell)&&(e&&(t.qtyField.value=String(o/this.market.baseUnitInfo.conventional.conversionFactor)),this.previewQuoteAmt(!0))}},{key:"marketBuyChanged",value:function(){var e=this.page,t=ka(e.mktBuyField.value||"",this.market.quoteUnitInfo.conventional.conversionFactor),n=this.midGap();if(!n||!t)return e.mktBuyLots.textContent="0",void(e.mktBuyScore.textContent="0");var r=this.market.cfg.lotsize,a=t/n;e.mktBuyLots.textContent=(a/r).toFixed(1),e.mktBuyScore.textContent=zt.formatCoinValue(a,this.market.baseUnitInfo)}},{key:"rateFieldChanged",value:function(){var e=this.adjustedRate();if(e<=0)return this.depthLines.input=[],this.drawChartLines(),void(this.page.rateField.value="0");var t=this.parseOrder(),n=e/this.market.rateConversionFactor;this.page.rateField.value=String(n),this.depthLines.input=[{rate:n,color:t.sell?this.depthChart.theme.sellLine:this.depthChart.theme.buyLine}],this.drawChartLines(),this.previewQuoteAmt(!0)}},{key:"adjustedRate",value:function(){var e=this.page.rateField.value;if(!e)return NaN;var t=ka(e,this.market.rateConversionFactor);return t-t%this.market.cfg.ratestep}},{key:"loadTable",value:function(){this.loadTableSide(!0),this.loadTableSide(!1)}},{key:"binOrdersByRateAndEpoch",value:function(e){if(!e||!e.length)return[];var t=[],n=[],r=[],a=e[0].msgRate;e[0].epoch?n.push(e[0]):r.push(e[0]);for(var o=1;o0}))}},{key:"loadTableSide",value:function(e){var t=this,n=e?this.book.sells:this.book.buys,r=e?this.page.sellRows:this.page.buyRows;zt.empty(r),n&&n.length&&this.binOrdersByRateAndEpoch(n).forEach((function(e){r.appendChild(t.orderTableRow(e))}))}},{key:"addTableOrder",value:function(e){var t=e.sell?this.page.sellRows:this.page.buyRows,n=t.firstChild;if(0!==e.rate){for(n&&0===n.manager.getRate()&&(n=n.nextSibling);n;){if(0===n.manager.compare(e))return void n.manager.insertOrder(e);if(n.manager.compare(e)>0){var r=this.orderTableRow([e]);return void t.insertBefore(r,n)}n=n.nextSibling}var a=this.orderTableRow([e]);t.appendChild(a)}else{if(0===e.qtyAtomic)return;n&&0===n.manager.getRate()?n.manager.insertOrder(e):(n=this.orderTableRow([e]),t.insertBefore(n,t.firstChild))}}},{key:"removeTableOrder",value:function(e){for(var t=e.token,n=0,r=[this.page.sellRows,this.page.buyRows];n36e5&&!i.disabled?(zt.show(n.expired),i.running&&xn().fetchBalance(o)):zt.hide(n.expired)}else zt.show(n.connect)}else zt.show(n.unsupported)}}},{key:"updateParent",value:function(e){var t=xn().assets[e.parentID],n=t.wallet.balance,r=t.unitInfo;e.parentBal&&(e.parentBal.textContent=zt.formatCoinValue(n.available,r))}},{key:"updateAsset",value:function(e){e===this.base.id?this.updateWallet(this.base):e===this.quote.id&&this.updateWallet(this.quote),e===this.base.parentID&&this.updateParent(this.base),e===this.quote.parentID&&this.updateParent(this.quote)}}]),e}();function ba(e,t,n){return{host:e,base:t,quote:n}}function wa(e,t){return"".concat(e,"_").concat(t)}function ka(e,t){return e?Math.round(parseFloat(e)*t):0}function xa(e,t){e.classList.remove("selected"),t.classList.add("selected")}function Ca(e){for(var t={},n=0,a=Object.entries(e.options);n1?(r.removeAttribute("hidden"),r.innerText=String(t),r.title="quantity is comprised of ".concat(t," orders")):r.setAttribute("hidden","true")}},{key:"insertOrder",value:function(e){this.orderBin.push(e),this.updateQtyNumOrdersEl()}},{key:"updateOrderQty",value:function(e){for(var t=e.token,n=e.qty,r=e.qtyAtomic,a=0;ae.msgRate===e.sell?1:-1:this.isEpoch()?1:-1}}]),e}(),Ea=new Intl.NumberFormat(navigator.languages,{maximumSignificantDigits:4}),Aa=new Intl.NumberFormat(navigator.languages,{minimumFractionDigits:1,maximumFractionDigits:1});function Fa(e){return e>=1e3||Math.round(e)===e?Aa.format(e):Ea.format(e)}function Ia(e,t,n){if(n.spot){e.price.textContent=Fa(xn().conventionalRate(n.baseid,n.quoteid,n.spot.rate,t));var r=n.spot.change24>0?"+":"";e.change.classList.remove("buycolor","sellcolor"),e.change.classList.add(n.spot.change24>=0?"buycolor":"sellcolor"),e.change.textContent="".concat(r).concat((100*n.spot.change24).toFixed(1),"%")}}var Ra=[.5,1/4,3/4,1/8,5/8,3/8,7/8];function Da(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Oa(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Oa(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Oa(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0&&(e.checked=!0)}))}};o(n.hostFilter,"hosts"),o(n.assetFilter,"assets"),o(n.statusFilter,"statuses");var i=[],c=function(e,n){var a=e.querySelector(".apply-bttn");i.push(a),zt.bind(a,"click",(function(){t.submitFilter(),i.forEach((function(e){return zt.hide(e)}))})),e.querySelectorAll("input").forEach((function(t){zt.bind(t,"change",(function(){!function(e,t){if(e.length!==t.length)return!1;var n,r=Da(e);try{for(r.s();!(n=r.n()).done;){var a=n.value;if(-1===t.indexOf(a))return!1}}catch(e){r.e(e)}finally{r.f()}return!0}(Na(e),r[n])?zt.show(a):zt.hide(a)}))}))};return c(n.hostFilter,"hosts"),c(n.assetFilter,"assets"),c(n.statusFilter,"statuses"),zt.bind(t.main,"scroll",(function(){t.loading||n.ordersTable.offsetHeight-t.main.offsetHeight-t.main.scrollTop<0&&t.nextPage()})),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){zt.hide(n.forms)}))})),zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||zt.hide(n.forms)})),zt.bind(n.exportOrders,"click",(function(){t.exportOrders()})),n.showArchivedDateField.addEventListener("change",(function(){n.showArchivedDateField.checked?zt.show(n.archivedDateField):zt.hide(n.archivedDateField,n.deleteArchivedRecordsErr)})),zt.bind(n.deleteArchivedRecords,"click",(function(){var e=t.page;e.showArchivedDateField.checked=!1,e.saveMatchesToFile.checked=!1,e.saveOrdersToFile.checked=!1,e.deleteArchivedRecordsErr.textContent="",e.archivedRecordsLocation.textContent="",e.deleteArchivedRecordsMsg.textContent="",zt.hide(e.deleteArchivedResult,e.deleteArchivedRecordsErr,e.deleteArchivedRecordsMsg,e.archivedRecordsLocation,e.archivedDateField),t.showForm(e.deleteArchivedRecordsForm)})),zt.bind(n.deleteArchivedRecordsSubmit,"click",(function(){var e=0;n.showArchivedDateField.checked&&(e=Date.parse(n.olderThan.value||""),isNaN(e)||e<=0)?zt.showFormError(n.deleteArchivedRecordsErr,It(qe)):t.deleteArchivedRecords(e)})),t.submitFilter(),t}return u(p,[{key:"showForm",value:(i=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,zt.hide(n.deleteArchivedRecordsForm),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(500,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0px";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return i.apply(this,arguments)})},{key:"setOrders",value:function(e){zt.empty(this.page.tableBody),this.appendOrders(e)}},{key:"appendOrders",value:function(e){var t,n=this,r=this.page.tableBody,a=Da(e);try{var o=function(){var e,a,o,s=t.value,i=n.orderTmpl.cloneNode(!0),c=function(e,t){zt.tmplElement(i,e).textContent=t},l="".concat(s.baseSymbol.toUpperCase(),"-").concat(s.quoteSymbol.toUpperCase());c("host","".concat(l," @ ").concat(s.host));var u="",h=[xn().unitInfo(s.baseID),xn().unitInfo(s.quoteID)],d=h[0],p=h[1];if(s.sell){var f=[s.baseSymbol,s.quoteSymbol];e=f[0],a=f[1],o=zt.formatCoinValue(s.qty,d),1===s.type&&(u=zt.formatCoinValue(s.qty/Tn*s.rate,p))}else{var m=[s.quoteSymbol,s.baseSymbol];e=m[0],a=m[1],2===s.type?o=zt.formatCoinValue(s.qty,d):(o=zt.formatCoinValue(s.qty/Tn*s.rate,p),u=zt.formatCoinValue(s.qty,d))}c("fromQty",o),zt.tmplElement(i,"fromLogo").src=zt.logoPath(e),c("fromSymbol",e),c("toQty",u),zt.tmplElement(i,"toLogo").src=zt.logoPath(a),c("toSymbol",a),c("type","".concat(Ln(s)," ").concat(Pn(s))),c("rate",zt.formatCoinValue(xn().conventionalRate(s.baseID,s.quoteID,s.rate))),c("status",Wn(s)),c("filled","".concat((qn(s)/s.qty*100).toFixed(1),"%")),c("settled","".concat((Nn(s)/s.qty*100).toFixed(1),"%"));var v=new Date(s.submitTime).toLocaleString();c("time","".concat(zt.timeSince(s.submitTime)," ago, ").concat(v)),zt.tmplElement(i,"link").href="order/".concat(s.id),xn().bindInternalNavigation(i),r.appendChild(i)};for(a.s();!(t=a.n()).done;)o()}catch(e){a.e(e)}finally{a.f()}50===e.length?this.offset=e[e.length-1].id:this.offset=""}},{key:"submitFilter",value:(a=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.offset="",(n=this.filterState).hosts=Na(t.hostFilter),n.assets=Na(t.assetFilter).map((function(e){return parseInt(e)})),n.statuses=Na(t.statusFilter).map((function(e){return parseInt(e)})),e.t0=this,e.next=9,this.fetchOrders();case 9:e.t1=e.sent,e.t0.setOrders.call(e.t0,e.t1);case 11:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"fetchOrders",value:(r=o(w().mark((function e(){var t,n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=xn().loading(this.main),e.next=3,yn("/api/orders",this.currentFilter());case 3:return n=e.sent,t(),e.abrupt("return",n.orders);case 6:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"exportOrders",value:function(){this.offset="";var e=this.currentFilter(),t=new URL(window.location.href),n=new URLSearchParams(""),r=function(t){e[t].forEach((function(e){n.append(t,e)}))};r("hosts"),r("assets"),r("statuses"),t.search=n.toString(),t.pathname="/orders/export",window.open(t.toString())}},{key:"deleteArchivedRecords",value:(n=o(w().mark((function e(t){var n,r,a,o,s,i;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=n.saveMatchesToFile.checked||!1,a=n.saveOrdersToFile.checked||!1,o={olderThanMs:t,saveMatchesToFile:r,saveOrdersToFile:a},s=xn().loading(this.main),e.next=7,yn("/api/deletearchivedrecords",o);case 7:if(i=e.sent,s(),xn().checkResponse(i)){e.next=11;break}return e.abrupt("return",zt.showFormError(n.deleteArchivedRecordsErr,i.msg));case 11:i.archivedRecordsDeleted>0?(n.deleteArchivedRecordsMsg.textContent=It(Ue,{nRecords:i.archivedRecordsDeleted}),(r||a)&&(n.archivedRecordsLocation.textContent=It(_e,{path:i.archivedRecordsPath}),zt.show(n.archivedRecordsLocation)),this.submitFilter()):n.deleteArchivedRecordsMsg.textContent=It(Ne),zt.show(n.deleteArchivedResult,n.deleteArchivedRecordsMsg);case 13:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"currentFilter",value:function(){var e=this.filterState;return{hosts:e.hosts,assets:e.assets.map((function(e){return parseInt(e)})),statuses:e.statuses.map((function(e){return parseInt(e)})),n:50,offset:this.offset}}},{key:"nextPage",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(""!==this.offset&&!this.loading){e.next=2;break}return e.abrupt("return");case 2:return this.loading=!0,zt.show(this.page.orderLoader),e.next=6,this.fetchOrders();case 6:t=e.sent,this.loading=!1,zt.hide(this.page.orderLoader),this.appendOrders(t);case 10:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),p}(fn);function Na(e){var t=[];return e.querySelectorAll("input").forEach((function(e){e.checked&&t.push(e.value)})),t}function Ua(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return _a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_a(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,a=function(){};return{s:a,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function _a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=3}(t)||t.revoked||t.refund?!function(e){return e.status<5&&1===e.side&&e.status>=4}(t)||t.revoked||t.refund?zt.hide(n.makerSwapMsg,n.takerSwapMsg,n.makerRedeemMsg,n.takerRedeemMsg):(n.takerRedeemMsg.textContent=Ha(t.redeem),zt.hide(n.makerSwapMsg,n.takerSwapMsg,n.makerRedeemMsg),zt.show(n.takerRedeemMsg)):(n.makerRedeemMsg.textContent=Ha(t.redeem),zt.hide(n.makerSwapMsg,n.takerSwapMsg,n.takerRedeemMsg),zt.show(n.makerRedeemMsg));else{var a=Ga(t);n.takerSwapMsg.textContent=Ha(a),zt.hide(n.makerSwapMsg,n.makerRedeemMsg,n.takerRedeemMsg),zt.show(n.takerSwapMsg)}else{var o=Ka(t);n.makerSwapMsg.textContent=Ha(o),zt.hide(n.takerSwapMsg,n.makerRedeemMsg,n.takerRedeemMsg),zt.show(n.makerSwapMsg)}zt.setVis(!t.isCancel&&(Ka(t)||!t.revoked),n.makerSwap),zt.setVis(!t.isCancel&&(Ga(t)||!t.revoked),n.takerSwap),zt.setVis(!t.isCancel&&(Xa(t)||!t.revoked),n.makerRedeem),zt.setVis(!t.isCancel&&(Qa(t)||!t.revoked&&t.active||1===t.side&&t.active&&(t.counterRedeem||!t.refund)),n.takerRedeem),zt.setVis(!t.isCancel&&(t.refund||t.revoked&&t.active&&!t.counterRedeem),n.refund)}}},{key:"addNewMatchCard",value:function(e){var t=this.page,n=t.matchCardTmpl.cloneNode(!0);n.dataset.matchID=e.matchID,this.setImmutableMatchCardElements(n,e),this.setMutableMatchCardElements(n,e),t.matchBox.appendChild(n)}},{key:"showMatchCards",value:function(){var e=this,t=this.order;t&&t.matches&&(t.matches.sort((function(e,t){return e.stamp-t.stamp})),t.matches.forEach((function(t){return e.addNewMatchCard(t)})))}},{key:"showCancel",value:function(){var e=this.order,t=this.page,n=e.qty-e.filled,r=Bn(e)?xn().assets[e.quoteID]:xn().assets[e.baseID];t.cancelRemain.textContent=zt.formatCoinValue(n,r.unitInfo),t.cancelUnit.textContent=r.unitInfo.conventional.unit.toUpperCase(),this.showForm(t.cancelForm)}},{key:"showForm",value:(r=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,zt.hide(n.cancelForm,n.accelerateForm),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(500,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0px";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"submitCancel",value:(n=o(w().mark((function e(){var t,n,r,a,o;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=this.order,r={orderID:n.id,pw:t.cancelPass.value},t.cancelPass.value="",a=xn().loading(t.cancelForm),e.next=7,yn("/api/cancel",r);case 7:if(o=e.sent,a(),xn().checkResponse(o)){e.next=11;break}return e.abrupt("return");case 11:t.status.textContent=It(K),zt.hide(t.forms),n.cancelling=!0;case 14:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"setAccelerationButtonVis",value:function(){var e=this.order;if(e){var t=this.page;zt.setVis(xn().canAccelerateOrder(e),t.accelerateBttn,t.actionsLabel)}}},{key:"showAccelerateForm",value:(t=o(w().mark((function e(){var t;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=xn().loading(this.page.accelerateBttn),this.accelerateOrderForm.refresh(this.order),t(),this.showForm(this.page.accelerateForm);case 4:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleOrderNote",value:function(e){var t=this.page,n=e.order;if(n.id===this.orderID){this.order=n;var r=t.cancelBttn;r&&n.status>2&&zt.hide(r),t.status.textContent=Wn(n);var a,o=Ua(n.matches||[]);try{for(o.s();!(a=o.n()).done;){var s=a.value;this.processMatch(s)}}catch(e){o.e(e)}finally{o.f()}this.setAccelerationButtonVis()}}},{key:"handleMatchNote",value:function(e){e.orderID===this.orderID&&(this.processMatch(e.match),this.setAccelerationButtonVis())}},{key:"processMatch",value:function(e){var t,n=null,r=Ua(zt.applySelector(this.page.matchBox,".match-card"));try{for(r.s();!(t=r.n()).done;){var a=t.value;if(a.dataset.matchID===e.matchID){n=a;break}}}catch(e){r.e(e)}finally{r.f()}n?this.setMutableMatchCardElements(n,e):this.addNewMatchCard(e)}}]),p}(fn);function Ha(e){return e.confs&&0!==e.confs.required?"".concat(e.confs.count," / ").concat(e.confs.required," ").concat(It(pt)):""}function Ka(e){return 0===e.side?e.swap:e.counterSwap}function Ga(e){return 0===e.side?e.counterSwap:e.swap}function Xa(e){return 0===e.side?e.redeem:e.counterRedeem}function Qa(e){return 0===e.side?e.counterRedeem:e.redeem}function Ya(e,t){var n=Za[e];if(n){var r=n[za];r&&(t.classList.remove("plainlink"),t.classList.add("subtlelink"),t.href=r(t.dataset.explorerCoin||""))}}var Ja=(h(Ta={},0,(function(e){if(e.startsWith(Va)){var t=e.substring(Va.length);return"https://etherscan.io/address/".concat(t)}return 42===e.length?"https://etherscan.io/address/".concat(e):"https://etherscan.io/tx/".concat(e)})),h(Ta,1,(function(e){if(e.startsWith(Va)){var t=e.substring(Va.length);return"https://goerli.etherscan.io/address/".concat(t)}return 42===e.length?"https://goerli.etherscan.io/address/".concat(e):"https://goerli.etherscan.io/tx/".concat(e)})),Ta),Za={42:(Pa={},h(Pa,0,(function(e){var t=r(e.split(":"),2),n=t[0],a=t[1];return"https://explorer.dcrdata.org/tx/".concat(n,"/out/").concat(a)})),h(Pa,1,(function(e){var t=r(e.split(":"),2),n=t[0],a=t[1];return"https://testnet.dcrdata.org/tx/".concat(n,"/out/").concat(a)})),Pa),0:(La={},h(La,0,(function(e){return"https://mempool.space/tx/".concat(e.split(":")[0])})),h(La,1,(function(e){return"https://mempool.space/testnet/tx/".concat(e.split(":")[0])})),La),2:(Ba={},h(Ba,0,(function(e){return"https://ltc.bitaps.com/".concat(e.split(":")[0])})),h(Ba,1,(function(e){return"https://sochain.com/tx/LTCTEST/".concat(e.split(":")[0])})),Ba),60:Ja,60001:Ja,3:(Ma={},h(Ma,0,(function(e){return"https://dogeblocks.com/tx/".concat(e.split(":")[0])})),h(Ma,1,(function(e){return"https://blockexplorer.one/dogecoin/testnet/tx/".concat(e.split(":")[0])})),Ma),145:(Wa={},h(Wa,0,(function(e){return"https://bch.loping.net/tx/".concat(e.split(":")[0])})),h(Wa,1,(function(e){return"https://tbch4.loping.net/tx/".concat(e.split(":")[0])})),Wa)};var $a=function(e){rn(g,e);var t,n,a,i,c,l,d,p,f,m,v,y=(m=g,v=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}(),function(){var e,t=on(m);if(v){var n=on(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return an(this,e)});function g(e){var t;s(this,g),h(tn(t=y.call(this)),"body",void 0),h(tn(t),"forms",void 0),h(tn(t),"currentForm",void 0),h(tn(t),"page",void 0),h(tn(t),"host",void 0),h(tn(t),"keyup",void 0),h(tn(t),"dexAddrForm",void 0),t.body=e,t.host=e.dataset.host?e.dataset.host:"";var n=t.page=zt.idDescendants(e);t.forms=zt.applySelector(n.forms,":scope > form"),zt.bind(n.exportDexBtn,"click",(function(){return t.prepareAccountExport(n.authorizeAccountExportForm)})),zt.bind(n.disableAcctBtn,"click",(function(){return t.prepareAccountDisable(n.disableAccountForm)})),zt.bind(n.updateBondOptionsBtn,"click",(function(){return t.prepareUpdateBondOptions()})),zt.bind(n.updateCertBtn,"click",(function(){return n.certFileInput.click()})),zt.bind(n.updateHostBtn,"click",(function(){return t.prepareUpdateHost()})),zt.bind(n.certFileInput,"change",(function(){return t.onCertFileChange()})),t.dexAddrForm=new Sr(n.dexAddrForm,function(){var e=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:window.location.assign("/dexsettings/".concat(t.host));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),void 0,t.host),Or(n.updateBondOptionsForm,n.updateBondOptionsConfirm,(function(){return t.updateBondOptions()})),Or(n.authorizeAccountExportForm,n.authorizeExportAccountConfirm,(function(){return t.exportAccount()})),Or(n.disableAccountForm,n.disableAccountConfirm,(function(){return t.disableAccount()}));var r=function(){zt.hide(n.forms)};return zt.bind(n.forms,"mousedown",(function(e){zt.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},zt.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){zt.bind(e,"click",(function(){r()}))})),xn().registerNoteFeeder({conn:function(){t.setConnectionStatus()}}),t.setConnectionStatus(),t}return u(g,[{key:"unload",value:function(){zt.unbind(document,"keyup",this.keyup)}},{key:"showForm",value:(f=o(w().mark((function e(t){var n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return zt.hide(e)})),t.style.right="10000px",zt.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,zt.animate(300,(function(e){t.style.right="".concat((1-e)*r,"px")}),"easeOutHard");case 8:t.style.right="0";case 9:case"end":return e.stop()}}),e,this)}))),function(e){return f.apply(this,arguments)})},{key:"exportAccount",value:(p=o(w().mark((function e(){var t,n,r,a,o,s,i,c;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.exportAccountAppPass.value,r=t.exportAccountHost.textContent,t.exportAccountAppPass.value="",a={pw:n,host:r},o=xn().loading(this.body),e.next=8,yn("/api/exportaccount",a);case 8:if(s=e.sent,o(),xn().checkResponse(s)){e.next=14;break}return t.exportAccountErr.textContent=s.msg,zt.show(t.exportAccountErr),e.abrupt("return");case 14:s.account.bonds=s.bonds,i=JSON.parse(JSON.stringify(s.account)),(c=document.createElement("a")).setAttribute("download","dcrAccount-"+r+".json"),c.setAttribute("href","data:text/json,"+JSON.stringify(i,null,2)),c.click(),zt.hide(t.forms);case 21:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"disableAccount",value:(d=o(w().mark((function e(){var t,n,r,a,o,s;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.disableAccountAppPW.value,r=t.disableAccountHost.textContent,t.disableAccountAppPW.value="",a={pw:n,host:r},o=xn().loading(this.body),e.next=8,yn("/api/disableaccount",a);case 8:if(s=e.sent,o(),xn().checkResponse(s)){e.next=14;break}return t.disableAccountErr.textContent=s.msg,zt.show(t.disableAccountErr),e.abrupt("return");case 14:zt.hide(t.forms),window.location.assign("/settings");case 16:case"end":return e.stop()}}),e,this)}))),function(){return d.apply(this,arguments)})},{key:"prepareAccountExport",value:(l=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).exportAccountHost.textContent=this.host,n.exportAccountErr.textContent="",en.passwordIsCached()?this.exportAccount():this.showForm(t);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return l.apply(this,arguments)})},{key:"prepareAccountDisable",value:(c=o(w().mark((function e(t){var n;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).disableAccountHost.textContent=this.host,n.disableAccountErr.textContent="",this.showForm(t);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"prepareUpdateBondOptions",value:(i=o(w().mark((function e(){var t,n,a,o,s,i,c,l;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:for(t=this.page,n=xn().user.exchanges[this.host],t.bondTargetTier.setAttribute("placeholder",n.bondOptions.targetTier.toString()),zt.empty(t.bondAssetSelect),a=0,o=Object.entries(n.bondAssets);a=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){throw e})),f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){i=!0,o=e})),f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function ro(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0)this.fetchMaxBuy(Math.round(e.basisPrice/this.currentMarket.atomToConv*Tn)),t.basisPrice.textContent=zt.formatFiveSigFigs(e.basisPrice),zt.show(t.absMaxBox,t.basisPrice),zt.hide(t.manualPriceInput,t.noFiatBox),this.page.gapFactorMax.textContent=zt.formatFiveSigFigs(e.basisPrice);else{t.lotEstQuoteLots.textContent="[no rate]",t.basisPrice.textContent="must set manually",zt.hide(t.basisPrice,t.absMaxBox),zt.show(t.manualPriceInput),t.manualPriceInput.focus();var n=xn().user;0===Object.keys(n.fiatRates).length&&zt.show(t.noFiatBox)}e&&e.breakEvenSpread>0?(zt.show(t.breakEvenGapBox),t.breakEvenGap.textContent=zt.formatFiveSigFigs(e.breakEvenSpread)):zt.hide(t.breakEvenGapBox)}},{key:"updateGapStrategyInputVisibility",value:function(){var e=this.page;switch(e.gapStrategySelect.value){case ao:zt.show(this.gapMultiplierOpt.node),zt.hide(e.absInputBox,e.absMaxBox,this.gapPercentOpt.node);break;case io:case co:zt.show(this.gapPercentOpt.node),zt.hide(e.absInputBox,e.absMaxBox,this.gapMultiplierOpt.node);break;case oo:case so:zt.hide(this.gapMultiplierOpt.node,this.gapPercentOpt.node),zt.show(e.absInputBox,e.absMaxBox)}}},{key:"updateGapStrategyInputs",value:function(e){switch(this.updateGapStrategyInputVisibility(),this.page.gapStrategySelect.value){case ao:e&&e.breakEvenSpread>0?(go.start.y=e.breakEvenSpread,go.end.y=100*e.breakEvenSpread,go.yUnit=this.rateUnit()):(go.start.y=100,go.end.y=1e4,go.yUnit="%"),this.gapMultiplierOpt.handler.setConfig(go);break;case io:case co:e&&e.breakEvenSpread>0?(ko.end.y=e.basisPrice,ko.yUnit=this.rateUnit(),this.gapPercentOpt.handler.setConfig(ko)):(ko.end.y=10,ko.yUnit="%")}}},{key:"setCurrentBasisPrice",value:function(e){if(e>0){var t=this.rateUnit();po.start.y=-.01*e,po.end.y=.01*e,po.yUnit=t,mo.start.y=0,mo.end.y=.01*e,mo.yUnit=t}else po.start.y=-1,po.end.y=1,po.yUnit="%",mo.start.y=0,mo.end.y=1,mo.yUnit="%";this.biasOpt.handler.setConfig(po),this.driftToleranceOpt.handler.setConfig(mo)}},{key:"rateUnit",value:function(){var e=this.currentMarket.quotesymbol.split(".")[0],t=this.currentMarket.basesymbol.split(".")[0];return"".concat(e,"/").concat(t)}},{key:"setMarket",value:(c=o(w().mark((function e(t,n){var r,a,o,s,i,c,l,u,h,d,p,f,m,v;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=this.page,a=t[0],this.currentMarket=a,this.specifiedPrice=0,this.setCurrentReport(null),r.manualPriceInput.value="",en.storeLocal(en.lastMMMarketLK,a),zt.empty(r.baseSelect,r.quoteSelect),r.baseSelect.appendChild(this.assetRow(a.basesymbol)),r.quoteSelect.appendChild(this.assetRow(a.quotesymbol)),this.hideAssetDropdown(),o=function(e,t){zt.setContent(t,zt.symbolize(e.basesymbol),new Text("-"),zt.symbolize(e.quotesymbol),new Text(" @ "),new Text(e.host))},zt.hide(r.marketSelect,r.marketOneChoice),1===t.length)zt.show(r.marketOneChoice),o(a,r.marketOneChoice);else{zt.show(r.marketSelect),zt.empty(r.marketSelect),s=no(t);try{for(s.s();!(i=s.n()).done;)c=i.value,l=document.createElement("option"),r.marketSelect.appendChild(l),l.value="".concat(c.host," ").concat(c.name),o(c,l)}catch(e){s.e(e)}finally{s.f()}}if(!n){e.next=16;break}return e.abrupt("return");case 16:if(zt.setContent(r.lotEstBaseSymbol,zt.symbolize(a.basesymbol)),zt.setContent(r.lotEstQuoteSymbol,zt.symbolize(a.quotesymbol)),u=function(e,t){zt.show(t);var n=zt.parseTemplate(t);return n.assetLogo.src=zt.logoPath(e.symbol),n.assetName.textContent=e.name,!1},zt.hide(r.lotEstQuoteBox,r.lotEstQuoteNoWallet,r.lotEstBaseBox,r.lotEstBaseNoWallet,r.availHeader,r.lotEstimateBox,r.marketInfo,r.oraclesBox,r.lotsBox,r.options,r.advancedBox),h=[xn().assets[a.baseid],xn().assets[a.quoteid]],p=h[1],!(d=h[0]).wallet||!p.wallet){e.next=35;break}return zt.show(r.lotEstQuoteBox,r.lotEstBaseBox,r.availHeader,r.fetchingMarkets,r.lotsBox,r.advancedBox),en.fetchLocal(en.optionsExpansionLK)&&zt.show(r.options),f=xn().loading(r.options),m=this.fetchOracleAndMaxBuy(),v=this.fetchMaxSell(),e.next=29,m;case 29:return e.next=31,v;case 31:return f(),zt.show(r.lotEstimateBox,r.marketInfo,r.oraclesBox),zt.hide(r.fetchingMarkets),e.abrupt("return");case 35:zt.show(r.lotEstimateBox),p.wallet?r.lotEstQuoteLots.textContent="0":u(p,r.lotEstQuoteNoWallet),d.wallet?r.lotEstBaseLots.textContent="0":u(d,r.lotEstBaseNoWallet);case 38:case"end":return e.stop()}}),e,this)}))),function(e,t){return c.apply(this,arguments)})},{key:"fetchMaxSell",value:(i=o(w().mark((function e(){var t,n,r;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.currentMarket,n=this.page,e.next=3,yn("/api/maxsell",{host:t.host,base:t.baseid,quote:t.quoteid});case 3:if(r=e.sent,this.currentMarket===t){e.next=6;break}return e.abrupt("return");case 6:if(xn().checkResponse(r)){e.next=10;break}return n.lotEstBaseLots.textContent="0",console.error(r),e.abrupt("return");case 10:n.lotEstBaseLots.textContent=String(r.maxSell.swap.lots);case 11:case"end":return e.stop()}}),e,this)}))),function(){return i.apply(this,arguments)})},{key:"fetchOracleAndMaxBuy",value:(a=o(w().mark((function e(){var t,n,r,a,o,s,i,c,l,u,h,d,p;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.currentMarket,r=this.page,e.next=3,yn("/api/marketreport",{host:n.host,baseID:n.baseid,quoteID:n.quoteid});case 3:if(a=e.sent,xn().checkResponse(a)){e.next=8;break}return r.lotEstQuoteLots.textContent="0",console.error(a),e.abrupt("return");case 8:if(o=a.report,zt.hide(r.manualPriceBttn),this.setCurrentReport(o),o.oracles&&0!==o.oracles.length){e.next=13;break}return e.abrupt("return");case 13:zt.empty(r.oracles),s=0,i=0,c=no(null!==(t=o.oracles)&&void 0!==t?t:[]);try{for(c.s();!(l=c.n()).done;)u=l.value,h=r.oracleTmpl.cloneNode(!0),r.oracles.appendChild(h),(d=zt.parseTemplate(h)).logo.src="img/"+u.host+".png",d.host.textContent=Fo[u.host],d.volume.textContent=zt.formatThreeSigFigs(u.usdVol),p=(u.bestBuy+u.bestSell)/2,i+=u.usdVol*p,s+=u.usdVol,d.price.textContent=zt.formatThreeSigFigs((u.bestBuy+u.bestSell)/2)}catch(e){c.e(e)}finally{c.f()}r.avgPrice.textContent=zt.formatFiveSigFigs(i/s);case 19:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"fetchMaxBuy",value:(n=o(w().mark((function e(t){var n,r,a;return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.currentMarket,r=this.page,e.next=3,yn("/api/maxbuy",{host:n.host,base:n.baseid,quote:n.quoteid,rate:null!=t?t:0});case 3:if(a=e.sent,this.currentMarket===n){e.next=6;break}return e.abrupt("return");case 6:r.lotEstQuoteLots.textContent=String(a.maxBuy.swap.lots);case 7:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"setEditProgram",value:function(e){var t=this,n=this.createOpts,r=this.page,a=e.program,o=[xn().assets[a.baseID],xn().assets[a.quoteID]],s=o[0],i=o[1],c=xn().exchanges[a.host].markets["".concat(s.symbol,"_").concat(i.symbol)];this.setMarket([to({host:a.host},c)],!0);for(var l=0,u=Object.values(this.programs);l0?(zt.show(e.programsHeader),zt.hide(e.noProgramsMessage)):(zt.hide(e.programsHeader),zt.show(e.noProgramsMessage))}},{key:"programDiv",value:function(e){var t=this,n=this.page.runningProgramTmpl.cloneNode(!0),r=zt.parseTemplate(n),a=function(){var t=o(w().mark((function t(a,o){var s,i;return w().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return zt.hide(r.startErr),s=xn().loading(n),t.next=4,yn(a,{programID:e.programID,appPW:o});case 4:i=t.sent,s(),xn().checkResponse(i)||(r.startErr.textContent=i.msg,zt.show(r.startErr));case 7:case"end":return t.stop()}}),t)})));return function(e,n){return t.apply(this,arguments)}}();zt.bind(r.pauseBttn,"click",(function(){return a("/api/stopbot")})),zt.bind(r.startBttn,"click",this.authedRoute(function(){var e=o(w().mark((function e(t){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",a("/api/startbot",t));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}())),zt.bind(r.retireBttn,"click",(function(){return a("/api/retirebot")})),zt.bind(r.configureBttn,"click",(function(n){n.stopPropagation(),t.setEditProgram(t.programs[e.programID])}));var s=[xn().assets[e.program.baseID],xn().assets[e.program.quoteID]],i=s[0],c=s[1];return r.base.appendChild(this.assetRow(i.symbol)),r.quote.appendChild(this.assetRow(c.symbol)),r.baseSymbol.textContent=i.symbol.toUpperCase(),r.quoteSymbol.textContent=c.symbol.toUpperCase(),r.host.textContent=e.program.host,this.updateProgramDiv(r,e),this.programs[e.programID]=Object.assign({tmpl:r,div:n},e),n}},{key:"authedRoute",value:function(e){var t=this;return o(w().mark((function n(){return w().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:if(!en.passwordIsCached()){n.next=4;break}return n.next=3,e("");case 3:return n.abrupt("return",n.sent);case 4:return t.pwHandler=e,n.next=7,t.showForm(t.page.pwForm);case 7:case"end":return n.stop()}}),n)})))}},{key:"updateProgramDiv",value:function(e,t){var n=t.program;zt.hide(e.programRunning,e.programPaused),t.running?zt.show(e.programRunning):zt.show(e.programPaused),e.lots.textContent=String(n.lots),e.boost.textContent="".concat((100*n.gapFactor).toFixed(1),"%"),e.driftTolerance.textContent="".concat((100*n.driftTolerance).toFixed(2),"%"),e.oracleWeight.textContent="".concat((100*n.oracleWeighting).toFixed(0),"%"),e.oracleBias.textContent="".concat((100*n.oracleBias).toFixed(1),"%")}},{key:"setMarketSubchoice",value:function(e,t){e===this.currentMarket.host&&t===this.currentMarket.name||this.leaveEditMode(),this.currentMarket=Object.assign({host:e},xn().exchanges[e].markets[t])}},{key:"createOptsUpdated",value:function(){this.createOpts.oracleWeighting?zt.show(this.biasOpt.node):zt.hide(this.biasOpt.node)}},{key:"handleBotNote",value:function(e){var t=this.page,n=e.report;switch(e.topic){case"BotCreated":t.runningPrograms.prepend(this.programDiv(n)),this.setProgramsHeader();break;case"BotRetired":this.programs[n.programID].div.remove(),delete this.programs[n.programID],this.setProgramsHeader();break;default:var r=this.programs[n.programID];Object.assign(r,n),this.updateProgramDiv(r.tmpl,n)}}},{key:"setCreationBase",value:function(e){for(var t=this.currentMarket.quotesymbol,n=So(),r=[],a=0,o=n;a0)return this.setMarket(r);for(var u=0,h=n;u0)return this.setMarket(r);for(var u=0,h=n;u 0 lots"));case 6:i=Object.assign({host:r.host,baseID:r.baseid,quoteID:r.quoteid},this.createOpts,{lots:s,gapStrategy:""}),c=n.gapStrategySelect.value,i.gapStrategy=null!=c?c:"",l={botType:"MakerV0",program:i,programID:0,appPW:t,manualRate:0},e.t0=c,e.next=e.t0===oo||e.t0===so?13:e.t0===io||e.t0===co?22:24;break;case 13:if(0!==(u=parseFloat(n.absInput.value||"0"))){e.next=18;break}return e.abrupt("return",o("gap must be specified for strategy = absolute"));case 18:if(!(null!=a&&a.basisPrice&&u>=a.basisPrice)){e.next=20;break}return e.abrupt("return",o("gap width cannot be > current spot price"));case 20:return i.gapFactor=u,e.abrupt("break",25);case 22:return i.gapFactor=this.gapRanges.gapPercent,e.abrupt("break",25);case 24:i.gapFactor=this.gapRanges.gapMultiplier;case 25:if(h="/api/createbot",null===this.editProgram){e.next=31;break}l.programID=this.editProgram.programID,h="/api/updatebotprogram",e.next=36;break;case 31:if(this.currentReport&&0!==this.currentReport.basisPrice){e.next=36;break}if(0!==this.specifiedPrice){e.next=35;break}return o("price must be set manually"),e.abrupt("return");case 35:l.program.manualRate=this.specifiedPrice;case 36:return d=xn().loading(n.botCreator),e.next=39,yn(h,l);case 39:if(p=e.sent,d(),xn().checkResponse(p)){e.next=45;break}return n.createErr.textContent=p.msg,zt.show(n.createErr),e.abrupt("return");case 45:this.leaveEditMode(),n.lotsInput.value="";case 47:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"newWalletCreated",value:function(e){var t=this.currentMarket;if(e===t.baseid)this.setCreationBase(t.basesymbol);else{if(e!==t.quoteid)return;this.setCreationQuote(t.quotesymbol)}this.closePopups()}}]),m}(fn);function So(){for(var e=[],t=function(e){return Object.values(e.markets).map((function(t){return Object.assign({host:e.host},t)}))},n=0,r=Object.values(xn().user.exchanges);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,o=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw o}}}}function Oo(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n-1:0),a=1;adocument.body.offsetWidth&&(r=document.body.offsetWidth-t.tooltip.offsetWidth-5),t.tooltip.style.left="".concat(r,"px"),t.tooltip.style.top="".concat(n.bodyTop-t.tooltip.offsetHeight-5,"px")})),Po(e,"mouseleave",(function(){t.tooltip.style.left="-10000px"}))}))}},{key:"attachHeader",value:function(){var e=this;this.header=To(document.body,"header"),this.headerSpace=zt.idel(this.header,"headerSpace"),this.popupNotes=To(document.body,"popupNotes"),this.popupTmpl=zt.tmplElement(this.popupNotes,"note"),this.popupTmpl?this.popupTmpl.remove():console.error("popupTmpl element not found"),this.tooltip=To(document.body,"tooltip");var t=this.page=zt.idDescendants(this.header);t.noteTmpl.removeAttribute("id"),t.noteTmpl.remove(),t.pokeTmpl.removeAttribute("id"),t.pokeTmpl.remove(),t.loader.remove(),zt.show(t.loader),Po(t.noteBell,"click",o(w().mark((function n(){var r,a,o;return w().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:zt.hide(t.pokeList),zt.show(t.noteList),e.ackNotes(),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),e.showDropdown(t.noteBell,t.noteBox),zt.hide(t.noteIndicator),r=Do(e.notes);try{for(r.s();!(a=r.n()).done;)(o=a.value).acked&&o.el.classList.remove("firstview")}catch(e){r.e(e)}finally{r.f()}e.setNoteTimes(t.noteList),e.setNoteTimes(t.pokeList),e.storeNotes();case 12:case"end":return n.stop()}}),n)})))),Po(t.burgerIcon,"click",(function(){zt.hide(t.logoutErr),e.showDropdown(t.burgerIcon,t.profileBox)})),Po(t.innerNoteIcon,"click",(function(){zt.hide(t.noteBox)})),Po(t.innerBurgerIcon,"click",(function(){zt.hide(t.profileBox)})),Po(t.profileSignout,"click",o(w().mark((function t(){return w().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,e.signOut();case 2:return t.abrupt("return",t.sent);case 3:case"end":return t.stop()}}),t)})))),Po(t.pokeCat,"click",(function(){e.setNoteTimes(t.pokeList),t.pokeCat.classList.add("active"),t.noteCat.classList.remove("active"),zt.hide(t.noteList),zt.show(t.pokeList),e.ackNotes()})),Po(t.noteCat,"click",(function(){e.setNoteTimes(t.noteList),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),zt.hide(t.pokeList),zt.show(t.noteList),e.ackNotes()}))}},{key:"showDropdown",value:function(e,t){var n=this,r=e.getBoundingClientRect();zt.hide(this.page.noteBox,this.page.profileBox),zt.show(t),t.style.right="".concat(window.innerWidth-r.left-r.width+5,"px"),t.style.top="".concat(r.top-4,"px"),Po(document,"click",(function e(r){zt.mouseInElement(r,t)||(zt.hide(t),Lo(document,"click",e),t===n.page.noteBox&&zt.isDisplayed(n.page.noteList)&&n.ackNotes())}))}},{key:"ackNotes",value:function(){var e,t=[],n=Do(this.notes);try{for(n.s();!(e=n.n()).done;){var r=e.value;r.acked?r.el.classList.remove("firstview"):(r.acked=!0,r.id&&r.severity>2&&t.push(r.id))}}catch(e){n.e(e)}finally{n.f()}t.length&&Yr.request("acknotes",t),zt.hide(this.page.noteIndicator)}},{key:"setNoteTimes",value:function(e){for(var t=0,n=Array.from(e.children);t0?zt.show(e.marketsMenuEntry):zt.hide(e.marketsMenuEntry)}}},{key:"attachCommon",value:function(e){this.bindInternalNavigation(e)}},{key:"updateBondConfs",value:function(e,t,n,r){var a=this.exchanges[e],o=this.assets[r].symbol;a.pendingBonds[t]={confs:n,assetID:r,symbol:o}}},{key:"updateTier",value:function(e,t){this.exchanges[e].tier=t}},{key:"handleBondNote",value:function(e){switch(e.topic){case"RegUpdate":null!==e.coinID&&this.updateBondConfs(e.dex,e.coinID,e.confirmations,e.asset);break;case"BondConfirmed":null!==e.tier&&this.updateTier(e.dex,e.tier)}}},{key:"setNotes",value:function(e){this.log("notes","setNotes",e),this.notes=[],zt.empty(this.page.noteList);for(var t=0;t=0&&R.splice(D,1):D>=0?R[D]=A.report:R.push(A.report)}}else this.fiatRatesMap=e.fiatRates}},{key:"notify",value:function(e){this.log("notes","notify",e),this.updateUser(e);var t,n=Do(this.noteReceivers);try{for(n.s();!(t=n.n()).done;){var r=t.value[e.type];if(r)try{r(e)}catch(t){console.error("note feeder error:",t.message?t.message:t),console.log(e),console.log(t.stack)}}}catch(e){n.e(e)}finally{n.f()}if(!(e.severity<2)){var a=this.popupTmpl,s=this.popupNotes;if(this.showPopups){var i=a.cloneNode(!0);zt.tmplElement(i,"text").textContent="".concat(e.subject,": ").concat(e.details);var c=zt.tmplElement(i,"indicator");for(2===e.severity?zt.hide(c):Uo(c,e.severity),s.appendChild(i);s.children.length>5;)s.removeChild(s.firstChild);setTimeout(o(w().mark((function e(){return w().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,zt.animate(500,(function(e){i.style.opacity=String(1-e)}));case 2:i.remove();case 3:case"end":return e.stop()}}),e)}))),6e3)}2===e.severity?this.prependPokeElement(e):this.prependNoteElement(e)}}},{key:"registerNoteFeeder",value:function(e){this.noteReceivers.push(e)}},{key:"log",value:function(e){for(var t,n=arguments.length,r=new Array(n>1?n-1:0),a=1;a100;)this.pokes.shift();this.prependListElement(this.page.pokeList,a,n)}},{key:"prependNoteElement",value:function(e,t){var n=r(this.makeNote(e),2),a=n[0],o=n[1];for(this.notes.push(o);this.notes.length>100;)this.notes.shift();var s=this.page.noteList;if(this.prependListElement(s,o,a),t||this.storeNotes(),!(0===this.notes.length||zt.isDisplayed(this.page.noteBox)&&zt.isDisplayed(s))){var i=0,c=this.notes.reduce((function(e,t){return t.acked||i++,!t.acked&&t.severity>e?t.severity:e}),0),l=this.page.noteIndicator;Uo(l,c),i?(l.textContent=String(i>99?"".concat(99,"+"):i),zt.show(l)):zt.hide(l)}}},{key:"prependListElement",value:function(e,t,n){for(n.note=t,e.prepend(n);e.children.length>100;)e.removeChild(e.lastChild);this.setNoteTimes(e)}},{key:"makeNote",value:function(e){var t=this.page.noteTmpl.cloneNode(!0);if(e.severity>2){var n=3===e.severity?"good":4===e.severity?"warn":"bad";zt.safeSelector(t,"div.note-indicator").classList.add(n)}return zt.safeSelector(t,"div.note-subject").textContent=e.subject,zt.safeSelector(t,"div.note-details").textContent=e.details,[t,Ro({el:t},e)]}},{key:"makePoke",value:function(e){var t=this.page.pokeTmpl.cloneNode(!0),n=new Date(e.stamp);return zt.tmplElement(t,"dateTime").textContent="".concat(n.toLocaleDateString(),", ").concat(n.toLocaleTimeString()),zt.tmplElement(t,"details").textContent="".concat(e.subject,": ").concat(e.details),[t,Ro({el:t},e)]}},{key:"loading",value:function(e){var t=this.page.loader.cloneNode(!0);return e.appendChild(t),function(){t.remove()}}},{key:"orders",value:function(e,t){var n=[],r=this.user.exchanges[e].markets[t];return r.orders&&(n=n.concat(r.orders)),r.inflight&&(n=n.concat(r.inflight)),n}},{key:"haveActiveOrders",value:function(e){for(var t=0,n=Object.values(this.user.exchanges);t= 0; --i) {\n var entry = this.tryEntries[i],\n record = entry.completion;\n if (\"root\" === entry.tryLoc) return handle(\"end\");\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\"),\n hasFinally = hasOwn.call(entry, \"finallyLoc\");\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);\n if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);\n } else {\n if (!hasFinally) throw new Error(\"try statement without catch or finally\");\n if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);\n }\n }\n }\n },\n abrupt: function abrupt(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev && hasOwn.call(entry, \"finallyLoc\") && this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n finallyEntry && (\"break\" === type || \"continue\" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null);\n var record = finallyEntry ? finallyEntry.completion : {};\n return record.type = type, record.arg = arg, finallyEntry ? (this.method = \"next\", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record);\n },\n complete: function complete(record, afterLoc) {\n if (\"throw\" === record.type) throw record.arg;\n return \"break\" === record.type || \"continue\" === record.type ? this.next = record.arg : \"return\" === record.type ? (this.rval = this.arg = record.arg, this.method = \"return\", this.next = \"end\") : \"normal\" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel;\n },\n finish: function finish(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel;\n }\n },\n \"catch\": function _catch(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (\"throw\" === record.type) {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n throw new Error(\"illegal catch attempt\");\n },\n delegateYield: function delegateYield(iterable, resultName, nextLoc) {\n return this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n }, \"next\" === this.method && (this.arg = undefined), ContinueSentinel;\n }\n }, exports;\n}\nmodule.exports = _regeneratorRuntime, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _typeof(obj) {\n \"@babel/helpers - typeof\";\n\n return (module.exports = _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) {\n return typeof obj;\n } : function (obj) {\n return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports), _typeof(obj);\n}\nmodule.exports = _typeof, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","// TODO(Babel 8): Remove this file.\n\nvar runtime = require(\"../helpers/regeneratorRuntime\")();\nmodule.exports = runtime;\n\n// Copied from https://github.com/facebook/regenerator/blob/main/packages/runtime/runtime.js#L736=\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n if (typeof globalThis === \"object\") {\n globalThis.regeneratorRuntime = runtime;\n } else {\n Function(\"r\", \"regeneratorRuntime = r\")(runtime);\n }\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export default function _arrayLikeToArray(arr, len) {\n if (len == null || len > arr.length) len = arr.length;\n for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];\n return arr2;\n}","import arrayLikeToArray from \"./arrayLikeToArray.js\";\nexport default function _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);\n}","import arrayWithHoles from \"./arrayWithHoles.js\";\nimport iterableToArrayLimit from \"./iterableToArrayLimit.js\";\nimport unsupportedIterableToArray from \"./unsupportedIterableToArray.js\";\nimport nonIterableRest from \"./nonIterableRest.js\";\nexport default function _slicedToArray(arr, i) {\n return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || unsupportedIterableToArray(arr, i) || nonIterableRest();\n}","export default function _arrayWithHoles(arr) {\n if (Array.isArray(arr)) return arr;\n}","export default function _iterableToArrayLimit(arr, i) {\n var _i = null == arr ? null : \"undefined\" != typeof Symbol && arr[Symbol.iterator] || arr[\"@@iterator\"];\n if (null != _i) {\n var _s,\n _e,\n _x,\n _r,\n _arr = [],\n _n = !0,\n _d = !1;\n try {\n if (_x = (_i = _i.call(arr)).next, 0 === i) {\n if (Object(_i) !== _i) return;\n _n = !1;\n } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);\n } catch (err) {\n _d = !0, _e = err;\n } finally {\n try {\n if (!_n && null != _i[\"return\"] && (_r = _i[\"return\"](), Object(_r) !== _r)) return;\n } finally {\n if (_d) throw _e;\n }\n }\n return _arr;\n }\n}","export default function _nonIterableRest() {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}","function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {\n try {\n var info = gen[key](arg);\n var value = info.value;\n } catch (error) {\n reject(error);\n return;\n }\n if (info.done) {\n resolve(value);\n } else {\n Promise.resolve(value).then(_next, _throw);\n }\n}\nexport default function _asyncToGenerator(fn) {\n return function () {\n var self = this,\n args = arguments;\n return new Promise(function (resolve, reject) {\n var gen = fn.apply(self, args);\n function _next(value) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value);\n }\n function _throw(err) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err);\n }\n _next(undefined);\n });\n };\n}","export default function _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n}","export default function _typeof(obj) {\n \"@babel/helpers - typeof\";\n\n return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) {\n return typeof obj;\n } : function (obj) {\n return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n }, _typeof(obj);\n}","import _typeof from \"./typeof.js\";\nimport toPrimitive from \"./toPrimitive.js\";\nexport default function _toPropertyKey(arg) {\n var key = toPrimitive(arg, \"string\");\n return _typeof(key) === \"symbol\" ? key : String(key);\n}","import _typeof from \"./typeof.js\";\nexport default function _toPrimitive(input, hint) {\n if (_typeof(input) !== \"object\" || input === null) return input;\n var prim = input[Symbol.toPrimitive];\n if (prim !== undefined) {\n var res = prim.call(input, hint || \"default\");\n if (_typeof(res) !== \"object\") return res;\n throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n }\n return (hint === \"string\" ? String : Number)(input);\n}","import toPropertyKey from \"./toPropertyKey.js\";\nfunction _defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor);\n }\n}\nexport default function _createClass(Constructor, protoProps, staticProps) {\n if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n if (staticProps) _defineProperties(Constructor, staticProps);\n Object.defineProperty(Constructor, \"prototype\", {\n writable: false\n });\n return Constructor;\n}","import toPropertyKey from \"./toPropertyKey.js\";\nexport default function _defineProperty(obj, key, value) {\n key = toPropertyKey(key);\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n return obj;\n}","type Locale = Record\n\nexport const ID_NO_PASS_ERROR_MSG = 'ID_NO_PASS_ERROR_MSG'\nexport const ID_NO_APP_PASS_ERROR_MSG = 'ID_NO_APP_PASS_ERROR_MSG'\nexport const ID_SET_BUTTON_BUY = 'ID_SET_BUTTON_BUY'\nexport const ID_SET_BUTTON_SELL = 'ID_SET_BUTTON_SELL'\nexport const ID_OFF = 'ID_OFF'\nexport const ID_READY = 'ID_READY'\nexport const ID_NO_WALLET = 'ID_NO_WALLET'\nexport const ID_DISABLED_MSG = 'ID_DISABLED_MSG'\nexport const ID_WALLET_SYNC_PROGRESS = 'ID_WALLET_SYNC_PROGRESS'\nexport const ID_HIDE_ADDITIONAL_SETTINGS = 'ID_HIDE_ADDITIONAL_SETTINGS'\nexport const ID_SHOW_ADDITIONAL_SETTINGS = 'ID_SHOW_ADDITIONAL_SETTINGS'\nexport const ID_BUY = 'ID_BUY'\nexport const ID_SELL = 'ID_SELL'\nexport const ID_NOT_SUPPORTED = 'ID_NOT_SUPPORTED'\nexport const ID_VERSION_NOT_SUPPORTED = 'ID_VERSION_NOT_SUPPORTED'\nexport const ID_CONNECTION_FAILED = 'ID_CONNECTION_FAILED'\nexport const ID_ORDER_PREVIEW = 'ID_ORDER_PREVIEW'\nexport const ID_CALCULATING = 'ID_CALCULATING'\nexport const ID_ESTIMATE_UNAVAILABLE = 'ID_ESTIMATE_UNAVAILABLE'\nexport const ID_NO_ZERO_RATE = 'ID_NO_ZERO_RATE'\nexport const ID_NO_ZERO_QUANTITY = 'ID_NO_ZERO_QUANTITY'\nexport const ID_TRADE = 'ID_TRADE'\nexport const ID_NO_ASSET_WALLET = 'ID_NO_ASSET_WALLET'\nexport const ID_EXECUTED = 'ID_EXECUTED'\nexport const ID_BOOKED = 'ID_BOOKED'\nexport const ID_CANCELING = 'ID_CANCELING'\nexport const ID_PASSWORD_NOT_MATCH = 'ID_PASSWORD_NOT_MATCH'\nexport const ID_ACCT_UNDEFINED = 'ID_ACCT_UNDEFINED'\nexport const ID_KEEP_WALLET_PASS = 'ID_KEEP_WALLET_PASS'\nexport const ID_NEW_WALLET_PASS = 'ID_NEW_WALLET_PASS'\nexport const ID_LOT = 'ID_LOT'\nexport const ID_LOTS = 'ID_LOTS'\nexport const ID_UNKNOWN = 'ID_UNKNOWN'\nexport const ID_EPOCH = 'ID_EPOCH'\nexport const ID_ORDER_SUBMITTING = 'ID_ORDER_SUBMITTING'\nexport const ID_SETTLING = 'ID_SETTLING'\nexport const ID_NO_MATCH = 'ID_NO_MATCH'\nexport const ID_CANCELED = 'ID_CANCELED'\nexport const ID_REVOKED = 'ID_REVOKED'\nexport const ID_WAITING_FOR_CONFS = 'ID_WAITING_FOR_CONFS'\nexport const ID_NONE_SELECTED = 'ID_NONE_SELECTED' // unused?\nexport const ID_REGISTRATION_FEE_SUCCESS = 'ID_REGISTRATION_FEE_SUCCESS'\nexport const ID_API_ERROR = 'ID_API_ERROR'\nexport const ID_ADD = 'ID_ADD'\nexport const ID_CREATE = 'ID_CREATE'\nexport const ID_SETUP_WALLET = 'ID_SETUP_WALLET'\nexport const ID_WALLET_READY = 'ID_WALLET_READY'\nexport const ID_CHANGE_WALLET_TYPE = 'ID_CHANGE_WALLET_TYPE'\nexport const ID_KEEP_WALLET_TYPE = 'ID_KEEP_WALLET_TYPE'\nexport const WALLET_READY = 'WALLET_READY'\nexport const WALLET_PENDING = 'WALLET_PENDING'\nexport const SETUP_NEEDED = 'SETUP_NEEDED'\nexport const ID_SEND_SUCCESS = 'SEND_SUCCESS'\nexport const ID_RECONFIG_SUCCESS = 'RECONFIG_SUCCESS'\nexport const ID_RESCAN_STARTED = 'RESCAN_STARTED'\nexport const ID_NEW_WALLET_SUCCESS = 'NEW_WALLET_SUCCESS'\nexport const ID_WALLET_UNLOCKED = 'WALLET_UNLOCKED'\nexport const ID_SELLING = 'ID_SELLING'\nexport const ID_BUYING = 'ID_BUYING'\nexport const ID_WALLET_DISABLED_MSG = 'WALLET_DISABLED'\nexport const ID_WALLET_ENABLED_MSG = 'WALLET_ENABLED'\nexport const ID_ACTIVE_ORDERS_ERR_MSG = 'ACTIVE_ORDERS_ERR_MSG'\nexport const ID_AVAILABLE = 'AVAILABLE'\nexport const ID_LOCKED = 'LOCKED'\nexport const ID_IMMATURE = 'IMMATURE'\nexport const ID_FEE_BALANCE = 'FEE_BALANCE'\nexport const ID_CANDLES_LOADING = 'CANDLES_LOADING'\nexport const ID_DEPTH_LOADING = 'DEPTH_LOADING'\nexport const ID_INVALID_ADDRESS_MSG = 'INVALID_ADDRESS_MSG'\nexport const ID_TXFEE_UNSUPPORTED = 'TXFEE_UNSUPPORTED'\nexport const ID_TXFEE_ERR_MSG = 'TXFEE_ERR_MSG'\nexport const ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG = 'ACTIVE_ORDERS_LOGOUT_ERR_MSG'\nexport const ID_INVALID_DATE_ERR_MSG = 'INVALID_DATE_ERR_MSG'\nexport const ID_NO_ARCHIVED_RECORDS = 'NO_ARCHIVED_RECORDS'\nexport const ID_DELETE_ARCHIVED_RECORDS_RESULT = 'DELETE_ARCHIVED_RECORDS_RESULT'\nexport const ID_ARCHIVED_RECORDS_PATH = 'ARCHIVED_RECORDS_PATH'\nexport const ID_DEFAULT = 'DEFAULT'\nexport const ID_ADDED = 'USER_ADDED'\nexport const ID_DISCOVERED = 'DISCOVERED'\nexport const ID_UNSUPPORTED_ASSET_INFO_ERR_MSG = 'UNSUPPORTED_ASSET_INFO_ERR_MSG'\nexport const ID_LIMIT_ORDER = 'LIMIT_ORDER'\nexport const ID_LIMIT_ORDER_IMMEDIATE_TIF = 'LIMIT_ORDER_IMMEDIATE_TIF'\nexport const ID_MARKET_ORDER = 'MARKET_ORDER'\nexport const ID_MATCH_STATUS_NEWLY_MATCHED = 'MATCH_STATUS_NEWLY_MATCHED'\nexport const ID_MATCH_STATUS_MAKER_SWAP_CAST = 'MATCH_STATUS_MAKER_SWAP_CAST'\nexport const ID_MATCH_STATUS_TAKER_SWAP_CAST = 'MATCH_STATUS_TAKER_SWAP_CAST'\nexport const ID_MATCH_STATUS_MAKER_REDEEMED = 'MATCH_STATUS_MAKER_REDEEMED'\nexport const ID_MATCH_STATUS_REDEMPTION_SENT = 'MATCH_STATUS_REDEMPTION_SENT'\nexport const ID_MATCH_STATUS_REDEMPTION_CONFIRMED = 'MATCH_REDEMPTION_CONFIRMED'\nexport const ID_MATCH_STATUS_REVOKED = 'MATCH_STATUS_REVOKED'\nexport const ID_MATCH_STATUS_REFUNDED = 'MATCH_STATUS_REFUNDED'\nexport const ID_MATCH_STATUS_REFUND_PENDING = 'MATCH_STATUS_REFUND_PENDING'\nexport const ID_MATCH_STATUS_REDEEM_PENDING = 'MATCH_STATUS_REDEEM_PENDING'\nexport const ID_MATCH_STATUS_COMPLETE = 'MATCH_STATUS_COMPLETE'\nexport const ID_TAKER_FOUND_MAKER_REDEMPTION = 'TAKER_FOUND_MAKER_REDEMPTION'\nexport const ID_OPEN_WALLET_ERR_MSG = 'OPEN_WALLET_ERR_MSG'\nexport const ID_ORDER_ACCELERATION_FEE_ERR_MSG = 'ORDER_ACCELERATION_FEE_ERR_MSG'\nexport const ID_ORDER_ACCELERATION_ERR_MSG = 'ORDER_ACCELERATION_FEE_ERR_MSG'\nexport const ID_CONNECTED = 'CONNECTED'\nexport const ID_DISCONNECTED = 'DISCONNECTED'\nexport const ID_INVALID_CERTIFICATE = 'INVALID_CERTIFICATE'\nexport const ID_CONFIRMATIONS = 'ID_CONFIRMATIONS'\nexport const ID_TAKER = 'TAKER'\nexport const ID_MAKER = 'MAKER'\nexport const ID_EMPTY_DEX_ADDRESS_MSG = 'EMPTY_DEX_ADDRESS_MSG'\nexport const ID_SELECT_WALLET_FOR_FEE_PAYMENT = 'SELECT_WALLET_FOR_FEE_PAYMENT'\nexport const ID_UNAVAILABLE = 'UNAVAILABLE'\nexport const ID_WALLET_SYNC_FINISHING_UP = 'WALLET_SYNC_FINISHING_UP'\n\nexport const enUS: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'password cannot be empty',\n [ID_NO_APP_PASS_ERROR_MSG]: 'app password cannot be empty',\n [ID_PASSWORD_NOT_MATCH]: 'passwords do not match',\n [ID_SET_BUTTON_BUY]: 'Place order to buy {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Place order to sell {{ asset }}',\n [ID_OFF]: 'off',\n [ID_READY]: 'ready',\n [ID_LOCKED]: 'locked',\n [ID_NO_WALLET]: 'no wallet',\n [ID_WALLET_SYNC_PROGRESS]: 'wallet is {{ syncProgress }}% synced',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'hide additional settings',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'show additional settings',\n [ID_BUY]: 'Buy',\n [ID_SELL]: 'Sell',\n [ID_NOT_SUPPORTED]: '{{ asset }} is not supported',\n [ID_VERSION_NOT_SUPPORTED]: '{{ asset }} (v{{version}}) is not supported',\n [ID_CONNECTION_FAILED]: 'Connection to dex server failed. You can close dexc and try again later or wait for it to reconnect.',\n [ID_ORDER_PREVIEW]: 'Total: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'calculating...',\n [ID_ESTIMATE_UNAVAILABLE]: 'estimate unavailable',\n [ID_NO_ZERO_RATE]: 'zero rate not allowed',\n [ID_NO_ZERO_QUANTITY]: 'zero quantity not allowed',\n [ID_TRADE]: 'trade',\n [ID_NO_ASSET_WALLET]: 'No {{ asset }} wallet',\n [ID_EXECUTED]: 'executed',\n [ID_BOOKED]: 'booked',\n [ID_CANCELING]: 'canceling',\n [ID_ACCT_UNDEFINED]: 'Account undefined.',\n [ID_KEEP_WALLET_PASS]: 'keep current wallet password',\n [ID_NEW_WALLET_PASS]: 'set a new wallet password',\n [ID_LOT]: 'lot',\n [ID_LOTS]: 'lots',\n [ID_UNKNOWN]: 'unknown',\n [ID_EPOCH]: 'epoch',\n [ID_SETTLING]: 'settling',\n [ID_NO_MATCH]: 'no match',\n [ID_CANCELED]: 'canceled',\n [ID_REVOKED]: 'revoked',\n [ID_WAITING_FOR_CONFS]: 'Waiting for confirmations...',\n [ID_NONE_SELECTED]: 'none selected',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Fidelity bond accepted!',\n [ID_API_ERROR]: 'API error',\n [ID_ADD]: 'Add',\n [ID_CREATE]: 'Create',\n [ID_WALLET_READY]: 'Ready',\n [ID_SETUP_WALLET]: 'Setup',\n [ID_CHANGE_WALLET_TYPE]: 'change the wallet type',\n [ID_KEEP_WALLET_TYPE]: 'don\\'t change the wallet type',\n [WALLET_READY]: 'Wallet Ready',\n [SETUP_NEEDED]: 'Setup Needed',\n [WALLET_PENDING]: 'Creating Wallet',\n [ID_SEND_SUCCESS]: '{{ assetName }} Sent!',\n [ID_RECONFIG_SUCCESS]: 'Wallet Reconfigured!',\n [ID_RESCAN_STARTED]: 'Wallet Rescan Running',\n [ID_NEW_WALLET_SUCCESS]: '{{ assetName }} Wallet Created!',\n [ID_WALLET_UNLOCKED]: 'Wallet Unlocked',\n [ID_SELLING]: 'Selling',\n [ID_BUYING]: 'Buying',\n [ID_WALLET_ENABLED_MSG]: '{{ assetName }} Wallet Enabled',\n [ID_WALLET_DISABLED_MSG]: '{{ assetName }} Wallet Disabled',\n [ID_DISABLED_MSG]: 'wallet is disabled',\n [ID_ACTIVE_ORDERS_ERR_MSG]: '{{ assetName }} wallet is actively managing orders',\n [ID_AVAILABLE]: 'available',\n [ID_IMMATURE]: 'immature',\n [ID_FEE_BALANCE]: 'fee balance',\n [ID_CANDLES_LOADING]: 'waiting for candlesticks',\n [ID_DEPTH_LOADING]: 'retrieving depth data',\n [ID_INVALID_ADDRESS_MSG]: 'invalid address: {{ address }}',\n [ID_TXFEE_UNSUPPORTED]: 'fee estimation is not supported for this wallet type',\n [ID_TXFEE_ERR_MSG]: 'fee estimation failed: {{ err }}',\n [ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG]: 'cannot logout with active orders',\n [ID_INVALID_DATE_ERR_MSG]: 'error: invalid date or time',\n [ID_NO_ARCHIVED_RECORDS]: 'No archived records found',\n [ID_DELETE_ARCHIVED_RECORDS_RESULT]: 'Message: {{ nRecords }} archived records has been deleted',\n [ID_ARCHIVED_RECORDS_PATH]: 'File Location: {{ path }}',\n [ID_ORDER_SUBMITTING]: 'submitting',\n [ID_DEFAULT]: 'Default',\n [ID_ADDED]: 'Added',\n [ID_DISCOVERED]: 'Discovered',\n [ID_UNSUPPORTED_ASSET_INFO_ERR_MSG]: 'no supported asset info for id = {{ assetID }}, and no exchange info provided',\n [ID_LIMIT_ORDER]: 'limit',\n [ID_LIMIT_ORDER_IMMEDIATE_TIF]: 'limit (i)',\n [ID_MARKET_ORDER]: 'market',\n [ID_MATCH_STATUS_NEWLY_MATCHED]: 'Newly Matched',\n [ID_MATCH_STATUS_MAKER_SWAP_CAST]: 'Maker Swap Sent',\n [ID_MATCH_STATUS_TAKER_SWAP_CAST]: 'Taker Swap Sent',\n [ID_MATCH_STATUS_MAKER_REDEEMED]: 'Maker Redeemed',\n [ID_MATCH_STATUS_REDEMPTION_SENT]: 'Redemption Sent',\n [ID_MATCH_STATUS_REVOKED]: 'Revoked - {{ status }}',\n [ID_MATCH_STATUS_REFUND_PENDING]: 'Refund PENDING',\n [ID_MATCH_STATUS_REFUNDED]: 'Refunded',\n [ID_MATCH_STATUS_REDEEM_PENDING]: 'Redeem PENDING',\n [ID_MATCH_STATUS_REDEMPTION_CONFIRMED]: 'Redemption Confirmed',\n [ID_MATCH_STATUS_COMPLETE]: 'Complete',\n [ID_TAKER_FOUND_MAKER_REDEMPTION]: 'redeemed by {{ makerAddr }}',\n [ID_OPEN_WALLET_ERR_MSG]: 'Error opening wallet: {{ msg }}',\n [ID_ORDER_ACCELERATION_FEE_ERR_MSG]: 'Error estimating acceleration fee: {{ msg }}',\n [ID_ORDER_ACCELERATION_ERR_MSG]: 'Error accelerating order: {{ msg }}',\n [ID_CONNECTED]: 'Connected',\n [ID_DISCONNECTED]: 'Disconnected',\n [ID_INVALID_CERTIFICATE]: 'Invalid Certificate',\n [ID_CONFIRMATIONS]: 'confirmations',\n [ID_TAKER]: 'Taker',\n [ID_MAKER]: 'Maker',\n [ID_UNAVAILABLE]: 'unavailable',\n [ID_EMPTY_DEX_ADDRESS_MSG]: 'DEX address cannot be empty',\n [ID_SELECT_WALLET_FOR_FEE_PAYMENT]: 'Select a valid wallet to post a bond',\n [ID_WALLET_SYNC_FINISHING_UP]: 'finishing up'\n}\n\nexport const ptBR: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'senha não pode ser vazia',\n [ID_NO_APP_PASS_ERROR_MSG]: 'senha do app não pode ser vazia',\n [ID_PASSWORD_NOT_MATCH]: 'senhas diferentes',\n [ID_SET_BUTTON_BUY]: 'Ordem de compra de {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Ordem de venda de {{ asset }}',\n [ID_OFF]: 'desligar',\n [ID_READY]: 'pronto',\n [ID_LOCKED]: 'trancado',\n [ID_NO_WALLET]: 'sem carteira',\n [ID_WALLET_SYNC_PROGRESS]: 'carteira está {{ syncProgress }}% sincronizada',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'esconder configurações adicionais',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'mostrar configurações adicionais',\n [ID_BUY]: 'Comprar',\n [ID_SELL]: 'Vender',\n [ID_NOT_SUPPORTED]: '{{ asset }} não tem suporte',\n [ID_CONNECTION_FAILED]: 'Conexão ao server dex falhou. Pode fechar dexc e tentar novamente depois ou esperar para tentar se reconectar.',\n [ID_ORDER_PREVIEW]: 'Total: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'calculando...',\n [ID_ESTIMATE_UNAVAILABLE]: 'estimativa indisponível',\n [ID_NO_ZERO_RATE]: 'taxa não pode ser zero',\n [ID_NO_ZERO_QUANTITY]: 'quantidade não pode ser zero',\n [ID_TRADE]: 'troca',\n [ID_NO_ASSET_WALLET]: 'Sem carteira {{ asset }}',\n [ID_EXECUTED]: 'executado',\n [ID_BOOKED]: 'reservado',\n [ID_CANCELING]: 'cancelando',\n [ID_ACCT_UNDEFINED]: 'conta não definida.',\n [ID_KEEP_WALLET_PASS]: 'manter senha da carteira',\n [ID_NEW_WALLET_PASS]: 'definir nova senha para carteira',\n [ID_LOT]: 'lote',\n [ID_LOTS]: 'lotes',\n [ID_UNKNOWN]: 'desconhecido',\n [ID_EPOCH]: 'epoque',\n [ID_SETTLING]: 'assentando',\n [ID_NO_MATCH]: 'sem combinações',\n [ID_CANCELED]: 'cancelado',\n [ID_REVOKED]: 'revocado',\n [ID_WAITING_FOR_CONFS]: 'Esperando confirmações...',\n [ID_NONE_SELECTED]: 'nenhuma selecionado',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Sucesso no pagamento da taxa de registro!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'Erro de API',\n [ID_ADD]: 'Adicionar',\n [ID_CREATE]: 'Criar',\n [ID_WALLET_READY]: 'Escolher',\n [ID_SETUP_WALLET]: 'Configurar',\n [ID_CHANGE_WALLET_TYPE]: 'trocar o tipo de carteira',\n [ID_KEEP_WALLET_TYPE]: 'Não trocara tipo de carteira',\n [WALLET_READY]: 'Carteira Pronta',\n [SETUP_NEEDED]: 'Configuração Necessária',\n [ID_AVAILABLE]: 'disponível',\n [ID_IMMATURE]: 'imaturo'\n}\n\nexport const zhCN: Locale = {\n [ID_NO_PASS_ERROR_MSG]: '密码不能为空',\n [ID_NO_APP_PASS_ERROR_MSG]: '应用密码不能为空',\n [ID_PASSWORD_NOT_MATCH]: '密码不相同',\n [ID_SET_BUTTON_BUY]: '来自{{ asset }}的买入订单',\n [ID_SET_BUTTON_SELL]: '来自{{ asset }}的卖出订单',\n [ID_OFF]: '关闭',\n [ID_READY]: '准备就绪', // alt. 准备好\n [ID_LOCKED]: '锁',\n [ID_NO_WALLET]: '未连接钱包', // alt. 没有钱包\n [ID_WALLET_SYNC_PROGRESS]: '钱包同步进度{{ syncProgress }}%',\n [ID_HIDE_ADDITIONAL_SETTINGS]: '隐藏其它设置',\n [ID_SHOW_ADDITIONAL_SETTINGS]: '显示其它设置',\n [ID_BUY]: '买入',\n [ID_SELL]: '卖出',\n [ID_NOT_SUPPORTED]: '{{ asset }}不受支持',\n [ID_CONNECTION_FAILED]: '连接到服务器 dex 失败。您可以关闭 dexc 并稍后重试或等待尝试重新连接。',\n [ID_ORDER_PREVIEW]: '总计: {{ total }} {{ asset }}',\n [ID_CALCULATING]: '计算中...',\n [ID_ESTIMATE_UNAVAILABLE]: '估计不可用',\n [ID_NO_ZERO_RATE]: '汇率不能为零',\n [ID_NO_ZERO_QUANTITY]: '数量不能为零',\n [ID_TRADE]: '交易',\n [ID_NO_ASSET_WALLET]: '没有钱包 {{ asset }}',\n [ID_EXECUTED]: '执行',\n [ID_BOOKED]: '保留',\n [ID_CANCELING]: '取消',\n [ID_ACCT_UNDEFINED]: '帐户未定义。',\n [ID_KEEP_WALLET_PASS]: '保留钱包密码',\n [ID_NEW_WALLET_PASS]: '设置新的钱包密码',\n [ID_LOT]: '批处理',\n [ID_LOTS]: '批', // alt. 很多\n [ID_EPOCH]: '时间',\n [ID_API_ERROR]: '接口错误',\n [ID_ADD]: '添加',\n [ID_CREATE]: '创建',\n [ID_AVAILABLE]: '可用',\n [ID_IMMATURE]: '不成熟'\n}\n\nexport const plPL: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'hasło nie może być puste',\n [ID_NO_APP_PASS_ERROR_MSG]: 'hasło aplikacji nie może być puste',\n [ID_PASSWORD_NOT_MATCH]: 'hasła nie są jednakowe',\n [ID_SET_BUTTON_BUY]: 'Złóż zlecenie, aby kupić {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Złóż zlecenie, aby sprzedać {{ asset }}',\n [ID_OFF]: 'wyłączony',\n [ID_READY]: 'gotowy',\n [ID_LOCKED]: 'zablokowany',\n [ID_NO_WALLET]: 'brak portfela',\n [ID_WALLET_SYNC_PROGRESS]: 'portfel zsynchronizowany w {{ syncProgress }}%',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'ukryj dodatkowe ustawienia',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'pokaż dodatkowe ustawienia',\n [ID_BUY]: 'Kup',\n [ID_SELL]: 'Sprzedaj',\n [ID_NOT_SUPPORTED]: '{{ asset }} nie jest wspierany',\n [ID_CONNECTION_FAILED]: 'Połączenie z serwerem dex nie powiodło się. Możesz zamknąć dexc i spróbować ponownie później, lub poczekać na wznowienie połączenia.',\n [ID_ORDER_PREVIEW]: 'W sumie: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'obliczanie...',\n [ID_ESTIMATE_UNAVAILABLE]: 'brak szacunkowego wyliczenia',\n [ID_NO_ZERO_RATE]: 'zero nie może być ceną',\n [ID_NO_ZERO_QUANTITY]: 'zero nie może być ilością',\n [ID_TRADE]: 'handluj',\n [ID_NO_ASSET_WALLET]: 'Brak portfela {{ asset }}',\n [ID_EXECUTED]: 'wykonano',\n [ID_BOOKED]: 'zapisano',\n [ID_CANCELING]: 'anulowanie',\n [ID_ACCT_UNDEFINED]: 'Niezdefiniowane konto.',\n [ID_KEEP_WALLET_PASS]: 'zachowaj obecne hasło portfela',\n [ID_NEW_WALLET_PASS]: 'ustaw nowe hasło portfela',\n [ID_LOT]: 'lot',\n [ID_LOTS]: 'loty(ów)',\n [ID_UNKNOWN]: 'nieznane',\n [ID_EPOCH]: 'epoka',\n [ID_SETTLING]: 'rozliczanie',\n [ID_NO_MATCH]: 'brak spasowania',\n [ID_CANCELED]: 'anulowano',\n [ID_REVOKED]: 'unieważniono',\n [ID_WAITING_FOR_CONFS]: 'Oczekiwanie na potwierdzenia...',\n [ID_NONE_SELECTED]: 'brak zaznaczenia',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Płatność rejestracyjna powiodła się!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'błąd API',\n [ID_ADD]: 'Dodaj',\n [ID_CREATE]: 'Utwórz',\n [ID_WALLET_READY]: 'Gotowy',\n [ID_SETUP_WALLET]: 'Konfiguracja',\n [ID_CHANGE_WALLET_TYPE]: 'zmień typ portfela',\n [ID_KEEP_WALLET_TYPE]: 'nie zmieniaj typu portfela',\n [WALLET_READY]: 'Portfel jest gotowy',\n [SETUP_NEEDED]: 'Potrzebna konfiguracja',\n [ID_AVAILABLE]: 'dostępne',\n [ID_IMMATURE]: 'niedojrzałe'\n}\n\nexport const deDE: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'Passwort darf nicht leer sein',\n [ID_NO_APP_PASS_ERROR_MSG]: 'App-Passwort darf nicht leer sein',\n [ID_PASSWORD_NOT_MATCH]: 'Passwörter stimmen nicht überein',\n [ID_SET_BUTTON_BUY]: 'Platziere Auftrag zum Kauf von {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Platziere Auftrag zum Verkauf von {{ asset }}',\n [ID_OFF]: 'aus',\n [ID_READY]: 'bereit',\n [ID_LOCKED]: 'gesperrt',\n [ID_NO_WALLET]: 'kein Wallet',\n [ID_WALLET_SYNC_PROGRESS]: 'Wallet ist zu {{ syncProgress }}% synchronisiert',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'zusätzliche Einstellungen ausblenden',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'zusätzliche Einstellungen anzeigen',\n [ID_BUY]: 'Kaufen',\n [ID_SELL]: 'Verkaufen',\n [ID_NOT_SUPPORTED]: '{{ asset }} wird nicht unterstützt',\n [ID_CONNECTION_FAILED]: 'Die Verbindung zum Dex-Server fehlgeschlagen. Du kannst dexc schließen und es später erneut versuchen oder warten bis die Verbindung wiederhergestellt ist.',\n [ID_ORDER_PREVIEW]: 'Insgesamt: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'kalkuliere...',\n [ID_ESTIMATE_UNAVAILABLE]: 'Schätzung nicht verfügbar',\n [ID_NO_ZERO_RATE]: 'Null-Satz nicht erlaubt',\n [ID_NO_ZERO_QUANTITY]: 'Null-Menge nicht erlaubt',\n [ID_TRADE]: 'Handel',\n [ID_NO_ASSET_WALLET]: 'Kein {{ asset }} Wallet',\n [ID_EXECUTED]: 'ausgeführt',\n [ID_BOOKED]: 'gebucht',\n [ID_CANCELING]: 'Abbruch',\n [ID_ACCT_UNDEFINED]: 'Account undefiniert.',\n [ID_KEEP_WALLET_PASS]: 'aktuelles Passwort für das Wallet behalten',\n [ID_NEW_WALLET_PASS]: 'ein neues Passwort für das Wallet festlegen',\n [ID_LOT]: 'Lot',\n [ID_LOTS]: 'Lots',\n [ID_UNKNOWN]: 'unbekannt',\n [ID_EPOCH]: 'Epoche',\n [ID_SETTLING]: 'Abwicklung',\n [ID_NO_MATCH]: 'kein Match',\n [ID_CANCELED]: 'abgebrochen',\n [ID_REVOKED]: 'widerrufen',\n [ID_WAITING_FOR_CONFS]: 'Warten auf Bestätigungen...',\n [ID_NONE_SELECTED]: 'keine ausgewählt',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Zahlung der Registrierungsgebühr erfolgreich!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'API Fehler',\n [ID_ADD]: 'Hinzufügen',\n [ID_CREATE]: 'Erstellen',\n [ID_WALLET_READY]: 'Bereit',\n [ID_SETUP_WALLET]: 'Einrichten',\n [ID_CHANGE_WALLET_TYPE]: 'den Wallet-Typ ändern',\n [ID_KEEP_WALLET_TYPE]: 'den Wallet-Typ nicht ändern',\n [WALLET_READY]: 'Wallet bereit',\n [SETUP_NEEDED]: 'Einrichtung erforderlich',\n [WALLET_PENDING]: 'Erstelle Wallet',\n [ID_SEND_SUCCESS]: '{{ assetName }} gesendet!',\n [ID_RECONFIG_SUCCESS]: 'Wallet neu konfiguriert!',\n [ID_RESCAN_STARTED]: 'Wallet Rescan läuft',\n [ID_NEW_WALLET_SUCCESS]: '{{ assetName }} Wallet erstellt!',\n [ID_WALLET_UNLOCKED]: 'Wallet entsperrt'\n}\n\nexport const ar: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'لا يمكن أن تكون كلمة المرور فارغة',\n [ID_NO_APP_PASS_ERROR_MSG]: 'لا يمكن أن تكون كلمة مرور التطبيق فارغة',\n [ID_PASSWORD_NOT_MATCH]: 'كلمات المرور غير متطابقة',\n [ID_SET_BUTTON_BUY]: 'ضع طلبًا للشراء {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'ضع طلبًا للبيع {{ asset }}',\n [ID_OFF]: 'إيقاف',\n [ID_READY]: 'متوقف',\n [ID_LOCKED]: 'مقفل',\n [ID_NO_WALLET]: 'لا توجد أي محفظة',\n [ID_WALLET_SYNC_PROGRESS]: 'تمت مزامنة {{ syncProgress }}% المحفظة',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'إخفاء الإعدادات الإضافية',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'عرض الإعدادات الإضافية',\n [ID_BUY]: 'شراء',\n [ID_SELL]: 'بيع',\n [ID_NOT_SUPPORTED]: '{{ asset }} غير مدعوم',\n [ID_CONNECTION_FAILED]: 'فشل الاتصال بخادم dex. يمكنك إغلاق dexc والمحاولة مرة أخرى لاحقًا أو انتظار إعادة الاتصال.',\n [ID_ORDER_PREVIEW]: 'إجمالي: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'جاري الحساب ...',\n [ID_ESTIMATE_UNAVAILABLE]: 'التقديرات غير متاحة',\n [ID_NO_ZERO_RATE]: 'معدل الصفر غير مسموح به',\n [ID_NO_ZERO_QUANTITY]: 'غير مسموح بالكمية الصفرية',\n [ID_TRADE]: 'التداول',\n [ID_NO_ASSET_WALLET]: 'لا توجد {{ asset }} محفظة',\n [ID_EXECUTED]: 'تم تنفيذها',\n [ID_BOOKED]: 'تم الحجز',\n [ID_CANCELING]: 'جارٍ الإلغاء',\n [ID_ACCT_UNDEFINED]: 'حساب غير محدد.',\n [ID_KEEP_WALLET_PASS]: 'احتفظ بكلمة مرور المحفظة الحالية',\n [ID_NEW_WALLET_PASS]: 'قم بتعيين كلمة مرور جديدة للمحفظة',\n [ID_LOT]: 'الحصة',\n [ID_LOTS]: 'الحصص',\n [ID_UNKNOWN]: 'غير معروف',\n [ID_EPOCH]: 'الحقبة الزمنية',\n [ID_SETTLING]: 'الإعدادات',\n [ID_NO_MATCH]: 'غير متطابقة',\n [ID_CANCELED]: 'ملغاة',\n [ID_REVOKED]: 'مستعادة',\n [ID_WAITING_FOR_CONFS]: 'في انتظار التأكيدات ...',\n [ID_NONE_SELECTED]: 'لم يتم تحديد أي شيء',\n [ID_REGISTRATION_FEE_SUCCESS]: 'تم دفع رسوم التسجيل بنجاح!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'خطأ في واجهة برمجة التطبيقات',\n [ID_ADD]: 'إضافة',\n [ID_CREATE]: 'إنشاء',\n [ID_WALLET_READY]: 'جاهزة',\n [ID_SETUP_WALLET]: 'إعداد',\n [ID_CHANGE_WALLET_TYPE]: 'تغيير نوع المحفظة',\n [ID_KEEP_WALLET_TYPE]: 'لا تغير نوع المحفظة',\n [WALLET_READY]: 'المحفظة جاهزة',\n [SETUP_NEEDED]: 'الإعداد مطلوب',\n [WALLET_PENDING]: 'إنشاء المحفظة',\n [ID_SEND_SUCCESS]: '{{ assetName }} تم الإرسال!',\n [ID_RECONFIG_SUCCESS]: 'تمت إعادة تهيئة المحفظة!!',\n [ID_RESCAN_STARTED]: 'إعادة فحص المحفظة قيد التشغيل',\n [ID_NEW_WALLET_SUCCESS]: '{{ assetName }} تم إنشاء المحفظة!',\n [ID_WALLET_UNLOCKED]: 'المحفظة غير مقفلة',\n [ID_SELLING]: 'البيع',\n [ID_BUYING]: 'Bالشراء',\n [ID_WALLET_ENABLED_MSG]: '{{ assetName }} المحفظة ممكنة',\n [ID_WALLET_DISABLED_MSG]: '{{ assetName }} المحفظة معطلة',\n [ID_DISABLED_MSG]: 'تم تعطيل المحفظة',\n [ID_ACTIVE_ORDERS_ERR_MSG]: '{{ assetName }} تدير المحفظة الطلبات بفعالية'\n}\n\nconst localesMap: Record = {\n 'en-us': enUS,\n 'pt-br': ptBR,\n 'zh-cn': zhCN,\n 'pl-pl': plPL,\n 'de-de': deDE,\n 'ar': ar\n}\n\n/* locale will hold the locale loaded via setLocale. */\nlet locale: Locale\n\nconst defaultLocale = enUS\n\n/*\n * setLocale read the language tag from the current document's html element lang\n * attribute and sets the locale. setLocale should be called once by the\n * application before prep is used.\n*/\nexport function setLocale () { locale = localesMap[document.documentElement.lang.toLowerCase()] }\n\n/* prep will format the message to the current locale. */\nexport function prep (k: string, args?: Record) {\n return stringTemplateParser(locale[k] || defaultLocale[k], args || {})\n}\n\n/*\n * stringTemplateParser is a template string matcher, where expression is any\n * text. It switches what is inside double brackets (e.g. 'buy {{ asset }}')\n * for the value described into args. args is an object with keys\n * equal to the placeholder keys. (e.g. {\"asset\": \"dcr\"}).\n * So that will be switched for: 'asset dcr'.\n */\nfunction stringTemplateParser (expression: string, args: Record) {\n // templateMatcher matches any text which:\n // is some {{ text }} between two brackets, and a space between them.\n // It is global, therefore it will change all occurrences found.\n // text can be anything, but brackets '{}' and space '\\s'\n const templateMatcher = /{{\\s?([^{}\\s]*)\\s?}}/g\n return expression.replace(templateMatcher, (_, value) => args[value])\n}\n\nwindow.localeDiscrepancies = () => {\n const ref = enUS\n for (const [lang, dict] of Object.entries(localesMap)) {\n if (dict === ref) continue\n for (const [k, s] of Object.entries(ref)) {\n if (!dict[k]) console.log(`${lang} needs a tranlation for: ${s}`)\n }\n }\n}\n","import * as intl from './locales'\nimport {\n UnitInfo,\n LayoutMetrics,\n WalletState,\n PageElement\n} from './registry'\n\nconst parser = new window.DOMParser()\n\nconst FPS = 30\n\nconst BipIDs: Record = {\n 0: 'btc',\n 42: 'dcr',\n 2: 'ltc',\n 22: 'mona',\n 28: 'vtc',\n 3: 'doge',\n 145: 'bch',\n 60: 'eth',\n 133: 'zec',\n 60000: 'dextt.eth',\n 60001: 'usdc.eth'\n}\n\nconst BipSymbols = Object.values(BipIDs)\n\nconst intFormatter = new Intl.NumberFormat((navigator.languages as string[]))\n\nconst threeSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 3,\n maximumSignificantDigits: 3\n})\n\nconst fiveSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 5,\n maximumSignificantDigits: 5\n})\n\n/* A cache for formatters used for Doc.formatCoinValue. */\nconst decimalFormatters = {}\n\n/*\n * decimalFormatter gets the formatCoinValue formatter for the specified decimal\n * precision.\n */\nfunction decimalFormatter (prec: number) {\n return formatter(decimalFormatters, 2, prec)\n}\n\n/* A cache for formatters used for Doc.formatFullPrecision. */\nconst fullPrecisionFormatters = {}\n\n/*\n * fullPrecisionFormatter gets the formatFullPrecision formatter for the\n * specified decimal precision.\n */\nfunction fullPrecisionFormatter (prec: number) {\n return formatter(fullPrecisionFormatters, prec, prec)\n}\n\n/*\n * formatter gets the formatter from the supplied cache if it already exists,\n * else creates it.\n */\nfunction formatter (formatters: Record, min: number, max: number): Intl.NumberFormat {\n const k = `${min}-${max}`\n let fmt = formatters[k]\n if (!fmt) {\n fmt = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumFractionDigits: min,\n maximumFractionDigits: max\n })\n formatters[k] = fmt\n }\n return fmt\n}\n\n/*\n * convertToConventional converts the value in atomic units to conventional\n * units.\n */\nfunction convertToConventional (v: number, unitInfo?: UnitInfo) {\n let prec = 8\n if (unitInfo) {\n const f = unitInfo.conventional.conversionFactor\n v /= f\n prec = Math.round(Math.log10(f))\n }\n return [v, prec]\n}\n\n// Helpers for working with the DOM.\nexport default class Doc {\n /*\n * idel is the element with the specified id that is the descendent of the\n * specified node.\n */\n static idel (el: Document | Element, id: string): HTMLElement {\n return el.querySelector(`#${id}`) as HTMLElement\n }\n\n /* bind binds the function to the event for the element. */\n static bind (el: EventTarget, ev: string, f: EventListenerOrEventListenerObject): void {\n el.addEventListener(ev, f)\n }\n\n /* unbind removes the handler for the event from the element. */\n static unbind (el: EventTarget, ev: string, f: (e: Event) => void): void {\n el.removeEventListener(ev, f)\n }\n\n /* noderize creates a Document object from a string of HTML. */\n static noderize (html: string): Document {\n return parser.parseFromString(html, 'text/html')\n }\n\n /*\n * mouseInElement returns true if the position of mouse event, e, is within\n * the bounds of the specified element or any of its descendents.\n */\n static mouseInElement (e: MouseEvent, el: HTMLElement): boolean {\n if (el.contains(e.target as Node)) return true\n const rect = el.getBoundingClientRect()\n return e.pageX >= rect.left && e.pageX <= rect.right &&\n e.pageY >= rect.top && e.pageY <= rect.bottom\n }\n\n /*\n * layoutMetrics gets information about the elements position on the page.\n */\n static layoutMetrics (el: HTMLElement): LayoutMetrics {\n const box = el.getBoundingClientRect()\n const docEl = document.documentElement\n const top = box.top + docEl.scrollTop\n const left = box.left + docEl.scrollLeft\n const w = el.offsetWidth\n const h = el.offsetHeight\n return {\n bodyTop: top,\n bodyLeft: left,\n width: w,\n height: h,\n centerX: left + w / 2,\n centerY: top + h / 2\n }\n }\n\n static descendentMetrics (parent: PageElement, kid: PageElement): LayoutMetrics {\n const parentMetrics = Doc.layoutMetrics(parent)\n const kidMetrics = Doc.layoutMetrics(kid)\n return {\n bodyTop: kidMetrics.bodyTop - parentMetrics.bodyTop,\n bodyLeft: kidMetrics.bodyLeft - parentMetrics.bodyLeft,\n width: kidMetrics.width,\n height: kidMetrics.height,\n centerX: kidMetrics.centerX - parentMetrics.bodyLeft,\n centerY: kidMetrics.centerY - parentMetrics.bodyTop\n }\n }\n\n /* empty removes all child nodes from the specified element. */\n static empty (...els: Element[]) {\n for (const el of els) while (el.firstChild) el.removeChild(el.firstChild)\n }\n\n /*\n * setContent removes all child nodes from the specified element and appends\n * passed elements.\n */\n static setContent (ancestor: PageElement, ...kids: PageElement[]) {\n Doc.empty(ancestor)\n for (const k of kids) ancestor.appendChild(k)\n }\n\n /*\n * hide hides the specified elements. This is accomplished by adding the\n * bootstrap d-hide class to the element. Use Doc.show to undo.\n */\n static hide (...els: Element[]) {\n for (const el of els) el.classList.add('d-hide')\n }\n\n /*\n * show shows the specified elements. This is accomplished by removing the\n * bootstrap d-hide class as added with Doc.hide.\n */\n static show (...els: Element[]) {\n for (const el of els) el.classList.remove('d-hide')\n }\n\n /*\n * show or hide the specified elements, based on value of the truthiness of\n * vis.\n */\n static setVis (vis: any, ...els: Element[]) {\n if (vis) Doc.show(...els)\n else Doc.hide(...els)\n }\n\n /* isHidden returns true if the specified element is hidden */\n static isHidden (el: Element): boolean {\n return el.classList.contains('d-hide')\n }\n\n /* isDisplayed returns true if the specified element is not hidden */\n static isDisplayed (el: Element): boolean {\n return !el.classList.contains('d-hide')\n }\n\n /*\n * animate runs the supplied function, which should be a \"progress\" function\n * accepting one argument. The progress function will be called repeatedly\n * with the argument varying from 0.0 to 1.0. The exact path that animate\n * takes from 0.0 to 1.0 will vary depending on the choice of easing\n * algorithm. See the Easing object for the available easing algo choices. The\n * default easing algorithm is linear.\n */\n static async animate (duration: number, f: (progress: number) => void, easingAlgo?: string) {\n await new Animation(duration, f, easingAlgo).wait()\n }\n\n static applySelector (ancestor: HTMLElement, k: string): PageElement[] {\n return Array.from(ancestor.querySelectorAll(k)) as PageElement[]\n }\n\n static kids (ancestor: HTMLElement): PageElement[] {\n return Array.from(ancestor.children) as PageElement[]\n }\n\n static safeSelector (ancestor: HTMLElement, k: string): PageElement {\n const el = ancestor.querySelector(k)\n if (el) return el as PageElement\n console.warn(`no element found for selector '${k}' on element ->`, ancestor)\n return document.createElement('div')\n }\n\n /*\n * idDescendants creates an object mapping to elements which are descendants\n * of the ancestor and have id attributes. Elements are keyed by their id\n * value.\n */\n static idDescendants (ancestor: HTMLElement): Record {\n const d: Record = {}\n for (const el of Doc.applySelector(ancestor, '[id]')) d[el.id] = el\n return d\n }\n\n /*\n * formatCoinValue formats the value in atomic units into a string\n * representation in conventional units. If the value happens to be an\n * integer, no decimals are displayed. Trailing zeros may be truncated.\n */\n static formatCoinValue (vAtomic: number, unitInfo?: UnitInfo): string {\n const [v, prec] = convertToConventional(vAtomic, unitInfo)\n if (Number.isInteger(v)) return intFormatter.format(v)\n return decimalFormatter(prec).format(v)\n }\n\n static formatThreeSigFigs (v: number): string {\n if (v >= 1000) return intFormatter.format(Math.round(v))\n return threeSigFigs.format(v)\n }\n\n static formatFiveSigFigs (v: number, prec?: number): string {\n if (v >= 10000) return intFormatter.format(Math.round(v))\n else if (v < 1e5) return fullPrecisionFormatter(prec ?? 8 /* rate encoding factor */).format(v)\n return fiveSigFigs.format(v)\n }\n\n /*\n * formatFullPrecision formats the value in atomic units into a string\n * representation in conventional units using the full decimal precision\n * associated with the conventional unit's conversion factor.\n */\n static formatFullPrecision (vAtomic: number, unitInfo?: UnitInfo): string {\n const [v, prec] = convertToConventional(vAtomic, unitInfo)\n return fullPrecisionFormatter(prec).format(v)\n }\n\n /*\n * formatFiatConversion formats the value in atomic units to its representation in\n * conventional units and returns the fiat value as a string.\n */\n static formatFiatConversion (vAtomic: number, rate: number, unitInfo?: UnitInfo): string {\n if (!rate || rate === 0) return intl.prep(intl.ID_UNAVAILABLE)\n const prec = 2\n const [v] = convertToConventional(vAtomic, unitInfo)\n const value = v * rate\n return fullPrecisionFormatter(prec).format(value)\n }\n\n /*\n * logoPath creates a path to a png logo for the specified ticker symbol. If\n * the symbol is not a supported asset, the generic letter logo will be\n * requested instead.\n */\n static logoPath (symbol: string): string {\n if (BipSymbols.indexOf(symbol) === -1) symbol = symbol.substring(0, 1)\n return `/img/coins/${symbol}.png`\n }\n\n static bipSymbol (assetID: number): string {\n return BipIDs[assetID]\n }\n\n static logoPathFromID (assetID: number): string {\n return Doc.logoPath(BipIDs[assetID])\n }\n\n /*\n * symbolize creates a token-aware symbol element for the asset's symbol. For\n * non-token assets, this is simply a SYMBOL. For tokens, it'll\n * be SYMBOLPARENT.\n */\n static symbolize (symbol: string): PageElement {\n const parts = symbol.split('.')\n const assetSymbol = document.createElement('span')\n assetSymbol.textContent = parts[0].toUpperCase()\n if (parts.length === 1) return assetSymbol\n const span = document.createElement('span')\n span.classList.add('token-aware-symbol')\n span.appendChild(assetSymbol)\n const parent = document.createElement('sup')\n parent.textContent = parts[1].toUpperCase()\n span.appendChild(parent)\n return span\n }\n\n /*\n * shortSymbol removes the short format of a symbol, with any parent chain\n * identifier removed\n */\n static shortSymbol (symbol: string): string {\n return symbol.split('.')[0].toUpperCase()\n }\n\n /*\n * cleanTemplates removes the elements from the DOM and deletes the id\n * attribute.\n */\n static cleanTemplates (...tmpls: HTMLElement[]) {\n tmpls.forEach(tmpl => {\n tmpl.remove()\n tmpl.removeAttribute('id')\n })\n }\n\n /*\n * tmplElement is a helper function for grabbing sub-elements of the market list\n * template.\n */\n static tmplElement (ancestor: Document | HTMLElement, s: string): PageElement {\n return ancestor.querySelector(`[data-tmpl=\"${s}\"]`) || document.createElement('div')\n }\n\n /*\n * parseTemplate returns an object of data-tmpl elements, keyed by their\n * data-tmpl values.\n */\n static parseTemplate (ancestor: HTMLElement): Record {\n const d: Record = {}\n for (const el of Doc.applySelector(ancestor, '[data-tmpl]')) d[el.dataset.tmpl || ''] = el\n return d\n }\n\n /*\n * timeSince returns a string representation of the duration since the\n * specified unix timestamp.\n */\n static timeSince (t: number): string {\n return Doc.formatDuration((new Date().getTime()) - t)\n }\n\n /* formatDuration returns a string representation of the duration */\n static formatDuration (dur: number): string {\n let seconds = Math.floor(dur)\n let result = ''\n let count = 0\n const add = (n: number, s: string) => {\n if (n > 0 || count > 0) count++\n if (n > 0) result += `${n} ${s} `\n return count >= 2\n }\n let y, mo, d, h, m, s\n [y, seconds] = timeMod(seconds, aYear)\n if (add(y, 'y')) { return result }\n [mo, seconds] = timeMod(seconds, aMonth)\n if (add(mo, 'mo')) { return result }\n [d, seconds] = timeMod(seconds, aDay)\n if (add(d, 'd')) { return result }\n [h, seconds] = timeMod(seconds, anHour)\n if (add(h, 'h')) { return result }\n [m, seconds] = timeMod(seconds, aMinute)\n if (add(m, 'm')) { return result }\n [s, seconds] = timeMod(seconds, 1000)\n add(s, 's')\n return result || '0 s'\n }\n\n /*\n * disableMouseWheel can be used to disable the mouse wheel for any\n * input. It is very easy to unknowingly scroll up on a number input\n * and then submit an unexpected value. This function prevents the\n * scroll increment/decrement behavior for a wheel action on a\n * number input.\n */\n static disableMouseWheel (...inputFields: Element[]) {\n for (const inputField of inputFields) {\n inputField.addEventListener('wheel', (ev) => {\n ev.preventDefault()\n })\n }\n }\n\n // showFormError can be used to set and display error message on forms.\n static showFormError (el: PageElement, msg: any) {\n el.textContent = msg\n Doc.show(el)\n }\n}\n\n/*\n * Animation is a handler for starting and stopping animations.\n */\nexport class Animation {\n done: (() => void) | undefined\n endAnimation: boolean\n thread: Promise\n static Forever: number\n\n constructor (duration: number, f: (progress: number) => void, easingAlgo?: string, done?: () => void) {\n this.done = done\n this.thread = this.run(duration, f, easingAlgo)\n }\n\n /*\n * run runs the animation function, increasing progress from 0 to 1 in a\n * manner dictated by easingAlgo.\n */\n async run (duration: number, f: (progress: number) => void, easingAlgo?: string) {\n duration = duration >= 0 ? duration : 1000 * 86400 * 365 * 10 // 10 years, in ms\n const easer = easingAlgo ? Easing[easingAlgo] : Easing.linear\n const start = new Date().getTime()\n const end = (duration === Animation.Forever) ? Number.MAX_SAFE_INTEGER : start + duration\n const range = end - start\n const frameDuration = 1000 / FPS\n let now = start\n this.endAnimation = false\n while (now < end) {\n if (this.endAnimation) return this.runCompletionFunction()\n f(easer((now - start) / range))\n await sleep(frameDuration)\n now = new Date().getTime()\n }\n f(1)\n this.runCompletionFunction()\n }\n\n /* wait returns a promise that will resolve when the animation completes. */\n async wait () {\n await this.thread\n }\n\n /* stop schedules the animation to exit at its next frame. */\n stop () {\n this.endAnimation = true\n }\n\n /*\n * stopAndWait stops the animations and returns a promise that will resolve\n * when the animation exits.\n */\n async stopAndWait () {\n this.stop()\n await this.wait()\n }\n\n /* runCompletionFunction runs any registered callback function */\n runCompletionFunction () {\n if (this.done) this.done()\n }\n}\nAnimation.Forever = -1\n\n/* Easing algorithms for animations. */\nexport const Easing: Record number> = {\n linear: t => t,\n easeIn: t => t * t,\n easeOut: t => t * (2 - t),\n easeInHard: t => t * t * t,\n easeOutHard: t => (--t) * t * t + 1,\n easeOutElastic: t => {\n const c4 = (2 * Math.PI) / 3\n return t === 0\n ? 0\n : t === 1\n ? 1\n : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1\n }\n}\n\n/* WalletIcons are used for controlling wallets in various places. */\nexport class WalletIcons {\n icons: Record\n status: Element\n\n constructor (box: HTMLElement) {\n const stateElement = (name: string) => box.querySelector(`[data-state=${name}]`) as HTMLElement\n this.icons = {}\n this.icons.sleeping = stateElement('sleeping')\n this.icons.locked = stateElement('locked')\n this.icons.unlocked = stateElement('unlocked')\n this.icons.nowallet = stateElement('nowallet')\n this.icons.syncing = stateElement('syncing')\n this.icons.nopeers = stateElement('nopeers')\n this.icons.disabled = stateElement('disabled')\n this.status = stateElement('status')\n }\n\n /* sleeping sets the icons to indicate that the wallet is not connected. */\n sleeping () {\n const i = this.icons\n Doc.hide(i.locked, i.unlocked, i.nowallet, i.syncing, i.disabled)\n Doc.show(i.sleeping)\n if (this.status) this.status.textContent = intl.prep(intl.ID_OFF)\n }\n\n /*\n * locked sets the icons to indicate that the wallet is connected, but locked.\n */\n locked () {\n const i = this.icons\n Doc.hide(i.unlocked, i.nowallet, i.sleeping, i.disabled)\n Doc.show(i.locked)\n if (this.status) this.status.textContent = intl.prep(intl.ID_LOCKED)\n }\n\n /*\n * unlocked sets the icons to indicate that the wallet is connected and\n * unlocked.\n */\n unlocked () {\n const i = this.icons\n Doc.hide(i.locked, i.nowallet, i.sleeping, i.disabled)\n Doc.show(i.unlocked)\n if (this.status) this.status.textContent = intl.prep(intl.ID_READY)\n }\n\n /* nowallet sets the icons to indicate that no wallet exists. */\n nowallet () {\n const i = this.icons\n Doc.hide(i.locked, i.unlocked, i.sleeping, i.syncing, i.disabled)\n Doc.show(i.nowallet)\n if (this.status) this.status.textContent = intl.prep(intl.ID_NO_WALLET)\n }\n\n /* set the icons to indicate that the wallet is disabled */\n disabled () {\n const i = this.icons\n Doc.hide(i.locked, i.unlocked, i.sleeping, i.syncing, i.nowallet, i.nopeers)\n Doc.show(i.disabled)\n i.disabled.dataset.tooltip = intl.prep(intl.ID_DISABLED_MSG)\n }\n\n setSyncing (wallet: WalletState | null) {\n const syncIcon = this.icons.syncing\n if (!wallet || !wallet.running || wallet.disabled) {\n Doc.hide(syncIcon)\n return\n }\n\n if (wallet.peerCount === 0) {\n Doc.show(this.icons.nopeers)\n Doc.hide(syncIcon) // potentially misleading with no peers\n return\n }\n Doc.hide(this.icons.nopeers)\n\n if (!wallet.synced) {\n Doc.show(syncIcon)\n syncIcon.dataset.tooltip = intl.prep(intl.ID_WALLET_SYNC_PROGRESS, { syncProgress: (wallet.syncProgress * 100).toFixed(1) })\n return\n }\n Doc.hide(syncIcon)\n }\n\n /* reads the core.Wallet state and sets the icon visibility. */\n readWallet (wallet: WalletState | null) {\n this.setSyncing(wallet)\n if (!wallet) return this.nowallet()\n switch (true) {\n case (wallet.disabled):\n this.disabled()\n break\n case (!wallet.running):\n this.sleeping()\n break\n case (!wallet.open):\n this.locked()\n break\n case (wallet.open):\n this.unlocked()\n break\n default:\n console.error('wallet in unknown state', wallet)\n }\n }\n}\n\n/* sleep can be used by async functions to pause for a specified period. */\nfunction sleep (ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nconst aYear = 31536000000\nconst aMonth = 2592000000\nconst aDay = 86400000\nconst anHour = 3600000\nconst aMinute = 60000\n\n/* timeMod returns the quotient and remainder of t / dur. */\nfunction timeMod (t: number, dur: number) {\n const n = Math.floor(t / dur)\n return [n, t - n * dur]\n}\n","// State is a set of static methods for working with the user state. It has\n// utilities for setting and retrieving cookies and storing user configuration\n// to localStorage.\nexport default class State {\n // Cookie keys.\n static darkModeCK = 'darkMode'\n static authCK = 'dexauth'\n static pwKeyCK = 'sessionkey'\n // Local storage keys (for data that we don't need at the server).\n static popupsLK = 'popups'\n static loggersLK = 'loggers'\n static recordersLK = 'recorders'\n static lastMarketLK = 'selectedMarket'\n static depthZoomLK = 'depthZoom'\n static lastMMMarketLK = 'mmMarket'\n static optionsExpansionLK = 'mmOptsExpand'\n static leftMarketDockLK = 'leftmarketdock'\n static selectedAssetLK = 'selectedasset'\n static notificationsLK = 'notifications'\n static orderDisclaimerAckedLK = 'ordAck'\n\n static setCookie (cname: string, cvalue: string) {\n const d = new Date()\n // Set cookie to expire in ten years.\n d.setTime(d.getTime() + (86400 * 365 * 10 * 1000))\n const expires = 'expires=' + d.toUTCString()\n document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/'\n }\n\n /*\n * getCookie returns the value at the specified cookie name, otherwise null.\n */\n static getCookie (cname: string) {\n for (const cstr of document.cookie.split(';')) {\n const [k, v] = cstr.split('=')\n if (k.trim() === cname) return v\n }\n return null\n }\n\n /*\n * removeCookie tells the browser to stop using cookie. It's not enough to simply\n * erase cookie value because browser will still send it to the server (with empty\n * value), and that's not what server expects.\n */\n static removeCookie (cKey: string) {\n document.cookie = `${cKey}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`\n }\n\n /*\n * isDark returns true if the dark-mode cookie is currently set to '1' = true.\n */\n static isDark () {\n return document.cookie.split(';').filter((item) => item.includes(`${State.darkModeCK}=1`)).length\n }\n\n /* passwordIsCached returns whether or not there is a cached password in the cookies. */\n static passwordIsCached () {\n return !!this.getCookie(State.pwKeyCK)\n }\n\n /* storeLocal puts the key-value pair into Window.localStorage. */\n static storeLocal (k: string, v: any) {\n window.localStorage.setItem(k, JSON.stringify(v))\n }\n\n /*\n * fetchLocal the value associated with the key in Window.localStorage, or\n * null if the no value exists for the key.\n */\n static fetchLocal (k: string) {\n const v = window.localStorage.getItem(k)\n if (v !== null) {\n return JSON.parse(v)\n }\n return null\n }\n\n /* removeLocal removes the key-value pair from Window.localStorage. */\n static removeLocal (k: string) {\n window.localStorage.removeItem(k)\n }\n}\n\n// Setting defaults here, unless specific cookie (or local storage) value was already chosen by the user.\nif (State.getCookie(State.darkModeCK) === null) State.setCookie(State.darkModeCK, '1')\nif (State.fetchLocal(State.popupsLK) === null) State.storeLocal(State.popupsLK, '1')\nif (State.fetchLocal(State.leftMarketDockLK) === null) State.storeLocal(State.leftMarketDockLK, '1')\n","export default function _assertThisInitialized(self) {\n if (self === void 0) {\n throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n }\n return self;\n}","export default function _setPrototypeOf(o, p) {\n _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\n return _setPrototypeOf(o, p);\n}","import setPrototypeOf from \"./setPrototypeOf.js\";\nexport default function _inherits(subClass, superClass) {\n if (typeof superClass !== \"function\" && superClass !== null) {\n throw new TypeError(\"Super expression must either be null or a function\");\n }\n subClass.prototype = Object.create(superClass && superClass.prototype, {\n constructor: {\n value: subClass,\n writable: true,\n configurable: true\n }\n });\n Object.defineProperty(subClass, \"prototype\", {\n writable: false\n });\n if (superClass) setPrototypeOf(subClass, superClass);\n}","import _typeof from \"./typeof.js\";\nimport assertThisInitialized from \"./assertThisInitialized.js\";\nexport default function _possibleConstructorReturn(self, call) {\n if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) {\n return call;\n } else if (call !== void 0) {\n throw new TypeError(\"Derived constructors may only return object or undefined\");\n }\n return assertThisInitialized(self);\n}","export default function _getPrototypeOf(o) {\n _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {\n return o.__proto__ || Object.getPrototypeOf(o);\n };\n return _getPrototypeOf(o);\n}","/*\n * requestJSON encodes the object and sends the JSON to the specified address.\n */\nexport async function requestJSON (method: string, addr: string, reqBody?: any): Promise {\n try {\n const response = await window.fetch(addr, {\n method: method,\n headers: new window.Headers({ 'content-type': 'application/json' }),\n // credentials: \"same-origin\",\n body: reqBody\n })\n if (response.status !== 200) { throw response }\n const obj = await response.json()\n obj.requestSuccessful = true\n return obj\n } catch (response) {\n response.requestSuccessful = false\n response.msg = await response.text()\n return response\n }\n}\n\n/*\n * postJSON sends a POST request with JSON-formatted data and returns the\n * response.\n */\nexport async function postJSON (addr: string, data?: any) {\n return requestJSON('POST', addr, JSON.stringify(data))\n}\n\n/*\n * getJSON sends a GET request and returns the response.\n */\nexport async function getJSON (addr: string): Promise {\n return requestJSON('GET', addr)\n}\n\nexport enum Errors {\n walletErr,\n walletAuthErr,\n walletBalanceErr,\n dupeDEXErr,\n assetSupportErr,\n registerErr,\n signatureErr,\n zeroFeeErr,\n feeMismatchErr,\n feeSendErr,\n passwordErr,\n emptyHostErr,\n connectionErr,\n acctKeyErr,\n unknownOrderErr,\n orderParamsErr,\n dbErr,\n authErr,\n connectWalletErr,\n missingWalletErr,\n encryptionErr,\n decodeErr,\n accountVerificationErr,\n accountProofErr,\n parseKeyErr,\n marketErr,\n addressParseErr,\n addrErr,\n fileReadErr,\n unknownDEXErr,\n accountRetrieveErr,\n accountDisableErr,\n suspendedAcctErr,\n existenceCheckErr,\n createWalletErr,\n activeOrdersErr,\n newAddrErr,\n}\n","declare global {\n interface Window {\n log: (...args: any) => void\n enableLogger: (loggerID: string, enable: boolean) => void\n recordLogger: (loggerID: string, enable: boolean) => void\n dumpLogger: (loggerID: string) => void\n localeDiscrepancies: () => void\n }\n}\n\nexport enum ConnectionStatus {\n Disconnected = 0,\n Connected = 1,\n InvalidCert = 2,\n}\n\nexport interface BondOptions {\n bondAsset: number\n targetTier: number\n maxBondedAmt: number\n}\n\nexport interface Exchange {\n host: string\n acctID: string\n markets: Record\n assets: Record\n connectionStatus: ConnectionStatus\n viewOnly: boolean\n bondAssets: Record\n tier: number\n bondOptions: BondOptions\n pendingBonds: Record\n candleDurs: string[]\n}\n\nexport interface Candle {\n startStamp: number\n endStamp: number\n matchVolume: number\n quoteVolume: number\n highRate: number\n lowRate: number\n startRate: number\n endRate: number\n}\n\nexport interface CandlesPayload {\n dur: string\n ms: number\n candles: Candle[]\n}\n\nexport interface Market {\n name: string\n baseid: number\n basesymbol: string\n quoteid: number\n quotesymbol: string\n lotsize: number\n ratestep: number\n epochlen: number\n startepoch: number\n buybuffer: number\n orders: Order[]\n spot: Spot | undefined\n atomToConv: number\n inflight: InFlightOrder[]\n}\n\nexport interface InFlightOrder extends Order {\n tempID: number\n}\n\nexport interface Order {\n host: string\n baseID: number\n baseSymbol: string\n quoteID: number\n quoteSymbol: string\n market: string\n type: number\n id: string\n stamp: number\n submitTime: number\n sig: string\n status: number\n epoch: number\n qty: number\n sell: boolean\n filled: number\n matches: Match[]\n cancelling: boolean\n canceled: boolean\n feesPaid: FeeBreakdown\n fundingCoins: Coin[]\n accelerationCoins: Coin[]\n lockedamt: number\n rate: number // limit only\n tif: number // limit only\n targetOrderID: string // cancel only\n readyToTick: boolean\n}\n\nexport interface Match {\n matchID: string\n status: number\n active: boolean\n revoked: boolean\n rate: number\n qty: number\n side: number\n feeRate: number\n swap: Coin\n counterSwap: Coin\n redeem: Coin\n counterRedeem: Coin\n refund: Coin\n stamp: number\n isCancel: boolean\n}\n\nexport interface Spot {\n stamp: number\n baseID: number\n quoteID: number\n rate: number\n bookVolume: number // Unused?\n change24: number\n vol24: number\n}\n\nexport interface Asset {\n id: number\n symbol: string\n version: number\n maxFeeRate: number\n swapSize: number\n swapSizeBase: number\n redeemSize: number\n swapConf: number\n unitInfo: UnitInfo\n}\n\nexport interface BondAsset {\n ver: number\n id: number\n confs: number\n amount: number\n}\n\nexport interface PendingBondState {\n symbol: string\n assetID: number\n confs: number\n}\n\nexport interface FeeBreakdown {\n swap: number\n redemption: number\n}\n\nexport interface SupportedAsset {\n id: number\n symbol: string\n name: string\n wallet: WalletState\n info?: WalletInfo\n token?: Token\n unitInfo: UnitInfo\n walletCreationPending: boolean\n}\n\nexport interface Token {\n parentID: number\n name: string\n unitInfo: UnitInfo\n definition: WalletDefinition\n}\n\nexport interface WalletState {\n symbol: string\n assetID: number\n version: number\n type: string\n traits: number\n open: boolean\n running: boolean\n disabled: boolean\n balance: WalletBalance\n address: string\n units: string\n encrypted: boolean\n peerCount: number\n synced: boolean\n syncProgress: number\n}\n\nexport interface WalletInfo {\n name: string\n version: number\n availablewallets: WalletDefinition[]\n versions: number[]\n emptyidx: number\n unitinfo: UnitInfo\n}\n\nexport interface WalletBalance {\n available: number\n immature: number\n locked: number\n stamp: string // time.Time\n orderlocked: number\n contractlocked: number\n bondlocked: number\n other: Record\n}\n\nexport interface WalletDefinition {\n seeded: boolean\n type: string\n tab: string\n description: string\n configpath: string\n configopts: ConfigOption[]\n noauth: boolean\n}\n\nexport interface ConfigOption {\n key: string\n displayname: string\n description: string\n default: any\n max: any\n min: any\n noecho: boolean\n isboolean: boolean\n isdate: boolean\n disablewhenactive: boolean\n isBirthdayConfig: boolean\n repeatable?: string\n noauth: boolean\n regAsset?: number\n required?: boolean\n}\n\nexport interface Coin {\n id: string\n stringID: string\n assetID: number\n symbol: string\n confs: Confirmations\n}\n\nexport interface Confirmations {\n required: number\n count: number\n}\n\nexport interface UnitInfo {\n atomicUnit: string\n conventional: Denomination\n denominations: Denomination[]\n}\n\nexport interface Denomination {\n unit: string\n conversionFactor: number\n}\n\nexport interface User {\n exchanges: Record\n inited: boolean\n seedgentime: number\n assets: Record\n fiatRates: Record\n authed: boolean // added by webserver\n ok: boolean // added by webserver\n bots: BotReport[]\n}\n\nexport interface CoreNote {\n type: string\n topic: string\n subject: string\n details: string\n severity: number\n stamp: number\n acked: boolean\n id: string\n}\n\nexport interface BondNote extends CoreNote {\n asset: number\n confirmations: number\n dex: string\n coinID: string | null\n tier: number | null\n}\n\nexport interface BalanceNote extends CoreNote {\n assetID: number\n balance: WalletBalance\n}\n\nexport interface RateNote extends CoreNote {\n fiatRates: Record\n}\n\nexport interface WalletConfigNote extends CoreNote {\n wallet: WalletState\n}\n\nexport type WalletStateNote = WalletConfigNote\n\nexport interface WalletCreationNote extends CoreNote {\n assetID: number\n}\n\nexport interface SpotPriceNote extends CoreNote {\n host: string\n spots: Record\n}\n\nexport interface BotNote extends CoreNote {\n report: BotReport\n}\n\nexport interface MatchNote extends CoreNote {\n orderID: string\n match: Match\n host: string\n marketID: string\n}\n\nexport interface ConnEventNote extends CoreNote {\n host: string\n connectionStatus: ConnectionStatus\n}\n\nexport interface OrderNote extends CoreNote {\n order: Order\n tempID: number\n}\n\nexport interface RecentMatch {\n rate: number\n qty: number\n stamp: number\n sell: boolean\n}\n\nexport interface EpochNote extends CoreNote {\n host: string\n marketID: string\n epoch: number\n}\n\nexport interface APIResponse {\n requestSuccessful: boolean\n ok: boolean\n msg: string\n err?: string\n}\n\nexport interface LogMessage {\n time: string\n msg: string\n}\n\nexport interface NoteElement extends HTMLElement {\n note: CoreNote\n}\n\nexport interface BalanceResponse extends APIResponse {\n balance: WalletBalance\n}\n\nexport interface LayoutMetrics {\n bodyTop: number\n bodyLeft: number\n width: number\n height: number\n centerX: number\n centerY: number\n}\n\nexport interface PasswordCache {\n pw: string\n}\n\nexport interface PageElement extends HTMLElement {\n value?: string\n src?: string\n files?: FileList\n checked?: boolean\n href?: string\n htmlFor?: string\n}\n\nexport interface BooleanConfig {\n reason: string\n}\n\nexport interface XYRangePoint {\n label: string\n x: number\n y: number\n}\n\nexport interface XYRange {\n start: XYRangePoint\n end: XYRangePoint\n xUnit: string\n yUnit: string\n}\n\nexport interface OrderOption extends ConfigOption {\n boolean?: BooleanConfig\n xyRange?: XYRange\n showByDefault?: boolean\n}\n\nexport interface SwapEstimate {\n lots: number\n value: number\n maxFees: number\n realisticWorstCase: number\n realisticBestCase: number\n}\n\nexport interface RedeemEstimate {\n realisticBestCase: number\n realisticWorstCase: number\n}\n\nexport interface PreSwap {\n estimate: SwapEstimate\n options: OrderOption[]\n}\n\nexport interface PreRedeem {\n estimate: RedeemEstimate\n options: OrderOption[]\n}\n\nexport interface OrderEstimate {\n swap: PreSwap\n redeem: PreRedeem\n}\n\nexport interface MaxOrderEstimate {\n swap: SwapEstimate\n redeem: RedeemEstimate\n}\n\nexport interface MaxSell {\n maxSell: MaxOrderEstimate\n}\n\nexport interface MaxBuy {\n maxBuy: MaxOrderEstimate\n}\n\nexport interface TradeForm {\n host: string\n isLimit: boolean\n sell: boolean\n base: number\n quote: number\n qty: number\n rate: number\n tifnow: boolean\n options: Record\n}\n\nexport interface BookUpdate {\n action: string\n host: string\n marketID: string\n matchesSummary: RecentMatch[]\n payload: any\n}\n\nexport interface MiniOrder {\n qty: number\n qtyAtomic: number\n rate: number\n msgRate: number\n epoch: number\n sell: boolean\n token: string\n}\n\nexport interface CoreOrderBook {\n sells: MiniOrder[]\n buys: MiniOrder[]\n epoch: MiniOrder[]\n recentMatches: RecentMatch[]\n}\n\nexport interface MarketOrderBook {\n base: number\n quote: number\n book: CoreOrderBook\n}\n\nexport interface RemainderUpdate {\n token: string\n qty: number\n qtyAtomic: number\n}\n\nexport interface OrderFilter {\n n?: number\n offset?: string\n hosts: string[]\n assets: number[]\n statuses: number[]\n}\n\nexport interface MakerProgram {\n host: string\n baseID: number\n quoteID: number\n lots: number\n oracleWeighting: number\n oracleBias: number\n driftTolerance: number\n gapFactor: number\n gapStrategy: string\n}\n\nexport interface BotOrder {\n host: string\n marketID: string\n orderID: string\n}\n\nexport interface BotReport {\n programID: number\n program: MakerProgram\n running: boolean\n orders: BotOrder\n}\n\nexport interface MarketReport {\n basisPrice: number\n price: number\n oracles: OracleReport[]\n breakEvenSpread: number\n}\n\nexport interface OracleReport {\n host: string\n usdVol: number\n bestBuy: number\n bestSell: number\n}\n\n// changing the order of the elements in this enum will affect\n// the sorting of the peers table in wallets.ts.\nexport enum PeerSource {\n WalletDefault,\n UserAdded,\n Discovered,\n}\n\nexport interface WalletPeer {\n addr: string\n source: PeerSource\n connected: boolean\n}\n\nexport interface Application {\n assets: Record\n seedGenTime: number\n user: User\n header: HTMLElement\n headerSpace: HTMLElement\n walletMap: Record\n exchanges: Record\n fiatRatesMap: Record\n showPopups: boolean\n commitHash: string\n authed(): boolean\n start (): Promise\n reconnected (): void\n fetchUser (): Promise\n loadPage (page: string, data?: any, skipPush?: boolean): Promise\n attach (data: any): void\n bindTooltips (ancestor: HTMLElement): void\n attachHeader (): void\n showDropdown (icon: HTMLElement, dialog: HTMLElement): void\n ackNotes (): void\n setNoteTimes (noteList: HTMLElement): void\n bindInternalNavigation (ancestor: HTMLElement): void\n storeNotes (): void\n updateMenuItemsDisplay (): void\n attachCommon (node: HTMLElement): void\n updateBondConfs (dexAddr: string, coinID: string, confs: number, assetID: number): void\n handleBondNote (note: BondNote): void\n setNotes (notes: CoreNote[]): void\n notify (note: CoreNote): void\n log (loggerID: string, ...msg: any): void\n prependPokeElement (note: CoreNote): void\n prependNoteElement (note: CoreNote, skipSave?: boolean): void\n prependListElement (noteList: HTMLElement, note: CoreNote, el: NoteElement): void\n loading (el: HTMLElement): () => void\n orders (host: string, mktID: string): Order[]\n haveActiveOrders (assetID: number): boolean\n order (oid: string): Order | null\n canAccelerateOrder(order: Order): boolean\n unitInfo (assetID: number, xc?: Exchange): UnitInfo\n conventionalRate (baseID: number, quoteID: number, encRate: number, xc?: Exchange): number\n walletDefinition (assetID: number, walletType: string): WalletDefinition\n currentWalletDefinition (assetID: number): WalletDefinition\n fetchBalance (assetID: number): Promise\n checkResponse (resp: APIResponse): boolean\n signOut (): Promise\n registerNoteFeeder (receivers: Record void>): void\n}\n\n// TODO: Define an interface for Application?\nlet application: Application\n\nexport function registerApplication (a: Application) {\n application = a\n}\n\nexport function app (): Application {\n return application\n}\n","import Doc from './doc'\nimport {\n PageElement,\n XYRange,\n OrderOption\n} from './registry'\n\ninterface OptionsReporters {\n enable: () => void\n disable: () => void\n}\n\n// Having the caller set these vars on load using an exported function makes\n// life easier.\nlet orderOptTmpl: HTMLElement, booleanOptTmpl: HTMLElement, rangeOptTmpl: HTMLElement\n\n// setOptionTemplates sets the package vars for the templates and application.\nexport function setOptionTemplates (page: Record): void {\n [booleanOptTmpl, rangeOptTmpl, orderOptTmpl] = [page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl]\n}\n\nconst threeSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 3,\n maximumSignificantDigits: 3\n})\n\n/*\n * Option is a base class for option elements. Option stores some common\n * parameters and monitors the toggle switch, calling the child class's\n * enable/disable methods when the user manually turns the option on or off.\n */\nexport class Option {\n opt: OrderOption\n node: HTMLElement\n tmpl: Record\n on: boolean\n\n constructor (opt: OrderOption, symbol: string, report: OptionsReporters) {\n this.opt = opt\n const node = this.node = orderOptTmpl.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(node)\n\n tmpl.optName.textContent = opt.displayname\n tmpl.tooltip.dataset.tooltip = opt.description\n\n // const isBaseChain = (isSwapOption && order.sell) || (!isSwapOption && !order.sell)\n // const symbol = isBaseChain ? this.baseSymbol() : this.quoteSymbol()\n if (symbol) tmpl.chainIcon.src = Doc.logoPath(symbol)\n else Doc.hide(tmpl.chainIcon)\n\n this.on = false\n Doc.bind(node, 'click', () => {\n if (this.on) return\n this.on = true\n node.classList.add('selected')\n report.enable()\n })\n Doc.bind(tmpl.toggle, 'click', e => {\n if (!this.on) return\n e.stopPropagation()\n this.on = false\n node.classList.remove('selected')\n report.disable()\n })\n }\n}\n\n/*\n * BooleanOption is a simple on/off option with a short summary of it's effects.\n * BooleanOrderOption is the handler for a *BooleanConfig from client/asset.\n */\nexport class BooleanOption extends Option {\n control: HTMLElement\n changed: () => void\n dict: Record\n\n constructor (opt: OrderOption, symbol: string, dict: Record, changed: () => void) {\n super(opt, symbol, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.dict = dict\n this.changed = () => changed()\n if (opt.boolean === undefined) throw Error('not a boolean opt')\n const cfg = opt.boolean\n const control = this.control = booleanOptTmpl.cloneNode(true) as HTMLElement\n // Append to parent's options div.\n this.tmpl.controls.appendChild(control)\n const tmpl = Doc.parseTemplate(control)\n tmpl.reason.textContent = cfg.reason\n this.on = typeof dict[opt.key] !== 'undefined' ? dict[opt.key] : opt.default\n if (this.on) this.node.classList.add('selected')\n }\n\n store (): void {\n if (this.on === this.opt.default) delete this.dict[this.opt.key]\n else this.dict[this.opt.key] = this.on\n this.changed()\n }\n\n enable (): void {\n this.store()\n }\n\n disable (): void {\n this.store()\n }\n}\n\n/*\n * XYRangeOption is an order option that contains an XYRangeHandler. The logic\n * for handling the slider to is defined in XYRangeHandler so that the slider\n * can be used without being contained in an order option.\n */\nexport class XYRangeOption extends Option {\n handler: XYRangeHandler\n x: number\n changed: () => void\n dict: Record\n\n constructor (opt: OrderOption, symbol: string, dict: Record, changed: () => void) {\n super(opt, symbol, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.dict = dict\n this.changed = changed\n if (opt.xyRange === undefined) throw Error('not an xy range opt')\n const cfg = opt.xyRange\n const setVal = dict[opt.key]\n this.on = typeof setVal !== 'undefined'\n if (this.on) {\n this.node.classList.add('selected')\n this.x = setVal\n } else {\n this.x = opt.default\n }\n const onUpdate = (x: number) => {\n this.x = x\n this.dict[this.opt.key] = x\n }\n const onChange = () => { this.changed() }\n const selected = () => { this.node.classList.add('selected') }\n this.handler = new XYRangeHandler(cfg, this.x, onUpdate, onChange, selected)\n this.tmpl.controls.appendChild(this.handler.control)\n }\n\n enable (): void {\n this.dict[this.opt.key] = this.x\n this.changed()\n }\n\n disable (): void {\n delete this.dict[this.opt.key]\n this.changed()\n }\n\n setValue (x: number): void {\n this.handler.setValue(x)\n this.on = true\n this.node.classList.add('selected')\n }\n}\n\n/*\n * XYRangeHandler is the handler for an *XYRange from client/asset. XYRange\n * has a slider which allows adjusting the x and y, linearly between two limits.\n * The user can also manually enter values for x or y.\n */\nexport class XYRangeHandler {\n control: HTMLElement\n cfg: XYRange\n tmpl: Record\n x: number\n scrollingX: number\n y: number\n r: number\n roundY: boolean\n updated: (x:number, y:number) => void\n changed: () => void\n selected: () => void\n setConfig: (cfg: XYRange) => void\n\n constructor (cfg: XYRange, initVal: number, updated: (x:number, y:number) => void, changed: () => void, selected: () => void, roundY?: boolean) {\n const control = this.control = rangeOptTmpl.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(control)\n this.roundY = Boolean(roundY)\n this.cfg = cfg\n\n this.changed = changed\n this.selected = selected\n this.updated = updated\n\n const { slider, handle } = tmpl\n\n let rangeX = cfg.end.x - cfg.start.x\n let rangeY = cfg.end.y - cfg.start.y\n const normalizeX = (x: number) => (x - cfg.start.x) / rangeX\n\n const setConfig = (newCfg: XYRange) => {\n rangeX = newCfg.end.x - newCfg.start.x\n rangeY = newCfg.end.y - newCfg.start.y\n cfg = this.cfg = newCfg\n tmpl.rangeLblStart.textContent = cfg.start.label\n tmpl.rangeLblEnd.textContent = cfg.end.label\n tmpl.xUnit.textContent = cfg.xUnit\n tmpl.yUnit.textContent = cfg.yUnit\n this.y = this.r * rangeY + cfg.start.y\n this.r = (this.y - cfg.start.y) / rangeY\n this.scrollingX = this.r * rangeX + cfg.start.x\n }\n setConfig(cfg)\n\n this.setConfig = (cfg: XYRange) => {\n setConfig(cfg)\n this.accept(this.scrollingX)\n }\n\n // r, x, and y will be updated by the various input event handlers. r is\n // x (or y) normalized on its range, e.g. [x_min, x_max] -> [0, 1]\n this.r = normalizeX(initVal)\n this.scrollingX = this.x = initVal\n this.y = this.r * rangeY + cfg.start.y\n\n // Set up the handlers for the x and y text input fields.\n const clickOutX = (e: MouseEvent) => {\n if (e.type !== 'change' && e.target === tmpl.xInput) return\n const s = tmpl.xInput.value\n if (s) {\n const xx = parseFloat(s)\n if (!isNaN(xx)) {\n this.scrollingX = clamp(xx, cfg.start.x, cfg.end.x)\n this.r = normalizeX(this.scrollingX)\n this.y = this.r * rangeY + cfg.start.y\n this.accept(this.scrollingX)\n }\n }\n Doc.hide(tmpl.xInput)\n Doc.show(tmpl.x)\n Doc.unbind(document, 'click', clickOutX)\n this.changed()\n }\n\n Doc.bind(tmpl.x, 'click', e => {\n Doc.hide(tmpl.x)\n Doc.show(tmpl.xInput)\n tmpl.xInput.focus()\n tmpl.xInput.value = threeSigFigs.format(this.scrollingX)\n Doc.bind(document, 'click', clickOutX)\n e.stopPropagation()\n })\n\n Doc.bind(tmpl.xInput, 'change', clickOutX)\n\n const clickOutY = (e: MouseEvent) => {\n if (e.type !== 'change' && e.target === tmpl.yInput) return\n const s = tmpl.yInput.value\n if (s) {\n const yy = parseFloat(s)\n if (!isNaN(yy)) {\n this.y = clamp(yy, cfg.start.y, cfg.end.y)\n this.r = (this.y - cfg.start.y) / rangeY\n this.scrollingX = cfg.start.x + this.r * rangeX\n this.accept(this.scrollingX)\n }\n }\n Doc.hide(tmpl.yInput)\n Doc.show(tmpl.y)\n Doc.unbind(document, 'click', clickOutY)\n this.changed()\n }\n\n Doc.bind(tmpl.y, 'click', e => {\n Doc.hide(tmpl.y)\n Doc.show(tmpl.yInput)\n tmpl.yInput.focus()\n tmpl.yInput.value = threeSigFigs.format(this.y)\n Doc.bind(document, 'click', clickOutY)\n e.stopPropagation()\n })\n\n Doc.bind(tmpl.yInput, 'change', clickOutY)\n\n // Read the slider.\n Doc.bind(handle, 'mousedown', (e: MouseEvent) => {\n if (e.button !== 0) return\n e.preventDefault()\n this.selected()\n const startX = e.pageX\n const w = slider.clientWidth - handle.offsetWidth\n const startLeft = normalizeX(this.scrollingX) * w\n const left = (ee: MouseEvent) => Math.max(Math.min(startLeft + (ee.pageX - startX), w), 0)\n const trackMouse = (ee: MouseEvent) => {\n ee.preventDefault()\n this.r = left(ee) / w\n this.scrollingX = this.r * rangeX + cfg.start.x\n this.y = this.r * rangeY + cfg.start.y\n this.accept(this.scrollingX)\n }\n const mouseUp = (ee: MouseEvent) => {\n trackMouse(ee)\n Doc.unbind(document, 'mousemove', trackMouse)\n Doc.unbind(document, 'mouseup', mouseUp)\n this.changed()\n }\n Doc.bind(document, 'mousemove', trackMouse)\n Doc.bind(document, 'mouseup', mouseUp)\n })\n\n this.accept(this.scrollingX, true)\n }\n\n accept (x: number, skipUpdate?: boolean): void {\n const tmpl = this.tmpl\n if (this.roundY) this.y = Math.round(this.y)\n tmpl.x.textContent = threeSigFigs.format(x)\n tmpl.y.textContent = threeSigFigs.format(this.y)\n if (this.roundY) tmpl.y.textContent = `${this.y}`\n tmpl.handle.style.left = `calc(${this.r * 100}% - ${this.r * 14}px)`\n this.x = x\n this.scrollingX = x\n if (!skipUpdate) this.updated(x, this.y)\n }\n\n setValue (x: number) {\n const cfg = this.cfg\n this.r = (x - cfg.start.x) / (cfg.end.x - cfg.start.x)\n this.y = cfg.start.y + this.r * (cfg.end.y - cfg.start.y)\n this.accept(x, true)\n }\n}\n\nconst clamp = (v: number, min: number, max: number): number => v < min ? min : v > max ? max : v\n","export default class BasePage {\n /* unload is called when the user navigates away from the page. */\n unload () {\n // should be implemented by inheriting class.\n }\n}\n","import arrayWithoutHoles from \"./arrayWithoutHoles.js\";\nimport iterableToArray from \"./iterableToArray.js\";\nimport unsupportedIterableToArray from \"./unsupportedIterableToArray.js\";\nimport nonIterableSpread from \"./nonIterableSpread.js\";\nexport default function _toConsumableArray(arr) {\n return arrayWithoutHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableSpread();\n}","import arrayLikeToArray from \"./arrayLikeToArray.js\";\nexport default function _arrayWithoutHoles(arr) {\n if (Array.isArray(arr)) return arrayLikeToArray(arr);\n}","export default function _iterableToArray(iter) {\n if (typeof Symbol !== \"undefined\" && iter[Symbol.iterator] != null || iter[\"@@iterator\"] != null) return Array.from(iter);\n}","export default function _nonIterableSpread() {\n throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}","import * as intl from './locales'\nimport {\n app,\n Order,\n TradeForm,\n OrderOption,\n Match\n} from './registry'\nimport { BooleanOption, XYRangeOption } from './opts'\n\nexport const Limit = 1\nexport const Market = 2\nexport const Cancel = 3\n\n/* The time-in-force specifiers are a mirror of dex/order.TimeInForce. */\nexport const ImmediateTiF = 0\nexport const StandingTiF = 1\n\n/* The order statuses are a mirror of dex/order.OrderStatus. */\nexport const StatusUnknown = 0\nexport const StatusEpoch = 1\nexport const StatusBooked = 2\nexport const StatusExecuted = 3\nexport const StatusCanceled = 4\nexport const StatusRevoked = 5\n\n/* The match statuses are a mirror of dex/order.MatchStatus. */\nexport const NewlyMatched = 0\nexport const MakerSwapCast = 1\nexport const TakerSwapCast = 2\nexport const MakerRedeemed = 3\nexport const MatchComplete = 4\nexport const MatchConfirmed = 5\n\n/* The match sides are a mirror of dex/order.MatchSide. */\nexport const Maker = 0\nexport const Taker = 1\n\n/*\n * RateEncodingFactor is used when encoding an atomic exchange rate as an\n * integer. See docs on message-rate encoding @\n * https://github.com/decred/dcrdex/blob/master/spec/comm.mediawiki#Rate_Encoding\n */\nexport const RateEncodingFactor = 1e8\n\nexport function sellString (ord: Order) {\n const key = ord.sell ? intl.ID_SELL : intl.ID_BUY\n const lang = document.documentElement.lang.toLowerCase()\n return intl.prep(key).toLocaleLowerCase(lang)\n}\n\nexport function typeString (ord: Order) {\n return ord.type === Limit ? (ord.tif === ImmediateTiF ? intl.prep(intl.ID_LIMIT_ORDER_IMMEDIATE_TIF) : intl.prep(intl.ID_LIMIT_ORDER)) : intl.prep(intl.ID_MARKET_ORDER)\n}\n\n/* isMarketBuy will return true if the order is a market buy order. */\nexport function isMarketBuy (ord: Order) {\n return ord.type === Market && !ord.sell\n}\n\n/*\n * hasActiveMatches returns true if the order has matches that have not completed\n * settlement yet.\n */\nexport function hasActiveMatches (order: Order) {\n if (!order.matches) return false\n for (const match of order.matches) {\n if (match.active) return true\n }\n return false\n}\n\n/**\n * statusString converts the order status to a string.\n *\n * IMPORTANT: we have similar function in Golang, it must match this one exactly,\n * when updating make sure to update both!\n */\nexport function statusString (order: Order): string {\n if (!order.id) return intl.prep(intl.ID_ORDER_SUBMITTING) // order ID is empty.\n const isLive = hasActiveMatches(order)\n switch (order.status) {\n case StatusUnknown: return intl.prep(intl.ID_UNKNOWN)\n case StatusEpoch: return intl.prep(intl.ID_EPOCH)\n case StatusBooked:\n if (order.cancelling) return intl.prep(intl.ID_CANCELING)\n return isLive ? `${intl.prep(intl.ID_BOOKED)}/${intl.prep(intl.ID_SETTLING)}` : intl.prep(intl.ID_BOOKED)\n case StatusExecuted:\n if (isLive) return intl.prep(intl.ID_SETTLING)\n if (order.filled === 0 && order.type !== Cancel) return intl.prep(intl.ID_NO_MATCH)\n return intl.prep(intl.ID_EXECUTED)\n case StatusCanceled:\n return isLive ? `${intl.prep(intl.ID_CANCELED)}/${intl.prep(intl.ID_SETTLING)}` : intl.prep(intl.ID_CANCELED)\n case StatusRevoked:\n return isLive ? `${intl.prep(intl.ID_REVOKED)}/${intl.prep(intl.ID_SETTLING)}` : intl.prep(intl.ID_REVOKED)\n }\n return intl.prep(intl.ID_UNKNOWN)\n}\n\n/* filled sums the quantities of non-cancel matches available. */\nexport function filled (order: Order) {\n if (!order.matches) return 0\n const qty = isMarketBuy(order) ? (m: Match) => m.qty * m.rate / RateEncodingFactor : (m: Match) => m.qty\n return order.matches.reduce((filled, match) => {\n if (match.isCancel) return filled\n return filled + qty(match)\n }, 0)\n}\n\n/* settled sums the quantities of the matches that have completed. */\nexport function settled (order: Order) {\n if (!order.matches) return 0\n const qty = isMarketBuy(order) ? (m: Match) => m.qty * m.rate / RateEncodingFactor : (m: Match) => m.qty\n return order.matches.reduce((settled, match) => {\n if (match.isCancel) return settled\n const redeemed = (match.side === Maker && match.status >= MakerRedeemed) ||\n (match.side === Taker && match.status >= MatchComplete)\n return redeemed ? settled + qty(match) : settled\n }, 0)\n}\n\n/* baseToQuote returns the quantity of the quote asset. */\nexport function baseToQuote (rate: number, base: number) : number {\n return rate * base / RateEncodingFactor\n}\n\n/* orderPortion returns a string stating the percentage of the order a match\n makes up. */\nexport function orderPortion (order: Order, match: Match) : string {\n let matchQty = match.qty\n if (isMarketBuy(order)) {\n matchQty = baseToQuote(match.rate, match.qty)\n }\n return ((matchQty / order.qty) * 100).toFixed(1) + ' %'\n}\n\n/*\n * matchStatusString is a string used to create a displayable string describing\n * describing the match status.\n */\nexport function matchStatusString (m: Match) {\n if (m.revoked) {\n // When revoked, match status is less important than pending action if still\n // active, or the outcome if inactive.\n if (m.active) {\n if (m.redeem) return revokedMatchStatus(intl.ID_MATCH_STATUS_REDEMPTION_SENT) // must require confirmation if active\n // If maker and we have not redeemed, waiting to refund, assuming it's not\n // revoked while waiting for confs on an unspent/unexpired taker swap.\n if (m.side === Maker) return revokedMatchStatus(intl.ID_MATCH_STATUS_REFUND_PENDING)\n // As taker, resolution depends on maker's actions while waiting to refund.\n if (m.counterRedeem) return revokedMatchStatus(intl.ID_MATCH_STATUS_REDEEM_PENDING) // this should be very brief if we see the maker's redeem\n return revokedMatchStatus(intl.ID_MATCH_STATUS_REFUND_PENDING) // may switch to redeem if maker redeems on the sly\n }\n if (m.refund) {\n return revokedMatchStatus(intl.ID_MATCH_STATUS_REFUNDED)\n }\n if (m.redeem) {\n return revokedMatchStatus(intl.ID_MATCH_STATUS_REDEMPTION_CONFIRMED)\n }\n return revokedMatchStatus(intl.ID_MATCH_STATUS_COMPLETE) // i.e. we sent no swap\n }\n\n switch (m.status) {\n case NewlyMatched:\n return intl.prep(intl.ID_MATCH_STATUS_NEWLY_MATCHED)\n case MakerSwapCast:\n return intl.prep(intl.ID_MATCH_STATUS_MAKER_SWAP_CAST)\n case TakerSwapCast:\n return intl.prep(intl.ID_MATCH_STATUS_TAKER_SWAP_CAST)\n case MakerRedeemed:\n if (m.side === Maker) {\n return intl.prep(intl.ID_MATCH_STATUS_REDEMPTION_SENT)\n }\n return intl.prep(intl.ID_MATCH_STATUS_MAKER_REDEEMED)\n case MatchComplete:\n return intl.prep(intl.ID_MATCH_STATUS_REDEMPTION_SENT)\n case MatchConfirmed:\n return intl.prep(intl.ID_MATCH_STATUS_REDEMPTION_CONFIRMED)\n }\n return intl.prep(intl.ID_UNKNOWN)\n}\n\n// revokedMatchStatus is a helper function that returns the revoked match status\n// string.\nfunction revokedMatchStatus (matchStatus: string): string {\n return intl.prep(intl.ID_MATCH_STATUS_REVOKED, { status: intl.prep(matchStatus) })\n}\n\n/*\n * optionElement is a getter for an element matching the *OrderOption from\n * client/asset. change is a function with no arguments that is called when the\n * returned option's value has changed.\n */\nexport function optionElement (opt: OrderOption, order: TradeForm, change: () => void, isSwap: boolean): HTMLElement {\n const isBaseChain = (isSwap && order.sell) || (!isSwap && !order.sell)\n const symbol = isBaseChain ? dexAssetSymbol(order.host, order.base) : dexAssetSymbol(order.host, order.quote)\n\n switch (true) {\n case !!opt.boolean:\n return new BooleanOption(opt, symbol, order.options, change).node\n case !!opt.xyRange:\n return new XYRangeOption(opt, symbol, order.options, change).node\n default:\n console.error('no option type specified', opt)\n }\n console.error('unknown option type', opt)\n return document.createElement('div')\n}\n\nfunction dexAssetSymbol (host: string, assetID: number): string {\n return app().exchanges[host].assets[assetID].symbol\n}\n","import Doc, { Animation } from './doc'\nimport { RateEncodingFactor } from './orderutil'\nimport OrderBook from './orderbook'\nimport State from './state'\nimport { UnitInfo, Market, Candle, CandlesPayload } from './registry'\n\nconst bind = Doc.bind\nconst PIPI = 2 * Math.PI\nconst plusChar = String.fromCharCode(59914)\nconst minusChar = String.fromCharCode(59915)\n\ninterface Point {\n x: number\n y: number\n}\n\ninterface MinMax {\n min: number\n max: number\n}\n\ninterface Label {\n val: number\n txt: string\n}\n\ninterface LabelSet {\n widest?: number\n lbls: Label[]\n}\n\ninterface Translator {\n x: (x: number) => number\n y: (y: number) => number\n unx: (x: number) => number\n uny: (y: number) => number\n w: (w: number) => number\n h: (h: number) => number\n dataCoords: (f: () => void) => void\n}\n\nexport interface MouseReport {\n rate: number\n depth: number\n dotColor: string\n hoverMarkers: number[]\n}\n\nexport interface VolumeReport {\n buyBase: number\n buyQuote: number\n sellBase: number\n sellQuote: number\n}\n\nexport interface DepthReporters {\n mouse: (r: MouseReport | null) => void\n click: (x: number) => void\n volume: (r: VolumeReport) => void\n zoom: (z: number) => void\n}\n\nexport interface CandleReporters {\n mouse: (r: Candle | null) => void\n}\n\nexport interface ChartReporters {\n resize: () => void,\n click: (e: MouseEvent) => void,\n zoom: (bigger: boolean) => void\n}\n\nexport interface DepthLine {\n rate: number\n color: string\n}\n\nexport interface DepthMarker {\n rate: number\n active: boolean\n}\n\ninterface DepthMark extends DepthMarker {\n qty: number\n sell: boolean\n}\n\ninterface Theme {\n axisLabel: string\n gridBorder: string\n gridLines: string\n gapLine: string\n value: string\n zoom: string\n zoomHover: string\n sellLine: string\n buyLine: string\n sellFill: string\n buyFill: string\n crosshairs: string\n legendFill: string\n legendText: string\n}\n\nconst darkTheme: Theme = {\n axisLabel: '#b1b1b1',\n gridBorder: '#383f4b',\n gridLines: '#383f4b',\n gapLine: '#6b6b6b',\n value: '#9a9a9a',\n zoom: '#5b5b5b',\n zoomHover: '#aaa',\n sellLine: '#ae3333',\n buyLine: '#05a35a',\n sellFill: '#591a1a',\n buyFill: '#02572f',\n crosshairs: '#888',\n legendFill: 'black',\n legendText: '#d5d5d5'\n}\n\nconst lightTheme: Theme = {\n axisLabel: '#1b1b1b',\n gridBorder: '#ddd',\n gridLines: '#ddd',\n gapLine: '#595959',\n value: '#4d4d4d',\n zoom: '#777',\n zoomHover: '#333',\n sellLine: '#99302b',\n buyLine: '#207a46',\n sellFill: '#bd5959',\n buyFill: '#4cad75',\n crosshairs: '#595959',\n legendFill: '#e6e6e6',\n legendText: '#1b1b1b'\n}\n\n// Chart is the base class for charts.\nexport class Chart {\n parent: HTMLElement\n report: ChartReporters\n theme: Theme\n canvas: HTMLCanvasElement\n visible: boolean\n renderScheduled: boolean\n ctx: CanvasRenderingContext2D\n mousePos: Point | null\n rect: DOMRect\n wheelLimiter: number | null\n boundResizer: () => void\n plotRegion: Region\n xRegion: Region\n yRegion: Region\n dataExtents: Extents\n unattachers: (() => void)[]\n\n constructor (parent: HTMLElement, reporters: ChartReporters) {\n this.parent = parent\n this.report = reporters\n this.theme = State.isDark() ? darkTheme : lightTheme\n this.canvas = document.createElement('canvas')\n this.visible = true\n parent.appendChild(this.canvas)\n const ctx = this.canvas.getContext('2d')\n if (!ctx) {\n console.error('error getting canvas context')\n return\n }\n this.ctx = ctx\n this.ctx.textAlign = 'center'\n this.ctx.textBaseline = 'middle'\n // Mouse handling\n this.mousePos = null\n bind(this.canvas, 'mousemove', (e: MouseEvent) => {\n // this.rect will be set in resize().\n if (!this.rect) return\n this.mousePos = {\n x: e.clientX - this.rect.left,\n y: e.clientY - this.rect.y\n }\n this.draw()\n })\n bind(this.canvas, 'mouseleave', () => {\n this.mousePos = null\n this.draw()\n })\n\n // Bind resize.\n const resizeObserver = new ResizeObserver(() => this.resize())\n resizeObserver.observe(this.parent)\n\n // Scrolling by wheel is smoother when the rate is slightly limited.\n this.wheelLimiter = null\n bind(this.canvas, 'wheel', (e: WheelEvent) => { this.wheel(e) })\n bind(this.canvas, 'click', (e: MouseEvent) => { this.click(e) })\n const setVis = () => {\n this.visible = document.visibilityState !== 'hidden'\n if (this.visible && this.renderScheduled) {\n this.renderScheduled = false\n this.draw()\n }\n }\n bind(document, 'visibilitychange', setVis)\n this.unattachers = [() => { Doc.unbind(document, 'visibilitychange', setVis) }]\n }\n\n wheeled () {\n this.wheelLimiter = window.setTimeout(() => { this.wheelLimiter = null }, 100)\n }\n\n /* clear the canvas. */\n clear () {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)\n }\n\n /* draw calls the child class's render method. */\n draw () {\n this.render()\n }\n\n /* click is the handler for a click event on the canvas. */\n click (e: MouseEvent) {\n this.report.click(e)\n }\n\n /* wheel is a mousewheel event handler. */\n wheel (e: WheelEvent) {\n this.zoom(e.deltaY < 0)\n e.preventDefault()\n }\n\n /*\n * resize updates the chart size. The parentHeight is an argument to support\n * updating the height programmatically after the caller sets a style.height\n * but before the clientHeight has been updated.\n */\n resize () {\n this.canvas.width = this.parent.clientWidth\n this.canvas.height = this.parent.clientHeight\n const xLblHeight = 30\n const yGuess = 40 // y label width guess. Will be adjusted when drawn.\n const plotExtents = new Extents(yGuess, this.canvas.width, 10, this.canvas.height - xLblHeight)\n const xLblExtents = new Extents(yGuess, this.canvas.width, this.canvas.height - xLblHeight, this.canvas.height)\n const yLblExtents = new Extents(0, yGuess, 10, this.canvas.height - xLblHeight)\n this.plotRegion = new Region(this.ctx, plotExtents)\n this.xRegion = new Region(this.ctx, xLblExtents)\n this.yRegion = new Region(this.ctx, yLblExtents)\n // After changing the visibility, this.canvas.getBoundingClientRect will\n // return nonsense until a render.\n window.requestAnimationFrame(() => {\n this.rect = this.canvas.getBoundingClientRect()\n this.report.resize()\n })\n }\n\n /* zoom is called when the user scrolls the mouse wheel on the canvas. */\n zoom (bigger: boolean) {\n if (this.wheelLimiter) return\n this.report.zoom(bigger)\n }\n\n /* The market handler will call unattach when the markets page is unloaded. */\n unattach () {\n for (const u of this.unattachers) u()\n this.unattachers = []\n }\n\n /* render must be implemented by the child class. */\n render () {\n console.error('child class must override render method')\n }\n\n /* applyLabelStyle applies the style used for axis tick labels. */\n applyLabelStyle (fontSize?: number) {\n this.ctx.textAlign = 'center'\n this.ctx.textBaseline = 'middle'\n this.ctx.font = `${fontSize ?? '14'}px 'sans', sans-serif`\n this.ctx.fillStyle = this.theme.axisLabel\n }\n\n /* plotXLabels applies the provided labels to the x axis and draws the grid. */\n plotXLabels (labels: LabelSet, minX: number, maxX: number, unitLines: string[]) {\n const extents = new Extents(minX, maxX, 0, 1)\n this.xRegion.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n this.applyLabelStyle()\n const centerX = (maxX + minX) / 2\n let lastX = minX\n let unitCenter = centerX\n labels.lbls.forEach(lbl => {\n ctx.fillText(lbl.txt, tools.x(lbl.val), tools.y(0.5))\n if (centerX >= lastX && centerX < lbl.val) {\n unitCenter = (lastX + lbl.val) / 2\n }\n lastX = lbl.val\n })\n ctx.font = '11px \\'sans\\', sans-serif'\n if (unitLines.length === 2) {\n ctx.fillText(unitLines[0], tools.x(unitCenter), tools.y(0.63))\n ctx.fillText(unitLines[1], tools.x(unitCenter), tools.y(0.23))\n } else if (unitLines.length === 1) {\n ctx.fillText(unitLines[0], tools.x(unitCenter), tools.y(0.5))\n }\n }, true)\n this.plotRegion.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n ctx.lineWidth = 1\n ctx.strokeStyle = this.theme.gridLines\n labels.lbls.forEach(lbl => {\n line(ctx, tools.x(lbl.val), tools.y(0), tools.x(lbl.val), tools.y(1))\n })\n }, true)\n }\n\n /*\n * plotYLabels applies the y labels based on the provided plot region, and\n * draws the grid.\n */\n plotYLabels (region: Region, labels: LabelSet, minY: number, maxY: number, unit: string) {\n const extents = new Extents(0, 1, minY, maxY)\n this.yRegion.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n this.applyLabelStyle()\n const centerY = maxY / 2\n let lastY = 0\n let unitCenter = centerY\n labels.lbls.forEach(lbl => {\n ctx.fillText(lbl.txt, tools.x(0.5), tools.y(lbl.val))\n if (centerY >= lastY && centerY < lbl.val) {\n unitCenter = (lastY + lbl.val) / 2\n }\n lastY = lbl.val\n })\n ctx.fillText(unit, tools.x(0.5), tools.y(unitCenter))\n }, true)\n region.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n ctx.lineWidth = 1\n ctx.strokeStyle = this.theme.gridLines\n labels.lbls.forEach(lbl => {\n line(ctx, tools.x(0), tools.y(lbl.val), tools.x(1), tools.y(lbl.val))\n })\n }, true)\n }\n\n /*\n * doYLabels generates and applies the y-axis labels, based upon the\n * provided plot region.\n */\n doYLabels (region: Region, step: number, unit: string, valFmt?: (v: number) => string) {\n this.applyLabelStyle()\n const yLabels = makeLabels(this.ctx, region.height(), this.dataExtents.y.min,\n this.dataExtents.y.max, 50, step, unit, valFmt)\n\n // Reassign the width of the y-label column to accommodate the widest text.\n const yAxisWidth = (yLabels.widest || 0) + 20 /* x padding */\n this.yRegion.extents.x.max = yAxisWidth\n this.yRegion.extents.y.max = region.extents.y.max\n\n this.plotRegion.extents.x.min = yAxisWidth\n this.xRegion.extents.x.min = yAxisWidth\n // Print the y labels.\n this.plotYLabels(region, yLabels, this.dataExtents.y.min, this.dataExtents.y.max, unit)\n return yLabels\n }\n\n // drawFrame draws an outline around the plotRegion.\n drawFrame () {\n this.plotRegion.plot(new Extents(0, 1, 0, 1), (ctx: CanvasRenderingContext2D, tools: Translator) => {\n ctx.lineWidth = 1\n ctx.strokeStyle = this.theme.gridBorder\n ctx.beginPath()\n tools.dataCoords(() => {\n ctx.moveTo(0, 0)\n ctx.lineTo(0, 1)\n ctx.lineTo(1, 1)\n ctx.lineTo(1, 0)\n ctx.lineTo(0, 0)\n })\n ctx.stroke()\n })\n }\n}\n\n/* DepthChart is a javascript Canvas-based depth chart renderer. */\nexport class DepthChart extends Chart {\n reporters: DepthReporters\n book: OrderBook\n zoomLevel: number\n lotSize: number\n rateStep: number\n lines: DepthLine[]\n markers: Record\n zoomInBttn: Region\n zoomOutBttn: Region\n baseUnit: string\n quoteUnit: string\n\n constructor (parent: HTMLElement, reporters: DepthReporters, zoom: number) {\n super(parent, {\n resize: () => this.resized(),\n click: (e: MouseEvent) => this.clicked(e),\n zoom: (bigger: boolean) => this.zoomed(bigger)\n })\n this.reporters = reporters\n this.zoomLevel = zoom\n this.lines = []\n this.markers = {\n buys: [],\n sells: []\n }\n this.setZoomBttns() // can't wait for requestAnimationFrame -> resized\n this.resize()\n }\n\n // setZoomBttns creates new regions for zoom in and zoom out buttons. It is\n // used in initiation of the buttons and resizing.\n setZoomBttns () {\n this.zoomInBttn = new Region(this.ctx, new Extents(0, 0, 0, 0))\n this.zoomOutBttn = new Region(this.ctx, new Extents(0, 0, 0, 0))\n }\n\n /* resized is called when the window or parent element are resized. */\n resized () {\n // The button region extents are set during drawing.\n this.setZoomBttns()\n if (this.book) this.draw()\n }\n\n /* zoomed zooms the current view in or out. bigger=true is zoom in. */\n zoomed (bigger: boolean) {\n if (!this.zoomLevel) return\n if (!this.book.buys || !this.book.sells) return\n this.wheeled()\n // Zoom in to 66%, but out to 150% = 1 / (2/3) so that the same zoom levels\n // are hit when reversing direction.\n this.zoomLevel *= bigger ? 2 / 3 : 3 / 2\n this.zoomLevel = clamp(this.zoomLevel, 0.005, 2)\n this.draw()\n this.reporters.zoom(this.zoomLevel)\n }\n\n /* clicked is the canvas 'click' event handler. */\n clicked (e: MouseEvent) {\n if (!this.dataExtents) return\n const x = e.clientX - this.rect.left\n const y = e.clientY - this.rect.y\n if (this.zoomInBttn.contains(x, y)) { this.zoom(true); return }\n if (this.zoomOutBttn.contains(x, y)) { this.zoom(false); return }\n const translator = this.plotRegion.translator(this.dataExtents)\n this.reporters.click(translator.unx(x))\n }\n\n // clear the canvas.\n clear () {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)\n }\n\n // set sets the current data set and draws.\n set (book: OrderBook, lotSize: number, rateStep: number, baseUnitInfo: UnitInfo, quoteUnitInfo: UnitInfo) {\n this.book = book\n this.lotSize = lotSize / baseUnitInfo.conventional.conversionFactor\n const [qFactor, bFactor] = [quoteUnitInfo.conventional.conversionFactor, baseUnitInfo.conventional.conversionFactor]\n this.rateStep = rateStep / RateEncodingFactor * qFactor / bFactor\n this.baseUnit = baseUnitInfo.conventional.unit\n this.quoteUnit = quoteUnitInfo.conventional.unit\n if (!this.zoomLevel) {\n const [midGap, gapWidth] = this.gap()\n // Default to 5% zoom, but with a minimum of 5 * midGap, but still observing\n // the hard cap of 200%.\n const minZoom = Math.max(gapWidth / midGap * 5, 0.05)\n this.zoomLevel = Math.min(minZoom, 2)\n }\n this.draw()\n }\n\n /*\n * render draws the chart.\n * 1. Calculate the data extents and translate the order book data to a\n * cumulative form.\n * 2. Draw axis ticks and grid, mid-gap line and value, zoom buttons, mouse\n * position indicator...\n * 4. Tick labels.\n * 5. Data.\n * 6. Epoch line legend.\n * 7. Hover legend.\n */\n render () {\n // if connection fails it is not possible to get book.\n if (!this.book || !this.visible || this.canvas.width === 0) {\n this.renderScheduled = true\n return\n }\n\n this.clear()\n // if (!this.book || this.book.empty()) return\n const ctx = this.ctx\n const mousePos = this.mousePos\n const buys = this.book.buys\n const sells = this.book.sells\n const [midGap, gapWidth] = this.gap()\n\n const halfWindow = this.zoomLevel * midGap / 2\n const high = midGap + halfWindow\n const low = midGap - halfWindow\n\n // Get a sorted copy of the markers list.\n const buyMarkers = [...this.markers.buys]\n const sellMarkers = [...this.markers.sells]\n buyMarkers.sort((a, b) => b.rate - a.rate)\n sellMarkers.sort((a, b) => a.rate - b.rate)\n const markers: DepthMark[] = []\n\n const buyDepth: [number, number][] = []\n const buyEpoch: [number, number][] = []\n const sellDepth: [number, number][] = []\n const sellEpoch: [number, number][] = []\n const volumeReport = {\n buyBase: 0,\n buyQuote: 0,\n sellBase: 0,\n sellQuote: 0\n }\n let sum = 0\n // The epoch line is above the non-epoch region, so the epochSum y value\n // must account for non-epoch orders too.\n let epochSum = 0\n\n for (let i = 0; i < buys.length; i++) {\n const ord = buys[i]\n epochSum += ord.qty\n if (ord.rate >= low) buyEpoch.push([ord.rate, epochSum])\n if (ord.epoch) continue\n sum += ord.qty\n buyDepth.push([ord.rate, sum])\n volumeReport.buyBase += ord.qty\n volumeReport.buyQuote += ord.qty * ord.rate\n while (buyMarkers.length && floatCompare(buyMarkers[0].rate, ord.rate)) {\n const mark = buyMarkers.shift()\n if (!mark) continue\n markers.push({\n rate: mark.rate,\n qty: ord.epoch ? epochSum : sum,\n sell: ord.sell,\n active: mark.active\n })\n }\n }\n const buySum = buyDepth.length ? last(buyDepth)[1] : 0\n buyDepth.push([low, buySum])\n const epochBuySum = buyEpoch.length ? last(buyEpoch)[1] : 0\n buyEpoch.push([low, epochBuySum])\n\n epochSum = sum = 0\n for (let i = 0; i < sells.length; i++) {\n const ord = sells[i]\n epochSum += ord.qty\n if (ord.rate <= high) sellEpoch.push([ord.rate, epochSum])\n if (ord.epoch) continue\n sum += ord.qty\n sellDepth.push([ord.rate, sum])\n volumeReport.sellBase += ord.qty\n volumeReport.sellQuote += ord.qty * ord.rate\n while (sellMarkers.length && floatCompare(sellMarkers[0].rate, ord.rate)) {\n const mark = sellMarkers.shift()\n if (!mark) continue\n markers.push({\n rate: mark.rate,\n qty: ord.epoch ? epochSum : sum,\n sell: ord.sell,\n active: mark.active\n })\n }\n }\n // Add a data point going to the left so that the data doesn't end with a\n // vertical line.\n const sellSum = sellDepth.length ? last(sellDepth)[1] : 0\n sellDepth.push([high, sellSum])\n const epochSellSum = sellEpoch.length ? last(sellEpoch)[1] : 0\n sellEpoch.push([high, epochSellSum])\n\n // Add ~30px padding to the top of the chart.\n const h = this.xRegion.extents.y.min\n const growthFactor = (h + 30) / h\n const maxY = (epochSellSum && epochBuySum ? Math.max(epochBuySum, epochSellSum) : epochSellSum || epochBuySum || 1) * growthFactor\n\n const dataExtents = new Extents(low, high, 0, maxY)\n this.dataExtents = dataExtents\n\n this.doYLabels(this.plotRegion, this.lotSize, this.baseUnit)\n\n // Print the x labels\n const xLabels = makeLabels(ctx, this.plotRegion.width(), dataExtents.x.min,\n dataExtents.x.max, 100, this.rateStep, '')\n\n this.plotXLabels(xLabels, low, high, [`${this.quoteUnit}/`, this.baseUnit])\n\n // A function to be run at the end if there is legend data to display.\n let mouseData: MouseReport | null = null\n\n // Draw the grid.\n this.drawFrame()\n this.plotRegion.plot(dataExtents, (ctx, tools) => {\n ctx.lineWidth = 1\n // first, a square around the plot area.\n ctx.strokeStyle = this.theme.gridBorder\n // draw a line to indicate mid-gap\n ctx.lineWidth = 2.5\n ctx.strokeStyle = this.theme.gapLine\n line(ctx, tools.x(midGap), tools.y(0), tools.x(midGap), tools.y(0.3 * dataExtents.y.max))\n\n ctx.font = '30px \\'demi-sans\\', sans-serif'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.fillStyle = this.theme.value\n const y = 0.5 * dataExtents.y.max\n ctx.fillText(formatLabelValue(midGap), tools.x(midGap), tools.y(y))\n ctx.font = '12px \\'sans\\', sans-serif'\n // ctx.fillText('mid-market price', tools.x(midGap), tools.y(y) + 24)\n ctx.fillText(`${(gapWidth / midGap * 100).toFixed(2)}% spread`,\n tools.x(midGap), tools.y(y) + 24)\n\n // Draw zoom buttons.\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n const topCenterX = this.plotRegion.extents.midX\n const topCenterY = tools.y(maxY * 0.9)\n const zoomPct = dataExtents.xRange / midGap * 100\n const zoomText = `${zoomPct.toFixed(1)}%`\n const w = ctx.measureText(zoomText).width\n ctx.font = '13px \\'sans\\', sans-serif'\n ctx.fillText(zoomText, topCenterX, topCenterY + 1)\n // define the region for the zoom button\n const bttnSize = 20\n const xPad = 10\n let bttnLeft = topCenterX - w / 2 - xPad - bttnSize\n const bttnTop = topCenterY - bttnSize / 2\n this.zoomOutBttn.setExtents(\n bttnLeft,\n bttnLeft + bttnSize,\n bttnTop,\n bttnTop + bttnSize\n )\n let hover = mousePos && this.zoomOutBttn.contains(mousePos.x, mousePos.y)\n this.zoomOutBttn.plot(new Extents(0, 1, 0, 1), ctx => {\n ctx.font = '12px \\'icomoon\\''\n ctx.fillStyle = this.theme.zoom\n if (hover) {\n ctx.fillStyle = this.theme.zoomHover\n ctx.font = '13px \\'icomoon\\''\n }\n ctx.fillText(minusChar, this.zoomOutBttn.extents.midX, this.zoomOutBttn.extents.midY)\n })\n bttnLeft = topCenterX + w / 2 + xPad\n this.zoomInBttn.setExtents(\n bttnLeft,\n bttnLeft + bttnSize,\n bttnTop,\n bttnTop + bttnSize\n )\n hover = mousePos && this.zoomInBttn.contains(mousePos.x, mousePos.y)\n this.zoomInBttn.plot(new Extents(0, 1, 0, 1), ctx => {\n ctx.font = '12px \\'icomoon\\''\n ctx.fillStyle = this.theme.zoom\n if (hover) {\n ctx.fillStyle = this.theme.zoomHover\n ctx.font = '14px \\'icomoon\\''\n }\n ctx.fillText(plusChar, this.zoomInBttn.extents.midX, this.zoomInBttn.extents.midY)\n })\n\n // Draw a dotted vertical line where the mouse is, and a dot at the level\n // of the depth line.\n const drawLine = (x: number, color: string) => {\n if (x > high || x < low) return\n ctx.save()\n ctx.setLineDash([3, 5])\n ctx.lineWidth = 1.5\n ctx.strokeStyle = color\n line(ctx, tools.x(x), tools.y(0), tools.x(x), tools.y(maxY))\n ctx.restore()\n }\n\n for (const line of this.lines || []) {\n drawLine(line.rate, line.color)\n }\n\n const tolerance = (high - low) * 0.005\n const hoverMarkers = []\n for (const marker of markers || []) {\n const hovered = (mousePos && withinTolerance(marker.rate, tools.unx(mousePos.x), tolerance))\n if (hovered) hoverMarkers.push(marker.rate)\n ctx.save()\n ctx.lineWidth = (hovered || marker.active) ? 5 : 3\n ctx.strokeStyle = marker.sell ? this.theme.sellLine : this.theme.buyLine\n ctx.fillStyle = marker.sell ? this.theme.sellFill : this.theme.buyFill\n const size = (hovered || marker.active) ? 10 : 8\n ctx.beginPath()\n const tip = {\n x: tools.x(marker.rate),\n y: tools.y(marker.qty) - 8\n }\n const top = tip.y - (Math.sqrt(3) * size / 2) // cos(30)\n ctx.moveTo(tip.x, tip.y)\n ctx.lineTo(tip.x - size / 2, top)\n ctx.lineTo(tip.x + size / 2, top)\n ctx.closePath()\n ctx.stroke()\n ctx.fill()\n ctx.restore()\n }\n\n // If the mouse is in the chart area, draw the crosshairs.\n if (!mousePos) return\n if (!this.plotRegion.contains(mousePos.x, mousePos.y)) return\n // The mouse is in the plot region. Get the data coordinates and find the\n // side and depth for the x value.\n const dataX = tools.unx(mousePos.x)\n let evalSide = sellDepth\n let trigger = (ptX: number) => ptX >= dataX\n let dotColor = this.theme.sellLine\n if (dataX < midGap) {\n evalSide = buyDepth\n trigger = (ptX) => ptX <= dataX\n dotColor = this.theme.buyLine\n }\n let bestDepth = evalSide[0]\n for (let i = 0; i < evalSide.length; i++) {\n const pt = evalSide[i]\n if (trigger(pt[0])) break\n bestDepth = pt\n }\n drawLine(dataX, this.theme.crosshairs)\n mouseData = {\n rate: dataX,\n depth: bestDepth[1],\n dotColor: dotColor,\n hoverMarkers: hoverMarkers\n }\n })\n\n // Draw the epoch lines\n ctx.lineWidth = 1.5\n ctx.setLineDash([3, 3])\n // epoch sells\n ctx.fillStyle = this.theme.sellFill\n ctx.strokeStyle = this.theme.sellLine\n this.drawDepth(sellEpoch)\n // epoch buys\n ctx.fillStyle = this.theme.buyFill\n ctx.strokeStyle = this.theme.buyLine\n this.drawDepth(buyEpoch)\n\n // Draw the book depth.\n ctx.lineWidth = 2.5\n ctx.setLineDash([])\n // book sells\n ctx.fillStyle = this.theme.sellFill\n ctx.strokeStyle = this.theme.sellLine\n this.drawDepth(sellDepth)\n // book buys\n ctx.fillStyle = this.theme.buyFill\n ctx.strokeStyle = this.theme.buyLine\n this.drawDepth(buyDepth)\n\n // Display the dot at the intersection of the mouse hover line and the depth\n // line. This should be drawn after the depths.\n if (mouseData) {\n this.plotRegion.plot(dataExtents, (ctx, tools) => {\n if (!mouseData) return // For TypeScript. Duh.\n dot(ctx, tools.x(mouseData.rate), tools.y(mouseData.depth), mouseData.dotColor, 5)\n })\n }\n\n // Report the book volumes.\n this.reporters.volume(volumeReport)\n this.reporters.mouse(mouseData)\n }\n\n /* drawDepth draws a single side's depth chart data. */\n drawDepth (depth: [number, number][]) {\n const firstPt = depth[0]\n let y = firstPt[1]\n let x: number\n this.plotRegion.plot(this.dataExtents, (ctx, tools) => {\n tools.dataCoords(() => {\n ctx.beginPath()\n ctx.moveTo(firstPt[0], firstPt[1])\n for (let i = 0; i < depth.length; i++) {\n // Set x, but don't set y until we draw the horizontal line.\n x = depth[i][0]\n ctx.lineTo(x, y)\n // If this is past the render edge, quit drawing.\n y = depth[i][1]\n ctx.lineTo(x, y)\n }\n })\n ctx.stroke()\n tools.dataCoords(() => {\n ctx.lineTo(x, 0)\n ctx.lineTo(firstPt[0], 0)\n })\n ctx.closePath()\n ctx.globalAlpha = 0.25\n ctx.fill()\n })\n }\n\n /* returns the mid-gap rate and gap width as a tuple. */\n gap () {\n const [b, s] = [this.book.bestGapBuy(), this.book.bestGapSell()]\n if (!b) {\n if (!s) return [1, 0]\n return [s.rate, 0]\n } else if (!s) return [b.rate, 0]\n return [(s.rate + b.rate) / 2, s.rate - b.rate]\n }\n\n /* setLines stores the indicator lines to draw. */\n setLines (lines: DepthLine[]) {\n this.lines = lines\n }\n\n /* setMarkers sets the indicator markers to draw. */\n setMarkers (markers: Record) {\n this.markers = markers\n }\n}\n\n/* CandleChart is a candlestick data renderer. */\nexport class CandleChart extends Chart {\n reporters: CandleReporters\n data: CandlesPayload\n zoomLevel: number\n numToShow: number\n candleRegion: Region\n volumeRegion: Region\n resizeTimer: number\n zoomLevels: number[]\n market: Market\n rateConversionFactor: number\n\n constructor (parent: HTMLElement, reporters: CandleReporters) {\n super(parent, {\n resize: () => this.resized(),\n click: (/* e: MouseEvent */) => { this.clicked() },\n zoom: (bigger: boolean) => this.zoomed(bigger)\n })\n this.reporters = reporters\n this.zoomLevel = 1\n this.numToShow = 100\n this.resize()\n }\n\n /* resized is called when the window or parent element are resized. */\n resized () {\n const ext = this.plotRegion.extents\n const candleExtents = new Extents(ext.x.min, ext.x.max, ext.y.min, ext.y.min + ext.yRange * 0.85)\n this.candleRegion = new Region(this.ctx, candleExtents)\n const volumeExtents = new Extents(ext.x.min, ext.x.max, ext.y.min + 0.85 * ext.yRange, ext.y.max)\n this.volumeRegion = new Region(this.ctx, volumeExtents)\n // Set a delay on the render to prevent lag.\n if (this.resizeTimer) clearTimeout(this.resizeTimer)\n this.resizeTimer = window.setTimeout(() => this.draw(), 100)\n }\n\n clicked (/* e: MouseEvent */) {\n // handle clicks\n }\n\n /* zoomed zooms the current view in or out. bigger=true is zoom in. */\n zoomed (bigger: boolean) {\n // bigger actually means fewer candles -> reduce zoomLevels index.\n const idx = this.zoomLevels.indexOf(this.numToShow)\n if (bigger) {\n if (idx === 0) return\n this.numToShow = this.zoomLevels[idx - 1]\n } else {\n if (this.zoomLevels.length <= idx + 1 || this.numToShow > this.data.candles.length) return\n this.numToShow = this.zoomLevels[idx + 1]\n }\n this.draw()\n }\n\n /* render draws the chart */\n render () {\n const data = this.data\n if (!data || !this.visible || this.canvas.width === 0) {\n this.renderScheduled = true\n return\n }\n const candleWidth = data.ms\n const mousePos = this.mousePos\n const allCandles = data.candles || []\n\n const n = Math.min(this.numToShow, allCandles.length)\n const candles = allCandles.slice(allCandles.length - n)\n\n this.clear()\n\n // If there are no candles. just don't draw anything.\n if (n === 0) return\n\n // padding definition and some helper functions to parse candles.\n const candleWidthPadding = 0.2\n const start = (c: Candle) => truncate(c.endStamp, candleWidth)\n const end = (c: Candle) => start(c) + candleWidth\n const paddedStart = (c: Candle) => start(c) + candleWidthPadding * candleWidth\n const paddedWidth = (1 - 2 * candleWidthPadding) * candleWidth\n\n const first = candles[0]\n const last = candles[n - 1]\n\n let [high, low, highVol] = [first.highRate, first.lowRate, first.matchVolume]\n for (const c of candles) {\n if (c.highRate > high) high = c.highRate\n if (c.lowRate < low) low = c.lowRate\n if (c.matchVolume > highVol) highVol = c.matchVolume\n }\n\n // Calculate data extents and store them. They are used to apply labels.\n const rateStep = this.market.ratestep\n const dataExtents = new Extents(start(first), end(last), low, high)\n if (low === high) {\n // If there is no price movement at all in the window, show a little more\n // top and bottom so things render nicely.\n dataExtents.y.min -= rateStep\n dataExtents.y.max += rateStep\n }\n this.dataExtents = dataExtents\n\n // Apply labels.\n const rFactor = this.rateConversionFactor\n this.doYLabels(this.candleRegion, rateStep, this.market.quotesymbol, v => formatLabelValue(v / rFactor))\n this.candleRegion.extents.x.min = this.yRegion.extents.x.max\n this.volumeRegion.extents.x.min = this.yRegion.extents.x.max\n\n const xLabels = makeCandleTimeLabels(candles, candleWidth, this.plotRegion.width(), 100)\n\n this.plotXLabels(xLabels, start(first), end(last), [])\n\n this.drawFrame()\n\n // Highlight the candle if the user mouse is over the canvas.\n let mouseCandle: Candle | null = null\n if (mousePos) {\n this.plotRegion.plot(new Extents(dataExtents.x.min, dataExtents.x.max, 0, 1), (ctx, tools) => {\n const selectedStartStamp = truncate(tools.unx(mousePos.x), candleWidth)\n for (const c of candles) {\n if (start(c) === selectedStartStamp) {\n mouseCandle = c\n ctx.fillStyle = this.theme.gridLines\n ctx.fillRect(tools.x(start(c)), tools.y(0), tools.w(candleWidth), tools.h(1))\n break\n }\n }\n })\n if (mouseCandle) {\n const yExt = this.xRegion.extents.y\n this.xRegion.plot(new Extents(dataExtents.x.min, dataExtents.x.max, yExt.min, yExt.max), (ctx, tools) => {\n if (!mouseCandle) return // For TypeScript. Duh.\n this.applyLabelStyle()\n const rangeTxt = `${new Date(start(mouseCandle)).toLocaleString()} - ${new Date(end(mouseCandle)).toLocaleString()}`\n const [xPad, yPad] = [25, 2]\n const rangeWidth = ctx.measureText(rangeTxt).width + 2 * xPad\n const rangeHeight = 16\n let centerX = tools.x((start(mouseCandle) + end(mouseCandle)) / 2)\n let left = centerX - rangeWidth / 2\n const xExt = this.xRegion.extents.x\n if (left < xExt.min) left = xExt.min\n else if (left + rangeWidth > xExt.max) left = xExt.max - rangeWidth\n centerX = left + rangeWidth / 2\n const top = yExt.min + (this.xRegion.height() - rangeHeight) / 2\n ctx.fillStyle = this.theme.legendFill\n ctx.strokeStyle = this.theme.gridBorder\n const rectArgs: [number, number, number, number] = [left - xPad, top - yPad, rangeWidth + 2 * xPad, rangeHeight + 2 * yPad]\n ctx.fillRect(...rectArgs)\n ctx.strokeRect(...rectArgs)\n this.applyLabelStyle()\n ctx.fillText(rangeTxt, centerX, this.xRegion.extents.midY, rangeWidth)\n })\n }\n }\n\n // Draw the volume bars.\n const volDataExtents = new Extents(start(first), end(last), 0, highVol)\n this.volumeRegion.plot(volDataExtents, (ctx, tools) => {\n ctx.fillStyle = this.theme.gridBorder\n for (const c of candles) {\n ctx.fillRect(tools.x(paddedStart(c)), tools.y(0), tools.w(paddedWidth), tools.h(c.matchVolume))\n }\n })\n\n // Draw the candles.\n this.candleRegion.plot(dataExtents, (ctx, tools) => {\n ctx.lineWidth = 1\n for (const c of candles) {\n const desc = c.startRate > c.endRate\n const [x, y, w, h] = [tools.x(paddedStart(c)), tools.y(c.startRate), tools.w(paddedWidth), tools.h(c.endRate - c.startRate)]\n const [high, low, cx] = [tools.y(c.highRate), tools.y(c.lowRate), w / 2 + x]\n ctx.strokeStyle = desc ? this.theme.sellLine : this.theme.buyLine\n ctx.fillStyle = desc ? this.theme.sellFill : this.theme.buyFill\n\n ctx.beginPath()\n ctx.moveTo(cx, high)\n ctx.lineTo(cx, low)\n ctx.stroke()\n\n ctx.fillRect(x, y, w, h)\n ctx.strokeRect(x, y, w, h)\n }\n })\n\n // Report the mouse candle.\n this.reporters.mouse(mouseCandle)\n }\n\n /* setCandles sets the candle data and redraws the chart. */\n setCandles (data: CandlesPayload, market: Market, baseUnitInfo: UnitInfo, quoteUnitInfo: UnitInfo) {\n this.data = data\n if (!data.candles) return\n this.market = market\n const [qFactor, bFactor] = [quoteUnitInfo.conventional.conversionFactor, baseUnitInfo.conventional.conversionFactor]\n this.rateConversionFactor = RateEncodingFactor * qFactor / bFactor\n let n = 25\n this.zoomLevels = []\n const maxCandles = Math.max(data.candles.length, 1000)\n while (n < maxCandles) {\n this.zoomLevels.push(n)\n n *= 2\n }\n this.numToShow = 100\n this.draw()\n }\n}\n\ninterface WaveOpts {\n message?: string\n backgroundColor?: string | boolean // true for background color\n}\n\n/* Wave is a loading animation that displays a colorful line that oscillates */\nexport class Wave extends Chart {\n ani: Animation\n size: [number, number]\n region: Region\n colorShift: number\n opts: WaveOpts\n msgRegion: Region\n fontSize: number\n\n constructor (parent: HTMLElement, opts?: WaveOpts) {\n super(parent, {\n resize: () => this.resized(),\n click: (/* e: MouseEvent */) => { /* pass */ },\n zoom: (/* bigger: boolean */) => { /* pass */ }\n })\n this.canvas.classList.add('fill-abs')\n this.canvas.style.zIndex = '5'\n\n this.opts = opts ?? {}\n\n const period = 1500 // ms\n const start = Math.random() * period\n this.colorShift = Math.random() * 360\n\n // y = A*cos(k*x + theta*t + c)\n // combine three waves with different periods and speeds and phases.\n const amplitudes = [1, 0.65, 0.75]\n const ks = [3, 3, 2]\n const speeds = [Math.PI, Math.PI * 10 / 9, Math.PI / 2.5]\n const phases = [0, 0, Math.PI * 1.5]\n const n = 75\n const single = (n: number, angularX: number, angularTime: number): number => {\n return amplitudes[n] * Math.cos(ks[n] * angularX + speeds[n] * angularTime + phases[n])\n }\n const value = (x: number, angularTime: number): number => {\n const angularX = x * Math.PI * 2\n return (single(0, angularX, angularTime) + single(1, angularX, angularTime) + single(2, angularX, angularTime)) / 3\n }\n this.resize()\n this.ani = new Animation(Animation.Forever, () => {\n const angularTime = (new Date().getTime() - start) / period * Math.PI * 2\n const values = []\n for (let i = 0; i < n; i++) {\n values.push(value(i / (n - 1), angularTime))\n }\n this.drawValues(values)\n })\n }\n\n resized () {\n const opts = this.opts\n const [maxW, maxH] = [150, 100]\n const [cw, ch] = [this.canvas.width, this.canvas.height]\n let [w, h] = [cw * 0.8, ch * 0.8]\n if (w > maxW) w = maxW\n if (h > maxH) h = maxH\n let [l, t] = [(cw - w) / 2, (ch - h) / 2]\n if (opts.message) {\n this.fontSize = clamp(h * 0.15, 10, 14)\n this.applyLabelStyle(this.fontSize)\n const ypad = this.fontSize * 0.5\n const halfH = (this.fontSize / 2) + ypad\n t -= halfH\n this.msgRegion = new Region(this.ctx, new Extents(0, cw, t + h, t + h + 2 * halfH))\n }\n this.region = new Region(this.ctx, new Extents(l, l + w, t, t + h))\n }\n\n drawValues (values: number[]) {\n if (!this.region) return\n this.clear()\n const hsl = (h: number) => `hsl(${h}, 35%, 50%)`\n\n const { region, msgRegion, canvas: { width: w, height: h }, opts: { backgroundColor: bg, message: msg }, colorShift, ctx } = this\n\n if (bg) {\n if (bg === true) ctx.fillStyle = window.getComputedStyle(document.body, null).getPropertyValue('background-color')\n else ctx.fillStyle = bg\n ctx.fillRect(0, 0, w, h)\n }\n\n region.plot(new Extents(0, 1, -1, 1), (ctx: CanvasRenderingContext2D, t: Translator) => {\n ctx.lineWidth = 4\n ctx.lineCap = 'round'\n\n const shift = colorShift + (new Date().getTime() % 2000) / 2000 * 360 // colors move with frequency 1 / 2s\n const grad = ctx.createLinearGradient(t.x(0), 0, t.x(1), 0)\n grad.addColorStop(0, hsl(shift))\n ctx.strokeStyle = grad\n\n ctx.beginPath()\n ctx.moveTo(t.x(0), t.y(values[0]))\n for (let i = 1; i < values.length; i++) {\n const prog = i / (values.length - 1)\n grad.addColorStop(prog, hsl(prog * 300 + shift))\n ctx.lineTo(t.x(prog), t.y(values[i]))\n }\n ctx.stroke()\n })\n if (!msg) return\n msgRegion.plot(new Extents(0, 1, 0, 1), (ctx: CanvasRenderingContext2D, t: Translator) => {\n ctx.fillText(msg, t.x(0.5), t.y(0.5), this.msgRegion.width())\n })\n }\n\n render () { /* pass */ }\n\n stop () {\n this.ani.stop()\n this.canvas.remove()\n }\n}\n\n/*\n * Extents holds a min and max in both the x and y directions, and provides\n * getters for related data.\n */\nclass Extents {\n x: MinMax\n y: MinMax\n\n constructor (xMin: number, xMax: number, yMin: number, yMax: number) {\n this.setExtents(xMin, xMax, yMin, yMax)\n }\n\n setExtents (xMin: number, xMax: number, yMin: number, yMax: number) {\n this.x = {\n min: xMin,\n max: xMax\n }\n this.y = {\n min: yMin,\n max: yMax\n }\n }\n\n get xRange (): number {\n return this.x.max - this.x.min\n }\n\n get midX (): number {\n return (this.x.max + this.x.min) / 2\n }\n\n get yRange (): number {\n return this.y.max - this.y.min\n }\n\n get midY (): number {\n return (this.y.max + this.y.min) / 2\n }\n}\n\n/*\n * Region applies an Extents to the canvas, providing utilities for coordinate\n * transformations and restricting drawing to a specified region of the canvas.\n */\nclass Region {\n context: CanvasRenderingContext2D\n extents: Extents\n\n constructor (context: CanvasRenderingContext2D, extents: Extents) {\n this.context = context\n this.extents = extents\n }\n\n setExtents (xMin: number, xMax: number, yMin: number, yMax: number) {\n this.extents.setExtents(xMin, xMax, yMin, yMax)\n }\n\n width (): number {\n return this.extents.xRange\n }\n\n height (): number {\n return this.extents.yRange\n }\n\n contains (x: number, y: number): boolean {\n const ext = this.extents\n return (x < ext.x.max && x > ext.x.min &&\n y < ext.y.max && y > ext.y.min)\n }\n\n /*\n * A translator provides 4 function for coordinate transformations. x and y\n * translate data coordinates to canvas coordinates for the specified data\n * Extents. unx and uny translate canvas coordinates to data coordinates.\n */\n translator (dataExtents: Extents): Translator {\n const region = this.extents\n const xMin = dataExtents.x.min\n // const xMax = dataExtents.x.max\n const yMin = dataExtents.y.min\n // const yMax = dataExtents.y.max\n const yRange = dataExtents.yRange\n const xRange = dataExtents.xRange\n const screenMinX = region.x.min\n const screenW = region.x.max - screenMinX\n const screenMaxY = region.y.max\n const screenH = screenMaxY - region.y.min\n const xFactor = screenW / xRange\n const yFactor = screenH / yRange\n return {\n x: (x: number) => (x - xMin) * xFactor + screenMinX,\n y: (y: number) => screenMaxY - (y - yMin) * yFactor,\n unx: (x: number) => (x - screenMinX) / xFactor + xMin,\n uny: (y: number) => yMin - (y - screenMaxY) / yFactor,\n w: (w: number) => w / xRange * screenW,\n h: (h: number) => -h / yRange * screenH,\n dataCoords: () => { /* Added when using plot() */ }\n }\n }\n\n /* clear clears the region. */\n clear () {\n const ext = this.extents\n this.context.clearRect(ext.x.min, ext.y.min, ext.xRange, ext.yRange)\n }\n\n /* plot prepares tools for drawing using data coordinates. */\n plot (dataExtents: Extents, drawFunc: (ctx: CanvasRenderingContext2D, tools: Translator) => void, skipMask?: boolean) {\n const ctx = this.context\n const region = this.extents\n ctx.save() // Save the original state\n if (!skipMask) {\n ctx.beginPath()\n ctx.rect(region.x.min, region.y.min, region.xRange, region.yRange)\n ctx.clip()\n }\n\n // The drawFunc will be passed a set of tool that can be used to assist\n // drawing. The tools start with the transformation functions.\n const tools = this.translator(dataExtents)\n\n // Create a transformation that allows drawing in data coordinates. It's\n // not advisable to stroke or add text with this transform in place, as the\n // result will be distorted. You can however use ctx.moveTo and ctx.lineTo\n // with this transform in place using data coordinates, and remove the\n // transform before stroking. The dataCoords method of the supplied tool\n // provides this functionality.\n const yRange = dataExtents.yRange\n const xFactor = region.xRange / dataExtents.xRange\n const yFactor = region.yRange / yRange\n const xMin = dataExtents.x.min\n const yMin = dataExtents.y.min\n // These translation factors are complicated because the (0, 0) of the\n // region is not necessarily the (0, 0) of the canvas.\n const tx = (region.x.min + xMin) - xMin * xFactor\n const ty = -region.y.min - (yRange - yMin) * yFactor\n const setTransform = () => {\n // Data coordinates are flipped about y. Flip the coordinates and\n // translate top left corner to canvas (0, 0).\n ctx.transform(1, 0, 0, -1, -xMin, yMin)\n // Scale to data coordinates and shift into place for the region's offset\n // on the canvas.\n ctx.transform(xFactor, 0, 0, yFactor, tx, ty)\n }\n // dataCoords allows some drawing to be performed directly in data\n // coordinates. Most actual drawing functions like ctx.stroke and\n // ctx.fillRect should not be called from inside dataCoords, but\n // ctx.moveTo and ctx.LineTo are fine.\n tools.dataCoords = f => {\n ctx.save()\n setTransform()\n f()\n ctx.restore()\n }\n\n drawFunc(this.context, tools)\n ctx.restore()\n }\n}\n\n/*\n * makeLabels attempts to create the appropriate labels for the specified\n * screen size, context, and label spacing.\n */\nfunction makeLabels (\n ctx: CanvasRenderingContext2D,\n screenW: number,\n min: number,\n max: number,\n spacingGuess: number,\n step: number,\n unit: string,\n valFmt?: (v: number) => string\n): LabelSet {\n valFmt = valFmt || formatLabelValue\n const n = screenW / spacingGuess\n const diff = max - min\n if (n < 1 || diff <= 0) return { lbls: [] }\n const tickGuess = diff / n\n // make the tick spacing a multiple of the step\n const tick = tickGuess + step - (tickGuess % step)\n let x = min + tick - (min % tick)\n const absMax = Math.max(Math.abs(max), Math.abs(min))\n // The Math.round part is the minimum precision required to see the change in the numbers.\n // The 2 accounts for the precision of the tick.\n const sigFigs = Math.round(Math.log10(absMax / tick)) + 2\n const pts: Label[] = []\n let widest = 0\n while (x < max) {\n x = Number(x.toPrecision(sigFigs))\n const lbl = valFmt(x)\n widest = Math.max(widest, ctx.measureText(lbl).width)\n pts.push({\n val: x,\n txt: lbl\n })\n x += tick\n }\n const unitW = ctx.measureText(unit).width\n if (unitW > widest) widest = unitW\n return {\n widest: widest,\n lbls: pts\n }\n}\n\nconst months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']\n\n/* makeCandleTimeLabels prepares labels for candlestick data. */\nfunction makeCandleTimeLabels (candles: Candle[], dur: number, screenW: number, spacingGuess: number): LabelSet {\n const first = candles[0]\n const last = candles[candles.length - 1]\n const start = truncate(first.endStamp, dur)\n const end = truncate(last.endStamp, dur) + dur\n const diff = end - start\n const n = Math.min(candles.length, screenW / spacingGuess)\n const tick = truncate(diff / n, dur)\n if (tick === 0) {\n console.error('zero tick', dur, diff, n) // probably won't happen, but it'd suck if it did\n return { lbls: [] }\n }\n let x = start\n const zoneOffset = new Date().getTimezoneOffset()\n const dayStamp = (x: number) => {\n x = x - zoneOffset * 60000\n return x - (x % 86400000)\n }\n let lastDay = dayStamp(start)\n let lastYear = 0 // new Date(start).getFullYear()\n if (dayStamp(first.endStamp) === dayStamp(last.endStamp)) lastDay = 0 // Force at least one day stamp.\n const pts = []\n let label\n if (dur < 86400000) {\n label = (d: Date, x: number) => {\n const day = dayStamp(x)\n if (day !== lastDay) return `${months[d.getMonth()]}${d.getDate()} ${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`\n else return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`\n }\n } else {\n label = (d: Date) => {\n const year = d.getFullYear()\n if (year !== lastYear) return `${months[d.getMonth()]}${d.getDate()} '${String(year).slice(2, 4)}`\n else return `${months[d.getMonth()]}${d.getDate()}`\n }\n }\n while (x <= end) {\n const d = new Date(x)\n pts.push({\n val: x,\n txt: label(d, x)\n })\n lastDay = dayStamp(x)\n lastYear = d.getFullYear()\n x += tick\n }\n return { lbls: pts }\n}\n\n/* The last element of an array. */\nfunction last (arr: any[]): any {\n return arr[arr.length - 1]\n}\n\n/* line draws a line with the provided context. */\nfunction line (ctx: CanvasRenderingContext2D, x0: number, y0: number, x1: number, y1: number, skipStroke?: boolean) {\n ctx.beginPath()\n ctx.moveTo(x0, y0)\n ctx.lineTo(x1, y1)\n if (!skipStroke) ctx.stroke()\n}\n\n/* dot draws a circle with the provided context. */\nfunction dot (ctx: CanvasRenderingContext2D, x: number, y: number, color: string, radius: number) {\n ctx.fillStyle = color\n ctx.beginPath()\n ctx.arc(x, y, radius, 0, PIPI)\n ctx.fill()\n}\n\n/* clamp returns v if min <= v <= max, else min or max. */\nfunction clamp (v: number, min: number, max: number): number {\n if (v < min) return min\n if (v > max) return max\n return v\n}\n\n/* labelSpecs is specifications for axis tick labels. */\nconst labelSpecs = {\n minimumSignificantDigits: 4,\n maximumSignificantDigits: 5\n}\n\n/* formatLabelValue formats the provided value using the labelSpecs format. */\nfunction formatLabelValue (x: number) {\n return x.toLocaleString('en-us', labelSpecs)\n}\n\n/* floatCompare compares two floats to within a tolerance of 1e-8. */\nfunction floatCompare (a: number, b: number) {\n return withinTolerance(a, b, 1e-8)\n}\n\n/*\n * withinTolerance returns true if the difference between a and b are with\n * the specified tolerance.\n */\nfunction withinTolerance (a: number, b: number, tolerance: number) {\n return Math.abs(a - b) < Math.abs(tolerance)\n}\n\nfunction truncate (v: number, w: number): number {\n return v - (v % w)\n}\n","import Doc from './doc'\nimport { postJSON } from './http'\nimport State from './state'\nimport * as intl from './locales'\nimport * as OrderUtil from './orderutil'\nimport { Wave } from './charts'\nimport {\n app,\n PasswordCache,\n SupportedAsset,\n PageElement,\n WalletDefinition,\n ConfigOption,\n Exchange,\n Market,\n UnitInfo,\n BondAsset,\n WalletState,\n BalanceNote,\n Order,\n XYRange,\n WalletStateNote,\n WalletInfo,\n Token,\n WalletCreationNote,\n CoreNote\n} from './registry'\nimport { XYRangeHandler } from './opts'\n\ninterface ConfigOptionInput extends HTMLInputElement {\n configOpt: ConfigOption\n}\n\ninterface ProgressPoint {\n stamp: number\n progress: number\n}\n\ninterface CurrentAsset {\n asset: SupportedAsset\n parentAsset?: SupportedAsset\n winfo: WalletInfo | Token\n // selectedDef is used in a strange way for tokens. If a token's parent wallet\n // already exists, then selectedDef is going to be the Token.definition.\n // BUT, if the token's parent wallet doesn't exist yet, the NewWalletForm\n // operates in a combined configuration mode, and the selectedDef will be the\n // currently selected parent asset definition. There is no loss of info\n // in such a case, because the token wallet only has one definition.\n selectedDef: WalletDefinition\n}\n\ninterface WalletConfig {\n assetID: number\n config: Record\n walletType: string\n}\n\n/*\n * NewWalletForm should be used with the \"newWalletForm\" template. The enclosing\n *
element should be the first argument of the constructor.\n */\nexport class NewWalletForm {\n page: Record\n form: HTMLElement\n pwCache: PasswordCache | null\n success: (assetID: number) => void\n current: CurrentAsset\n pwHiders: HTMLElement[]\n subform: WalletConfigForm\n currentWalletType: string\n parentSyncer: null | ((w: WalletState) => void)\n createUpdater: null | ((note: WalletCreationNote) => void)\n\n constructor (form: HTMLElement, success: (assetID: number) => void, pwCache?: PasswordCache, backFunc?: () => void) {\n this.form = form\n this.success = success\n this.pwCache = pwCache || null\n const page = this.page = Doc.parseTemplate(form)\n this.pwHiders = Array.from(form.querySelectorAll('.hide-pw'))\n this.refresh()\n\n if (backFunc) {\n Doc.show(page.goBack)\n Doc.bind(page.goBack, 'click', () => { backFunc() })\n }\n\n Doc.empty(page.walletTabTmpl)\n page.walletTabTmpl.removeAttribute('id')\n\n // WalletConfigForm will set the global app variable.\n this.subform = new WalletConfigForm(page.walletSettings, true)\n\n bind(form, page.submitAdd, () => this.submit())\n bind(form, page.oneBttn, () => this.submit())\n\n app().registerNoteFeeder({\n walletstate: (note: WalletStateNote) => { this.reportWalletState(note.wallet) },\n createwallet: (note: WalletCreationNote) => { this.reportCreationUpdate(note) }\n })\n }\n\n /*\n * reportWalletState should be called when a 'walletstate' notification is\n * received.\n * TODO: Let form classes register for notifications.\n */\n reportWalletState (w: WalletState): void {\n if (this.parentSyncer) this.parentSyncer(w)\n }\n\n /*\n * reportWalletState should be called when a 'createwallet' notification is\n * received.\n */\n reportCreationUpdate (note: WalletCreationNote) {\n if (this.createUpdater) this.createUpdater(note)\n }\n\n refresh () {\n const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(...this.pwHiders)\n else Doc.show(...this.pwHiders)\n }\n\n async createWallet (assetID: number, walletType: string, pw: string, parentForm?: WalletConfig) {\n const createForm = {\n assetID: assetID,\n pass: this.page.newWalletPass.value || '',\n config: this.subform.map(assetID),\n appPass: pw,\n walletType: walletType,\n parentForm: parentForm\n }\n\n const ani = new Wave(this.page.mainForm, { backgroundColor: true })\n const res = await postJSON('/api/newwallet', createForm)\n ani.stop()\n return res\n }\n\n async submit () {\n const page = this.page\n const appPass = page.appPass as HTMLInputElement\n const newWalletPass = page.newWalletPass as HTMLInputElement\n const pw = appPass.value || (this.pwCache ? this.pwCache.pw : '')\n if (!pw && !State.passwordIsCached()) {\n page.newWalletErr.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.newWalletErr)\n return\n }\n Doc.hide(page.newWalletErr)\n\n const { asset, parentAsset } = this.current\n const selectedDef = this.current.selectedDef\n let parentForm\n if (parentAsset) {\n parentForm = {\n assetID: parentAsset.id,\n config: this.subform.map(parentAsset.id),\n walletType: selectedDef.type\n }\n }\n // Register the selected asset.\n const res = await this.createWallet(asset.id, selectedDef.type, pw, parentForm)\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n page.appPass.value = ''\n newWalletPass.value = ''\n if (parentAsset) await this.runParentSync()\n else this.success(this.current.asset.id)\n }\n\n /*\n * runParentSync shows a syncing sub-dialog that tracks the parent asset's\n * syncProgress and informs the user that the token wallet will be created\n * after sync is complete.\n */\n async runParentSync () {\n const { page, current: { parentAsset, asset } } = this\n if (!parentAsset) return\n\n page.parentSyncPct.textContent = '0'\n page.parentName.textContent = parentAsset.name\n page.parentLogo.src = Doc.logoPath(parentAsset.symbol)\n page.childName.textContent = asset.name\n page.childLogo.src = Doc.logoPath(asset.symbol)\n Doc.hide(page.mainForm)\n Doc.show(page.parentSyncing)\n\n try {\n await this.syncParent(parentAsset)\n this.success(this.current.asset.id)\n } catch (error) {\n this.setError(error.message || error)\n }\n Doc.show(page.mainForm)\n Doc.hide(page.parentSyncing)\n }\n\n /*\n * syncParent monitors the sync progress of a token's parent asset, generating\n * an Error if the token wallet creation does not complete successfully.\n */\n syncParent (parentAsset: SupportedAsset): Promise {\n const { page, current: { asset } } = this\n return new Promise((resolve, reject) => {\n // First, check if it's already synced.\n const w = app().assets[parentAsset.id].wallet\n if (w && w.synced) return resolve()\n // Not synced, so create a syncer to update the parent sync pane.\n this.parentSyncer = (w: WalletState) => {\n if (w.assetID !== parentAsset.id) return\n page.parentSyncPct.textContent = (w.syncProgress * 100).toFixed(1)\n }\n // Handle the async result.\n this.createUpdater = (note: WalletCreationNote) => {\n if (note.assetID !== asset.id) return\n switch (note.topic) {\n case 'QueuedCreationFailed':\n reject(new Error(`${note.subject}: ${note.details}`))\n break\n case 'QueuedCreationSuccess':\n resolve()\n break\n default:\n return\n }\n this.parentSyncer = null\n this.createUpdater = null\n }\n })\n }\n\n /* setAsset sets the current asset of the NewWalletForm */\n async setAsset (assetID: number) {\n if (!this.parseAsset(assetID)) return // nothing to change\n const page = this.page\n const tabs = page.walletTypeTabs\n const { winfo, asset, parentAsset } = this.current\n page.assetName.textContent = winfo.name\n page.newWalletPass.value = ''\n\n Doc.empty(tabs)\n Doc.hide(tabs, page.newWalletErr)\n page.header.classList.remove('bordertop')\n this.page.assetLogo.src = Doc.logoPath(asset.symbol)\n\n const pinfo = parentAsset ? parentAsset.info : null\n const walletDefs = pinfo ? pinfo.availablewallets : (winfo as WalletInfo).availablewallets ? (winfo as WalletInfo).availablewallets : [(winfo as Token).definition]\n\n if (walletDefs.length > 1) {\n Doc.show(tabs)\n for (const wDef of walletDefs) {\n const tab = page.walletTabTmpl.cloneNode(true) as HTMLElement\n tab.dataset.tooltip = wDef.description\n tab.textContent = wDef.tab\n tabs.appendChild(tab)\n Doc.bind(tab, 'click', () => {\n for (const t of Doc.kids(tabs)) t.classList.remove('selected')\n tab.classList.add('selected')\n this.update(wDef)\n })\n }\n app().bindTooltips(tabs)\n const first = tabs.firstChild as HTMLElement\n first.classList.add('selected')\n }\n\n await this.update(this.current.selectedDef)\n if (asset.walletCreationPending) await this.runParentSync()\n }\n\n /*\n * parseAsset parses the current data for the asset ID.\n */\n parseAsset (assetID: number) {\n if (this.current && this.current.asset.id === assetID) return false\n const asset = app().assets[assetID]\n const token = asset.token\n if (!token) {\n if (!asset.info) throw Error('this non-token asset has no wallet info!')\n this.current = { asset, winfo: asset.info, selectedDef: asset.info.availablewallets[0] }\n return true\n }\n const parentAsset = app().user.assets[token.parentID]\n if (parentAsset.wallet) {\n // If the parent asset already has a wallet, there's no need to configure\n // the parent too. Just configure the token.\n this.current = { asset, winfo: token, selectedDef: token.definition }\n return true\n }\n if (!parentAsset.info) throw Error('this parent has no wallet info!')\n this.current = { asset, parentAsset, winfo: token, selectedDef: parentAsset.info.availablewallets[0] }\n return true\n }\n\n async update (walletDef: WalletDefinition) {\n const page = this.page\n this.current.selectedDef = walletDef\n const appPwCached = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n Doc.hide(page.auth, page.oneBttnBox, page.newWalletPassBox)\n const configOpts = walletDef.configopts || []\n // If a config represents a wallet's birthday, we update the default\n // selection to the current date if this installation of the client\n // generated a seed.\n configOpts.map((opt) => {\n if (opt.isBirthdayConfig && app().seedGenTime > 0) {\n opt.default = toUnixDate(new Date())\n }\n return opt\n })\n // Either this is a walletDef for a token's uncreated parent asset, or this\n // is the definition for the token.\n let containsRequired = false\n for (const opt of configOpts) {\n if (opt.required) {\n containsRequired = true\n break\n }\n }\n const noWalletPWNeeded = !containsRequired && (walletDef.seeded || Boolean(this.current.asset.token))\n if (appPwCached && noWalletPWNeeded) {\n Doc.show(page.oneBttnBox)\n } else if (noWalletPWNeeded) {\n Doc.show(page.auth)\n page.newWalletPass.value = ''\n page.submitAdd.textContent = intl.prep(intl.ID_CREATE)\n } else {\n Doc.show(page.auth)\n if (!walletDef.noauth) Doc.show(page.newWalletPassBox)\n page.submitAdd.textContent = intl.prep(intl.ID_ADD)\n }\n\n const { asset, parentAsset, winfo } = this.current\n\n if (parentAsset) {\n const parentAndTokenOpts = JSON.parse(JSON.stringify(configOpts))\n // Add the regAsset field to the configurations so proper logos will be displayed\n // next to them, and map can filter them out. The opts are copied here so the originals\n // do not have the regAsset field added to them.\n for (const opt of parentAndTokenOpts) opt.regAsset = parentAsset.id\n const tokenOpts = (winfo as Token).definition.configopts || []\n if (tokenOpts.length > 0) {\n const tokenOptsCopy = JSON.parse(JSON.stringify(tokenOpts))\n for (const opt of tokenOptsCopy) opt.regAsset = asset.id\n parentAndTokenOpts.push(...tokenOptsCopy)\n }\n this.subform.update(parentAndTokenOpts, false)\n } else this.subform.update(configOpts, false)\n\n if (this.subform.dynamicOpts.children.length || this.subform.defaultSettings.children.length) {\n Doc.show(page.walletSettingsHeader)\n } else Doc.hide(page.walletSettingsHeader)\n // A seeded or token wallet is internal to the dex client and as such does\n // not have an external config file to select.\n if (walletDef.seeded || Boolean(this.current.asset.token)) Doc.hide(this.subform.fileSelector)\n else Doc.show(this.subform.fileSelector)\n\n this.refresh()\n await this.loadDefaults()\n }\n\n /* setError sets and shows the in-form error message. */\n async setError (errMsg: string) {\n this.page.newWalletErr.textContent = errMsg\n Doc.show(this.page.newWalletErr)\n }\n\n /*\n * loadDefaults attempts to load the ExchangeWallet configuration from the\n * default wallet config path on the server and will auto-fill the page on\n * the subform if settings are found.\n */\n async loadDefaults () {\n // No default config files for seeded assets right now.\n const { asset, parentAsset, selectedDef } = this.current\n if (!selectedDef.configpath) return\n let configID = asset.id\n if (parentAsset) {\n if (selectedDef.seeded) return\n configID = parentAsset.id\n }\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/defaultwalletcfg', {\n assetID: configID,\n type: selectedDef.type\n })\n loaded()\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n this.subform.setLoadedConfig(res.config)\n }\n}\n\nlet repeatableCounter = 0\n\n/*\n * WalletConfigForm is a dynamically generated sub-form for setting\n * asset-specific wallet configuration options.\n*/\nexport class WalletConfigForm {\n form: HTMLElement\n configElements: [ConfigOption, HTMLElement][]\n configOpts: ConfigOption[]\n sectionize: boolean\n allSettings: PageElement\n dynamicOpts: PageElement\n textInputTmpl: PageElement\n dateInputTmpl: PageElement\n checkboxTmpl: PageElement\n repeatableTmpl: PageElement\n fileSelector: PageElement\n fileInput: PageElement\n errMsg: PageElement\n showOther: PageElement\n showIcon: PageElement\n hideIcon: PageElement\n showHideMsg: PageElement\n otherSettings: PageElement\n loadedSettingsMsg: PageElement\n loadedSettings: PageElement\n defaultSettingsMsg: PageElement\n defaultSettings: PageElement\n assetHasActiveOrders: boolean\n\n constructor (form: HTMLElement, sectionize: boolean) {\n this.form = form\n // A configElement is a div containing an input and its label.\n this.configElements = []\n // configOpts is the wallet options provided by core.\n this.configOpts = []\n this.sectionize = sectionize\n\n // Get template elements\n this.allSettings = Doc.tmplElement(form, 'allSettings')\n this.dynamicOpts = Doc.tmplElement(form, 'dynamicOpts')\n this.textInputTmpl = Doc.tmplElement(form, 'textInput')\n this.textInputTmpl.remove()\n this.dateInputTmpl = Doc.tmplElement(form, 'dateInput')\n this.dateInputTmpl.remove()\n this.checkboxTmpl = Doc.tmplElement(form, 'checkbox')\n this.checkboxTmpl.remove()\n this.repeatableTmpl = Doc.tmplElement(form, 'repeatableInput')\n this.repeatableTmpl.remove()\n this.fileSelector = Doc.tmplElement(form, 'fileSelector')\n this.fileInput = Doc.tmplElement(form, 'fileInput')\n this.errMsg = Doc.tmplElement(form, 'errMsg')\n this.showOther = Doc.tmplElement(form, 'showOther')\n this.showIcon = Doc.tmplElement(form, 'showIcon')\n this.hideIcon = Doc.tmplElement(form, 'hideIcon')\n this.showHideMsg = Doc.tmplElement(form, 'showHideMsg')\n this.otherSettings = Doc.tmplElement(form, 'otherSettings')\n this.loadedSettingsMsg = Doc.tmplElement(form, 'loadedSettingsMsg')\n this.loadedSettings = Doc.tmplElement(form, 'loadedSettings')\n this.defaultSettingsMsg = Doc.tmplElement(form, 'defaultSettingsMsg')\n this.defaultSettings = Doc.tmplElement(form, 'defaultSettings')\n\n if (!sectionize) Doc.hide(this.showOther)\n\n Doc.bind(this.fileSelector, 'click', () => this.fileInput.click())\n\n // config file upload\n Doc.bind(this.fileInput, 'change', async () => this.fileInputChanged())\n\n Doc.bind(this.showOther, 'click', () => {\n this.setOtherSettingsViz(this.hideIcon.classList.contains('d-hide'))\n })\n }\n\n /*\n * fileInputChanged will read the selected file and attempt to load the\n * configuration settings. All loaded settings will be made visible for\n * inspection by the user.\n */\n async fileInputChanged () {\n Doc.hide(this.errMsg)\n if (!this.fileInput.value) return\n const files = this.fileInput.files\n if (!files || files.length === 0) return\n const loaded = app().loading(this.form)\n const config = await files[0].text()\n if (!config) return\n const res = await postJSON('/api/parseconfig', {\n configtext: config\n })\n loaded()\n if (!app().checkResponse(res)) {\n this.errMsg.textContent = res.msg\n Doc.show(this.errMsg)\n return\n }\n if (Object.keys(res.map).length === 0) return\n this.dynamicOpts.append(...this.setConfig(res.map))\n this.reorder(this.dynamicOpts)\n const [loadedOpts, defaultOpts] = [this.loadedSettings.children.length, this.defaultSettings.children.length]\n if (loadedOpts === 0) Doc.hide(this.loadedSettings, this.loadedSettingsMsg)\n if (defaultOpts === 0) Doc.hide(this.defaultSettings, this.defaultSettingsMsg)\n if (loadedOpts + defaultOpts === 0) Doc.hide(this.showOther, this.otherSettings)\n }\n\n addOpt (box: HTMLElement, opt: ConfigOption, insertAfter?: PageElement, n?: number): PageElement {\n const elID = 'wcfg-' + opt.key + (n ? String(n) : '')\n let el: HTMLElement\n if (opt.isboolean) el = this.checkboxTmpl.cloneNode(true) as HTMLElement\n else if (opt.isdate) el = this.dateInputTmpl.cloneNode(true) as HTMLElement\n else if (opt.repeatable) {\n el = this.repeatableTmpl.cloneNode(true) as HTMLElement\n el.classList.add('repeatable')\n Doc.bind(Doc.tmplElement(el, 'add'), 'click', () => {\n repeatableCounter++\n this.addOpt(box, opt, el, repeatableCounter)\n })\n } else el = this.textInputTmpl.cloneNode(true) as HTMLElement\n this.configElements.push([opt, el])\n const input = el.querySelector('input') as ConfigOptionInput\n input.dataset.configKey = opt.key\n input.id = elID\n const label = Doc.safeSelector(el, 'label')\n label.htmlFor = elID // 'for' attribute, but 'for' is a keyword\n label.prepend(opt.displayname)\n if (opt.regAsset !== undefined) {\n const logo = new window.Image(15, 15)\n logo.src = Doc.logoPathFromID(opt.regAsset || -1)\n label.prepend(logo)\n }\n if (insertAfter) insertAfter.after(el)\n else box.appendChild(el)\n if (opt.noecho) {\n input.type = 'password'\n input.autocomplete = 'off'\n }\n if (opt.description) label.dataset.tooltip = opt.description\n if (opt.isboolean) input.checked = opt.default\n else if (opt.isdate) {\n const getMinMaxVal = (minMax: string | number) => {\n if (!minMax) return ''\n if (minMax === 'now') return dateToString(new Date())\n return dateToString(new Date((minMax as number) * 1000))\n }\n input.max = getMinMaxVal(opt.max)\n input.min = getMinMaxVal(opt.min)\n const date = opt.default ? new Date(opt.default * 1000) : new Date()\n // UI shows Dates in valueAsDate as UTC, but user interprets local. Set a\n // local date string so the UI displays what the user expects. alt:\n // input.valueAsDate = dateApplyOffset(date)\n input.value = dateToString(date)\n } else input.value = opt.default !== null ? opt.default : ''\n input.disabled = Boolean(opt.disablewhenactive && this.assetHasActiveOrders)\n return el\n }\n\n /*\n * update creates the dynamic form.\n */\n update (configOpts: ConfigOption[] | null, activeOrders: boolean) {\n this.assetHasActiveOrders = activeOrders\n this.configElements = []\n this.configOpts = configOpts || []\n Doc.empty(this.dynamicOpts, this.defaultSettings, this.loadedSettings)\n\n // If there are no options, just hide the entire form.\n if (this.configOpts.length === 0) return Doc.hide(this.form)\n Doc.show(this.form)\n\n this.setOtherSettingsViz(false)\n Doc.hide(\n this.loadedSettingsMsg, this.loadedSettings, this.defaultSettingsMsg,\n this.defaultSettings, this.errMsg\n )\n const defaultedOpts = []\n for (const opt of this.configOpts) {\n if (this.sectionize && opt.default !== null) defaultedOpts.push(opt)\n else this.addOpt(this.dynamicOpts, opt)\n }\n if (defaultedOpts.length) {\n for (const opt of defaultedOpts) {\n this.addOpt(this.defaultSettings, opt)\n }\n Doc.show(this.showOther, this.defaultSettingsMsg, this.defaultSettings)\n } else {\n Doc.hide(this.showOther)\n }\n app().bindTooltips(this.allSettings)\n if (this.dynamicOpts.children.length) Doc.show(this.dynamicOpts)\n else Doc.hide(this.dynamicOpts)\n }\n\n /*\n * setOtherSettingsViz sets the visibility of the additional settings section.\n */\n setOtherSettingsViz (visible: boolean) {\n if (visible) {\n Doc.hide(this.showIcon)\n Doc.show(this.hideIcon, this.otherSettings)\n this.showHideMsg.textContent = intl.prep(intl.ID_HIDE_ADDITIONAL_SETTINGS)\n return\n }\n Doc.hide(this.hideIcon, this.otherSettings)\n Doc.show(this.showIcon)\n this.showHideMsg.textContent = intl.prep(intl.ID_SHOW_ADDITIONAL_SETTINGS)\n }\n\n /*\n * setConfig looks for inputs with configOpt keys matching the cfg object, and\n * sets the inputs value to the corresponding cfg value. A list of matching\n * configElements is returned.\n */\n setConfig (cfg: Record): HTMLElement[] {\n const finds: HTMLElement[] = []\n const handledRepeatables: Record = {}\n const removes: [ConfigOption, PageElement][] = []\n for (const r of [...this.configElements]) {\n const [opt, el] = r\n const v = cfg[opt.key]\n if (v === undefined) continue\n if (opt.repeatable) {\n if (handledRepeatables[opt.key]) {\n removes.push(r)\n continue\n }\n handledRepeatables[opt.key] = true\n const vals = v.split(opt.repeatable)\n const firstVal = vals[0]\n finds.push(el)\n Doc.safeSelector(el, 'input').value = firstVal\n for (let i = 1; i < vals.length; i++) {\n repeatableCounter++\n const newEl = this.addOpt(el.parentElement as PageElement, opt, el, repeatableCounter)\n Doc.safeSelector(newEl, 'input').value = vals[i]\n finds.push(newEl)\n }\n continue\n }\n finds.push(el)\n const input = Doc.safeSelector(el, 'input') as HTMLInputElement\n if (opt.isboolean) input.checked = isTruthyString(v)\n else if (opt.isdate) {\n input.value = dateToString(new Date(parseInt(v) * 1000))\n // alt: input.valueAsDate = dateApplyOffset(...)\n } else input.value = v\n }\n for (const r of removes) {\n const i = this.configElements.indexOf(r)\n if (i >= 0) this.configElements.splice(i, 1)\n }\n\n return finds\n }\n\n /*\n * setLoadedConfig sets the input values for the entries in cfg, and moves\n * them to the loadedSettings box.\n */\n setLoadedConfig (cfg: Record) {\n const finds = this.setConfig(cfg)\n if (!this.sectionize || finds.length === 0) return\n this.loadedSettings.append(...finds)\n this.reorder(this.loadedSettings)\n Doc.show(this.loadedSettings, this.loadedSettingsMsg)\n if (this.defaultSettings.children.length === 0) Doc.hide(this.defaultSettings, this.defaultSettingsMsg)\n }\n\n /*\n * map reads all inputs and constructs an object from the configOpt keys and\n * values.\n */\n map (assetID: number): Record {\n const config: Record = {}\n for (const [opt, el] of this.configElements) {\n const input = Doc.safeSelector(el, 'input') as HTMLInputElement\n if (opt.regAsset !== undefined && opt.regAsset !== assetID) continue\n if (opt.isboolean && opt.key) {\n config[opt.key] = input.checked ? '1' : '0'\n } else if (opt.isdate && opt.key) {\n // Force local time interpretation by appending a time to the date\n // string, otherwise the Date constructor considers it UTC.\n const minDate = input.min ? toUnixDate(new Date(input.min + 'T00:00')) : Number.MIN_SAFE_INTEGER\n const maxDate = input.max ? toUnixDate(new Date(input.max + 'T00:00')) : Number.MAX_SAFE_INTEGER\n let date = input.value ? toUnixDate(new Date(input.value + 'T00:00')) : 0\n if (date < minDate) date = minDate\n else if (date > maxDate) date = maxDate\n config[opt.key] = '' + date\n } else if (input.value) {\n if (opt.repeatable && config[opt.key]) config[opt.key] += opt.repeatable + input.value\n else config[opt.key] = input.value\n }\n }\n return config\n }\n\n /*\n * reorder sorts the configElements in the box by the order of the\n * server-provided configOpts array.\n */\n reorder (box: HTMLElement) {\n const inputs: Record = {}\n box.querySelectorAll('input').forEach((input: ConfigOptionInput) => {\n const k = input.dataset.configKey\n if (!k) return // TS2538\n const els = []\n for (const [opt, el] of this.configElements) if (opt.key === k) els.push(el)\n inputs[k] = els\n })\n for (const opt of this.configOpts) {\n const els = inputs[opt.key] || []\n for (const el of els) box.append(el)\n }\n }\n}\n\n/*\n * ConfirmRegistrationForm should be used with the \"confirmRegistrationForm\"\n * template.\n */\nexport class ConfirmRegistrationForm {\n form: HTMLElement\n success: () => void\n page: Record\n xc: Exchange\n certFile: string\n bondAssetID: number\n pwCache: PasswordCache\n\n constructor (form: HTMLElement, success: () => void, goBack: () => void, pwCache: PasswordCache) {\n this.form = form\n this.success = success\n this.page = Doc.parseTemplate(form)\n this.certFile = ''\n this.pwCache = pwCache\n\n Doc.bind(this.page.goBack, 'click', () => goBack())\n // bondStrengthField is presently hidden since there is no scaling of user\n // limits yet, and there needs to be considerable explanation of why\n // anything other than 1 would be used. Unhide bondStrengthInput to show it\n // when we are ready. (TODO)\n Doc.bind(this.page.bondStrengthField, 'input', () => {\n const asset = app().assets[this.bondAssetID]\n if (!asset) return\n const ui = asset.unitInfo\n const bondAsset = this.xc.bondAssets[asset.symbol]\n this.page.bondAmt.textContent = Doc.formatCoinValue(this.totalBondAmount(bondAsset.amount), ui)\n })\n bind(form, this.page.submit, () => this.submitForm())\n }\n\n setExchange (xc: Exchange, certFile: string) {\n this.xc = xc\n this.certFile = certFile\n const page = this.page\n if (State.passwordIsCached() || (this.pwCache && this.pwCache.pw)) Doc.hide(page.passBox)\n else Doc.show(page.passBox)\n page.host.textContent = xc.host\n }\n\n setAsset (assetID: number) {\n const asset = app().assets[assetID]\n const ui = asset.unitInfo\n this.bondAssetID = asset.id\n const page = this.page\n const bondAsset = this.xc.bondAssets[asset.symbol]\n page.bondAmt.textContent = Doc.formatCoinValue(this.totalBondAmount(bondAsset.amount), ui)\n page.bondUnit.textContent = ui.conventional.unit.toUpperCase()\n page.logo.src = Doc.logoPath(asset.symbol)\n }\n\n totalBondAmount (singleBondAmount: number): number {\n const bondStrength = +(this.page.bondStrengthField.value ?? 1)\n return bondStrength * singleBondAmount\n }\n\n /* Form expands into its space quickly from the lower-right as it fades in. */\n async animate () {\n const form = this.form\n Doc.animate(400, prog => {\n form.style.transform = `scale(${prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n const offset = `${(1 - prog) * 500}px`\n form.style.top = offset\n form.style.left = offset\n })\n }\n\n /*\n * submitForm is called when the form is submitted.\n */\n async submitForm () {\n const page = this.page\n // if button is selected it can be clickable.\n if (!page.submit.classList.contains('selected')) {\n return\n }\n const asset = app().assets[this.bondAssetID]\n if (!asset) {\n page.regErr.innerText = intl.prep(intl.ID_SELECT_WALLET_FOR_FEE_PAYMENT)\n Doc.show(page.regErr)\n return\n }\n Doc.hide(page.regErr)\n const bondAsset = this.xc.bondAssets[asset.wallet.symbol]\n const cert = await this.certFile\n const dexAddr = this.xc.host\n const pw = page.appPass.value || (this.pwCache ? this.pwCache.pw : '')\n const postBondForm = {\n addr: dexAddr,\n cert: cert,\n pass: pw,\n bond: this.totalBondAmount(bondAsset.amount),\n asset: bondAsset.id\n }\n page.appPass.value = ''\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/postbond', postBondForm)\n loaded()\n if (!app().checkResponse(res)) {\n page.regErr.textContent = res.msg\n Doc.show(page.regErr)\n return\n }\n this.success()\n }\n}\n\n/*\n * FeeAssetSelectionForm should be used with the \"regAssetForm\" template.\n */\nexport class FeeAssetSelectionForm {\n form: HTMLElement\n success: (assetID: number) => void\n xc: Exchange\n page: Record\n assetTmpls: Record>\n\n constructor (form: HTMLElement, success: (assetID: number) => Promise) {\n this.form = form\n this.success = success\n this.page = Doc.parseTemplate(form)\n Doc.cleanTemplates(this.page.marketTmpl, this.page.assetTmpl)\n\n app().registerNoteFeeder({\n createwallet: (note: WalletCreationNote) => {\n if (note.topic === 'QueuedCreationSuccess') this.walletCreated(note.assetID)\n }\n })\n }\n\n setExchange (xc: Exchange) {\n this.xc = xc\n this.assetTmpls = {}\n const page = this.page\n Doc.empty(page.assets, page.allMarkets)\n\n const cFactor = (ui: UnitInfo) => ui.conventional.conversionFactor\n\n const marketNode = (mkt: Market, excludeIcon?: number) => {\n const n = page.marketTmpl.cloneNode(true) as HTMLElement\n const marketTmpl = Doc.parseTemplate(n)\n\n const baseAsset = xc.assets[mkt.baseid]\n const baseUnitInfo = app().unitInfo(mkt.baseid, xc)\n const quoteAsset = xc.assets[mkt.quoteid]\n const quoteUnitInfo = app().unitInfo(mkt.quoteid, xc)\n\n if (cFactor(baseUnitInfo) === 0 || cFactor(quoteUnitInfo) === 0) return null\n\n if (typeof excludeIcon !== 'undefined') {\n const excludeBase = excludeIcon === mkt.baseid\n const otherSymbol = xc.assets[excludeBase ? mkt.quoteid : mkt.baseid].symbol\n marketTmpl.logo.src = Doc.logoPath(otherSymbol)\n } else {\n const otherLogo = marketTmpl.logo.cloneNode(true) as PageElement\n marketTmpl.logo.src = Doc.logoPath(baseAsset.symbol)\n otherLogo.src = Doc.logoPath(quoteAsset.symbol)\n const parent = marketTmpl.logo.parentNode\n if (parent) parent.insertBefore(otherLogo, marketTmpl.logo.nextSibling)\n }\n\n const baseSymbol = baseAsset.symbol.toUpperCase()\n const quoteSymbol = quoteAsset.symbol.toUpperCase()\n\n marketTmpl.baseName.replaceWith(Doc.symbolize(baseSymbol))\n marketTmpl.quoteName.replaceWith(Doc.symbolize(quoteSymbol))\n\n marketTmpl.lotSize.textContent = Doc.formatCoinValue(mkt.lotsize, baseUnitInfo)\n marketTmpl.lotSizeSymbol.replaceWith(Doc.symbolize(baseSymbol))\n\n if (mkt.spot) {\n Doc.show(marketTmpl.quoteLotSize)\n const r = cFactor(quoteUnitInfo) / cFactor(baseUnitInfo)\n const quoteLot = mkt.lotsize * mkt.spot.rate / OrderUtil.RateEncodingFactor * r\n const s = Doc.formatCoinValue(quoteLot, quoteUnitInfo)\n marketTmpl.quoteLotSize.textContent = `(~${s} ${quoteSymbol})`\n }\n return n\n }\n\n for (const [symbol, bondAsset] of Object.entries(xc.bondAssets)) {\n const asset = app().assets[bondAsset.id]\n if (!asset) continue\n const unitInfo = asset.unitInfo\n const assetNode = page.assetTmpl.cloneNode(true) as HTMLElement\n Doc.bind(assetNode, 'click', () => { this.success(bondAsset.id) })\n const assetTmpl = this.assetTmpls[bondAsset.id] = Doc.parseTemplate(assetNode)\n page.assets.appendChild(assetNode)\n assetTmpl.logo.src = Doc.logoPath(symbol)\n const fee = Doc.formatCoinValue(bondAsset.amount, unitInfo)\n assetTmpl.feeAmt.textContent = String(fee)\n assetTmpl.feeSymbol.replaceWith(Doc.symbolize(asset.symbol))\n assetTmpl.confs.textContent = String(bondAsset.confs)\n setReadyMessage(assetTmpl.ready, asset)\n\n let count = 0\n for (const mkt of Object.values(xc.markets)) {\n if (mkt.baseid !== bondAsset.id && mkt.quoteid !== bondAsset.id) continue\n const node = marketNode(mkt, bondAsset.id)\n if (!node) continue\n count++\n assetTmpl.markets.appendChild(node)\n }\n if (count < 3) Doc.hide(assetTmpl.fader)\n }\n\n page.host.textContent = xc.host\n for (const mkt of Object.values(xc.markets)) {\n const node = marketNode(mkt)\n if (!node) continue\n page.allMarkets.appendChild(node)\n }\n }\n\n /*\n * walletCreated should be called when an asynchronous wallet creation\n * completes successfully.\n */\n walletCreated (assetID: number) {\n const tmpl = this.assetTmpls[assetID]\n const asset = app().assets[assetID]\n setReadyMessage(tmpl.ready, asset)\n }\n\n refresh () {\n this.setExchange(this.xc)\n }\n\n /*\n * Animation to make the elements sort of expand into their space from the\n * bottom as they fade in.\n */\n async animate () {\n const { page, form } = this\n const how = page.how\n const extraMargin = 75\n const extraTop = 50\n const fontSize = 24\n const regAssetElements = Array.from(page.assets.children) as PageElement[]\n regAssetElements.push(page.allmkts)\n form.style.opacity = '0'\n\n const aniLen = 350\n await Doc.animate(aniLen, prog => {\n for (const el of regAssetElements) {\n el.style.marginTop = `${(1 - prog) * extraMargin}px`\n el.style.transform = `scale(${prog})`\n }\n form.style.opacity = Math.pow(prog, 4).toFixed(1)\n form.style.paddingTop = `${(1 - prog) * extraTop}px`\n how.style.fontSize = `${fontSize * prog}px`\n }, 'easeOut')\n }\n}\n\n/*\n * setReadyMessage sets an asset's status message on the FeeAssetSelectionForm.\n */\nfunction setReadyMessage (el: PageElement, asset: SupportedAsset) {\n if (asset.wallet) el.textContent = intl.prep(intl.WALLET_READY)\n else if (asset.walletCreationPending) el.textContent = intl.prep(intl.WALLET_PENDING)\n else el.textContent = intl.prep(intl.SETUP_NEEDED)\n el.classList.remove('readygreen', 'setuporange')\n el.classList.add(asset.wallet ? 'readygreen' : 'setuporange')\n}\n\n/*\n * WalletWaitForm is a form used to track the wallet sync status and balance\n * in preparation for posting a bond.\n */\nexport class WalletWaitForm {\n form: HTMLElement\n success: () => void\n goBack: () => void\n page: Record\n assetID: number\n parentID?: number\n xc: Exchange\n bondAsset: BondAsset\n progressCache: ProgressPoint[]\n progressed: boolean\n funded: boolean\n // if progressed && funded, stop reporting balance or state; call success()\n bondFeeBuffer: number // in parent asset\n parentAssetSynced: boolean\n\n constructor (form: HTMLElement, success: () => void, goBack: () => void) {\n this.form = form\n this.success = success\n this.page = Doc.parseTemplate(form)\n this.assetID = -1\n this.progressCache = []\n this.progressed = false\n this.funded = false\n\n Doc.bind(this.page.goBack, 'click', () => {\n this.assetID = -1\n goBack()\n })\n\n app().registerNoteFeeder({\n walletstate: (note: WalletStateNote) => this.reportWalletState(note.wallet),\n balance: (note: BalanceNote) => this.reportBalance(note.assetID)\n })\n }\n\n /* setExchange sets the exchange for which the fee is being paid. */\n setExchange (xc: Exchange) {\n this.xc = xc\n }\n\n /* setWallet must be called before showing the WalletWaitForm. */\n setWallet (wallet: WalletState, bondFeeBuffer: number) {\n this.assetID = wallet.assetID\n this.progressCache = []\n this.progressed = false\n this.funded = false\n this.bondFeeBuffer = bondFeeBuffer // in case we're a token, parent's balance must cover\n this.parentAssetSynced = false\n const page = this.page\n const asset = app().assets[wallet.assetID]\n this.parentID = asset.token?.parentID\n const bondAsset = this.bondAsset = this.xc.bondAssets[asset.symbol]\n\n const symbolize = (el: PageElement, symbol: string) => {\n Doc.empty(el)\n el.appendChild(Doc.symbolize(symbol))\n }\n\n for (const span of Doc.applySelector(this.form, '.unit')) symbolize(span, asset.symbol)\n page.logo.src = Doc.logoPath(asset.symbol)\n page.depoAddr.textContent = wallet.address\n page.fee.textContent = Doc.formatCoinValue(bondAsset.amount, asset.unitInfo)\n\n Doc.hide(page.syncUncheck, page.syncCheck, page.balUncheck, page.balCheck, page.syncRemainBox)\n Doc.show(page.balanceBox)\n\n if (bondFeeBuffer > 0) {\n // overlap * increment + buffer\n page.totalForBond.textContent = Doc.formatCoinValue(2 * bondAsset.amount + bondFeeBuffer, asset.unitInfo)\n Doc.hide(page.sendEnough) // generic msg when no fee info available when\n Doc.hide(page.txFeeBox, page.sendEnoughForToken, page.txFeeBalanceBox) // for tokens\n Doc.hide(page.sendEnoughWithEst) // non-tokens\n\n if (asset.token) {\n Doc.show(page.txFeeBox, page.sendEnoughForToken, page.txFeeBalanceBox)\n const parentAsset = app().assets[asset.token.parentID]\n page.txFee.textContent = Doc.formatCoinValue(bondFeeBuffer, parentAsset.unitInfo)\n page.parentFees.textContent = Doc.formatCoinValue(bondFeeBuffer, parentAsset.unitInfo)\n page.tokenFees.textContent = Doc.formatCoinValue(bondAsset.amount, asset.unitInfo)\n symbolize(page.txFeeUnit, parentAsset.symbol)\n symbolize(page.parentUnit, parentAsset.symbol)\n symbolize(page.parentBalUnit, parentAsset.symbol)\n page.parentBal.textContent = parentAsset.wallet ? Doc.formatCoinValue(parentAsset.wallet.balance.available, parentAsset.unitInfo) : '0'\n } else {\n Doc.show(page.sendEnoughWithEst)\n }\n } else { // show some generic message with no amounts, this shouldn't happen... show wallet error?\n Doc.show(page.sendEnough)\n }\n\n Doc.show(wallet.synced ? page.syncCheck : wallet.syncProgress >= 1 ? page.syncSpinner : page.syncUncheck)\n Doc.show(wallet.balance.available >= 2 * bondAsset.amount + bondFeeBuffer ? page.balCheck : page.balUncheck)\n\n page.progress.textContent = (wallet.syncProgress * 100).toFixed(1)\n\n if (wallet.synced) {\n this.progressed = true\n }\n this.reportBalance(wallet.assetID)\n }\n\n /*\n * reportWalletState sets the progress and balance, ultimately calling the\n * success function if conditions are met.\n */\n reportWalletState (wallet: WalletState) {\n if (this.progressed && this.funded) return\n if (wallet.assetID === this.assetID) this.reportProgress(wallet.synced, wallet.syncProgress)\n this.reportBalance(wallet.assetID)\n }\n\n /*\n * reportBalance sets the balance display and calls success if we go over the\n * threshold.\n */\n reportBalance (assetID: number) {\n if (this.funded || this.assetID === -1) return\n if (assetID !== this.assetID && assetID !== this.parentID) return\n const page = this.page\n const asset = app().assets[this.assetID]\n\n const avail = asset.wallet.balance.available\n page.balance.textContent = Doc.formatCoinValue(avail, asset.unitInfo)\n\n if (asset.token) {\n const parentAsset = app().assets[asset.token.parentID]\n const parentAvail = parentAsset.wallet.balance.available\n page.parentBal.textContent = Doc.formatCoinValue(parentAvail, parentAsset.unitInfo)\n if (parentAvail < this.bondFeeBuffer) return\n }\n\n // NOTE: when/if we allow one-time bond post (no maintenance) from the UI we\n // may allow to proceed as long as they have enough for tx fees. For now,\n // the balance check box will remain unchecked and we will not proceed.\n if (avail < 2 * this.bondAsset.amount + this.bondFeeBuffer) return\n\n Doc.show(page.balCheck)\n Doc.hide(page.balUncheck, page.balanceBox, page.sendEnough)\n this.funded = true\n if (this.progressed) this.success()\n }\n\n /*\n * reportProgress sets the progress display and calls success if we are fully\n * synced.\n */\n reportProgress (synced: boolean, prog: number) {\n const page = this.page\n if (synced) {\n page.progress.textContent = '100'\n Doc.hide(page.syncUncheck, page.syncRemainBox, page.syncSpinner)\n Doc.show(page.syncCheck)\n this.progressed = true\n if (this.funded) this.success()\n return\n } else if (prog === 1) {\n Doc.hide(page.syncUncheck)\n Doc.show(page.syncSpinner)\n } else {\n Doc.hide(page.syncSpinner)\n Doc.show(page.syncUncheck)\n }\n page.progress.textContent = (prog * 100).toFixed(1)\n\n if (prog >= 0.999) {\n Doc.hide(page.syncRemaining)\n Doc.show(page.syncFinishingUp)\n Doc.show(page.syncRemainBox)\n // The final stage of wallet sync process can take a while (it might hang\n // at 99.9% for many minutes, indexing addresses for example), the simplest\n // way to handle it is to keep displaying \"finishing up\" message until the\n // sync is finished, since we can't reasonably show it progressing over time.\n page.syncFinishingUp.textContent = intl.prep(intl.ID_WALLET_SYNC_FINISHING_UP)\n return\n }\n // Before we get to 99.9% the remaining time estimate must be based on more\n // than one progress report. We'll cache up to the last 20 and look at the\n // difference between the first and last to make the estimate.\n const cacheSize = 20\n const cache = this.progressCache\n cache.push({\n stamp: new Date().getTime(),\n progress: prog\n })\n if (cache.length < 2) {\n // Can't meaningfully estimate remaining until we have at least 2 data points.\n return\n }\n while (cache.length > cacheSize) cache.shift()\n const [first, last] = [cache[0], cache[cache.length - 1]]\n const progDelta = last.progress - first.progress\n if (progDelta === 0) {\n // Having no progress for a while likely means we are experiencing network\n // issues, can't reasonably estimate time remaining in this case.\n return\n }\n Doc.hide(page.syncFinishingUp)\n Doc.show(page.syncRemaining)\n Doc.show(page.syncRemainBox)\n const timeDelta = last.stamp - first.stamp\n const progRate = progDelta / timeDelta\n const toGoProg = 1 - last.progress\n const toGoTime = toGoProg / progRate\n page.syncRemain.textContent = Doc.formatDuration(toGoTime)\n }\n}\n\nexport class UnlockWalletForm {\n form: HTMLElement\n success: (assetID: number) => void\n pwCache: PasswordCache | null\n page: Record\n currentAsset: SupportedAsset\n\n constructor (form: HTMLElement, success: (assetID: number) => void, pwCache?: PasswordCache) {\n this.page = Doc.idDescendants(form)\n this.form = form\n this.pwCache = pwCache || null\n this.success = success\n bind(form, this.page.submitUnlock, () => this.submit())\n }\n\n refresh (asset: SupportedAsset) {\n const page = this.page\n this.currentAsset = asset\n page.uwAssetLogo.src = Doc.logoPath(asset.symbol)\n page.uwAssetName.textContent = asset.name\n page.uwAppPass.value = ''\n page.unlockErr.textContent = ''\n Doc.hide(page.unlockErr)\n const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(page.uwAppPassBox)\n else Doc.show(page.uwAppPassBox)\n }\n\n /*\n * setError displays an error on the form.\n */\n setError (msg: string) {\n this.page.unlockErr.textContent = msg\n Doc.show(this.page.unlockErr)\n }\n\n /*\n * showErrorOnly displays only an error on the form. Hides the\n * app pass field and the submit button.\n */\n showErrorOnly (msg: string) {\n this.setError(msg)\n Doc.hide(this.page.uwAppPassBox)\n Doc.hide(this.page.submitUnlockDiv)\n }\n\n async submit () {\n const page = this.page\n const pw = page.uwAppPass.value || (this.pwCache ? this.pwCache.pw : '')\n if (!pw && !State.passwordIsCached()) {\n page.unlockErr.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.unlockErr)\n return\n }\n const assetID = this.currentAsset.id\n Doc.hide(this.page.unlockErr)\n const open = {\n assetID: assetID,\n pass: pw\n }\n page.uwAppPass.value = ''\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/openwallet', open)\n loaded()\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success(assetID)\n }\n}\n\ninterface EarlyAcceleration {\n timePast: number,\n wasAcceleration: boolean\n}\n\ninterface PreAccelerate {\n swapRate: number\n suggestedRate: number\n suggestedRange: XYRange\n earlyAcceleration?: EarlyAcceleration\n}\n\n/*\n * AccelerateOrderForm is used to submit an acceleration request for an order.\n */\nexport class AccelerateOrderForm {\n form: HTMLElement\n page: Record\n order: Order\n acceleratedRate: number\n earlyAcceleration?: EarlyAcceleration\n currencyUnit: string\n success: () => void\n\n constructor (form: HTMLElement, success: () => void) {\n this.form = form\n this.success = success\n const page = this.page = Doc.idDescendants(form)\n\n Doc.bind(page.accelerateSubmit, 'click', () => {\n this.submit()\n })\n Doc.bind(page.submitEarlyConfirm, 'click', () => {\n this.sendAccelerateRequest()\n })\n }\n\n /*\n * displayEarlyAccelerationMsg displays a message asking for confirmation\n * when the user tries to submit an acceleration transaction very soon after\n * the swap transaction was broadcast, or very soon after a previous\n * acceleration.\n */\n displayEarlyAccelerationMsg () {\n const page = this.page\n // this is checked in submit, but another check is needed for ts compiler\n if (!this.earlyAcceleration) return\n page.recentAccelerationTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n page.recentSwapTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n if (this.earlyAcceleration.wasAcceleration) {\n Doc.show(page.recentAccelerationMsg)\n Doc.hide(page.recentSwapMsg)\n page.recentAccelerationTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n } else {\n Doc.show(page.recentSwapMsg)\n Doc.hide(page.recentAccelerationMsg)\n page.recentSwapTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n }\n Doc.hide(page.configureAccelerationDiv, page.accelerateErr)\n Doc.show(page.earlyAccelerationDiv)\n }\n\n // sendAccelerateRequest makes an accelerate order request to the client\n // backend.\n async sendAccelerateRequest () {\n const order = this.order\n const page = this.page\n const req = {\n pw: page.acceleratePass.value,\n orderID: order.id,\n newRate: this.acceleratedRate\n }\n page.acceleratePass.value = ''\n const loaded = app().loading(page.accelerateMainDiv)\n const res = await postJSON('/api/accelerateorder', req)\n loaded()\n if (app().checkResponse(res)) {\n page.accelerateTxID.textContent = res.txID\n Doc.hide(page.accelerateMainDiv, page.preAccelerateErr, page.accelerateErr)\n Doc.show(page.accelerateMsgDiv, page.accelerateSuccess)\n this.success()\n } else {\n page.accelerateErr.textContent = intl.prep(intl.ID_ORDER_ACCELERATION_ERR_MSG, { msg: res.msg })\n Doc.hide(page.earlyAccelerationDiv)\n Doc.show(page.accelerateErr, page.configureAccelerationDiv)\n }\n }\n\n // submit is called when the submit button is clicked.\n async submit () {\n if (this.earlyAcceleration) {\n this.displayEarlyAccelerationMsg()\n } else {\n this.sendAccelerateRequest()\n }\n }\n\n // refresh should be called before the form is displayed. It makes a\n // preaccelerate request to the client backend and sets up the form\n // based on the results.\n async refresh (order: Order) {\n const page = this.page\n this.order = order\n const res = await postJSON('/api/preaccelerate', order.id)\n if (!app().checkResponse(res)) {\n page.preAccelerateErr.textContent = intl.prep(intl.ID_ORDER_ACCELERATION_ERR_MSG, { msg: res.msg })\n Doc.hide(page.accelerateMainDiv, page.accelerateSuccess)\n Doc.show(page.accelerateMsgDiv, page.preAccelerateErr)\n return\n }\n Doc.hide(page.accelerateMsgDiv, page.preAccelerateErr, page.accelerateErr, page.feeEstimateDiv, page.earlyAccelerationDiv)\n Doc.show(page.accelerateMainDiv, page.accelerateSuccess, page.configureAccelerationDiv)\n const preAccelerate: PreAccelerate = res.preAccelerate\n this.earlyAcceleration = preAccelerate.earlyAcceleration\n this.currencyUnit = preAccelerate.suggestedRange.yUnit\n page.accelerateAvgFeeRate.textContent = `${preAccelerate.swapRate} ${preAccelerate.suggestedRange.yUnit}`\n page.accelerateCurrentFeeRate.textContent = `${preAccelerate.suggestedRate} ${preAccelerate.suggestedRange.yUnit}`\n this.acceleratedRate = preAccelerate.suggestedRange.start.y\n const selected = () => { /* do nothing */ }\n const roundY = true\n const updateRate = (_: number, newY: number) => { this.acceleratedRate = newY }\n const rangeHandler = new XYRangeHandler(preAccelerate.suggestedRange,\n preAccelerate.suggestedRange.start.x, updateRate, () => this.updateAccelerationEstimate(), selected, roundY)\n Doc.empty(page.sliderContainer)\n page.sliderContainer.appendChild(rangeHandler.control)\n this.updateAccelerationEstimate()\n }\n\n // updateAccelerationEstimate makes an accelerate estimate request to the\n // client backend using the currently selected rate on the slider, and\n // displays the results.\n async updateAccelerationEstimate () {\n const page = this.page\n const order = this.order\n const req = {\n orderID: order.id,\n newRate: this.acceleratedRate\n }\n const loaded = app().loading(page.sliderContainer)\n const res = await postJSON('/api/accelerationestimate', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.accelerateErr.textContent = intl.prep(intl.ID_ORDER_ACCELERATION_FEE_ERR_MSG, { msg: res.msg })\n Doc.show(page.accelerateErr)\n return\n }\n page.feeRateEstimate.textContent = `${this.acceleratedRate} ${this.currencyUnit}`\n let assetID\n let assetSymbol\n if (order.sell) {\n assetID = order.baseID\n assetSymbol = order.baseSymbol\n } else {\n assetID = order.quoteID\n assetSymbol = order.quoteSymbol\n }\n const unitInfo = app().unitInfo(assetID)\n page.feeEstimate.textContent = `${res.fee / unitInfo.conventional.conversionFactor} ${assetSymbol}`\n Doc.show(page.feeEstimateDiv)\n }\n}\n\n/* DEXAddressForm accepts a DEX address and performs account discovery. */\nexport class DEXAddressForm {\n form: HTMLElement\n success: (xc: Exchange, cert: string) => void\n pwCache: PasswordCache | null\n page: Record\n knownExchanges: HTMLElement[]\n dexToUpdate?: string\n\n constructor (form: HTMLElement, success: (xc: Exchange, cert: string) => void, pwCache?: PasswordCache, dexToUpdate?: string) {\n this.form = form\n this.success = success\n this.pwCache = pwCache || null\n\n const page = this.page = Doc.parseTemplate(form)\n\n page.selectedCert.textContent = intl.prep(intl.ID_NONE_SELECTED)\n\n Doc.bind(page.skipRegistration, 'change', () => this.showOrHidePWBox())\n Doc.bind(page.certFile, 'change', () => this.onCertFileChange())\n Doc.bind(page.removeCert, 'click', () => this.clearCertFile())\n Doc.bind(page.addCert, 'click', () => page.certFile.click())\n Doc.bind(page.showCustom, 'click', () => {\n Doc.hide(page.showCustom)\n Doc.show(page.customBox, page.auth)\n })\n\n this.knownExchanges = Array.from(page.knownXCs.querySelectorAll('.known-exchange'))\n for (const div of this.knownExchanges) {\n Doc.bind(div, 'click', () => {\n const host = div.dataset.host\n for (const d of this.knownExchanges) d.classList.remove('selected')\n // If we don't intend to register or we have the password cached, we're\n // good to go.\n if (this.skipRegistration() || State.passwordIsCached() || (pwCache && pwCache.pw)) {\n return this.checkDEX(host)\n }\n // Highlight the entry, but the user will have to enter their password\n // and click submit.\n div.classList.add('selected')\n page.appPW.focus()\n page.addr.value = host\n })\n }\n\n bind(form, page.submit, () => this.checkDEX())\n\n if (dexToUpdate) {\n Doc.hide(page.addDexHdr, page.skipRegistrationBox)\n Doc.show(page.updateDexHdr)\n this.dexToUpdate = dexToUpdate\n }\n\n this.refresh()\n }\n\n refresh () {\n const page = this.page\n page.addr.value = ''\n page.appPW.value = ''\n this.clearCertFile()\n Doc.hide(page.err)\n if (this.knownExchanges.length === 0 || this.dexToUpdate) {\n Doc.show(page.customBox, page.auth)\n Doc.hide(page.showCustom, page.knownXCs, page.pickServerMsg, page.addCustomMsg)\n } else {\n Doc.hide(page.customBox)\n Doc.show(page.showCustom)\n }\n for (const div of this.knownExchanges) div.classList.remove('selected')\n this.showOrHidePWBox()\n }\n\n /**\n * Show or hide appPWBox depending on if password is required. Show the\n * submit button if connecting a custom server or password is required).\n */\n showOrHidePWBox () {\n const passwordCached = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n const passwordRequired = !passwordCached && !this.skipRegistration()\n const page = this.page\n if (passwordRequired) {\n Doc.show(page.appPWBox, page.auth)\n } else {\n Doc.hide(page.appPWBox)\n Doc.setVis(Doc.isDisplayed(page.customBox), page.auth)\n }\n }\n\n skipRegistration () : boolean {\n return this.page.skipRegistration.checked ?? false\n }\n\n /* Just a small size tweak and fade-in. */\n async animate () {\n const form = this.form\n Doc.animate(550, prog => {\n form.style.transform = `scale(${0.9 + 0.1 * prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n }, 'easeOut')\n }\n\n async checkDEX (addr?: string) {\n const page = this.page\n Doc.hide(page.err)\n addr = addr || page.addr.value\n if (addr === '') {\n page.err.textContent = intl.prep(intl.ID_EMPTY_DEX_ADDRESS_MSG)\n Doc.show(page.err)\n return\n }\n let cert = ''\n if (page.certFile.value) {\n const files = page.certFile.files\n if (files && files.length) {\n cert = await files[0].text()\n }\n }\n const skipRegistration = this.skipRegistration()\n let pw = ''\n if (!skipRegistration && !State.passwordIsCached()) {\n pw = page.appPW.value || (this.pwCache ? this.pwCache.pw : '')\n }\n let endpoint : string, req: any\n if (this.dexToUpdate) {\n endpoint = '/api/updatedexhost'\n req = {\n newHost: addr,\n cert: cert,\n pw: pw,\n oldHost: this.dexToUpdate\n }\n } else {\n endpoint = skipRegistration ? '/api/adddex' : '/api/discoveracct'\n req = {\n addr: addr,\n cert: cert,\n pass: pw\n }\n }\n const loaded = app().loading(this.form)\n const res = await postJSON(endpoint, req)\n loaded()\n if (!app().checkResponse(res)) {\n if (String(res.msg).includes('certificate required')) {\n Doc.show(page.needCert)\n } else {\n page.err.textContent = res.msg\n Doc.show(page.err)\n }\n return\n }\n if (!this.dexToUpdate && (skipRegistration || res.paid || Object.keys(res.xc.pendingBonds).length > 0)) {\n await app().fetchUser()\n await app().loadPage('markets')\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success(res.xc, cert)\n }\n\n /**\n * onCertFileChange when the input certFile changed, read the file\n * and setting cert name into text of selectedCert to display on the view\n */\n async onCertFileChange () {\n const page = this.page\n const files = page.certFile.files\n if (!files || !files.length) return\n page.selectedCert.textContent = files[0].name\n Doc.show(page.removeCert)\n Doc.hide(page.addCert)\n }\n\n /* clearCertFile cleanup certFile value and selectedCert text */\n clearCertFile () {\n const page = this.page\n page.certFile.value = ''\n page.selectedCert.textContent = intl.prep(intl.ID_NONE_SELECTED)\n Doc.hide(page.removeCert)\n Doc.show(page.addCert)\n }\n}\n\n/* DiscoverAccountForm performs account discovery for a pre-selected DEX. */\nexport class DiscoverAccountForm {\n form: HTMLElement\n addr: string\n success: (xc: Exchange) => void\n pwCache: PasswordCache | null\n page: Record\n\n constructor (form: HTMLElement, addr: string, success: (xc: Exchange) => void, pwCache?: PasswordCache) {\n this.form = form\n this.addr = addr\n this.success = success\n this.pwCache = pwCache || null\n\n const page = this.page = Doc.parseTemplate(form)\n page.dexHost.textContent = addr\n bind(form, page.submit, () => this.checkDEX())\n\n this.refresh()\n }\n\n refresh () {\n const page = this.page\n page.appPW.value = ''\n Doc.hide(page.err)\n const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(page.appPWBox)\n else Doc.show(page.appPWBox)\n }\n\n /* Just a small size tweak and fade-in. */\n async animate () {\n const form = this.form\n Doc.animate(550, prog => {\n form.style.transform = `scale(${0.9 + 0.1 * prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n }, 'easeOut')\n }\n\n async checkDEX () {\n const page = this.page\n Doc.hide(page.err)\n let pw = ''\n if (!State.passwordIsCached()) {\n pw = page.appPW.value || (this.pwCache ? this.pwCache.pw : '')\n }\n const req = {\n addr: this.addr,\n pass: pw\n }\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/discoveracct', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.err.textContent = res.msg\n Doc.show(page.err)\n return\n }\n if (res.paid) {\n await app().fetchUser()\n await app().loadPage('markets')\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success(res.xc)\n }\n}\n\n/* LoginForm is used to sign into the app. */\nexport class LoginForm {\n form: HTMLElement\n success: () => void\n pwCache: PasswordCache | null\n headerTxt: string\n page: Record\n\n constructor (form: HTMLElement, success: () => void, pwCache?: PasswordCache) {\n this.success = success\n this.form = form\n this.pwCache = pwCache || null\n const page = this.page = Doc.parseTemplate(form)\n this.headerTxt = page.header.textContent || ''\n\n bind(form, page.submit, () => { this.submit() })\n\n app().registerNoteFeeder({\n login: (note: CoreNote) => { this.handleLoginNote(note) }\n })\n }\n\n handleLoginNote (n: CoreNote) {\n if (n.details === '') return\n const loginMsg = Doc.idel(this.form, 'loaderMsg')\n if (loginMsg) loginMsg.textContent = n.details\n }\n\n focus () {\n this.page.pw.focus()\n }\n\n async submit () {\n const page = this.page\n Doc.hide(page.errMsg)\n const pw = page.pw.value || ''\n page.pw.value = ''\n const rememberPass = page.rememberPass.checked\n if (pw === '') {\n page.errMsg.textContent = intl.prep(intl.ID_NO_PASS_ERROR_MSG)\n Doc.show(page.errMsg)\n return\n }\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/login', { pass: pw, rememberPass })\n loaded()\n if (!app().checkResponse(res)) {\n page.errMsg.textContent = res.msg\n Doc.show(page.errMsg)\n return\n }\n if (res.notes) {\n res.notes.reverse()\n app().setNotes(res.notes)\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success()\n }\n\n /* Just a small size tweak and fade-in. */\n async animate () {\n const form = this.form\n Doc.animate(550, prog => {\n form.style.transform = `scale(${0.9 + 0.1 * prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n }, 'easeOut')\n }\n}\n\nconst traitNewAddresser = 1 << 1\n\n/*\n * DepositAddress displays a deposit address, a QR code, and a button to\n * generate a new address (if supported).\n */\nexport class DepositAddress {\n form: PageElement\n page: Record\n assetID: number\n\n constructor (form: PageElement) {\n this.form = form\n const page = this.page = Doc.idDescendants(form)\n Doc.bind(page.newDepAddrBttn, 'click', async () => { this.newDepositAddress() })\n Doc.bind(page.copyAddressBtn, 'click', () => { this.copyAddress() })\n }\n\n /* Display a deposit address. */\n async setAsset (assetID: number) {\n this.assetID = assetID\n const page = this.page\n Doc.hide(page.depositErr)\n const asset = app().assets[assetID]\n page.depositLogo.src = Doc.logoPath(asset.symbol)\n const wallet = app().walletMap[assetID]\n page.depositName.textContent = asset.name\n page.depositAddress.textContent = wallet.address\n page.qrcode.src = `/generateqrcode?address=${wallet.address}`\n if ((wallet.traits & traitNewAddresser) !== 0) Doc.show(page.newDepAddrBttn)\n else Doc.hide(page.newDepAddrBttn)\n }\n\n /* Fetch a new address from the wallet. */\n async newDepositAddress () {\n const page = this.page\n Doc.hide(page.depositErr)\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/depositaddress', {\n assetID: this.assetID\n })\n loaded()\n if (!app().checkResponse(res)) {\n page.depositErr.textContent = res.msg\n Doc.show(page.depositErr)\n return\n }\n page.depositAddress.textContent = res.address\n page.qrcode.src = `/generateqrcode?address=${res.address}`\n }\n\n async copyAddress () {\n const page = this.page\n navigator.clipboard.writeText(page.depositAddress.textContent || '')\n .then(() => {\n Doc.show(page.copyAlert)\n setTimeout(() => {\n Doc.hide(page.copyAlert)\n }, 800)\n })\n .catch((reason) => {\n console.error('Unable to copy: ', reason)\n })\n }\n}\n\nconst animationLength = 300\n\n/* Swap form1 for form2 with an animation. */\nexport async function slideSwap (form1: HTMLElement, form2: HTMLElement) {\n const shift = document.body.offsetWidth / 2\n await Doc.animate(animationLength, progress => {\n form1.style.right = `${progress * shift}px`\n }, 'easeInHard')\n Doc.hide(form1)\n form1.style.right = '0'\n form2.style.right = String(-shift)\n Doc.show(form2)\n if (form2.querySelector('input')) {\n Doc.safeSelector(form2, 'input').focus()\n }\n await Doc.animate(animationLength, progress => {\n form2.style.right = `${-shift + progress * shift}px`\n }, 'easeOutHard')\n form2.style.right = '0'\n}\n\n/*\n * bind binds the click and submit events and prevents page reloading on\n * submission.\n */\nexport function bind (form: HTMLElement, submitBttn: HTMLElement, handler: (e: Event) => void) {\n const wrapper = (e: Event) => {\n if (e.preventDefault) e.preventDefault()\n handler(e)\n }\n Doc.bind(submitBttn, 'click', wrapper)\n Doc.bind(form, 'submit', wrapper)\n}\n\n// isTruthyString will be true if the provided string is recognized as a\n// value representing true.\nfunction isTruthyString (s: string) {\n return s === '1' || s.toLowerCase() === 'true'\n}\n\n// toUnixDate converts a javascript date object to a unix date, which is\n// the number of *seconds* since the start of the epoch.\nfunction toUnixDate (date: Date) {\n return Math.floor(date.getTime() / 1000)\n}\n\n// dateApplyOffset shifts a date by the timezone offset. This is used to make\n// UTC dates show the local date. This can be used to prepare a Date so\n// toISOString generates a local date string. This is also used to trick an html\n// input element to show the local date when setting the valueAsDate field. When\n// reading the date back to JS, the value field should be interpreted as local\n// using the \"T00:00\" suffix, or the Date in valueAsDate should be shifted in\n// the opposite direction.\nfunction dateApplyOffset (date: Date) {\n return new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000)\n}\n\n// dateToString converts a javascript date object to a YYYY-MM-DD format string,\n// in the local time zone.\nfunction dateToString (date: Date) {\n return dateApplyOffset(date).toISOString().split('T')[0]\n // Another common hack:\n // date.toLocaleString(\"sv-SE\", { year: \"numeric\", month: \"2-digit\", day: \"2-digit\" })\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport {\n NewWalletForm,\n DEXAddressForm,\n DiscoverAccountForm,\n LoginForm,\n ConfirmRegistrationForm,\n FeeAssetSelectionForm,\n WalletWaitForm,\n slideSwap,\n bind as bindForm\n} from './forms'\nimport * as intl from './locales'\nimport {\n app,\n PasswordCache,\n Exchange,\n PageElement\n} from './registry'\n\nexport default class RegistrationPage extends BasePage {\n body: HTMLElement\n pwCache: PasswordCache\n currentDEX: Exchange\n page: Record\n loginForm: LoginForm\n dexAddrForm: DEXAddressForm\n discoverAcctForm: DiscoverAccountForm\n newWalletForm: NewWalletForm\n regAssetForm: FeeAssetSelectionForm\n walletWaitForm: WalletWaitForm\n confirmRegisterForm: ConfirmRegistrationForm\n\n constructor (body: HTMLElement, data: any) {\n super()\n this.body = body\n this.pwCache = { pw: '' }\n const page = this.page = Doc.idDescendants(body)\n\n if (data.host && page.dexAddrForm.classList.contains('selected')) {\n page.dexAddrForm.classList.remove('selected')\n page.discoverAcctForm.classList.add('selected')\n page.discoverAcctForm.dataset.host = data.host\n }\n\n // Hide the form closers for the registration process.\n body.querySelectorAll('.form-closer').forEach(el => Doc.hide(el))\n\n // SET APP PASSWORD\n bindForm(page.appPWForm, page.appPWSubmit, () => this.setAppPass())\n Doc.bind(page.showSeedRestore, 'click', () => {\n Doc.show(page.seedRestore)\n Doc.hide(page.showSeedRestore)\n })\n\n this.loginForm = new LoginForm(page.loginForm, async () => {\n await app().fetchUser()\n if (this.discoverAcctForm) {\n this.discoverAcctForm.refresh()\n slideSwap(page.loginForm, page.discoverAcctForm)\n } else {\n this.dexAddrForm.refresh()\n slideSwap(page.loginForm, page.dexAddrForm)\n }\n }, this.pwCache)\n\n this.newWalletForm = new NewWalletForm(\n page.newWalletForm,\n assetID => this.newWalletCreated(assetID),\n this.pwCache,\n () => this.animateRegAsset(page.newWalletForm)\n )\n\n // ADD DEX\n this.dexAddrForm = new DEXAddressForm(page.dexAddrForm, async (xc, certFile) => {\n this.requestFeepayment(page.dexAddrForm, xc, certFile)\n }, this.pwCache)\n\n const addr = page.discoverAcctForm.dataset.host\n if (addr) {\n this.discoverAcctForm = new DiscoverAccountForm(page.discoverAcctForm, addr, async (xc) => {\n this.requestFeepayment(page.discoverAcctForm, xc, '')\n }, this.pwCache)\n }\n\n // SELECT REG ASSET\n this.regAssetForm = new FeeAssetSelectionForm(page.regAssetForm, async assetID => {\n this.confirmRegisterForm.setAsset(assetID)\n\n const asset = app().assets[assetID]\n const wallet = asset.wallet\n if (wallet) {\n const bondAsset = this.currentDEX.bondAssets[asset.symbol]\n if (wallet.synced && wallet.balance.available > bondAsset.amount) {\n this.animateConfirmForm(page.regAssetForm)\n return\n }\n const bondsFeeBuffer = await this.getBondsFeeBuffer(assetID, page.regAssetForm)\n this.walletWaitForm.setWallet(wallet, bondsFeeBuffer)\n slideSwap(page.regAssetForm, page.walletWait)\n return\n }\n this.newWalletForm.setAsset(assetID)\n slideSwap(page.regAssetForm, page.newWalletForm)\n })\n\n this.walletWaitForm = new WalletWaitForm(page.walletWait, () => {\n this.animateConfirmForm(page.walletWait)\n }, () => { this.animateRegAsset(page.walletWait) })\n\n // SUBMIT DEX REGISTRATION\n this.confirmRegisterForm = new ConfirmRegistrationForm(page.confirmRegForm, () => {\n this.registerDEXSuccess()\n }, () => {\n this.animateRegAsset(page.confirmRegForm)\n }, this.pwCache)\n\n const currentForm = Doc.safeSelector(page.forms, ':scope > form.selected')\n currentForm.classList.remove('selected')\n switch (currentForm) {\n case page.loginForm:\n this.loginForm.animate()\n break\n case page.dexAddrForm:\n this.dexAddrForm.animate()\n break\n case page.discoverAcctForm:\n this.discoverAcctForm.animate()\n }\n Doc.show(currentForm)\n\n if (app().authed()) this.auth()\n }\n\n unload () {\n this.pwCache.pw = ''\n }\n\n // auth should be called once user is known to be authed with the server.\n async auth () {\n await app().fetchUser()\n }\n\n async requestFeepayment (oldForm: HTMLElement, xc: Exchange, certFile: string) {\n this.currentDEX = xc\n this.confirmRegisterForm.setExchange(xc, certFile)\n this.walletWaitForm.setExchange(xc)\n this.regAssetForm.setExchange(xc)\n this.animateRegAsset(oldForm)\n }\n\n /* Swap in the asset selection form and run the animation. */\n async animateRegAsset (oldForm: HTMLElement) {\n Doc.hide(oldForm)\n this.regAssetForm.animate()\n Doc.show(this.page.regAssetForm)\n }\n\n /* Swap in the confirmation form and run the animation. */\n async animateConfirmForm (oldForm: HTMLElement) {\n this.confirmRegisterForm.animate()\n Doc.hide(oldForm)\n Doc.show(this.page.confirmRegForm)\n }\n\n // Retrieve an estimate for the tx fee needed to pay the registration fee.\n async getBondsFeeBuffer (assetID: number, form: HTMLElement) {\n const loaded = app().loading(form)\n const res = await postJSON('/api/bondsfeebuffer', { assetID })\n loaded()\n if (!app().checkResponse(res)) {\n return 0\n }\n return res.feeBuffer\n }\n\n /* Set the application password. Attached to form submission. */\n async setAppPass () {\n const page = this.page\n Doc.hide(page.appPWErrMsg)\n const pw = page.appPW.value || ''\n const pwAgain = page.appPWAgain.value\n if (pw === '') {\n page.appPWErrMsg.textContent = intl.prep(intl.ID_NO_PASS_ERROR_MSG)\n Doc.show(page.appPWErrMsg)\n return\n }\n if (pw !== pwAgain) {\n page.appPWErrMsg.textContent = intl.prep(intl.ID_PASSWORD_NOT_MATCH)\n Doc.show(page.appPWErrMsg)\n return\n }\n\n // Clear the notification cache. Useful for development purposes, since\n // the Application will only clear them on login, which would leave old\n // browser-cached notifications in place after registering even if the\n // client db is wiped.\n app().setNotes([])\n page.appPW.value = ''\n page.appPWAgain.value = ''\n const loaded = app().loading(page.appPWForm)\n const seed = page.seedInput.value\n const rememberPass = page.rememberPass.checked\n const res = await postJSON('/api/init', {\n pass: pw,\n seed,\n rememberPass\n })\n loaded()\n if (!app().checkResponse(res)) {\n page.appPWErrMsg.textContent = res.msg\n Doc.show(page.appPWErrMsg)\n return\n }\n this.pwCache.pw = pw\n this.auth()\n app().updateMenuItemsDisplay()\n this.newWalletForm.refresh()\n this.dexAddrForm.refresh()\n await slideSwap(page.appPWForm, page.dexAddrForm)\n }\n\n /* gets the contents of the cert file */\n async getCertFile () {\n let cert = ''\n if (this.dexAddrForm.page.certFile.value) {\n const files = this.dexAddrForm.page.certFile.files\n if (files && files.length) cert = await files[0].text()\n }\n return cert\n }\n\n /* Called after successful registration to a DEX. */\n async registerDEXSuccess () {\n await app().fetchUser()\n await app().loadPage('markets')\n }\n\n async newWalletCreated (assetID: number) {\n this.regAssetForm.refresh()\n const user = await app().fetchUser()\n if (!user) return\n const page = this.page\n const asset = user.assets[assetID]\n const wallet = asset.wallet\n const bondAmt = this.currentDEX.bondAssets[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > bondAmt) {\n await this.animateConfirmForm(page.newWalletForm)\n return\n }\n\n const bondsFeeBuffer = await this.getBondsFeeBuffer(assetID, page.newWalletForm)\n this.walletWaitForm.setWallet(wallet, bondsFeeBuffer)\n await slideSwap(page.newWalletForm, page.walletWait)\n }\n}\n","import { app } from './registry'\nimport Doc from './doc'\nimport BasePage from './basepage'\nimport { LoginForm } from './forms'\n\nexport default class LoginPage extends BasePage {\n form: HTMLElement\n loginForm: LoginForm\n\n constructor (body: HTMLElement) {\n super()\n this.form = Doc.idel(body, 'loginForm')\n Doc.show(this.form)\n this.loginForm = new LoginForm(this.form, () => { this.loggedIn() })\n this.loginForm.focus()\n }\n\n /* login submits the sign-in form and parses the result. */\n async loggedIn () {\n await app().fetchUser()\n await app().loadPage('markets')\n }\n}\n","import Doc, { Animation } from './doc'\nimport BasePage from './basepage'\nimport { postJSON, Errors } from './http'\nimport {\n NewWalletForm,\n WalletConfigForm,\n UnlockWalletForm,\n DepositAddress,\n bind as bindForm\n} from './forms'\nimport State from './state'\nimport * as intl from './locales'\nimport * as OrderUtil from './orderutil'\nimport {\n app,\n PageElement,\n SupportedAsset,\n WalletDefinition,\n BalanceNote,\n WalletStateNote,\n RateNote,\n Order,\n OrderFilter,\n WalletCreationNote,\n Market,\n PeerSource,\n WalletPeer\n} from './registry'\n\nconst animationLength = 300\nconst traitRescanner = 1\nconst traitLogFiler = 1 << 2\nconst traitRecoverer = 1 << 5\nconst traitWithdrawer = 1 << 6\nconst traitRestorer = 1 << 8\nconst traitTxFeeEstimator = 1 << 10\nconst traitPeerManager = 1 << 11\nconst traitsExtraOpts = traitLogFiler & traitRecoverer & traitRestorer & traitRescanner & traitPeerManager\n\ninterface ReconfigRequest {\n assetID: number\n walletType: string\n config: Record\n newWalletPW?: string\n appPW: string\n}\n\ninterface RescanRecoveryRequest {\n assetID: number\n appPW?: string\n force?: boolean\n}\n\ninterface WalletRestoration {\n target: string\n seed: string\n seedName: string\n instructions: string\n}\n\ninterface AssetButton {\n tmpl: Record\n bttn: PageElement\n}\n\nexport default class WalletsPage extends BasePage {\n body: HTMLElement\n page: Record\n assetButtons: Record\n newWalletForm: NewWalletForm\n reconfigForm: WalletConfigForm\n unlockForm: UnlockWalletForm\n depositAddrForm: DepositAddress\n keyup: (e: KeyboardEvent) => void\n changeWalletPW: boolean\n displayed: HTMLElement\n animation: Animation\n forms: PageElement[]\n forceReq: RescanRecoveryRequest\n forceUrl: string\n currentForm: PageElement\n restoreInfoCard: HTMLElement\n selectedAssetID: number\n maxSend: number\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n const page = this.page = Doc.idDescendants(body)\n\n Doc.cleanTemplates(page.restoreInfoCard, page.connectedIconTmpl, page.disconnectedIconTmpl, page.removeIconTmpl)\n this.restoreInfoCard = page.restoreInfoCard.cloneNode(true) as HTMLElement\n Doc.show(page.connectedIconTmpl, page.disconnectedIconTmpl, page.removeIconTmpl)\n\n this.forms = Doc.applySelector(page.forms, ':scope > form')\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { this.closePopups() })\n })\n Doc.bind(page.cancelForce, 'click', () => { this.closePopups() })\n\n this.selectedAssetID = -1\n Doc.cleanTemplates(\n page.iconSelectTmpl, page.balanceDetailRow, page.recentOrderTmpl\n )\n\n Doc.bind(page.createWallet, 'click', () => this.showNewWallet(this.selectedAssetID))\n Doc.bind(page.connectBttn, 'click', () => this.doConnect(this.selectedAssetID))\n Doc.bind(page.send, 'click', () => this.showSendForm(this.selectedAssetID))\n Doc.bind(page.receive, 'click', () => this.showDeposit(this.selectedAssetID))\n Doc.bind(page.unlockBttn, 'click', () => this.openWallet(this.selectedAssetID))\n Doc.bind(page.lockBttn, 'click', () => this.lock(this.selectedAssetID))\n Doc.bind(page.reconfigureBttn, 'click', () => this.showReconfig(this.selectedAssetID))\n Doc.bind(page.rescanWallet, 'click', () => this.rescanWallet(this.selectedAssetID))\n\n // Bind the new wallet form.\n this.newWalletForm = new NewWalletForm(page.newWalletForm, (assetID: number) => {\n const fmtParams = { assetName: app().assets[assetID].name }\n this.assetUpdated(assetID, page.newWalletForm, intl.prep(intl.ID_NEW_WALLET_SUCCESS, fmtParams))\n this.sortAssetButtons()\n })\n\n // Bind the wallet reconfig form.\n this.reconfigForm = new WalletConfigForm(page.reconfigInputs, false)\n\n // Bind the wallet unlock form.\n this.unlockForm = new UnlockWalletForm(page.unlockWalletForm, (assetID: number) => this.openWalletSuccess(assetID, page.unlockWalletForm))\n\n // Bind the send form.\n bindForm(page.sendForm, page.submitSendForm, async () => { this.stepSend() })\n // Send confirmation form.\n bindForm(page.vSendForm, page.vSend, async () => { this.send() })\n // Cancel send confirmation form.\n Doc.bind(page.vCancelSend, 'click', async () => { this.cancelSend() })\n // Bind the wallet reconfiguration submission.\n bindForm(page.reconfigForm, page.submitReconfig, () => this.reconfig())\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => this.closePopups())\n })\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { this.closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (Doc.isDisplayed(this.page.forms)) this.closePopups()\n }\n }\n Doc.bind(document, 'keyup', this.keyup)\n\n Doc.bind(page.downloadLogs, 'click', async () => { this.downloadLogs() })\n Doc.bind(page.exportWallet, 'click', async () => { this.displayExportWalletAuth() })\n Doc.bind(page.recoverWallet, 'click', async () => { this.showRecoverWallet() })\n bindForm(page.exportWalletAuth, page.exportWalletAuthSubmit, async () => { this.exportWalletAuthSubmit() })\n bindForm(page.recoverWalletConfirm, page.recoverWalletSubmit, () => { this.recoverWallet() })\n bindForm(page.confirmForce, page.confirmForceSubmit, async () => { this.confirmForceSubmit() })\n Doc.bind(page.disableWallet, 'click', async () => { this.showToggleWalletStatus(true) })\n Doc.bind(page.enableWallet, 'click', async () => { this.showToggleWalletStatus(false) })\n bindForm(page.toggleWalletStatusConfirm, page.toggleWalletStatusSubmit, async () => { this.toggleWalletStatus() })\n Doc.bind(page.managePeers, 'click', async () => { this.showManagePeersForm() })\n Doc.bind(page.addPeerSubmit, 'click', async () => { this.submitAddPeer() })\n\n // New deposit address button.\n this.depositAddrForm = new DepositAddress(page.deposit)\n\n // Clicking on the available amount on the Send form populates the\n // amount field.\n Doc.bind(page.walletBal, 'click', () => { this.populateMaxSend() })\n\n // Display fiat value for current send amount.\n Doc.bind(page.sendAmt, 'input', () => {\n const { unitInfo: ui } = app().assets[this.selectedAssetID]\n const amt = parseFloat(page.sendAmt.value || '0')\n const conversionFactor = ui.conventional.conversionFactor\n this.showFiatValue(this.selectedAssetID, amt * conversionFactor, page.sendValue)\n })\n\n // Clicking on maxSend on the send form should populate the amount field.\n Doc.bind(page.maxSend, 'click', () => { this.populateMaxSend() })\n\n // Validate send address on input.\n Doc.bind(page.sendAddr, 'input', async () => {\n const asset = app().assets[this.selectedAssetID]\n Doc.hide(page.validAddr)\n page.sendAddr.classList.remove('invalid')\n const addr = page.sendAddr.value || ''\n if (!asset || addr === '') return\n const valid = await this.validateSendAddress(addr, asset.id)\n if (valid) Doc.show(page.validAddr)\n else page.sendAddr.classList.add('invalid')\n })\n\n // A link on the wallet reconfiguration form to show/hide the password field.\n Doc.bind(page.showChangePW, 'click', () => {\n this.changeWalletPW = !this.changeWalletPW\n this.setPWSettingViz(this.changeWalletPW)\n })\n\n // Changing the type of wallet.\n Doc.bind(page.changeWalletTypeSelect, 'change', () => {\n this.changeWalletType()\n })\n Doc.bind(page.showChangeType, 'click', () => {\n if (Doc.isHidden(page.changeWalletType)) {\n Doc.show(page.changeWalletType, page.changeTypeHideIcon)\n Doc.hide(page.changeTypeShowIcon)\n page.changeTypeMsg.textContent = intl.prep(intl.ID_KEEP_WALLET_TYPE)\n } else this.showReconfig(this.selectedAssetID, true)\n })\n\n app().registerNoteFeeder({\n fiatrateupdate: (note: RateNote) => { this.handleRatesNote(note) },\n balance: (note: BalanceNote) => { this.handleBalanceNote(note) },\n walletstate: (note: WalletStateNote) => { this.handleWalletStateNote(note) },\n walletconfig: (note: WalletStateNote) => { this.handleWalletStateNote(note) },\n createwallet: (note: WalletCreationNote) => { this.handleCreateWalletNote(note) }\n })\n\n const firstAsset = this.sortAssetButtons()\n let selectedAsset = firstAsset.id\n const assetIDStr = State.fetchLocal(State.selectedAssetLK)\n if (assetIDStr) selectedAsset = Number(assetIDStr)\n this.setSelectedAsset(selectedAsset)\n }\n\n closePopups () {\n Doc.hide(this.page.forms)\n if (this.animation) this.animation.stop()\n }\n\n // stepSend makes a request to get an estimated fee and displays the confirm\n // send form.\n async stepSend () {\n const page = this.page\n Doc.hide(page.vSendErr, page.sendErr, page.vSendEstimates, page.txFeeNotAvailable)\n const assetID = parseInt(page.sendForm.dataset.assetID || '')\n const token = app().assets[assetID].token\n const subtract = page.subtractCheckBox.checked || false\n const conversionFactor = app().unitInfo(assetID).conventional.conversionFactor\n const value = Math.round(parseFloat(page.sendAmt.value || '') * conversionFactor)\n const addr = page.sendAddr.value || ''\n if (addr === '') return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: addr }))\n const { wallet, unitInfo: ui, symbol } = app().assets[assetID]\n\n // txfee will not be available if wallet is not a fee estimator or the\n // request failed.\n let txfee = 0\n if ((wallet.traits & traitTxFeeEstimator) !== 0) {\n const open = {\n addr: page.sendAddr.value,\n assetID: assetID,\n subtract: subtract,\n value: value\n }\n\n const loaded = app().loading(page.sendForm)\n const res = await postJSON('/api/txfee', open)\n loaded()\n if (!app().checkResponse(res)) {\n page.txFeeNotAvailable.dataset.tooltip = intl.prep(intl.ID_TXFEE_ERR_MSG, { err: res.msg })\n Doc.show(page.txFeeNotAvailable)\n // We still want to ensure user address is valid before proceeding to send\n // confirm form if there's an error while calculating the transaction fee.\n const valid = await this.validateSendAddress(addr, assetID)\n if (!valid) return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: addr || '' }))\n } else if (res.ok) {\n if (!res.validaddress) return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: page.sendAddr.value || '' }))\n txfee = res.txfee\n Doc.show(page.vSendEstimates)\n }\n } else {\n // Validate only the send address for assets that are not fee estimators.\n const valid = await this.validateSendAddress(addr, assetID)\n if (!valid) return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: addr || '' }))\n }\n\n page.vSendSymbol.textContent = symbol.toUpperCase()\n page.vSendLogo.src = Doc.logoPath(symbol)\n\n if (token) {\n const { unitInfo: feeUI, symbol: feeSymbol } = app().assets[token.parentID]\n page.vSendFee.textContent = Doc.formatFullPrecision(txfee, feeUI) + ' ' + feeSymbol\n } else {\n page.vSendFee.textContent = Doc.formatFullPrecision(txfee, ui)\n }\n this.showFiatValue(assetID, txfee, page.vSendFeeFiat)\n page.vSendDestinationAmt.textContent = Doc.formatFullPrecision(value - txfee, ui)\n page.vTotalSend.textContent = Doc.formatFullPrecision(value, ui)\n this.showFiatValue(assetID, value, page.vTotalSendFiat)\n page.vSendAddr.textContent = page.sendAddr.value || ''\n const bal = wallet.balance.available - value\n page.balanceAfterSend.textContent = Doc.formatFullPrecision(bal, ui)\n this.showFiatValue(assetID, bal, page.balanceAfterSendFiat)\n Doc.show(page.approxSign)\n // NOTE: All tokens take this route because they cannot pay the fee.\n if (!subtract) {\n Doc.hide(page.approxSign)\n page.vSendDestinationAmt.textContent = Doc.formatFullPrecision(value, ui)\n let totalSend = value\n if (!token) totalSend += txfee\n page.vTotalSend.textContent = Doc.formatFullPrecision(totalSend, ui)\n this.showFiatValue(assetID, totalSend, page.vTotalSendFiat)\n let bal = wallet.balance.available - value\n if (!token) bal -= txfee\n // handle edge cases where bal is not enough to cover totalSend.\n // we don't want a minus display of user bal.\n if (bal <= 0) {\n page.balanceAfterSend.textContent = Doc.formatFullPrecision(0, ui)\n this.showFiatValue(assetID, 0, page.balanceAfterSendFiat)\n } else {\n page.balanceAfterSend.textContent = Doc.formatFullPrecision(bal, ui)\n this.showFiatValue(assetID, bal, page.balanceAfterSendFiat)\n }\n }\n Doc.hide(page.sendForm)\n await this.showForm(page.vSendForm)\n }\n\n // cancelSend displays the send form if user wants to make modification.\n async cancelSend () {\n const page = this.page\n Doc.hide(page.vSendForm, page.sendErr)\n await this.showForm(page.sendForm)\n }\n\n /*\n * validateSendAddress validates the provided address for an asset.\n */\n async validateSendAddress (addr: string, assetID: number): Promise {\n const resp = await postJSON('/api/validateaddress', { addr: addr, assetID: assetID })\n return app().checkResponse(resp)\n }\n\n /*\n * setPWSettingViz sets the visibility of the password field section.\n */\n setPWSettingViz (visible: boolean) {\n if (visible) {\n Doc.hide(this.page.showIcon)\n Doc.show(this.page.hideIcon, this.page.changePW)\n this.page.switchPWMsg.textContent = intl.prep(intl.ID_KEEP_WALLET_PASS)\n return\n }\n Doc.hide(this.page.hideIcon, this.page.changePW)\n Doc.show(this.page.showIcon)\n this.page.switchPWMsg.textContent = intl.prep(intl.ID_NEW_WALLET_PASS)\n }\n\n /*\n * updateWalletPeers retrieves the wallet peers and displays them in the\n * wallet peers table.\n */\n async updateWalletPeersTable () {\n const page = this.page\n\n Doc.hide(page.peerSpinner)\n\n const res = await postJSON('/api/getwalletpeers', {\n assetID: this.selectedAssetID\n })\n if (!app().checkResponse(res)) {\n page.managePeersErr.textContent = res.msg\n Doc.show(page.managePeersErr)\n return\n }\n\n while (page.peersTableBody.firstChild) {\n page.peersTableBody.removeChild(page.peersTableBody.firstChild)\n }\n\n const peers : WalletPeer[] = res.peers || []\n peers.sort((a: WalletPeer, b: WalletPeer) : number => {\n return a.source - b.source\n })\n\n const defaultText = intl.prep(intl.ID_DEFAULT)\n const addedText = intl.prep(intl.ID_ADDED)\n const discoveredText = intl.prep(intl.ID_DISCOVERED)\n\n peers.forEach((peer: WalletPeer) => {\n const row = page.peerTableRow.cloneNode(true) as PageElement\n const tmpl = Doc.parseTemplate(row)\n\n tmpl.addr.textContent = peer.addr\n\n switch (peer.source) {\n case PeerSource.WalletDefault:\n tmpl.source.textContent = defaultText\n break\n case PeerSource.UserAdded:\n tmpl.source.textContent = addedText\n break\n case PeerSource.Discovered:\n tmpl.source.textContent = discoveredText\n break\n }\n\n let connectionIcon\n if (peer.connected) {\n connectionIcon = this.page.connectedIconTmpl.cloneNode(true)\n } else {\n connectionIcon = this.page.disconnectedIconTmpl.cloneNode(true)\n }\n tmpl.connected.appendChild(connectionIcon)\n\n if (peer.source === PeerSource.UserAdded) {\n const removeIcon = this.page.removeIconTmpl.cloneNode(true)\n Doc.bind(removeIcon, 'click', async () => {\n Doc.hide(page.managePeersErr)\n const res = await postJSON('/api/removewalletpeer', {\n assetID: this.selectedAssetID,\n addr: peer.addr\n })\n if (!app().checkResponse(res)) {\n page.managePeersErr.textContent = res.msg\n Doc.show(page.managePeersErr)\n return\n }\n this.spinUntilPeersUpdate()\n })\n tmpl.remove.appendChild(removeIcon)\n }\n\n page.peersTableBody.appendChild(row)\n })\n }\n\n // showManagePeersForm displays the manage peers form.\n async showManagePeersForm () {\n const page = this.page\n await this.updateWalletPeersTable()\n Doc.hide(page.managePeersErr)\n this.showForm(page.managePeersForm)\n }\n\n // submitAddPeers sends a request for the the wallet to connect to a new\n // peer.\n async submitAddPeer () {\n const page = this.page\n Doc.hide(page.managePeersErr)\n const res = await postJSON('/api/addwalletpeer', {\n assetID: this.selectedAssetID,\n addr: page.addPeerInput.value\n })\n if (!app().checkResponse(res)) {\n page.managePeersErr.textContent = res.msg\n Doc.show(page.managePeersErr)\n return\n }\n this.spinUntilPeersUpdate()\n page.addPeerInput.value = ''\n }\n\n /*\n * spinUntilPeersUpdate will show the spinner on the manage peers fork.\n * If it is still showing after 10 seconds, the peers table will be updated\n * instead of waiting for a notification.\n */\n async spinUntilPeersUpdate () {\n const page = this.page\n Doc.show(page.peerSpinner)\n setTimeout(() => {\n if (Doc.isDisplayed(page.peerSpinner)) {\n this.updateWalletPeersTable()\n }\n }, 10000)\n }\n\n /*\n * showToggleWalletStatus displays the toggleWalletStatusConfirm form with\n * relevant help message.\n */\n showToggleWalletStatus (disable: boolean) {\n const page = this.page\n Doc.hide(page.toggleWalletStatusErr, page.walletStatusDisable, page.disableWalletMsg, page.walletStatusEnable, page.enableWalletMsg)\n if (disable) Doc.show(page.walletStatusDisable, page.disableWalletMsg)\n else Doc.show(page.walletStatusEnable, page.enableWalletMsg)\n this.showForm(page.toggleWalletStatusConfirm)\n }\n\n /*\n * toggleWalletStatus toggles a wallets status to either disabled or enabled.\n */\n async toggleWalletStatus () {\n const page = this.page\n Doc.hide(page.toggleWalletStatusErr)\n\n const asset = app().assets[this.selectedAssetID]\n const disable = !asset.wallet.disabled\n const url = '/api/togglewalletstatus'\n const req = {\n assetID: this.selectedAssetID,\n disable: disable\n }\n\n const fmtParams = { assetName: asset.name }\n const loaded = app().loading(page.toggleWalletStatusConfirm)\n const res = await postJSON(url, req)\n loaded()\n if (!app().checkResponse(res)) {\n if (res.code === Errors.activeOrdersErr) page.toggleWalletStatusErr.textContent = intl.prep(intl.ID_ACTIVE_ORDERS_ERR_MSG, fmtParams)\n else page.toggleWalletStatusErr.textContent = res.msg\n Doc.show(page.toggleWalletStatusErr)\n return\n }\n\n let successMsg = intl.prep(intl.ID_WALLET_DISABLED_MSG, fmtParams)\n if (!disable) successMsg = intl.prep(intl.ID_WALLET_ENABLED_MSG, fmtParams)\n this.assetUpdated(this.selectedAssetID, page.toggleWalletStatusConfirm, successMsg)\n }\n\n /*\n * showBox shows the box with a fade-in animation.\n */\n async showBox (box: HTMLElement, focuser?: PageElement) {\n box.style.opacity = '0'\n Doc.show(box)\n if (focuser) focuser.focus()\n await Doc.animate(animationLength, progress => {\n box.style.opacity = `${progress}`\n }, 'easeOut')\n box.style.opacity = '1'\n this.displayed = box\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: PageElement) {\n const page = this.page\n this.currentForm = form\n this.forms.forEach(form => Doc.hide(form))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n async showSuccess (msg: string) {\n const page = this.page\n page.successMessage.textContent = msg\n this.currentForm = page.checkmarkForm\n this.forms.forEach(form => Doc.hide(form))\n Doc.show(page.forms, page.checkmarkForm)\n page.checkmarkForm.style.right = '0'\n page.checkmark.style.fontSize = '0px'\n\n const [startR, startG, startB] = State.isDark() ? [223, 226, 225] : [51, 51, 51]\n const [endR, endG, endB] = [16, 163, 16]\n const [diffR, diffG, diffB] = [endR - startR, endG - startG, endB - startB]\n\n this.animation = new Animation(1200, (prog: number) => {\n page.checkmark.style.fontSize = `${prog * 80}px`\n page.checkmark.style.color = `rgb(${startR + prog * diffR}, ${startG + prog * diffG}, ${startB + prog * diffB})`\n }, 'easeOutElastic', () => {\n this.animation = new Animation(1500, () => { /* pass */ }, '', () => {\n if (this.currentForm === page.checkmarkForm) this.closePopups()\n })\n })\n }\n\n /* Show the new wallet form. */\n async showNewWallet (assetID: number) {\n const page = this.page\n const box = page.newWalletForm\n this.newWalletForm.setAsset(assetID)\n const defaultsLoaded = this.newWalletForm.loadDefaults()\n await this.showForm(box)\n await defaultsLoaded\n }\n\n // sortAssetButtons displays supported assets, sorted. Returns first asset in the\n // list.\n sortAssetButtons (): SupportedAsset {\n const page = this.page\n this.assetButtons = {}\n Doc.empty(page.assetSelect)\n const sortedAssets = [...Object.values(app().assets)]\n sortedAssets.sort((a: SupportedAsset, b: SupportedAsset) => {\n if (a.wallet && !b.wallet) return -1\n if (!a.wallet && b.wallet) return 1\n return a.symbol.localeCompare(b.symbol)\n })\n for (const a of sortedAssets) {\n const bttn = page.iconSelectTmpl.cloneNode(true) as HTMLElement\n page.assetSelect.appendChild(bttn)\n const tmpl = Doc.parseTemplate(bttn)\n this.assetButtons[a.id] = { tmpl, bttn }\n this.updateAssetButton(a.id)\n Doc.bind(bttn, 'click', () => {\n this.setSelectedAsset(a.id)\n State.storeLocal(State.selectedAssetLK, String(a.id))\n })\n }\n page.assetSelect.classList.remove('invisible')\n return sortedAssets[0]\n }\n\n updateAssetButton (assetID: number) {\n const a = app().assets[assetID]\n const { bttn, tmpl } = this.assetButtons[assetID]\n Doc.hide(tmpl.fiat, tmpl.noWallet)\n bttn.classList.add('nowallet')\n tmpl.img.src ||= Doc.logoPath(a.symbol) // don't initiate GET if already set (e.g. update on some notification)\n tmpl.name.textContent = a.name\n if (a.wallet) {\n bttn.classList.remove('nowallet')\n const { wallet: { balance: b }, unitInfo: ui } = a\n const totalBalance = b.available + b.locked + b.immature\n tmpl.balance.textContent = Doc.formatCoinValue(totalBalance, ui)\n const rate = app().fiatRatesMap[a.id]\n if (rate) {\n Doc.show(tmpl.fiat)\n tmpl.fiat.textContent = Doc.formatFiatConversion(totalBalance, rate, ui)\n }\n } else Doc.show(tmpl.noWallet)\n }\n\n setSelectedAsset (assetID: number) {\n const { assetSelect } = this.page\n for (const b of assetSelect.children) b.classList.remove('selected')\n this.assetButtons[assetID].bttn.classList.add('selected')\n this.selectedAssetID = assetID\n this.updateDisplayedAsset(assetID)\n this.showAvailableMarkets(assetID)\n this.showRecentActivity(assetID)\n }\n\n updateDisplayedAsset (assetID: number) {\n if (assetID !== this.selectedAssetID) return\n const { symbol, wallet, name } = app().assets[assetID]\n const page = this.page\n for (const el of document.querySelectorAll('[data-asset-name]')) el.textContent = name\n page.assetLogo.src = Doc.logoPath(symbol)\n Doc.hide(\n page.balanceBox, page.fiatBalanceBox, page.createWalletBox, page.walletDetails,\n page.sendReceive, page.connectBttnBox, page.statusLocked, page.statusReady,\n page.statusOff, page.unlockBttnBox, page.lockBttnBox, page.connectBttnBox,\n page.peerCountBox, page.syncProgressBox, page.statusDisabled\n )\n if (wallet) {\n this.updateDisplayedAssetBalance()\n\n const walletDef = app().walletDefinition(assetID, wallet.type)\n page.walletType.textContent = walletDef.tab\n const configurable = assetIsConfigurable(assetID)\n Doc.setVis(configurable, page.passwordWrapper)\n\n if (wallet.disabled) Doc.show(page.statusDisabled) // wallet is disabled\n else if (wallet.running) {\n Doc.show(page.sendReceive, page.peerCountBox, page.syncProgressBox)\n page.peerCount.textContent = String(wallet.peerCount)\n page.syncProgress.textContent = `${(wallet.syncProgress * 100).toFixed(1)}%`\n if (wallet.open) {\n Doc.show(page.statusReady)\n if (!app().haveActiveOrders(assetID) && wallet.encrypted) Doc.show(page.lockBttnBox)\n } else Doc.show(page.statusLocked, page.unlockBttnBox) // wallet not unlocked\n } else Doc.show(page.statusOff, page.connectBttnBox) // wallet not running\n } else Doc.show(page.createWalletBox) // no wallet\n\n page.walletDetailsBox.classList.remove('invisible')\n }\n\n updateDisplayedAssetBalance (): void {\n const page = this.page\n const { wallet, unitInfo: ui, symbol, id: assetID } = app().assets[this.selectedAssetID]\n const bal = wallet.balance\n Doc.show(page.balanceBox, page.walletDetails)\n const totalLocked = bal.locked + bal.contractlocked + bal.bondlocked\n const totalBalance = bal.available + totalLocked + bal.immature\n page.balance.textContent = Doc.formatCoinValue(totalBalance, ui)\n Doc.empty(page.balanceUnit)\n page.balanceUnit.appendChild(Doc.symbolize(symbol))\n const rate = app().fiatRatesMap[assetID]\n if (rate) {\n Doc.show(page.fiatBalanceBox)\n page.fiatBalance.textContent = Doc.formatFiatConversion(totalBalance, rate, ui)\n }\n let firstOther = false\n Doc.empty(page.balanceDetailBox)\n const addSubBalance = (category: string, subBalance: number) => {\n const row = page.balanceDetailRow.cloneNode(true) as PageElement\n if (firstOther) {\n row.classList.add('first-other')\n firstOther = false\n }\n page.balanceDetailBox.appendChild(row)\n const tmpl = Doc.parseTemplate(row)\n tmpl.category.textContent = category\n tmpl.subBalance.textContent = Doc.formatCoinValue(subBalance, ui)\n }\n addSubBalance('Available', bal.available)\n addSubBalance('Locked', totalLocked)\n addSubBalance('Immature', bal.immature)\n const sortedCats = Object.entries(bal.other || {})\n sortedCats.sort((a: [string, number], b: [string, number]): number => a[0].localeCompare(b[0]))\n firstOther = true\n if (bal.contractlocked > 0) addSubBalance('Swapping (locked)', bal.contractlocked)\n if (bal.bondlocked > 0) addSubBalance('Bonded (locked)', bal.bondlocked)\n for (const [cat, sub] of sortedCats) addSubBalance(cat, sub)\n }\n\n showAvailableMarkets (assetID: number) {\n const page = this.page\n const exchanges = app().user.exchanges\n const markets: [string, Market][] = []\n for (const xc of Object.values(exchanges)) {\n if (!xc.markets) continue\n for (const mkt of Object.values(xc.markets)) {\n if (mkt.baseid === assetID || mkt.quoteid === assetID) markets.push([xc.host, mkt])\n }\n }\n\n const spotVolume = (assetID: number, mkt: Market): number => {\n const spot = mkt.spot\n if (!spot) return 0\n const conversionFactor = app().unitInfo(assetID).conventional.conversionFactor\n const volume = assetID === mkt.baseid ? spot.vol24 : spot.vol24 * spot.rate / OrderUtil.RateEncodingFactor\n return volume / conversionFactor\n }\n\n markets.sort((a: [string, Market], b: [string, Market]): number => {\n const [hostA, mktA] = a\n const [hostB, mktB] = b\n if (!mktA.spot && !mktB.spot) return hostA.localeCompare(hostB)\n return spotVolume(assetID, mktB) - spotVolume(assetID, mktA)\n })\n Doc.empty(page.availableMarkets)\n\n for (const [host, mkt] of markets) {\n const { spot, baseid, basesymbol, quoteid, quotesymbol } = mkt\n const row = page.marketRow.cloneNode(true) as PageElement\n page.availableMarkets.appendChild(row)\n const tmpl = Doc.parseTemplate(row)\n tmpl.host.textContent = host\n tmpl.baseLogo.src = Doc.logoPath(basesymbol)\n tmpl.quoteLogo.src = Doc.logoPath(quotesymbol)\n Doc.empty(tmpl.baseSymbol, tmpl.quoteSymbol)\n tmpl.baseSymbol.appendChild(Doc.symbolize(basesymbol))\n tmpl.quoteSymbol.appendChild(Doc.symbolize(quotesymbol))\n\n if (spot) {\n const convRate = app().conventionalRate(baseid, quoteid, spot.rate, exchanges[host])\n tmpl.price.textContent = fourSigFigs(convRate)\n tmpl.priceQuoteUnit.textContent = quotesymbol.toUpperCase()\n tmpl.priceBaseUnit.textContent = basesymbol.toUpperCase()\n tmpl.volume.textContent = fourSigFigs(spotVolume(assetID, mkt))\n tmpl.volumeUnit.textContent = assetID === baseid ? basesymbol.toUpperCase() : quotesymbol.toUpperCase()\n } else Doc.hide(tmpl.priceBox, tmpl.volumeBox)\n Doc.bind(row, 'click', () => app().loadPage('markets', { host, base: baseid, quote: quoteid }))\n }\n page.marketsOverviewBox.classList.remove('invisible')\n }\n\n async showRecentActivity (assetID: number) {\n const page = this.page\n const loaded = app().loading(page.orderActivityBox)\n const filter: OrderFilter = {\n n: 20,\n assets: [assetID],\n hosts: [],\n statuses: []\n }\n const res = await postJSON('/api/orders', filter)\n loaded()\n Doc.hide(page.noActivity, page.orderActivity)\n if (!res.orders || res.orders.length === 0) {\n Doc.show(page.noActivity)\n page.orderActivityBox.classList.remove('invisible')\n return\n }\n Doc.show(page.orderActivity)\n Doc.empty(page.recentOrders)\n for (const ord of (res.orders as Order[])) {\n const row = page.recentOrderTmpl.cloneNode(true) as PageElement\n page.recentOrders.appendChild(row)\n const tmpl = Doc.parseTemplate(row)\n let from: string, to: string\n const [baseUnitInfo, quoteUnitInfo] = [app().unitInfo(ord.baseID), app().unitInfo(ord.quoteID)]\n if (ord.sell) {\n [from, to] = [ord.baseSymbol, ord.quoteSymbol]\n tmpl.fromQty.textContent = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n if (ord.type === OrderUtil.Limit) {\n tmpl.toQty.textContent = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n }\n } else {\n [from, to] = [ord.quoteSymbol, ord.baseSymbol]\n if (ord.type === OrderUtil.Market) {\n tmpl.fromQty.textContent = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n } else {\n tmpl.fromQty.textContent = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n tmpl.toQty.textContent = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n }\n }\n\n tmpl.fromLogo.src = Doc.logoPath(from)\n Doc.empty(tmpl.fromSymbol, tmpl.toSymbol)\n tmpl.fromSymbol.appendChild(Doc.symbolize(from))\n tmpl.toLogo.src = Doc.logoPath(to)\n tmpl.toSymbol.appendChild(Doc.symbolize(to))\n tmpl.status.textContent = OrderUtil.statusString(ord)\n tmpl.filled.textContent = `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`\n tmpl.age.textContent = Doc.timeSince(ord.submitTime)\n tmpl.link.href = `order/${ord.id}`\n app().bindInternalNavigation(row)\n }\n page.orderActivityBox.classList.remove('invisible')\n }\n\n async rescanWallet (assetID: number) {\n const page = this.page\n Doc.hide(page.reconfigErr)\n\n const url = '/api/rescanwallet'\n const req = { assetID: assetID }\n\n const loaded = app().loading(this.body)\n const res = await postJSON(url, req)\n loaded()\n if (res.code === Errors.activeOrdersErr) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n return\n }\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.reconfigErr, res.msg)\n return\n }\n this.assetUpdated(assetID, page.reconfigForm, intl.prep(intl.ID_RESCAN_STARTED))\n }\n\n showConfirmForce () {\n Doc.hide(this.page.confirmForceErr)\n this.showForm(this.page.confirmForce)\n }\n\n showRecoverWallet () {\n Doc.hide(this.page.recoverWalletErr)\n this.showForm(this.page.recoverWalletConfirm)\n }\n\n /* Show the open wallet form if the password is not cached, and otherwise\n * attempt to open the wallet.\n */\n async openWallet (assetID: number) {\n if (!State.passwordIsCached()) {\n this.showOpen(assetID)\n } else {\n const open = {\n assetID: assetID\n }\n const res = await postJSON('/api/openwallet', open)\n if (app().checkResponse(res)) {\n this.openWalletSuccess(assetID)\n } else {\n this.showOpen(assetID, intl.prep(intl.ID_OPEN_WALLET_ERR_MSG, { msg: res.msg }))\n }\n }\n }\n\n /* Show the form used to unlock a wallet. */\n async showOpen (assetID: number, errorMsg?: string) {\n const page = this.page\n // await this.hideBox()\n this.unlockForm.refresh(app().assets[assetID])\n if (errorMsg) this.unlockForm.showErrorOnly(errorMsg)\n this.showForm(page.unlockWalletForm)\n }\n\n /* Show the form used to change wallet configuration settings. */\n async showReconfig (assetID: number, skipAnimation?: boolean) {\n const page = this.page\n Doc.hide(page.changeWalletType, page.changeTypeHideIcon, page.reconfigErr, page.showChangeType, page.changeTypeHideIcon)\n Doc.hide(page.reconfigErr)\n Doc.hide(page.enableWallet, page.disableWallet)\n // Hide update password section by default\n this.changeWalletPW = false\n this.setPWSettingViz(this.changeWalletPW)\n const asset = app().assets[assetID]\n\n const currentDef = app().currentWalletDefinition(assetID)\n const walletDefs = asset.token ? [asset.token.definition] : asset.info ? asset.info.availablewallets : []\n\n if (walletDefs.length > 1) {\n Doc.empty(page.changeWalletTypeSelect)\n Doc.show(page.showChangeType, page.changeTypeShowIcon)\n page.changeTypeMsg.textContent = intl.prep(intl.ID_CHANGE_WALLET_TYPE)\n for (const wDef of walletDefs) {\n const option = document.createElement('option') as HTMLOptionElement\n if (wDef.type === currentDef.type) option.selected = true\n option.value = option.textContent = wDef.type\n page.changeWalletTypeSelect.appendChild(option)\n }\n } else {\n Doc.hide(page.showChangeType)\n }\n\n const wallet = app().walletMap[assetID]\n Doc.setVis(wallet.traits & traitLogFiler, page.downloadLogs)\n Doc.setVis(wallet.traits & traitRecoverer, page.recoverWallet)\n Doc.setVis(wallet.traits & traitRestorer, page.exportWallet)\n Doc.setVis(wallet.traits & traitRescanner, page.rescanWallet)\n Doc.setVis(wallet.traits & traitPeerManager, page.managePeers)\n\n Doc.setVis(wallet.traits & traitsExtraOpts, page.otherActionsLabel)\n\n if (wallet.disabled) Doc.show(page.enableWallet)\n else Doc.show(page.disableWallet)\n\n page.recfgAssetLogo.src = Doc.logoPath(asset.symbol)\n page.recfgAssetName.textContent = asset.name\n if (!skipAnimation) this.showForm(page.reconfigForm)\n const loaded = app().loading(page.reconfigForm)\n const res = await postJSON('/api/walletsettings', { assetID })\n loaded()\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.reconfigErr, res.msg)\n return\n }\n const assetHasActiveOrders = app().haveActiveOrders(assetID)\n this.reconfigForm.update(currentDef.configopts || [], assetHasActiveOrders)\n this.reconfigForm.setConfig(res.map)\n this.updateDisplayedReconfigFields(currentDef)\n }\n\n changeWalletType () {\n const page = this.page\n const walletType = page.changeWalletTypeSelect.value || ''\n const walletDef = app().walletDefinition(this.selectedAssetID, walletType)\n this.reconfigForm.update(walletDef.configopts || [], false)\n this.updateDisplayedReconfigFields(walletDef)\n }\n\n updateDisplayedReconfigFields (walletDef: WalletDefinition) {\n if (walletDef.seeded || walletDef.type === 'token') {\n Doc.hide(this.page.showChangePW, this.reconfigForm.fileSelector)\n this.changeWalletPW = false\n this.setPWSettingViz(false)\n } else Doc.show(this.page.showChangePW, this.reconfigForm.fileSelector)\n }\n\n /* Display a deposit address. */\n async showDeposit (assetID: number) {\n this.depositAddrForm.setAsset(assetID)\n this.showForm(this.page.deposit)\n }\n\n /* Show the form to either send or withdraw funds. */\n async showSendForm (assetID: number) {\n const page = this.page\n const box = page.sendForm\n const { wallet, name, unitInfo: ui, symbol, token } = app().assets[assetID]\n Doc.hide(page.toggleSubtract)\n page.subtractCheckBox.checked = false\n\n const isWithdrawer = (wallet.traits & traitWithdrawer) !== 0\n if (isWithdrawer) {\n Doc.show(page.toggleSubtract)\n }\n\n Doc.hide(page.validAddr, page.sendErr, page.maxSendDisplay)\n page.sendAddr.classList.remove('invalid')\n page.sendAddr.value = ''\n page.sendAmt.value = ''\n this.showFiatValue(assetID, 0, page.sendValue)\n page.walletBal.textContent = Doc.formatFullPrecision(wallet.balance.available, ui)\n page.sendLogo.src = Doc.logoPath(symbol)\n page.sendName.textContent = name\n // page.sendFee.textContent = wallet.feerate\n // page.sendUnit.textContent = wallet.units\n\n if (wallet.balance.available > 0 && (wallet.traits & traitTxFeeEstimator) !== 0) {\n const feeReq = {\n assetID: assetID,\n subtract: isWithdrawer,\n value: wallet.balance.available\n }\n\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/txfee', feeReq)\n loaded()\n if (app().checkResponse(res)) {\n let canSend = wallet.balance.available\n if (!token) {\n canSend -= res.txfee\n if (canSend < 0) canSend = 0\n }\n this.maxSend = canSend\n page.maxSend.textContent = Doc.formatFullPrecision(canSend, ui)\n this.showFiatValue(assetID, canSend, page.maxSendFiat)\n if (token) {\n const { unitInfo: feeUI, symbol: feeSymbol } = app().assets[token.parentID]\n page.maxSendFee.textContent = Doc.formatFullPrecision(res.txfee, feeUI) + ' ' + feeSymbol\n this.showFiatValue(token.parentID, res.txfee, page.maxSendFeeFiat)\n } else {\n page.maxSendFee.textContent = Doc.formatFullPrecision(res.txfee, ui)\n this.showFiatValue(assetID, res.txfee, page.maxSendFeeFiat)\n }\n Doc.show(page.maxSendDisplay)\n }\n }\n\n this.showFiatValue(assetID, 0, page.sendValue)\n page.walletBal.textContent = Doc.formatFullPrecision(wallet.balance.available, ui)\n page.sendLogo.src = Doc.logoPath(wallet.symbol)\n page.sendName.textContent = name\n box.dataset.assetID = String(assetID)\n this.showForm(box)\n }\n\n /* doConnect connects to a wallet via the connectwallet API route. */\n async doConnect (assetID: number) {\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/connectwallet', { assetID })\n loaded()\n if (!app().checkResponse(res)) return\n this.updateDisplayedAsset(assetID)\n }\n\n /* openWalletSuccess is the success callback for wallet unlocking. */\n async openWalletSuccess (assetID: number, form?: PageElement) {\n this.assetUpdated(assetID, form, intl.prep(intl.ID_WALLET_UNLOCKED))\n }\n\n assetUpdated (assetID: number, oldForm?: PageElement, successMsg?: string) {\n if (assetID !== this.selectedAssetID) return\n this.updateDisplayedAsset(assetID)\n if (oldForm && Object.is(this.currentForm, oldForm)) {\n if (successMsg) this.showSuccess(successMsg)\n else this.closePopups()\n }\n }\n\n /* populateMaxSend populates the amount field with the max amount the wallet\n can send. The max send amount can be the maximum amount based on our\n pre-estimation or the asset's wallet balance.\n */\n async populateMaxSend () {\n const page = this.page\n const asset = app().assets[this.selectedAssetID]\n if (!asset) return\n // Populate send amount with max send value and ensure we don't check\n // subtract checkbox for assets that don't have a withdraw method.\n if ((asset.wallet.traits & traitWithdrawer) === 0) {\n page.sendAmt.value = String(this.maxSend / asset.unitInfo.conventional.conversionFactor)\n this.showFiatValue(asset.id, this.maxSend, page.sendValue)\n page.subtractCheckBox.checked = false\n } else {\n const amt = asset.wallet.balance.available\n page.sendAmt.value = String(amt / asset.unitInfo.conventional.conversionFactor)\n this.showFiatValue(asset.id, amt, page.sendValue)\n page.subtractCheckBox.checked = true\n }\n }\n\n /* send submits the send form to the API. */\n async send (): Promise {\n const page = this.page\n const assetID = parseInt(page.sendForm.dataset.assetID ?? '')\n const subtract = page.subtractCheckBox.checked ?? false\n const conversionFactor = app().unitInfo(assetID).conventional.conversionFactor\n const pw = page.vSendPw.value || ''\n page.vSendPw.value = ''\n if (pw === '') {\n Doc.showFormError(page.vSendErr, intl.prep(intl.ID_NO_PASS_ERROR_MSG))\n return\n }\n const open = {\n assetID: assetID,\n address: page.sendAddr.value,\n subtract: subtract,\n value: Math.round(parseFloat(page.sendAmt.value ?? '') * conversionFactor),\n pw: pw\n }\n const loaded = app().loading(page.vSendForm)\n const res = await postJSON('/api/send', open)\n loaded()\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.vSendErr, res.msg)\n return\n }\n const name = app().assets[assetID].name\n this.assetUpdated(assetID, page.vSendForm, intl.prep(intl.ID_SEND_SUCCESS, { assetName: name }))\n }\n\n /* update wallet configuration */\n async reconfig (): Promise {\n const page = this.page\n const assetID = this.selectedAssetID\n Doc.hide(page.reconfigErr)\n if (!page.appPW.value && !State.passwordIsCached()) {\n Doc.showFormError(page.reconfigErr, intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG))\n return\n }\n\n let walletType = app().currentWalletDefinition(assetID).type\n if (!Doc.isHidden(page.changeWalletType)) {\n walletType = page.changeWalletTypeSelect.value || ''\n }\n\n const loaded = app().loading(page.reconfigForm)\n const req: ReconfigRequest = {\n assetID: assetID,\n config: this.reconfigForm.map(assetID),\n appPW: page.appPW.value ?? '',\n walletType: walletType\n }\n if (this.changeWalletPW) req.newWalletPW = page.newPW.value\n const res = await postJSON('/api/reconfigurewallet', req)\n page.appPW.value = ''\n page.newPW.value = ''\n loaded()\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.reconfigErr, res.msg)\n return\n }\n this.assetUpdated(assetID, page.reconfigForm, intl.prep(intl.ID_RECONFIG_SUCCESS))\n }\n\n /* lock instructs the API to lock the wallet. */\n async lock (assetID: number): Promise {\n const page = this.page\n const loaded = app().loading(page.newWalletForm)\n const res = await postJSON('/api/closewallet', { assetID: assetID })\n loaded()\n if (!app().checkResponse(res)) return\n this.updateDisplayedAsset(assetID)\n }\n\n async downloadLogs (): Promise {\n const search = new URLSearchParams('')\n search.append('assetid', `${this.selectedAssetID}`)\n const url = new URL(window.location.href)\n url.search = search.toString()\n url.pathname = '/wallets/logfile'\n window.open(url.toString())\n }\n\n // displayExportWalletAuth displays a form to warn the user about the\n // dangers of exporting a wallet, and asks them to enter their password.\n async displayExportWalletAuth (): Promise {\n const page = this.page\n Doc.hide(page.exportWalletErr)\n page.exportWalletPW.value = ''\n this.showForm(page.exportWalletAuth)\n }\n\n // exportWalletAuthSubmit is called after the user enters their password to\n // authorize looking up the information to restore their wallet in an\n // external wallet.\n async exportWalletAuthSubmit (): Promise {\n const page = this.page\n const req = {\n assetID: this.selectedAssetID,\n pass: page.exportWalletPW.value\n }\n const url = '/api/restorewalletinfo'\n const loaded = app().loading(page.forms)\n const res = await postJSON(url, req)\n loaded()\n if (app().checkResponse(res)) {\n page.exportWalletPW.value = ''\n this.displayRestoreWalletInfo(res.restorationinfo)\n } else {\n Doc.showFormError(page.exportWalletErr, res.msg)\n }\n }\n\n // displayRestoreWalletInfo displays the information needed to restore a\n // wallet in external wallets.\n async displayRestoreWalletInfo (info: WalletRestoration[]): Promise {\n const page = this.page\n Doc.empty(page.restoreInfoCardsList)\n for (const wr of info) {\n const card = this.restoreInfoCard.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(card)\n tmpl.name.textContent = wr.target\n tmpl.seed.textContent = wr.seed\n tmpl.seedName.textContent = `${wr.seedName}:`\n tmpl.instructions.textContent = wr.instructions\n page.restoreInfoCardsList.appendChild(card)\n }\n this.showForm(page.restoreWalletInfo)\n }\n\n async recoverWallet (): Promise {\n const page = this.page\n Doc.hide(page.recoverWalletErr)\n const req = {\n assetID: this.selectedAssetID,\n appPW: page.recoverWalletPW.value\n }\n page.recoverWalletPW.value = ''\n const url = '/api/recoverwallet'\n const loaded = app().loading(page.forms)\n const res = await postJSON(url, req)\n loaded()\n if (res.code === Errors.activeOrdersErr) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n } else if (app().checkResponse(res)) {\n this.closePopups()\n } else {\n Doc.showFormError(page.recoverWalletErr, res.msg)\n }\n }\n\n /*\n * confirmForceSubmit resubmits either the recover or rescan requests with\n * force set to true. These two requests require force to be set to true if\n * they are called while the wallet is managing active orders.\n */\n async confirmForceSubmit (): Promise {\n const page = this.page\n this.forceReq.force = true\n const loaded = app().loading(page.forms)\n const res = await postJSON(this.forceUrl, this.forceReq)\n loaded()\n if (app().checkResponse(res)) this.closePopups()\n else {\n Doc.showFormError(page.confirmForceErr, res.msg)\n }\n }\n\n /* handleBalance handles notifications updating a wallet's balance and assets'\n value in default fiat rate.\n . */\n handleBalanceNote (note: BalanceNote): void {\n this.updateAssetButton(note.assetID)\n if (note.assetID === this.selectedAssetID) this.updateDisplayedAssetBalance()\n }\n\n /* handleRatesNote handles fiat rate notifications, updating the fiat value of\n * all supported assets.\n */\n handleRatesNote (note: RateNote): void {\n this.updateAssetButton(this.selectedAssetID)\n if (!note.fiatRates[this.selectedAssetID]) return\n this.updateDisplayedAssetBalance()\n }\n\n // showFiatValue displays the fiat equivalent for the provided amount.\n showFiatValue (assetID: number, amount: number, display: PageElement): void {\n const rate = app().fiatRatesMap[assetID]\n if (rate) {\n display.textContent = Doc.formatFiatConversion(amount, rate, app().unitInfo(assetID))\n Doc.show(display.parentElement as Element)\n } else Doc.hide(display.parentElement as Element)\n }\n\n /*\n * handleWalletStateNote is a handler for both the 'walletstate' and\n * 'walletconfig' notifications.\n */\n handleWalletStateNote (note: WalletStateNote): void {\n this.updateAssetButton(note.wallet.assetID)\n this.assetUpdated(note.wallet.assetID)\n if (note.topic === 'WalletPeersUpdate' &&\n note.wallet.assetID === this.selectedAssetID &&\n Doc.isDisplayed(this.page.managePeersForm)) {\n this.updateWalletPeersTable()\n }\n }\n\n /*\n * handleCreateWalletNote is a handler for 'createwallet' notifications.\n */\n handleCreateWalletNote (note: WalletCreationNote) {\n this.updateAssetButton(note.assetID)\n this.assetUpdated(note.assetID)\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /wallets page.\n */\n unload (): void {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n}\n\n/*\n * assetIsConfigurable indicates whether there are any user-configurable wallet\n * settings for the asset.\n */\nfunction assetIsConfigurable (assetID: number) {\n const asset = app().assets[assetID]\n if (asset.token) {\n const opts = asset.token.definition.configopts\n return opts && opts.length > 0\n }\n if (!asset.info) throw Error('this asset isn\\'t an asset, I guess')\n const defs = asset.info.availablewallets\n const zerothOpts = defs[0].configopts\n return defs.length > 1 || (zerothOpts && zerothOpts.length > 0)\n}\n\nconst FourSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n maximumSignificantDigits: 4\n})\n\nconst OneFractionalDigit = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1\n})\n\nfunction fourSigFigs (v: number) {\n if (v >= 1000 || Math.round(v) === v) return OneFractionalDigit.format(v)\n return FourSigFigs.format(v)\n}\n","import objectWithoutPropertiesLoose from \"./objectWithoutPropertiesLoose.js\";\nexport default function _objectWithoutProperties(source, excluded) {\n if (source == null) return {};\n var target = objectWithoutPropertiesLoose(source, excluded);\n var key, i;\n if (Object.getOwnPropertySymbols) {\n var sourceSymbolKeys = Object.getOwnPropertySymbols(source);\n for (i = 0; i < sourceSymbolKeys.length; i++) {\n key = sourceSymbolKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;\n target[key] = source[key];\n }\n }\n return target;\n}","export default function _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n return target;\n}","import Doc from './doc'\nimport BasePage from './basepage'\nimport State from './state'\nimport { postJSON } from './http'\nimport * as forms from './forms'\nimport * as intl from './locales'\nimport {\n app,\n Exchange,\n PageElement,\n PasswordCache\n} from './registry'\n\nconst animationLength = 300\n\nexport default class SettingsPage extends BasePage {\n body: HTMLElement\n currentDEX: Exchange\n page: Record\n forms: PageElement[]\n fiatRateSources: PageElement[]\n regAssetForm: forms.FeeAssetSelectionForm\n confirmRegisterForm: forms.ConfirmRegistrationForm\n newWalletForm: forms.NewWalletForm\n walletWaitForm: forms.WalletWaitForm\n dexAddrForm: forms.DEXAddressForm\n currentForm: PageElement\n pwCache: PasswordCache\n keyup: (e: KeyboardEvent) => void\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n const page = this.page = Doc.idDescendants(body)\n\n this.forms = Doc.applySelector(page.forms, ':scope > form')\n this.fiatRateSources = Doc.applySelector(page.fiatRateSources, 'input[type=checkbox]')\n\n Doc.bind(page.darkMode, 'click', () => {\n State.setCookie(State.darkModeCK, page.darkMode.checked || false ? '1' : '0')\n if (page.darkMode.checked) {\n document.body.classList.add('dark')\n } else {\n document.body.classList.remove('dark')\n }\n })\n\n page.showPokes.checked = State.fetchLocal(State.popupsLK) === '1'\n Doc.bind(page.showPokes, 'click', () => {\n const show = page.showPokes.checked || false\n State.storeLocal(State.popupsLK, show ? '1' : '0')\n app().showPopups = show\n })\n\n page.commitHash.textContent = app().commitHash.substring(0, 7)\n Doc.bind(page.addADex, 'click', () => {\n this.dexAddrForm.refresh()\n this.showForm(page.dexAddrForm)\n })\n\n this.fiatRateSources.forEach(src => {\n Doc.bind(src, 'change', async () => {\n const res = await postJSON('/api/toggleratesource', {\n disable: !src.checked,\n source: src.value\n })\n if (!app().checkResponse(res)) {\n src.checked = !src.checked\n }\n // Update asset rate values and disable conversion status.\n await app().fetchUser()\n })\n })\n\n // Asset selection\n this.regAssetForm = new forms.FeeAssetSelectionForm(page.regAssetForm, async (assetID: number) => {\n this.confirmRegisterForm.setAsset(assetID)\n\n const asset = app().assets[assetID]\n const wallet = asset.wallet\n if (wallet) {\n const bondAsset = this.currentDEX.bondAssets[asset.symbol]\n if (wallet.synced && wallet.balance.available > bondAsset.amount) {\n this.animateConfirmForm(page.regAssetForm)\n return\n }\n const txFee = await this.getRegistrationTxFeeEstimate(assetID, page.regAssetForm)\n this.walletWaitForm.setWallet(wallet, txFee)\n forms.slideSwap(page.regAssetForm, page.walletWait)\n return\n }\n\n this.newWalletForm.setAsset(assetID)\n this.currentForm = page.newWalletForm\n forms.slideSwap(page.regAssetForm, page.newWalletForm)\n })\n\n // Approve fee payment\n this.confirmRegisterForm = new forms.ConfirmRegistrationForm(page.confirmRegForm, () => {\n this.registerDEXSuccess()\n }, () => {\n this.animateRegAsset(page.confirmRegForm)\n }, this.pwCache)\n\n // Create a new wallet\n this.newWalletForm = new forms.NewWalletForm(\n page.newWalletForm,\n assetID => this.newWalletCreated(assetID),\n this.pwCache,\n () => this.animateRegAsset(page.newWalletForm)\n )\n\n this.walletWaitForm = new forms.WalletWaitForm(page.walletWait, () => {\n this.animateConfirmForm(page.walletWait)\n }, () => { this.animateRegAsset(page.walletWait) })\n\n // Enter an address for a new DEX\n this.dexAddrForm = new forms.DEXAddressForm(page.dexAddrForm, async (xc: Exchange, certFile: string) => {\n this.currentDEX = xc\n this.confirmRegisterForm.setExchange(xc, certFile)\n this.walletWaitForm.setExchange(xc)\n this.regAssetForm.setExchange(xc)\n this.animateRegAsset(page.dexAddrForm)\n })\n\n Doc.bind(page.importAccount, 'click', () => this.prepareAccountImport(page.authorizeAccountImportForm))\n forms.bind(page.authorizeAccountImportForm, page.authorizeImportAccountConfirm, () => this.importAccount())\n\n Doc.bind(page.changeAppPW, 'click', () => this.showForm(page.changeAppPWForm))\n forms.bind(page.changeAppPWForm, page.submitNewPW, () => this.changeAppPW())\n\n Doc.bind(page.accountFile, 'change', () => this.onAccountFileChange())\n Doc.bind(page.removeAccount, 'click', () => this.clearAccountFile())\n Doc.bind(page.addAccount, 'click', () => page.accountFile.click())\n\n Doc.bind(page.exportSeed, 'click', () => this.showForm(page.exportSeedAuth))\n forms.bind(page.exportSeedAuth, page.exportSeedSubmit, () => this.submitExportSeedReq())\n\n const closePopups = () => {\n Doc.hide(page.forms)\n page.exportSeedPW.value = ''\n page.seedDiv.textContent = ''\n }\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n closePopups()\n }\n }\n Doc.bind(document, 'keyup', this.keyup)\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { closePopups() })\n })\n }\n\n // Retrieve an estimate for the tx fee needed to pay the registration fee.\n async getRegistrationTxFeeEstimate (assetID: number, form: HTMLElement) {\n const cert = await this.getCertFile()\n const loaded = app().loading(form)\n const res = await postJSON('/api/regtxfee', {\n addr: this.currentDEX.host,\n cert: cert,\n asset: assetID\n })\n loaded()\n if (!app().checkResponse(res)) {\n return 0\n }\n return res.txfee\n }\n\n async newWalletCreated (assetID: number) {\n const user = await app().fetchUser()\n if (!user) return\n const page = this.page\n const asset = user.assets[assetID]\n const wallet = asset.wallet\n const bondAmt = this.currentDEX.bondAssets[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > bondAmt) {\n await this.animateConfirmForm(page.newWalletForm)\n return\n }\n\n const txFee = await this.getRegistrationTxFeeEstimate(assetID, page.newWalletForm)\n this.walletWaitForm.setWallet(wallet, txFee)\n this.currentForm = page.walletWait\n await forms.slideSwap(page.newWalletForm, page.walletWait)\n }\n\n async onAccountFileChange () {\n const page = this.page\n const files = page.accountFile.files\n if (!files || !files.length) return\n page.selectedAccount.textContent = files[0].name\n Doc.show(page.removeAccount)\n Doc.hide(page.addAccount)\n }\n\n /* clearAccountFile cleanup accountFile value and selectedAccount text */\n clearAccountFile () {\n const page = this.page\n page.accountFile.value = ''\n page.selectedAccount.textContent = intl.prep(intl.ID_NONE_SELECTED)\n Doc.hide(page.removeAccount)\n Doc.show(page.addAccount)\n }\n\n async prepareAccountImport (authorizeAccountImportForm: HTMLElement) {\n const page = this.page\n page.importAccountErr.textContent = ''\n this.showForm(authorizeAccountImportForm)\n }\n\n // importAccount imports the account\n async importAccount () {\n const page = this.page\n const pw = page.importAccountAppPass.value\n page.importAccountAppPass.value = ''\n let accountString = ''\n if (page.accountFile.value) {\n const files = page.accountFile.files\n if (!files || !files.length) {\n console.error('importAccount: no file specified')\n return\n }\n accountString = await files[0].text()\n }\n let account\n try {\n account = JSON.parse(accountString)\n } catch (e) {\n page.importAccountErr.textContent = e.message\n Doc.show(page.importAccountErr)\n return\n }\n if (typeof account === 'undefined') {\n page.importAccountErr.textContent = intl.prep(intl.ID_ACCT_UNDEFINED)\n Doc.show(page.importAccountErr)\n return\n }\n const { bonds = [], ...acctInf } = account\n const req = {\n pw: pw,\n account: acctInf,\n bonds: bonds\n }\n const loaded = app().loading(this.body)\n const importResponse = await postJSON('/api/importaccount', req)\n loaded()\n if (!app().checkResponse(importResponse)) {\n page.importAccountErr.textContent = importResponse.msg\n Doc.show(page.importAccountErr)\n return\n }\n await app().fetchUser()\n Doc.hide(page.forms)\n // Initial method of displaying imported account.\n window.location.reload()\n }\n\n async submitExportSeedReq () {\n const page = this.page\n const pw = page.exportSeedPW.value\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/exportseed', { pass: pw })\n loaded()\n if (!app().checkResponse(res)) {\n page.exportAccountErr.textContent = res.msg\n Doc.show(page.exportSeedE)\n return\n }\n page.exportSeedPW.value = ''\n page.seedDiv.textContent = res.seed\n this.showForm(page.authorizeSeedDisplay)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n const page = this.page\n this.currentForm = form\n this.forms.forEach(form => Doc.hide(form))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n /* gets the contents of the cert file */\n async getCertFile () {\n let cert = ''\n if (this.dexAddrForm.page.certFile.value) {\n const files = this.dexAddrForm.page.certFile.files\n if (files && files.length) cert = await files[0].text()\n }\n return cert\n }\n\n /* Called after successful registration to a DEX. */\n async registerDEXSuccess () {\n const page = this.page\n Doc.hide(page.forms)\n await app().fetchUser()\n // Initial method of displaying added dex.\n window.location.reload()\n }\n\n /* Change application password */\n async changeAppPW () {\n const page = this.page\n Doc.hide(page.changePWErrMsg)\n\n const clearValues = () => {\n page.appPW.value = ''\n page.newAppPW.value = ''\n page.confirmNewPW.value = ''\n }\n // Ensure password fields are nonempty.\n if (!page.appPW.value || !page.newAppPW.value || !page.confirmNewPW.value) {\n page.changePWErrMsg.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.changePWErrMsg)\n clearValues()\n return\n }\n // Ensure password confirmation matches.\n if (page.newAppPW.value !== page.confirmNewPW.value) {\n page.changePWErrMsg.textContent = intl.prep(intl.ID_PASSWORD_NOT_MATCH)\n Doc.show(page.changePWErrMsg)\n clearValues()\n return\n }\n const loaded = app().loading(page.changeAppPW)\n const req = {\n appPW: page.appPW.value,\n newAppPW: page.newAppPW.value\n }\n clearValues()\n const res = await postJSON('/api/changeapppass', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.changePWErrMsg.textContent = res.msg\n Doc.show(page.changePWErrMsg)\n return\n }\n Doc.hide(page.forms)\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /settings page.\n */\n unload () {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n\n /* Swap in the asset selection form and run the animation. */\n async animateRegAsset (oldForm: HTMLElement) {\n Doc.hide(oldForm)\n const form = this.page.regAssetForm\n this.currentForm = form\n this.regAssetForm.animate()\n Doc.show(form)\n }\n\n /* Swap in the confirmation form and run the animation. */\n async animateConfirmForm (oldForm: HTMLElement) {\n this.confirmRegisterForm.animate()\n const form = this.page.confirmRegForm\n this.currentForm = form\n Doc.hide(oldForm)\n Doc.show(form)\n }\n}\n","import {\n MarketOrderBook,\n MiniOrder\n} from './registry'\n\nexport default class OrderBook {\n base: number\n baseSymbol: string\n quote: number\n quoteSymbol: string\n buys: MiniOrder[]\n sells: MiniOrder[]\n\n constructor (mktBook: MarketOrderBook, baseSymbol: string, quoteSymbol: string) {\n this.base = mktBook.base\n this.baseSymbol = baseSymbol\n this.quote = mktBook.quote\n this.quoteSymbol = quoteSymbol\n // Books are sorted mid-gap first.\n this.buys = mktBook.book.buys || []\n this.sells = mktBook.book.sells || []\n }\n\n /* add adds an order to the order book. */\n add (ord: MiniOrder) {\n if (ord.qtyAtomic === 0) {\n // TODO: Somebody, for the love of god, figure out why the hell this helps\n // with the ghost orders problem. As far as I know, this order is a booked\n // order that had more than one match in an epoch and completely filled.\n // Because the first match didn't exhaust the order, there would be a\n // 'update_remaining' notification scheduled for the order. But by the\n // time OrderRouter generates the notification long after matching, the\n // order has zero qty left to fill. It's all good though, kinda, because\n // the notification is quickly followed with an 'unbook_order'\n // notification. I have tried my damnedest to catch an update_remaining\n // note without an accompanying unbook_order note, and have thus failed.\n // Yet, this fix somehow seems to work. It's infuriating, tbh.\n window.log('zeroqty', 'zero quantity order encountered', ord)\n return\n }\n const side = ord.sell ? this.sells : this.buys\n side.splice(findIdx(side, ord.rate, !ord.sell), 0, ord)\n }\n\n /* remove removes an order from the order book. */\n remove (token: string) {\n if (this.removeFromSide(this.sells, token)) return\n this.removeFromSide(this.buys, token)\n }\n\n /* removeFromSide removes an order from the list of orders. */\n removeFromSide (side: MiniOrder[], token: string) {\n const [ord, i] = this.findOrder(side, token)\n if (ord) {\n side.splice(i, 1)\n return true\n }\n return false\n }\n\n /* findOrder finds an order in a specified side */\n findOrder (side: MiniOrder[], token: string): [MiniOrder | null, number] {\n for (let i = 0; i < side.length; i++) {\n if (side[i].token === token) {\n return [side[i], i]\n }\n }\n return [null, -1]\n }\n\n /* updates the remaining quantity of an order. */\n updateRemaining (token: string, qty: number, qtyAtomic: number) {\n if (this.updateRemainingSide(this.sells, token, qty, qtyAtomic)) return\n this.updateRemainingSide(this.buys, token, qty, qtyAtomic)\n }\n\n /*\n * updateRemainingSide looks for the order in the side and updates the\n * quantity, returning true on success, false if order not found.\n */\n updateRemainingSide (side: MiniOrder[], token: string, qty: number, qtyAtomic: number) {\n const ord = this.findOrder(side, token)[0]\n if (ord) {\n ord.qty = qty\n ord.qtyAtomic = qtyAtomic\n return true\n }\n return false\n }\n\n /*\n * setEpoch sets the current epoch and clear any orders from previous epochs.\n */\n setEpoch (epochIdx: number) {\n const approve = (ord: MiniOrder) => ord.epoch === undefined || ord.epoch === 0 || ord.epoch === epochIdx\n this.sells = this.sells.filter(approve)\n this.buys = this.buys.filter(approve)\n }\n\n /* empty will return true if both the buys and sells lists are empty. */\n empty () {\n return !this.sells.length && !this.buys.length\n }\n\n /* count is the total count of both buy and sell orders. */\n count () {\n return this.sells.length + this.buys.length\n }\n\n /* bestGapOrder will return the best non-epoch order if one exists, or the\n * best epoch order if there are only epoch orders, or null if there are no\n * orders.\n */\n bestGapOrder (side: MiniOrder[]) {\n let best = null\n for (const ord of side) {\n if (!ord.epoch) return ord\n if (!best) {\n best = ord\n }\n }\n return best\n }\n\n bestGapBuy () {\n return this.bestGapOrder(this.buys)\n }\n\n bestGapSell () {\n return this.bestGapOrder(this.sells)\n }\n}\n\n/*\n * findIdx find the index at which to insert the order into the list of orders.\n */\nfunction findIdx (side: MiniOrder[], rate: number, less: boolean): number {\n for (let i = 0; i < side.length; i++) {\n if ((side[i].rate < rate) === less) return i\n }\n return side.length\n}\n","// MessageSocket is a WebSocket manager that uses the Decred DEX Message format\n// for communications.\n//\n// Message request format:\n// {\n// route: 'name',\n// id: int,\n// payload: anything or nothing\n// }\n//\n// Message response payload will be a result object with either a valid 'result'\n// field or an 'error' field\n//\n// Functions for external use:\n// registerRoute (route, handler) -- register a function to handle events\n// of the given type\n// request (route, payload) -- create a JSON message in the above format and\n// send it\n//\n// Based on messagesocket_service.js by Jonathan Chappelow @ dcrdata, which is\n// based on ws_events_dispatcher.js by Ismael Celis\nconst typeRequest = 1\n\nfunction forward (route: string, payload: any, handlers: Record void)[]>) {\n if (!route && payload.error) {\n const err = payload.error\n console.error(`websocket error (code ${err.code}): ${err.message}`)\n return\n }\n if (typeof handlers[route] === 'undefined') {\n // console.log(`unhandled message for ${route}: ${payload}`)\n return\n }\n // call each handler\n for (let i = 0; i < handlers[route].length; i++) {\n handlers[route][i](payload)\n }\n}\n\nlet id = 0\n\ntype NoteReceiver = (payload: any) => void\n\nclass MessageSocket {\n uri: string\n connection: WebSocket | null\n handlers: Record\n queue: [string, any][]\n maxQlength: number\n reloader: () => void // appears unused\n\n constructor () {\n this.handlers = {}\n this.queue = []\n this.maxQlength = 5\n }\n\n registerRoute (route: string, handler: NoteReceiver) {\n this.handlers[route] = this.handlers[route] || []\n this.handlers[route].push(handler)\n }\n\n deregisterRoute (route: string) {\n this.handlers[route] = []\n }\n\n // request sends a request-type message to the server\n request (route: string, payload: any) {\n if (!this.connection || this.connection.readyState !== window.WebSocket.OPEN) {\n while (this.queue.length > this.maxQlength - 1) this.queue.shift()\n this.queue.push([route, payload])\n return\n }\n id++\n const message = JSON.stringify({\n route: route,\n type: typeRequest,\n id: id,\n payload: payload\n })\n\n window.log('ws', 'sending', message)\n this.connection.send(message)\n }\n\n close (reason: string) {\n window.log('ws', 'close, reason:', reason, this.handlers)\n this.handlers = {}\n if (this.connection) this.connection.close()\n }\n\n connect (uri: string, reloader: () => void) {\n this.uri = uri\n this.reloader = reloader\n let retrys = 0\n const go = () => {\n window.log('ws', `connecting to ${uri}`)\n let conn: WebSocket | null = this.connection = new window.WebSocket(uri)\n if (!conn) return\n const timeout = setTimeout(() => {\n // readyState is still WebSocket.CONNECTING. Cancel and trigger onclose.\n if (conn) conn.close()\n }, 500)\n\n // unmarshal message, and forward the message to registered handlers\n conn.onmessage = (evt: MessageEvent) => {\n const message = JSON.parse(evt.data)\n forward(message.route, message.payload, this.handlers)\n }\n\n // Stub out standard functions\n conn.onclose = (evt: CloseEvent) => {\n window.log('ws', 'onclose')\n clearTimeout(timeout)\n conn = this.connection = null\n forward('close', null, this.handlers)\n retrys++\n // 1.2, 1.6, 2.0, 2.4, 3.1, 3.8, 4.8, 6.0, 7.5, 9.3, ...\n const delay = Math.min(Math.pow(1.25, retrys), 10)\n console.error(`websocket disconnected (${evt.code}), trying again in ${delay.toFixed(1)} seconds`)\n setTimeout(() => {\n go()\n }, delay * 1000)\n }\n\n conn.onopen = () => {\n window.log('ws', 'onopen')\n clearTimeout(timeout)\n if (retrys > 0) {\n retrys = 0\n reloader()\n }\n forward('open', null, this.handlers)\n const queue = this.queue\n this.queue = []\n for (const [route, message] of queue) {\n this.request(route, message)\n }\n }\n\n conn.onerror = (evt: Event) => {\n window.log('ws', 'onerror:', evt)\n forward('error', evt, this.handlers)\n }\n }\n go()\n }\n}\n\nconst ws = new MessageSocket()\nexport default ws\n","import Doc, { WalletIcons } from './doc'\nimport State from './state'\nimport BasePage from './basepage'\nimport OrderBook from './orderbook'\nimport {\n CandleChart,\n DepthChart,\n DepthLine,\n CandleReporters,\n MouseReport,\n VolumeReport,\n DepthMarker,\n Wave\n} from './charts'\nimport { postJSON } from './http'\nimport {\n NewWalletForm,\n UnlockWalletForm,\n AccelerateOrderForm,\n DepositAddress,\n bind as bindForm\n} from './forms'\nimport * as OrderUtil from './orderutil'\nimport ws from './ws'\nimport * as intl from './locales'\nimport {\n app,\n SupportedAsset,\n PageElement,\n Order,\n Market,\n OrderEstimate,\n MaxOrderEstimate,\n Exchange,\n UnitInfo,\n Asset,\n Candle,\n CandlesPayload,\n TradeForm,\n BookUpdate,\n MaxSell,\n MaxBuy,\n SwapEstimate,\n MarketOrderBook,\n APIResponse,\n PreSwap,\n PreRedeem,\n WalletStateNote,\n WalletCreationNote,\n SpotPriceNote,\n BondNote,\n OrderNote,\n EpochNote,\n BalanceNote,\n MiniOrder,\n RemainderUpdate,\n ConnEventNote,\n OrderOption,\n ConnectionStatus,\n RecentMatch,\n MatchNote\n} from './registry'\nimport { setOptionTemplates } from './opts'\n\nconst bind = Doc.bind\n\nconst bookRoute = 'book'\nconst bookOrderRoute = 'book_order'\nconst unbookOrderRoute = 'unbook_order'\nconst updateRemainingRoute = 'update_remaining'\nconst epochOrderRoute = 'epoch_order'\nconst candlesRoute = 'candles'\nconst candleUpdateRoute = 'candle_update'\nconst unmarketRoute = 'unmarket'\nconst epochMatchSummaryRoute = 'epoch_match_summary'\n\nconst animationLength = 500\n\nconst anHour = 60 * 60 * 1000 // milliseconds\n\nconst check = document.createElement('span')\ncheck.classList.add('ico-check')\n\nconst buyBtnClass = 'buygreen-bg'\nconst sellBtnClass = 'sellred-bg'\n\nconst fiveMinBinKey = '5m'\n\nconst percentFormatter = new Intl.NumberFormat(document.documentElement.lang, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 2\n})\n\nconst parentIDNone = 0xFFFFFFFF\n\ninterface MetaOrder {\n div: HTMLElement\n header: Record\n details: Record\n ord: Order\n cancelling?: boolean\n status?: number\n}\n\ninterface CancelData {\n bttn: PageElement\n order: Order\n}\n\ninterface CurrentMarket {\n dex: Exchange\n sid: string // A string market identifier used by the DEX.\n cfg: Market\n base: SupportedAsset\n quote: SupportedAsset\n baseUnitInfo: UnitInfo\n quoteUnitInfo: UnitInfo\n maxSellRequested: boolean\n maxSell: MaxOrderEstimate | null\n sellBalance: number\n buyBalance: number\n maxBuys: Record\n candleCaches: Record\n baseCfg: Asset\n quoteCfg: Asset\n rateConversionFactor: number\n}\n\ninterface LoadTracker {\n loaded: () => void\n timer: number\n}\n\ninterface OrderRow extends HTMLElement {\n manager: OrderTableRowManager\n}\n\ninterface StatsDisplay {\n row: PageElement\n tmpl: Record\n}\n\nexport default class MarketsPage extends BasePage {\n page: Record\n main: HTMLElement\n maxLoaded: (() => void) | null\n maxOrderUpdateCounter: number\n market: CurrentMarket\n currentForm: HTMLElement\n openAsset: SupportedAsset\n openFunc: () => void\n currentCreate: SupportedAsset\n maxEstimateTimer: number | null\n book: OrderBook\n cancelData: CancelData\n metaOrders: Record\n preorderCache: Record\n currentOrder: TradeForm\n depthLines: Record\n activeMarkerRate: number | null\n hovers: HTMLElement[]\n ogTitle: string\n depthChart: DepthChart\n candleChart: CandleChart\n candleDur: string\n balanceWgt: BalanceWidget\n marketList: MarketList\n quoteUnits: NodeListOf\n baseUnits: NodeListOf\n unlockForm: UnlockWalletForm\n newWalletForm: NewWalletForm\n depositAddrForm: DepositAddress\n keyup: (e: KeyboardEvent) => void\n secondTicker: number\n candlesLoading: LoadTracker | null\n accelerateOrderForm: AccelerateOrderForm\n recentMatches: RecentMatch[]\n recentMatchesSortKey: string\n recentMatchesSortDirection: 1 | -1\n stats: [StatsDisplay, StatsDisplay]\n loadingAnimations: { candles?: Wave, depth?: Wave }\n\n constructor (main: HTMLElement, data: any) {\n super()\n const page = this.page = Doc.idDescendants(main)\n this.main = main\n if (!this.main.parentElement) return // Not gonna happen, but TypeScript cares.\n // There may be multiple pending updates to the max order. This makes sure\n // that the screen is updated with the most recent one.\n this.maxOrderUpdateCounter = 0\n this.metaOrders = {}\n this.recentMatches = []\n this.preorderCache = {}\n this.depthLines = {\n hover: [],\n input: []\n }\n this.hovers = []\n // 'Recent Matches' list sort key and direction.\n this.recentMatchesSortKey = 'age'\n this.recentMatchesSortDirection = -1\n // store original title so we can re-append it when updating market value.\n this.ogTitle = document.title\n\n const depthReporters = {\n click: (x: number) => { this.reportDepthClick(x) },\n volume: (r: VolumeReport) => { this.reportDepthVolume(r) },\n mouse: (r: MouseReport) => { this.reportDepthMouse(r) },\n zoom: (z: number) => { this.reportDepthZoom(z) }\n }\n this.depthChart = new DepthChart(page.depthChart, depthReporters, State.fetchLocal(State.depthZoomLK))\n\n const candleReporters: CandleReporters = {\n mouse: c => { this.reportMouseCandle(c) }\n }\n this.candleChart = new CandleChart(page.candlesChart, candleReporters)\n\n const success = () => { /* do nothing */ }\n // Do not call cleanTemplates before creating the AccelerateOrderForm\n this.accelerateOrderForm = new AccelerateOrderForm(page.accelerateForm, success)\n\n // TODO: Store user's state and reload last known configuration.\n this.candleDur = fiveMinBinKey\n\n // Setup the register to trade button.\n // TODO: Use dexsettings page?\n const registerBttn = Doc.tmplElement(page.notRegistered, 'registerBttn')\n bind(registerBttn, 'click', () => {\n app().loadPage('register', { host: this.market.dex.host })\n })\n\n // Set up the BalanceWidget.\n {\n page.walletInfoTmpl.removeAttribute('id')\n const bWidget = page.walletInfoTmpl\n const qWidget = page.walletInfoTmpl.cloneNode(true) as PageElement\n bWidget.after(qWidget)\n const wgt = this.balanceWgt = new BalanceWidget(bWidget, qWidget)\n const baseIcons = wgt.base.stateIcons.icons\n const quoteIcons = wgt.quote.stateIcons.icons\n bind(wgt.base.tmpl.connect, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.tmpl.connect, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(wgt.base.tmpl.expired, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.tmpl.expired, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(baseIcons.sleeping, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(quoteIcons.sleeping, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(baseIcons.locked, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(quoteIcons.locked, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(baseIcons.disabled, 'click', () => { this.showToggleWalletStatus(this.market.base) })\n bind(quoteIcons.disabled, 'click', () => { this.showToggleWalletStatus(this.market.quote) })\n bind(wgt.base.tmpl.newWalletBttn, 'click', () => { this.showCreate(this.market.base) })\n bind(wgt.quote.tmpl.newWalletBttn, 'click', () => { this.showCreate(this.market.quote) })\n Doc.bind(wgt.base.tmpl.walletAddr, 'click', () => { this.showDeposit(this.market.base.id) })\n Doc.bind(wgt.quote.tmpl.walletAddr, 'click', () => { this.showDeposit(this.market.quote.id) })\n this.depositAddrForm = new DepositAddress(page.deposit)\n }\n\n // Bind toggle wallet status form.\n bindForm(page.toggleWalletStatusConfirm, page.toggleWalletStatusSubmit, async () => { this.toggleWalletStatus() })\n\n // Prepare templates for the buy and sell tables and the user's order table.\n setOptionTemplates(page)\n Doc.cleanTemplates(page.rowTemplate, page.durBttnTemplate, page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl, page.userOrderTmpl)\n\n // Store the elements that need their ticker changed when the market\n // changes.\n this.quoteUnits = main.querySelectorAll('[data-unit=quote]')\n this.baseUnits = main.querySelectorAll('[data-unit=base]')\n\n // Buttons to set order type and side.\n bind(page.buyBttn, 'click', () => {\n swapBttns(page.sellBttn, page.buyBttn)\n page.submitBttn.classList.remove(sellBtnClass)\n page.submitBttn.classList.add(buyBtnClass)\n page.maxLbl.textContent = intl.prep(intl.ID_BUY)\n this.setOrderBttnText()\n this.setOrderVisibility()\n this.drawChartLines()\n })\n bind(page.sellBttn, 'click', () => {\n swapBttns(page.buyBttn, page.sellBttn)\n page.submitBttn.classList.add(sellBtnClass)\n page.submitBttn.classList.remove(buyBtnClass)\n page.maxLbl.textContent = intl.prep(intl.ID_SELL)\n this.setOrderBttnText()\n this.setOrderVisibility()\n this.drawChartLines()\n })\n bind(page.limitBttn, 'click', () => {\n swapBttns(page.marketBttn, page.limitBttn)\n this.setOrderVisibility()\n if (!page.rateField.value) return\n this.depthLines.input = [{\n rate: parseFloat(page.rateField.value || '0'),\n color: this.isSell() ? this.depthChart.theme.sellLine : this.depthChart.theme.buyLine\n }]\n this.drawChartLines()\n })\n bind(page.marketBttn, 'click', () => {\n swapBttns(page.limitBttn, page.marketBttn)\n this.setOrderVisibility()\n this.setMarketBuyOrderEstimate()\n this.depthLines.input = []\n this.drawChartLines()\n })\n bind(page.maxOrd, 'click', () => {\n if (this.isSell()) {\n const maxSell = this.market.maxSell\n if (!maxSell) return\n page.lotField.value = String(maxSell.swap.lots)\n } else page.lotField.value = String(this.market.maxBuys[this.adjustedRate()].swap.lots)\n this.lotChanged()\n })\n\n Doc.disableMouseWheel(page.rateField, page.lotField, page.qtyField, page.mktBuyField)\n\n // Handle the full orderbook sent on the 'book' route.\n ws.registerRoute(bookRoute, (data: BookUpdate) => { this.handleBookRoute(data) })\n // Handle the new order for the order book on the 'book_order' route.\n ws.registerRoute(bookOrderRoute, (data: BookUpdate) => { this.handleBookOrderRoute(data) })\n // Remove the order sent on the 'unbook_order' route from the orderbook.\n ws.registerRoute(unbookOrderRoute, (data: BookUpdate) => { this.handleUnbookOrderRoute(data) })\n // Update the remaining quantity on a booked order.\n ws.registerRoute(updateRemainingRoute, (data: BookUpdate) => { this.handleUpdateRemainingRoute(data) })\n // Handle the new order for the order book on the 'epoch_order' route.\n ws.registerRoute(epochOrderRoute, (data: BookUpdate) => { this.handleEpochOrderRoute(data) })\n // Handle the initial candlestick data on the 'candles' route.\n ws.registerRoute(candlesRoute, (data: BookUpdate) => { this.handleCandlesRoute(data) })\n // Handle the candles update on the 'candles' route.\n ws.registerRoute(candleUpdateRoute, (data: BookUpdate) => { this.handleCandleUpdateRoute(data) })\n\n // Handle the recent matches update on the 'epoch_report' route.\n ws.registerRoute(epochMatchSummaryRoute, (data: BookUpdate) => { this.handleEpochMatchSummary(data) })\n // Bind the wallet unlock form.\n this.unlockForm = new UnlockWalletForm(page.unlockWalletForm, async () => { this.openFunc() })\n // Create a wallet\n this.newWalletForm = new NewWalletForm(page.newWalletForm, async () => { this.createWallet() })\n // Main order form.\n bindForm(page.orderForm, page.submitBttn, async () => { this.stepSubmit() })\n // Order verification form.\n bindForm(page.verifyForm, page.vSubmit, async () => { this.submitOrder() })\n // Unlock for order estimation\n Doc.bind(page.vUnlockSubmit, 'click', async () => { this.submitEstimateUnlock() })\n // Cancel order form.\n bindForm(page.cancelForm, page.cancelSubmit, async () => { this.submitCancel() })\n // Order detail view.\n Doc.bind(page.vFeeDetails, 'click', () => this.showForm(page.vDetailPane))\n Doc.bind(page.closeDetailPane, 'click', () => this.showVerifyForm())\n // // Bind active orders list's header sort events.\n page.recentMatchesTable.querySelectorAll('[data-ordercol]')\n .forEach((th: HTMLElement) => bind(\n th, 'click', () => setRecentMatchesSortCol(th.dataset.ordercol || '')\n ))\n\n const setRecentMatchesSortCol = (key: string) => {\n // First unset header's current sorted col classes.\n unsetRecentMatchesSortColClasses()\n if (this.recentMatchesSortKey === key) {\n this.recentMatchesSortDirection *= -1\n } else {\n this.recentMatchesSortKey = key\n this.recentMatchesSortDirection = 1\n }\n this.refreshRecentMatchesTable()\n setRecentMatchesSortColClasses()\n }\n\n // sortClassByDirection receives a sort direction and return a class based on it.\n const sortClassByDirection = (element: 1 | -1) => {\n if (element === 1) return 'sorted-asc'\n return 'sorted-dsc'\n }\n\n const unsetRecentMatchesSortColClasses = () => {\n page.recentMatchesTable.querySelectorAll('[data-ordercol]')\n .forEach(th => th.classList.remove('sorted-asc', 'sorted-dsc'))\n }\n\n const setRecentMatchesSortColClasses = () => {\n const key = this.recentMatchesSortKey\n const sortCls = sortClassByDirection(this.recentMatchesSortDirection)\n Doc.safeSelector(page.recentMatchesTable, `[data-ordercol=${key}]`).classList.add(sortCls)\n }\n\n // Set default's sorted col header classes.\n setRecentMatchesSortColClasses()\n\n const closePopups = () => {\n Doc.hide(page.forms)\n page.vPass.value = ''\n page.cancelPass.value = ''\n }\n\n // If the user clicks outside of a form, it should close the page overlay.\n bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (Doc.isDisplayed(page.vDetailPane) && !Doc.mouseInElement(e, page.vDetailPane)) return this.showVerifyForm()\n if (!Doc.mouseInElement(e, this.currentForm)) {\n closePopups()\n }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n closePopups()\n }\n }\n bind(document, 'keyup', this.keyup)\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { closePopups() })\n })\n\n // Event listeners for interactions with the various input fields.\n bind(page.lotField, 'change', () => { this.lotChanged() })\n bind(page.lotField, 'keyup', () => { this.lotChanged() })\n bind(page.qtyField, 'change', () => { this.quantityChanged(true) })\n bind(page.qtyField, 'keyup', () => { this.quantityChanged(false) })\n bind(page.mktBuyField, 'change', () => { this.marketBuyChanged() })\n bind(page.mktBuyField, 'keyup', () => { this.marketBuyChanged() })\n bind(page.rateField, 'change', () => { this.rateFieldChanged() })\n bind(page.rateField, 'keyup', () => { this.previewQuoteAmt(true) })\n\n // Market search input bindings.\n bind(page.marketSearchV1, 'change', () => { this.filterMarkets() })\n bind(page.marketSearchV1, 'keyup', () => { this.filterMarkets() })\n\n // Acknowledge the order disclaimer.\n const setDisclaimerAckViz = (acked: boolean) => {\n Doc.setVis(!acked, page.disclaimer, page.disclaimerAck)\n Doc.setVis(acked, page.showDisclaimer)\n }\n bind(page.disclaimerAck, 'click', () => {\n State.storeLocal(State.orderDisclaimerAckedLK, true)\n setDisclaimerAckViz(true)\n })\n bind(page.showDisclaimer, 'click', () => {\n State.storeLocal(State.orderDisclaimerAckedLK, false)\n setDisclaimerAckViz(false)\n })\n setDisclaimerAckViz(State.fetchLocal(State.orderDisclaimerAckedLK))\n\n const clearChartLines = () => {\n this.depthLines.hover = []\n this.drawChartLines()\n }\n bind(page.buyRows, 'mouseleave', clearChartLines)\n bind(page.sellRows, 'mouseleave', clearChartLines)\n bind(page.userOrders, 'mouseleave', () => {\n this.activeMarkerRate = null\n this.setDepthMarkers()\n })\n\n const stats0 = page.marketStatsV1\n const stats1 = stats0.cloneNode(true) as PageElement\n stats1.classList.add('listopen')\n Doc.hide(stats0, stats1)\n stats1.removeAttribute('id')\n app().headerSpace.appendChild(stats1)\n this.stats = [{ row: stats0, tmpl: Doc.parseTemplate(stats0) }, { row: stats1, tmpl: Doc.parseTemplate(stats1) }]\n\n const closeMarketsList = () => {\n State.storeLocal(State.leftMarketDockLK, '0')\n page.leftMarketDock.classList.remove('default')\n page.leftMarketDock.classList.add('stashed')\n for (const s of this.stats) s.row.classList.remove('listopen')\n }\n const openMarketsList = () => {\n State.storeLocal(State.leftMarketDockLK, '1')\n page.leftMarketDock.classList.remove('default', 'stashed')\n for (const s of this.stats) s.row.classList.add('listopen')\n }\n Doc.bind(page.leftHider, 'click', () => closeMarketsList())\n Doc.bind(page.marketReopener, 'click', () => openMarketsList())\n for (const s of this.stats) {\n Doc.bind(s.tmpl.marketSelect, 'click', () => {\n if (page.leftMarketDock.clientWidth === 0) openMarketsList()\n else closeMarketsList()\n })\n }\n this.marketList = new MarketList(page.marketListV1)\n // Prepare the list of markets.\n for (const row of this.marketList.markets) {\n bind(row.node, 'click', () => {\n this.startLoadingAnimations()\n this.setMarket(row.mkt.xc.host, row.mkt.baseid, row.mkt.quoteid)\n })\n }\n if (State.fetchLocal(State.leftMarketDockLK) !== '1') { // It is shown by default, hiding if necessary.\n closeMarketsList()\n }\n\n // Notification filters.\n app().registerNoteFeeder({\n order: (note: OrderNote) => { this.handleOrderNote(note) },\n match: (note: MatchNote) => { this.handleMatchNote(note) },\n epoch: (note: EpochNote) => { this.handleEpochNote(note) },\n conn: (note: ConnEventNote) => { this.handleConnNote(note) },\n balance: (note: BalanceNote) => { this.handleBalanceNote(note) },\n bondpost: (note: BondNote) => { this.handleBondUpdate(note) },\n spots: (note: SpotPriceNote) => { this.handlePriceUpdate(note) }\n })\n\n this.loadingAnimations = {}\n this.startLoadingAnimations()\n\n // Start a ticker to update time-since values.\n this.secondTicker = window.setInterval(() => {\n for (const mord of Object.values(this.metaOrders)) {\n mord.details.age.textContent = Doc.timeSince(mord.ord.submitTime)\n }\n for (const td of Doc.applySelector(page.recentMatchesLiveList, '[data-tmpl=age]')) {\n td.textContent = Doc.timeSince(parseFloat(td.dataset.sinceStamp ?? '0'))\n }\n }, 1000)\n\n this.init(data)\n }\n\n async init (data: any) {\n // Fetch the first market in the list, or the users last selected market, if\n // it exists.\n let selected\n if (data && data.host && typeof data.base !== 'undefined' && typeof data.quote !== 'undefined') {\n selected = makeMarket(data.host, parseInt(data.base), parseInt(data.quote))\n } else {\n selected = State.fetchLocal(State.lastMarketLK)\n }\n if (!selected || !this.marketList.exists(selected.host, selected.base, selected.quote)) {\n const first = this.marketList.first()\n if (first) selected = { host: first.mkt.xc.host, base: first.mkt.baseid, quote: first.mkt.quoteid }\n }\n if (selected) this.setMarket(selected.host, selected.base, selected.quote)\n\n // set the initial state for the registration status\n this.setRegistrationStatusVisibility()\n this.setBalanceVisibility()\n }\n\n startLoadingAnimations () {\n const { page, loadingAnimations: anis, depthChart, candleChart } = this\n depthChart.canvas.classList.add('invisible')\n candleChart.canvas.classList.add('invisible')\n if (anis.candles) anis.candles.stop()\n anis.candles = new Wave(page.candlesChart, { message: intl.prep(intl.ID_CANDLES_LOADING) })\n if (anis.depth) anis.depth.stop()\n anis.depth = new Wave(page.depthChart, { message: intl.prep(intl.ID_DEPTH_LOADING) })\n }\n\n /* isSell is true if the user has selected sell in the order options. */\n isSell () {\n return this.page.sellBttn.classList.contains('selected')\n }\n\n /* isLimit is true if the user has selected the \"limit order\" tab. */\n isLimit () {\n return this.page.limitBttn.classList.contains('selected')\n }\n\n /* hasPendingBonds is true if there are pending bonds */\n hasPendingBonds (): boolean {\n return Object.keys(this.market.dex.pendingBonds).length > 0\n }\n\n /* setCurrMarketPrice updates the current market price on the stats displays\n and the orderbook display. */\n setCurrMarketPrice (): void {\n const selected = this.market\n if (!selected) return\n // Get an up-to-date Market.\n const xc = app().exchanges[selected.dex.host]\n const mkt = xc.markets[selected.cfg.name]\n if (!mkt.spot) return\n\n for (const s of this.stats) {\n const bconv = xc.assets[mkt.baseid].unitInfo.conventional.conversionFactor\n s.tmpl.volume.textContent = fourSigFigs(mkt.spot.vol24 / bconv)\n setPriceAndChange(s.tmpl, xc, mkt)\n }\n\n this.page.obPrice.textContent = Doc.formatFullPrecision(mkt.spot.rate / this.market.rateConversionFactor)\n this.page.obPrice.classList.remove('sellcolor', 'buycolor')\n this.page.obPrice.classList.add(mkt.spot.change24 >= 0 ? 'buycolor' : 'sellcolor')\n Doc.setVis(mkt.spot.change24 >= 0, this.page.obUp)\n Doc.setVis(mkt.spot.change24 < 0, this.page.obDown)\n }\n\n /* setMarketDetails updates the currency names on the stats displays. */\n setMarketDetails () {\n if (!this.market) return\n for (const s of this.stats) {\n s.tmpl.baseIcon.src = Doc.logoPath(this.market.cfg.basesymbol)\n s.tmpl.quoteIcon.src = Doc.logoPath(this.market.cfg.quotesymbol)\n Doc.empty(s.tmpl.baseSymbol, s.tmpl.quoteSymbol)\n s.tmpl.baseSymbol.appendChild(Doc.symbolize(this.market.cfg.basesymbol))\n s.tmpl.quoteSymbol.appendChild(Doc.symbolize(this.market.cfg.quotesymbol))\n }\n }\n\n /* setHighLow calculates the high and low rates over the last 24 hours. */\n setHighLow () {\n const cache = this.market?.candleCaches[fiveMinBinKey]\n if (!cache) {\n for (const s of this.stats) {\n s.tmpl.high.textContent = '-'\n s.tmpl.low.textContent = '-'\n }\n return\n }\n\n // We'll eventually get this data in the spot, but for now, we must set it\n // from candles.\n let [high, low] = [0, 0]\n for (let i = cache.candles.length - 1; i >= 0; i--) {\n const c = cache.candles[i]\n if (low === 0 || (c.lowRate > 0 && c.lowRate < low)) low = c.lowRate\n if (c.highRate > high) high = c.highRate\n }\n\n const qconv = app().unitInfo(this.market.cfg.quoteid, this.market.dex).conventional.conversionFactor\n for (const s of this.stats) {\n s.tmpl.high.textContent = high > 0 ? fourSigFigs(high / qconv) : '-'\n s.tmpl.low.textContent = low > 0 ? fourSigFigs(low / qconv) : '-'\n }\n }\n\n /* assetsAreSupported is true if all the assets of the current market are\n * supported\n */\n assetsAreSupported (): {\n isSupported: boolean;\n text: string;\n } {\n const { market: { base, quote, baseCfg, quoteCfg } } = this\n if (!base || !quote) {\n const symbol = base ? quoteCfg.symbol : baseCfg.symbol\n return {\n isSupported: false,\n text: intl.prep(intl.ID_NOT_SUPPORTED, { asset: symbol.toUpperCase() })\n }\n }\n // check if versions are supported. If asset is a token, we check if its\n // parent supports the version.\n const bVers = (base.token ? app().assets[base.token.parentID].info?.versions : base.info?.versions) as number[]\n const qVers = (quote.token ? app().assets[quote.token.parentID].info?.versions : quote.info?.versions) as number[]\n // if none them are token, just check if own asset is supported.\n let text = ''\n if (!bVers.includes(baseCfg.version)) {\n text = intl.prep(intl.ID_VERSION_NOT_SUPPORTED, { asset: base.symbol.toUpperCase(), version: baseCfg.version + '' })\n } else if (!qVers.includes(quoteCfg.version)) {\n text = intl.prep(intl.ID_VERSION_NOT_SUPPORTED, { asset: quote.symbol.toUpperCase(), version: quoteCfg.version + '' })\n }\n return {\n isSupported: bVers.includes(baseCfg.version) && qVers.includes(quoteCfg.version),\n text\n }\n }\n\n /*\n * setOrderVisibility sets which form is visible based on the specified\n * options.\n */\n setOrderVisibility () {\n const page = this.page\n if (this.isLimit()) {\n Doc.show(page.priceBox, page.tifBox, page.qtyBox, page.maxBox)\n Doc.hide(page.mktBuyBox)\n this.previewQuoteAmt(true)\n } else {\n Doc.hide(page.tifBox, page.maxBox, page.priceBox)\n if (this.isSell()) {\n Doc.hide(page.mktBuyBox)\n Doc.show(page.qtyBox)\n this.previewQuoteAmt(true)\n } else {\n Doc.show(page.mktBuyBox)\n Doc.hide(page.qtyBox)\n this.previewQuoteAmt(false)\n }\n }\n }\n\n /* resolveOrderFormVisibility displays or hides the 'orderForm' based on\n * a set of conditions to be met.\n */\n resolveOrderFormVisibility () {\n const page = this.page\n // By default the order form should be hidden, and only if market is set\n // and ready for trading the form should show up.\n Doc.hide(page.orderForm, page.orderTypeBttns)\n\n if (!this.assetsAreSupported().isSupported) return // assets not supported\n\n if (this.market.dex.tier < 1) return // acct suspended or not registered\n\n const { base, quote } = this.market\n const hasWallets = base && app().assets[base.id].wallet && quote && app().assets[quote.id].wallet\n if (!hasWallets) return\n\n Doc.show(page.orderForm, page.orderTypeBttns)\n }\n\n /* setLoaderMsgVisibility displays a message in case a dex asset is not\n * supported\n */\n setLoaderMsgVisibility () {\n const { page } = this\n\n const { isSupported, text } = this.assetsAreSupported()\n if (isSupported) {\n // make sure to hide the loader msg\n Doc.hide(page.loaderMsg)\n return\n }\n page.loaderMsg.textContent = text\n Doc.show(page.loaderMsg)\n Doc.hide(page.notRegistered)\n Doc.hide(page.noWallet)\n }\n\n /* setRegistrationStatusView sets the text content and class for the\n * registration status view\n */\n setRegistrationStatusView (titleContent: string, confStatusMsg: string, titleClass: string) {\n const page = this.page\n page.regStatusTitle.textContent = titleContent\n page.regStatusConfsDisplay.textContent = confStatusMsg\n page.registrationStatus.classList.remove('completed', 'error', 'waiting')\n page.registrationStatus.classList.add(titleClass)\n }\n\n /*\n * updateRegistrationStatusView updates the view based on the current\n * registration status\n */\n updateRegistrationStatusView () {\n const { page, market: { dex } } = this\n page.regStatusDex.textContent = dex.host\n\n if (dex.tier >= 1) {\n this.setRegistrationStatusView(intl.prep(intl.ID_REGISTRATION_FEE_SUCCESS), '', 'completed')\n return\n }\n\n const confStatuses = Object.values(dex.pendingBonds).map(pending => {\n const confirmationsRequired = dex.bondAssets[pending.symbol].confs\n return `${pending.confs} / ${confirmationsRequired}`\n })\n const confStatusMsg = confStatuses.join(', ')\n this.setRegistrationStatusView(intl.prep(intl.ID_WAITING_FOR_CONFS), confStatusMsg, 'waiting')\n }\n\n /*\n * setRegistrationStatusVisibility toggles the registration status view based\n * on the dex data.\n */\n setRegistrationStatusVisibility () {\n const { page, market } = this\n if (!market || !market.dex) return\n\n // If dex is not connected to server, is not possible to know the\n // registration status.\n if (market.dex.connectionStatus !== ConnectionStatus.Connected) return\n\n this.updateRegistrationStatusView()\n\n if (market.dex.tier >= 1) {\n const toggle = () => {\n Doc.hide(page.registrationStatus, page.bondRequired)\n this.resolveOrderFormVisibility()\n }\n if (Doc.isHidden(page.orderForm)) {\n // wait a couple of seconds before showing the form so the success\n // message is shown to the user\n setTimeout(toggle, 5000)\n return\n }\n toggle()\n } else if (market.dex.viewOnly) {\n page.unregisteredDex.textContent = market.dex.host\n Doc.show(page.notRegistered)\n } else if (this.hasPendingBonds()) {\n Doc.show(page.registrationStatus)\n } else {\n page.acctTier.textContent = `${market.dex.tier}`\n page.dexSettingsLink.href = `/dexsettings/${market.dex.host}`\n Doc.show(page.bondRequired)\n }\n }\n\n setOrderBttnText () {\n if (this.isSell()) {\n this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_SELL, { asset: Doc.shortSymbol(this.market.baseCfg.symbol) })\n } else this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_BUY, { asset: Doc.shortSymbol(this.market.baseCfg.symbol) })\n }\n\n setCandleDurBttns () {\n const { page, market } = this\n Doc.empty(page.durBttnBox)\n for (const dur of market.dex.candleDurs) {\n const bttn = page.durBttnTemplate.cloneNode(true)\n bttn.textContent = dur\n Doc.bind(bttn, 'click', () => this.candleDurationSelected(dur))\n page.durBttnBox.appendChild(bttn)\n }\n }\n\n /* setMarket sets the currently displayed market. */\n async setMarket (host: string, base: number, quote: number) {\n const dex = app().user.exchanges[host]\n const page = this.page\n\n // reset form inputs\n page.lotField.value = ''\n page.qtyField.value = ''\n page.rateField.value = ''\n\n Doc.hide(page.notRegistered, page.bondRequired, page.noWallet)\n\n // If we have not yet connected, there is no dex.assets or any other\n // exchange data, so just put up a message and wait for the connection to be\n // established, at which time handleConnNote will refresh and reload.\n if (!dex || !dex.markets || dex.connectionStatus !== ConnectionStatus.Connected) {\n page.chartErrMsg.textContent = intl.prep(intl.ID_CONNECTION_FAILED)\n Doc.show(page.chartErrMsg)\n return\n }\n\n for (const s of this.stats) Doc.show(s.row)\n\n const baseCfg = dex.assets[base]\n const quoteCfg = dex.assets[quote]\n\n const [bui, qui] = [app().unitInfo(base, dex), app().unitInfo(quote, dex)]\n\n const rateConversionFactor = OrderUtil.RateEncodingFactor / bui.conventional.conversionFactor * qui.conventional.conversionFactor\n Doc.hide(page.maxOrd, page.chartErrMsg)\n if (this.maxEstimateTimer) {\n window.clearTimeout(this.maxEstimateTimer)\n this.maxEstimateTimer = null\n }\n const mktId = marketID(baseCfg.symbol, quoteCfg.symbol)\n const baseAsset = app().assets[base]\n const quoteAsset = app().assets[quote]\n this.market = {\n dex: dex,\n sid: mktId, // A string market identifier used by the DEX.\n cfg: dex.markets[mktId],\n // app().assets is a map of core.SupportedAsset type, which can be found at\n // client/core/types.go.\n base: baseAsset,\n quote: quoteAsset,\n baseUnitInfo: bui,\n quoteUnitInfo: qui,\n maxSell: null,\n maxBuys: {},\n maxSellRequested: false,\n candleCaches: {},\n baseCfg,\n quoteCfg,\n rateConversionFactor,\n sellBalance: 0,\n buyBalance: 0\n }\n\n Doc.setVis(!(baseAsset && quoteAsset) || !(baseAsset.wallet && quoteAsset.wallet), page.noWallet)\n this.setMarketDetails()\n this.setCurrMarketPrice()\n this.refreshRecentMatchesTable()\n\n // if (!dex.candleDurs || dex.candleDurs.length === 0) this.currentChart = depthChart\n\n // depth chart\n ws.request('loadmarket', makeMarket(host, base, quote))\n\n // candlesticks\n this.candleDur = fiveMinBinKey\n this.loadCandles()\n\n this.setLoaderMsgVisibility()\n this.setRegistrationStatusVisibility()\n this.resolveOrderFormVisibility()\n this.setOrderBttnText()\n this.setCandleDurBttns()\n this.previewQuoteAmt(false)\n }\n\n /*\n * reportDepthClick is a callback used by the DepthChart when the user clicks\n * on the chart area. The rate field is set to the x-value of the click.\n */\n reportDepthClick (r: number) {\n this.page.rateField.value = String(r)\n this.rateFieldChanged()\n }\n\n /*\n * reportDepthVolume accepts a volume report from the DepthChart and sets the\n * values in the chart legend.\n */\n reportDepthVolume (r: VolumeReport) {\n const page = this.page\n const { baseUnitInfo: b, quoteUnitInfo: q } = this.market\n // DepthChart reports volumes in conventional units. We'll still use\n // formatCoinValue for formatting though.\n page.sellBookedBase.textContent = Doc.formatCoinValue(r.sellBase * b.conventional.conversionFactor, b)\n page.sellBookedQuote.textContent = Doc.formatCoinValue(r.sellQuote * q.conventional.conversionFactor, q)\n page.buyBookedBase.textContent = Doc.formatCoinValue(r.buyBase * b.conventional.conversionFactor, b)\n page.buyBookedQuote.textContent = Doc.formatCoinValue(r.buyQuote * q.conventional.conversionFactor, q)\n }\n\n /*\n * reportDepthMouse accepts informations about the mouse position on the\n * chart area.\n */\n reportDepthMouse (r: MouseReport) {\n while (this.hovers.length) (this.hovers.shift() as HTMLElement).classList.remove('hover')\n const page = this.page\n if (!r) {\n Doc.hide(page.hoverData, page.depthHoverData)\n return\n }\n Doc.show(page.hoverData, page.depthHoverData)\n\n // If the user is hovered to within a small percent (based on chart width)\n // of a user order, highlight that order's row.\n for (const { div, ord } of Object.values(this.metaOrders)) {\n if (ord.status !== OrderUtil.StatusBooked) continue\n if (r.hoverMarkers.indexOf(ord.rate) > -1) {\n div.classList.add('hover')\n this.hovers.push(div)\n }\n }\n\n page.hoverPrice.textContent = Doc.formatCoinValue(r.rate)\n page.hoverVolume.textContent = Doc.formatCoinValue(r.depth)\n page.hoverVolume.style.color = r.dotColor\n }\n\n /*\n * reportDepthZoom accepts informations about the current depth chart zoom level.\n * This information is saved to disk so that the zoom level can be maintained\n * across reloads.\n */\n reportDepthZoom (zoom: number) {\n State.storeLocal(State.depthZoomLK, zoom)\n }\n\n reportMouseCandle (candle: Candle | null) {\n const page = this.page\n if (!candle) {\n Doc.hide(page.hoverData, page.candleHoverData)\n return\n }\n Doc.show(page.hoverData, page.candleHoverData)\n page.candleStart.textContent = Doc.formatCoinValue(candle.startRate / this.market.rateConversionFactor)\n page.candleEnd.textContent = Doc.formatCoinValue(candle.endRate / this.market.rateConversionFactor)\n page.candleHigh.textContent = Doc.formatCoinValue(candle.highRate / this.market.rateConversionFactor)\n page.candleLow.textContent = Doc.formatCoinValue(candle.lowRate / this.market.rateConversionFactor)\n page.candleVol.textContent = Doc.formatCoinValue(candle.matchVolume, this.market.baseUnitInfo)\n }\n\n /*\n * parseOrder pulls the order information from the form fields. Data is not\n * validated in any way.\n */\n parseOrder (): TradeForm {\n const page = this.page\n let qtyField = page.qtyField\n const limit = this.isLimit()\n const sell = this.isSell()\n const market = this.market\n let qtyConv = market.baseUnitInfo.conventional.conversionFactor\n if (!limit && !sell) {\n qtyField = page.mktBuyField\n qtyConv = market.quoteUnitInfo.conventional.conversionFactor\n }\n return {\n host: market.dex.host,\n isLimit: limit,\n sell: sell,\n base: market.base.id,\n quote: market.quote.id,\n qty: convertToAtoms(qtyField.value || '', qtyConv),\n rate: convertToAtoms(page.rateField.value || '', market.rateConversionFactor), // message-rate\n tifnow: page.tifNow.checked || false,\n options: {}\n }\n }\n\n /**\n * previewQuoteAmt shows quote amount when rate or quantity input are changed\n */\n previewQuoteAmt (show: boolean) {\n const page = this.page\n if (!this.market.base || !this.market.quote) return // Not a supported asset\n const order = this.parseOrder()\n const adjusted = this.adjustedRate()\n page.orderErr.textContent = ''\n if (adjusted) {\n if (order.sell) this.preSell()\n else this.preBuy()\n }\n this.depthLines.input = []\n if (adjusted && this.isLimit()) {\n this.depthLines.input = [{\n rate: order.rate / this.market.rateConversionFactor,\n color: order.sell ? this.depthChart.theme.sellLine : this.depthChart.theme.buyLine\n }]\n }\n this.drawChartLines()\n if (!show || !adjusted || !order.qty) {\n page.orderPreview.textContent = ''\n this.drawChartLines()\n return\n }\n const quoteAsset = app().assets[order.quote]\n const quoteQty = order.qty * order.rate / OrderUtil.RateEncodingFactor\n const total = Doc.formatCoinValue(quoteQty, this.market.quoteUnitInfo)\n\n page.orderPreview.textContent = intl.prep(intl.ID_ORDER_PREVIEW, { total, asset: quoteAsset.symbol.toUpperCase() })\n if (this.isSell()) this.preSell()\n else this.preBuy()\n }\n\n /**\n * preSell populates the max order message for the largest available sell.\n */\n preSell () {\n const mkt = this.market\n const baseWallet = app().assets[mkt.base.id].wallet\n if (baseWallet.balance.available < mkt.cfg.lotsize) {\n this.setMaxOrder(null)\n return\n }\n if (mkt.maxSell) {\n this.setMaxOrder(mkt.maxSell.swap)\n return\n }\n if (mkt.maxSellRequested) return\n mkt.maxSellRequested = true\n // We only fetch pre-sell once per balance update, so don't delay.\n this.scheduleMaxEstimate('/api/maxsell', {}, 0, (res: MaxSell) => {\n mkt.maxSellRequested = false\n mkt.maxSell = res.maxSell\n mkt.sellBalance = baseWallet.balance.available\n this.setMaxOrder(res.maxSell.swap)\n })\n }\n\n /**\n * preBuy populates the max order message for the largest available buy.\n */\n preBuy () {\n const mkt = this.market\n const rate = this.adjustedRate()\n const quoteWallet = app().assets[mkt.quote.id].wallet\n if (!quoteWallet) return\n const aLot = mkt.cfg.lotsize * (rate / OrderUtil.RateEncodingFactor)\n if (quoteWallet.balance.available < aLot) {\n this.setMaxOrder(null)\n return\n }\n if (mkt.maxBuys[rate]) {\n this.setMaxOrder(mkt.maxBuys[rate].swap)\n return\n }\n // 0 delay for first fetch after balance update or market change, otherwise\n // meter these at 1 / sec.\n const delay = Object.keys(mkt.maxBuys).length ? 350 : 0\n this.scheduleMaxEstimate('/api/maxbuy', { rate }, delay, (res: MaxBuy) => {\n mkt.maxBuys[rate] = res.maxBuy\n mkt.buyBalance = app().assets[mkt.quote.id].wallet.balance.available\n this.setMaxOrder(res.maxBuy.swap)\n })\n }\n\n /**\n * scheduleMaxEstimate shows the loading icon and schedules a call to an order\n * estimate api endpoint. If another call to scheduleMaxEstimate is made before\n * this one is fired (after delay), this call will be canceled.\n */\n scheduleMaxEstimate (path: string, args: any, delay: number, success: (res: any) => void) {\n const page = this.page\n if (!this.maxLoaded) this.maxLoaded = app().loading(page.maxOrd)\n const [bid, qid] = [this.market.base.id, this.market.quote.id]\n const [bWallet, qWallet] = [app().assets[bid].wallet, app().assets[qid].wallet]\n if (!bWallet || !bWallet.running || !qWallet || !qWallet.running) return\n if (this.maxEstimateTimer) window.clearTimeout(this.maxEstimateTimer)\n\n Doc.show(page.maxOrd, page.maxLotBox)\n Doc.hide(page.maxAboveZero)\n page.maxFromLots.textContent = intl.prep(intl.ID_CALCULATING)\n page.maxFromLotsLbl.textContent = ''\n this.maxOrderUpdateCounter++\n const counter = this.maxOrderUpdateCounter\n this.maxEstimateTimer = window.setTimeout(async () => {\n this.maxEstimateTimer = null\n if (counter !== this.maxOrderUpdateCounter) return\n const res = await postJSON(path, {\n host: this.market.dex.host,\n base: bid,\n quote: qid,\n ...args\n })\n if (counter !== this.maxOrderUpdateCounter) return\n if (!app().checkResponse(res)) {\n console.warn('max order estimate not available:', res)\n page.maxFromLots.textContent = intl.prep(intl.ID_ESTIMATE_UNAVAILABLE)\n if (this.maxLoaded) {\n this.maxLoaded()\n this.maxLoaded = null\n }\n return\n }\n success(res)\n }, delay)\n }\n\n /* setMaxOrder sets the max order text. */\n setMaxOrder (maxOrder: SwapEstimate | null) {\n const page = this.page\n if (this.maxLoaded) {\n this.maxLoaded()\n this.maxLoaded = null\n }\n Doc.show(page.maxOrd, page.maxLotBox, page.maxAboveZero)\n const sell = this.isSell()\n\n let lots = 0\n if (maxOrder) lots = maxOrder.lots\n\n page.maxFromLots.textContent = lots.toString()\n // XXX add plural into format details, so we don't need this\n page.maxFromLotsLbl.textContent = intl.prep(lots === 1 ? intl.ID_LOT : intl.ID_LOTS)\n if (!maxOrder) {\n Doc.hide(page.maxAboveZero)\n return\n }\n // Could add the estimatedFees here, but that might also be\n // confusing.\n const fromAsset = sell ? this.market.base : this.market.quote\n // DRAFT NOTE: Maybe just use base qty from lots.\n page.maxFromAmt.textContent = Doc.formatCoinValue(maxOrder.value || 0, fromAsset.unitInfo)\n page.maxFromTicker.textContent = fromAsset.symbol.toUpperCase()\n // Could subtract the maxOrder.redemptionFees here.\n // The qty conversion doesn't fit well with the new design.\n // TODO: Make this work somehow?\n // const toConversion = sell ? this.adjustedRate() / OrderUtil.RateEncodingFactor : OrderUtil.RateEncodingFactor / this.adjustedRate()\n // page.maxToAmt.textContent = Doc.formatCoinValue((maxOrder.value || 0) * toConversion, toAsset.unitInfo)\n // page.maxToTicker.textContent = toAsset.symbol.toUpperCase()\n }\n\n /*\n * validateOrder performs some basic order sanity checks, returning boolean\n * true if the order appears valid.\n */\n validateOrder (order: TradeForm) {\n const page = this.page\n if (order.isLimit && !order.rate) {\n Doc.show(page.orderErr)\n page.orderErr.textContent = intl.prep(intl.ID_NO_ZERO_RATE)\n return false\n }\n if (!order.qty) {\n Doc.show(page.orderErr)\n page.orderErr.textContent = intl.prep(intl.ID_NO_ZERO_QUANTITY)\n return false\n }\n return true\n }\n\n /* handleBook accepts the data sent in the 'book' notification. */\n handleBook (data: MarketOrderBook) {\n const { cfg, baseUnitInfo, quoteUnitInfo, baseCfg, quoteCfg } = this.market\n this.book = new OrderBook(data, baseCfg.symbol, quoteCfg.symbol)\n this.loadTable()\n for (const order of (data.book.epoch || [])) {\n if (order.rate > 0) this.book.add(order)\n this.addTableOrder(order)\n }\n if (!this.book) {\n this.depthChart.clear()\n Doc.empty(this.page.buyRows)\n Doc.empty(this.page.sellRows)\n return\n }\n Doc.show(this.page.epochLine)\n if (this.loadingAnimations.depth) this.loadingAnimations.depth.stop()\n this.depthChart.canvas.classList.remove('invisible')\n this.depthChart.set(this.book, cfg.lotsize, cfg.ratestep, baseUnitInfo, quoteUnitInfo)\n this.recentMatches = data.book.recentMatches ?? []\n this.refreshRecentMatchesTable()\n }\n\n /*\n * midGapConventional is the same as midGap, but returns the mid-gap rate as\n * the conventional ratio. This is used to convert from a conventional\n * quantity from base to quote or vice-versa.\n */\n midGapConventional () {\n const gap = this.midGap()\n if (!gap) return gap\n const { baseUnitInfo: b, quoteUnitInfo: q } = this.market\n return gap * b.conventional.conversionFactor / q.conventional.conversionFactor\n }\n\n /*\n * midGap returns the value in the middle of the best buy and best sell. If\n * either one of the buy or sell sides are empty, midGap returns the best rate\n * from the other side. If both sides are empty, midGap returns the value\n * null. The rate returned is the atomic ratio.\n */\n midGap () {\n const book = this.book\n if (!book) return\n if (book.buys && book.buys.length) {\n if (book.sells && book.sells.length) {\n return (book.buys[0].msgRate + book.sells[0].msgRate) / 2 / OrderUtil.RateEncodingFactor\n }\n return book.buys[0].msgRate / OrderUtil.RateEncodingFactor\n }\n if (book.sells && book.sells.length) {\n return book.sells[0].msgRate / OrderUtil.RateEncodingFactor\n }\n return null\n }\n\n /*\n * setMarketBuyOrderEstimate sets the \"min. buy\" display for the current\n * market.\n */\n setMarketBuyOrderEstimate () {\n const market = this.market\n const lotSize = market.cfg.lotsize\n const xc = app().user.exchanges[market.dex.host]\n const buffer = xc.markets[market.sid].buybuffer\n const gap = this.midGapConventional()\n if (gap) {\n this.page.minMktBuy.textContent = Doc.formatCoinValue(lotSize * buffer * gap, market.baseUnitInfo)\n }\n }\n\n /* refreshActiveOrders refreshes the user's active order list. */\n refreshActiveOrders () {\n const page = this.page\n const metaOrders = this.metaOrders\n const market = this.market\n for (const oid in metaOrders) delete metaOrders[oid]\n const orders = app().orders(market.dex.host, marketID(market.baseCfg.symbol, market.quoteCfg.symbol))\n // Sort orders by sort key.\n orders.sort((a: Order, b: Order) => b.submitTime - a.submitTime)\n\n Doc.empty(page.userOrders)\n Doc.setVis(orders?.length, page.userOrders)\n Doc.setVis(!orders?.length, page.userNoOrders)\n\n let unreadyOrders = false\n for (const ord of orders) {\n const div = page.userOrderTmpl.cloneNode(true) as HTMLElement\n page.userOrders.appendChild(div)\n\n const headerEl = Doc.tmplElement(div, 'header')\n const header = Doc.parseTemplate(headerEl)\n const detailsDiv = Doc.tmplElement(div, 'details')\n const details = Doc.parseTemplate(detailsDiv)\n\n const mord: MetaOrder = {\n div: div,\n header: header,\n details: details,\n ord: ord\n }\n\n // No need to track in-flight orders here. We've already added it to\n // display.\n if (ord.id) metaOrders[ord.id] = mord\n\n if (!ord.readyToTick) {\n headerEl.classList.add('unready-user-order')\n unreadyOrders = true\n }\n header.sideLight.classList.add(ord.sell ? 'sell' : 'buy')\n details.side.textContent = header.side.textContent = OrderUtil.sellString(ord)\n details.side.classList.add(ord.sell ? 'sellcolor' : 'buycolor')\n header.side.classList.add(ord.sell ? 'sellcolor' : 'buycolor')\n details.qty.textContent = header.qty.textContent = fourSigFigs(ord.qty / this.market.baseUnitInfo.conventional.conversionFactor)\n details.rate.textContent = fourSigFigs(ord.rate / this.market.rateConversionFactor)\n header.baseSymbol.textContent = ord.baseSymbol.toUpperCase()\n details.type.textContent = OrderUtil.typeString(ord)\n this.updateMetaOrder(mord)\n\n Doc.bind(div, 'mouseenter', () => {\n this.activeMarkerRate = ord.rate\n this.setDepthMarkers()\n })\n\n if (!ord.id) {\n Doc.hide(details.accelerateBttn)\n Doc.hide(details.cancelBttn)\n Doc.hide(details.link)\n } else {\n if (ord.type === OrderUtil.Limit && (ord.tif === OrderUtil.StandingTiF && ord.status < OrderUtil.StatusExecuted)) {\n Doc.show(details.cancelBttn)\n bind(details.cancelBttn, 'click', e => {\n e.stopPropagation()\n this.showCancel(div, ord.id)\n })\n }\n\n bind(details.accelerateBttn, 'click', e => {\n e.stopPropagation()\n this.showAccelerate(ord)\n })\n if (app().canAccelerateOrder(ord)) {\n Doc.show(details.accelerateBttn)\n }\n details.link.href = `order/${ord.id}`\n app().bindInternalNavigation(div)\n }\n\n Doc.bind(headerEl, 'click', () => {\n if (Doc.isDisplayed(detailsDiv)) {\n Doc.hide(detailsDiv)\n header.expander.classList.add('ico-arrowdown')\n header.expander.classList.remove('ico-arrowup')\n return\n }\n Doc.show(detailsDiv)\n header.expander.classList.remove('ico-arrowdown')\n header.expander.classList.add('ico-arrowup')\n })\n app().bindTooltips(div)\n }\n Doc.setVis(unreadyOrders, page.unreadyOrdersMsg)\n this.setDepthMarkers()\n }\n\n /*\n * updateMetaOrder sets the td contents of the user's order table row.\n */\n updateMetaOrder (mord: MetaOrder) {\n const { header, details, ord } = mord\n if (ord.status <= OrderUtil.StatusBooked) header.activeLight.classList.add('active')\n else header.activeLight.classList.remove('active')\n details.status.textContent = header.status.textContent = OrderUtil.statusString(ord)\n details.age.textContent = Doc.timeSince(ord.submitTime)\n details.filled.textContent = `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`\n details.settled.textContent = `${(OrderUtil.settled(ord) / ord.qty * 100).toFixed(1)}%`\n }\n\n /* setMarkers sets the depth chart markers for booked orders. */\n setDepthMarkers () {\n const markers: Record = {\n buys: [],\n sells: []\n }\n const rateFactor = this.market.rateConversionFactor\n for (const { ord } of Object.values(this.metaOrders)) {\n if (ord.rate && ord.status === OrderUtil.StatusBooked) {\n if (ord.sell) {\n markers.sells.push({\n rate: ord.rate / rateFactor,\n active: ord.rate === this.activeMarkerRate\n })\n } else {\n markers.buys.push({\n rate: ord.rate / rateFactor,\n active: ord.rate === this.activeMarkerRate\n })\n }\n }\n }\n this.depthChart.setMarkers(markers)\n if (this.book) this.depthChart.draw()\n }\n\n /* updateTitle update the browser title based on the midgap value and the\n * selected assets.\n */\n updateTitle () {\n // gets first price value from buy or from sell, so we can show it on\n // title.\n const midGapValue = this.midGapConventional()\n if (!midGapValue) return\n\n const { baseCfg: b, quoteCfg: q } = this.market\n const baseSymb = b.symbol.toUpperCase()\n const quoteSymb = q.symbol.toUpperCase()\n // more than 6 numbers it gets too big for the title.\n document.title = `${Doc.formatCoinValue(midGapValue)} | ${baseSymb}${quoteSymb} | ${this.ogTitle}`\n }\n\n /* handleBookRoute is the handler for the 'book' notification, which is sent\n * in response to a new market subscription. The data received will contain\n * the entire order book.\n */\n handleBookRoute (note: BookUpdate) {\n app().log('book', 'handleBookRoute:', note)\n const mktBook = note.payload\n const market = this.market\n const page = this.page\n const host = market.dex.host\n const [b, q] = [market.baseCfg, market.quoteCfg]\n if (mktBook.base !== b.id || mktBook.quote !== q.id) return\n this.refreshActiveOrders()\n this.handleBook(mktBook)\n this.updateTitle()\n this.marketList.select(host, b.id, q.id)\n\n State.storeLocal(State.lastMarketLK, {\n host: note.host,\n base: mktBook.base,\n quote: mktBook.quote\n })\n\n page.lotSize.textContent = Doc.formatCoinValue(market.cfg.lotsize, market.baseUnitInfo)\n page.rateStep.textContent = Doc.formatCoinValue(market.cfg.ratestep / market.rateConversionFactor)\n this.baseUnits.forEach(el => {\n Doc.empty(el)\n el.appendChild(Doc.symbolize(b.symbol))\n })\n this.quoteUnits.forEach(el => {\n Doc.empty(el)\n el.appendChild(Doc.symbolize(q.symbol))\n })\n this.balanceWgt.setWallets(host, b.id, q.id)\n this.setMarketBuyOrderEstimate()\n this.refreshActiveOrders()\n }\n\n /* handleBookOrderRoute is the handler for 'book_order' notifications. */\n handleBookOrderRoute (data: BookUpdate) {\n app().log('book', 'handleBookOrderRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const order = data.payload as MiniOrder\n if (order.rate > 0) this.book.add(order)\n this.addTableOrder(order)\n this.updateTitle()\n this.depthChart.draw()\n }\n\n /* handleUnbookOrderRoute is the handler for 'unbook_order' notifications. */\n handleUnbookOrderRoute (data: BookUpdate) {\n app().log('book', 'handleUnbookOrderRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const order = data.payload\n this.book.remove(order.token)\n this.removeTableOrder(order)\n this.updateTitle()\n this.depthChart.draw()\n }\n\n /*\n * handleUpdateRemainingRoute is the handler for 'update_remaining'\n * notifications.\n */\n handleUpdateRemainingRoute (data: BookUpdate) {\n app().log('book', 'handleUpdateRemainingRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const update = data.payload\n this.book.updateRemaining(update.token, update.qty, update.qtyAtomic)\n this.updateTableOrder(update)\n this.depthChart.draw()\n }\n\n /* handleEpochOrderRoute is the handler for 'epoch_order' notifications. */\n handleEpochOrderRoute (data: BookUpdate) {\n app().log('book', 'handleEpochOrderRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const order = data.payload\n if (order.msgRate > 0) this.book.add(order) // No cancels or market orders\n if (order.qtyAtomic > 0) this.addTableOrder(order) // No cancel orders\n this.depthChart.draw()\n }\n\n /* handleCandlesRoute is the handler for 'candles' notifications. */\n handleCandlesRoute (data: BookUpdate) {\n if (this.candlesLoading) {\n clearTimeout(this.candlesLoading.timer)\n this.candlesLoading.loaded()\n this.candlesLoading = null\n }\n if (data.host !== this.market.dex.host || data.marketID !== this.market.cfg.name) return\n const dur = data.payload.dur\n this.market.candleCaches[dur] = data.payload\n this.setHighLow()\n if (this.candleDur !== dur) return\n if (this.loadingAnimations.candles) this.loadingAnimations.candles.stop()\n this.candleChart.canvas.classList.remove('invisible')\n this.candleChart.setCandles(data.payload, this.market.cfg, this.market.baseUnitInfo, this.market.quoteUnitInfo)\n }\n\n handleEpochMatchSummary (data: BookUpdate) {\n this.addRecentMatches(data.payload)\n this.refreshRecentMatchesTable()\n }\n\n /* handleCandleUpdateRoute is the handler for 'candle_update' notifications. */\n handleCandleUpdateRoute (data: BookUpdate) {\n if (data.host !== this.market.dex.host) return\n const { dur, candle } = data.payload\n const cache = this.market.candleCaches[dur]\n if (!cache) return // must not have seen the 'candles' notification yet?\n const candles = cache.candles\n if (candles.length === 0) candles.push(candle)\n else {\n const last = candles[candles.length - 1]\n if (last.startStamp === candle.startStamp) candles[candles.length - 1] = candle\n else candles.push(candle)\n }\n if (this.candleDur !== dur) return\n this.candleChart.draw()\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n this.currentForm = form\n const page = this.page\n Doc.hide(...Array.from(page.forms.children))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n /* showOpen shows the form to unlock a wallet. */\n async showOpen (asset: SupportedAsset, f: () => void) {\n const page = this.page\n this.openAsset = asset\n this.openFunc = f\n this.unlockForm.refresh(app().assets[asset.id])\n this.showForm(page.unlockWalletForm)\n page.uwAppPass.focus()\n }\n\n /*\n * showToggleWalletStatus displays the toggleWalletStatusConfirm form to\n * enable a wallet.\n */\n showToggleWalletStatus (asset: SupportedAsset) {\n const page = this.page\n this.openAsset = asset\n Doc.hide(page.toggleWalletStatusErr, page.walletStatusDisable, page.disableWalletMsg)\n Doc.show(page.walletStatusEnable, page.enableWalletMsg)\n this.showForm(page.toggleWalletStatusConfirm)\n }\n\n /*\n * toggleWalletStatus toggle wallets status to enabled.\n */\n async toggleWalletStatus () {\n const page = this.page\n Doc.hide(page.toggleWalletStatusErr)\n\n const url = '/api/togglewalletstatus'\n const req = {\n assetID: this.openAsset.id,\n disable: false\n }\n\n const loaded = app().loading(page.toggleWalletStatusConfirm)\n const res = await postJSON(url, req)\n loaded()\n if (!app().checkResponse(res)) {\n page.toggleWalletStatusErr.textContent = res.msg\n Doc.show(page.toggleWalletStatusErr)\n return\n }\n\n Doc.hide(this.page.forms)\n this.balanceWgt.updateAsset(this.openAsset.id)\n }\n\n /* showVerify shows the form to accept the currently parsed order information\n * and confirm submission of the order to the dex.\n */\n showVerify () {\n this.preorderCache = {}\n const page = this.page\n const order = this.currentOrder = this.parseOrder()\n const isSell = order.sell\n const baseAsset = app().assets[order.base]\n const quoteAsset = app().assets[order.quote]\n const toAsset = isSell ? quoteAsset : baseAsset\n const fromAsset = isSell ? baseAsset : quoteAsset\n\n const setIcon = (icon: PageElement) => {\n switch (icon.dataset.icon) {\n case 'from':\n if (fromAsset.token) {\n const parentAsset = app().assets[fromAsset.token.parentID]\n icon.src = Doc.logoPath(parentAsset.symbol)\n } else {\n icon.src = Doc.logoPath(fromAsset.symbol)\n }\n break\n case 'to':\n if (toAsset.token) {\n const parentAsset = app().assets[toAsset.token.parentID]\n icon.src = Doc.logoPath(parentAsset.symbol)\n } else {\n icon.src = Doc.logoPath(toAsset.symbol)\n }\n }\n }\n\n // Set the to and from icons in the fee details pane.\n for (const icon of Doc.applySelector(page.vDetailPane, '[data-icon]')) {\n setIcon(icon)\n }\n\n // Set the to and from icons in the fee summary pane.\n for (const icon of Doc.applySelector(page.vFeeSummary, '[data-icon]')) {\n setIcon(icon)\n }\n\n Doc.hide(page.vUnlockPreorder, page.vPreorderErr)\n Doc.show(page.vPreorder)\n\n page.vBuySell.textContent = isSell ? intl.prep(intl.ID_SELLING) : intl.prep(intl.ID_BUYING)\n const buySellStr = isSell ? intl.prep(intl.ID_SELL) : intl.prep(intl.ID_BUY)\n page.vSideSubmit.textContent = buySellStr\n page.vOrderHost.textContent = order.host\n if (order.isLimit) {\n Doc.show(page.verifyLimit)\n Doc.hide(page.verifyMarket)\n const orderDesc = `Limit ${buySellStr} Order`\n page.vOrderType.textContent = order.tifnow ? orderDesc + ' (immediate)' : orderDesc\n page.vRate.textContent = Doc.formatCoinValue(order.rate / this.market.rateConversionFactor)\n page.vQty.textContent = Doc.formatCoinValue(order.qty, baseAsset.unitInfo)\n const total = order.rate / OrderUtil.RateEncodingFactor * order.qty\n page.vTotal.textContent = Doc.formatCoinValue(total, quoteAsset.unitInfo)\n // Format total fiat value.\n this.showFiatValue(quoteAsset.id, total, page.vFiatTotal)\n } else {\n Doc.hide(page.verifyLimit)\n Doc.show(page.verifyMarket)\n page.vOrderType.textContent = `Market ${buySellStr} Order`\n const ui = order.sell ? this.market.baseUnitInfo : this.market.quoteUnitInfo\n page.vmFromTotal.textContent = Doc.formatCoinValue(order.qty, ui)\n page.vmFromAsset.textContent = fromAsset.symbol.toUpperCase()\n // Format fromAsset fiat value.\n this.showFiatValue(fromAsset.id, order.qty, page.vmFromTotalFiat)\n const gap = this.midGap()\n if (gap) {\n Doc.show(page.vMarketEstimate)\n const received = order.sell ? order.qty * gap : order.qty / gap\n page.vmToTotal.textContent = Doc.formatCoinValue(received, toAsset.unitInfo)\n page.vmToAsset.textContent = toAsset.symbol.toUpperCase()\n // Format received value to fiat equivalent.\n this.showFiatValue(toAsset.id, received, page.vmTotalFiat)\n } else {\n Doc.hide(page.vMarketEstimate)\n }\n }\n // Visually differentiate between buy/sell orders.\n if (isSell) {\n page.vHeader.classList.add(sellBtnClass)\n page.vHeader.classList.remove(buyBtnClass)\n page.vSubmit.classList.add(sellBtnClass)\n page.vSubmit.classList.remove(buyBtnClass)\n } else {\n page.vHeader.classList.add(buyBtnClass)\n page.vHeader.classList.remove(sellBtnClass)\n page.vSubmit.classList.add(buyBtnClass)\n page.vSubmit.classList.remove(sellBtnClass)\n }\n this.showVerifyForm()\n page.vPass.focus()\n\n if (baseAsset.wallet.open && quoteAsset.wallet.open) this.preOrder(order)\n else {\n Doc.hide(page.vPreorder)\n if (State.passwordIsCached()) this.unlockWalletsForEstimates('')\n else Doc.show(page.vUnlockPreorder)\n }\n }\n\n // showFiatValue displays the fiat equivalent for an order quantity.\n showFiatValue (assetID: number, qty: number, display: PageElement) {\n if (display) {\n const rate = app().fiatRatesMap[assetID]\n display.textContent = Doc.formatFiatConversion(qty, rate, app().unitInfo(assetID))\n if (rate) Doc.show(display.parentElement as Element)\n else Doc.hide(display.parentElement as Element)\n }\n }\n\n /* showVerifyForm displays form to verify an order */\n async showVerifyForm () {\n const page = this.page\n Doc.hide(page.vErr)\n this.showForm(page.verifyForm)\n }\n\n /*\n * submitEstimateUnlock reads the current vUnlockPass and unlocks any locked\n * wallets.\n */\n async submitEstimateUnlock () {\n const pw = this.page.vUnlockPass.value || ''\n return await this.unlockWalletsForEstimates(pw)\n }\n\n /*\n * unlockWalletsForEstimates unlocks any locked wallets with the provided\n * password.\n */\n async unlockWalletsForEstimates (pw: string) {\n const page = this.page\n const loaded = app().loading(page.verifyForm)\n const err = await this.attemptWalletUnlock(pw)\n loaded()\n if (err) return this.setPreorderErr(err)\n Doc.show(page.vPreorder)\n Doc.hide(page.vUnlockPreorder)\n this.preOrder(this.parseOrder())\n }\n\n /*\n * attemptWalletUnlock unlocks both the base and quote wallets for the current\n * market, if locked.\n */\n async attemptWalletUnlock (pw: string) {\n const { base, quote } = this.market\n const assetIDs = []\n if (!base.wallet.open) assetIDs.push(base.id)\n if (!quote.wallet.open) assetIDs.push(quote.id)\n const req = {\n pass: pw,\n assetID: -1\n }\n for (const assetID of assetIDs) {\n req.assetID = assetID\n const res = await postJSON('/api/openwallet', req)\n if (!app().checkResponse(res)) {\n return res.msg\n }\n }\n }\n\n /* fetchPreorder fetches the pre-order estimates and options. */\n async fetchPreorder (order: TradeForm) {\n const page = this.page\n const cacheKey = JSON.stringify(order.options)\n const cached = this.preorderCache[cacheKey]\n if (cached) return cached\n\n Doc.hide(page.vPreorderErr)\n const loaded = app().loading(page.verifyForm)\n const res = await postJSON('/api/preorder', wireOrder(order))\n loaded()\n if (!app().checkResponse(res)) return { err: res.msg }\n this.preorderCache[cacheKey] = res.estimate\n return res.estimate\n }\n\n /*\n * setPreorderErr sets and displays the pre-order error message and hides the\n * pre-order details box.\n */\n setPreorderErr (msg: string) {\n const page = this.page\n Doc.hide(page.vPreorder)\n Doc.show(page.vPreorderErr)\n page.vPreorderErrTip.dataset.tooltip = msg\n }\n\n showPreOrderAdvancedOptions () {\n const page = this.page\n Doc.hide(page.showAdvancedOptions)\n Doc.show(page.hideAdvancedOptions, page.vOtherOrderOpts)\n }\n\n hidePreOrderAdvancedOptions () {\n const page = this.page\n Doc.hide(page.hideAdvancedOptions, page.vOtherOrderOpts)\n Doc.show(page.showAdvancedOptions)\n }\n\n reloadOrderOpts (order: TradeForm, swap: PreSwap, redeem: PreRedeem, changed: ()=>void) {\n const page = this.page\n Doc.empty(page.vDefaultOrderOpts, page.vOtherOrderOpts)\n const addOption = (opt: OrderOption, isSwap: boolean) => {\n const el = OrderUtil.optionElement(opt, order, changed, isSwap)\n if (opt.showByDefault) page.vDefaultOrderOpts.appendChild(el)\n else page.vOtherOrderOpts.appendChild(el)\n }\n for (const opt of swap.options || []) addOption(opt, true)\n for (const opt of redeem.options || []) addOption(opt, false)\n app().bindTooltips(page.vDefaultOrderOpts)\n app().bindTooltips(page.vOtherOrderOpts)\n }\n\n /* preOrder loads the options and fetches pre-order estimates */\n async preOrder (order: TradeForm) {\n const page = this.page\n\n // Add swap options.\n const refreshPreorder = async () => {\n const res: APIResponse = await this.fetchPreorder(order)\n if (res.err) return this.setPreorderErr(res.err)\n const est = (res as any) as OrderEstimate\n Doc.hide(page.vPreorderErr)\n Doc.show(page.vPreorder)\n const { swap, redeem } = est\n swap.options = swap.options || []\n redeem.options = redeem.options || []\n this.setFeeEstimates(swap, redeem, order)\n\n const changed = async () => {\n await refreshPreorder()\n Doc.animate(400, progress => {\n page.vFeeSummary.style.backgroundColor = `rgba(128, 128, 128, ${0.5 - 0.5 * progress})`\n })\n }\n // bind show or hide advanced pre order options.\n Doc.bind(page.showAdvancedOptions, 'click', () => { this.showPreOrderAdvancedOptions() })\n Doc.bind(page.hideAdvancedOptions, 'click', () => { this.hidePreOrderAdvancedOptions() })\n this.reloadOrderOpts(order, swap, redeem, changed)\n }\n\n refreshPreorder()\n }\n\n /* setFeeEstimates sets all of the pre-order estimate fields */\n setFeeEstimates (swap: PreSwap, redeem: PreRedeem, order: TradeForm) {\n const { page, market } = this\n if (!swap.estimate || !redeem.estimate) {\n Doc.hide(page.vPreorderEstimates)\n return // preOrder may return just options, no fee estimates\n }\n Doc.show(page.vPreorderEstimates)\n const { baseUnitInfo, quoteUnitInfo, rateConversionFactor } = market\n const fmtPct = (value: number) => {\n if (value < 0.05) return '< 0.1'\n return percentFormatter.format(value)\n }\n\n // If the asset is a token, in order to calculate the fee as a percentage\n // of the total order, we try to use the fiat rates to find out the\n // exchange rate between the token and parent assets.\n // Initially these are set to 1, which we would use if the asset is not a\n // token and no conversion is needed.\n let baseExchangeRate = 1\n let quoteExchangeRate = 1\n let baseFeeAssetUI = baseUnitInfo\n let quoteFeeAssetUI = quoteUnitInfo\n\n if (market.base.token) {\n const parent = app().assets[market.base.token.parentID]\n baseFeeAssetUI = parent.unitInfo\n const tokenFiatRate = app().fiatRatesMap[market.base.id]\n const parentFiatRate = app().fiatRatesMap[parent.id]\n if (tokenFiatRate && parentFiatRate) {\n const conventionalRate = parentFiatRate / tokenFiatRate\n baseExchangeRate = conventionalRate * baseUnitInfo.conventional.conversionFactor / parent.unitInfo.conventional.conversionFactor\n } else {\n baseExchangeRate = 0\n }\n }\n\n if (market.quote.token) {\n const parent = app().assets[market.quote.token.parentID]\n quoteFeeAssetUI = parent.unitInfo\n const tokenFiatRate = app().fiatRatesMap[market.quote.id]\n const parentFiatRate = app().fiatRatesMap[parent.id]\n if (tokenFiatRate && parentFiatRate) {\n const conventionalRate = parentFiatRate / tokenFiatRate\n quoteExchangeRate = conventionalRate * quoteUnitInfo.conventional.conversionFactor / parent.unitInfo.conventional.conversionFactor\n } else {\n quoteExchangeRate = 0\n }\n }\n\n let [toFeeAssetUI, fromFeeAssetUI] = [baseFeeAssetUI, quoteFeeAssetUI]\n let [toExchangeRate, fromExchangeRate] = [baseExchangeRate, quoteExchangeRate]\n if (this.currentOrder.sell) {\n [fromFeeAssetUI, toFeeAssetUI] = [toFeeAssetUI, fromFeeAssetUI];\n [fromExchangeRate, toExchangeRate] = [toExchangeRate, fromExchangeRate]\n }\n\n const swapped = swap.estimate.value || 0\n const swappedInParentUnits = fromExchangeRate > 0 ? swapped / fromExchangeRate : swapped\n\n // Set swap fee estimates in the details pane.\n const bestSwapPct = swap.estimate.realisticBestCase / swappedInParentUnits * 100\n page.vSwapFeesLowPct.textContent = fromExchangeRate <= 0 ? '' : `(${fmtPct(bestSwapPct)}%)`\n page.vSwapFeesLow.textContent = Doc.formatCoinValue(swap.estimate.realisticBestCase, fromFeeAssetUI)\n\n const worstSwapPct = swap.estimate.realisticWorstCase / swappedInParentUnits * 100\n page.vSwapFeesHighPct.textContent = fromExchangeRate <= 0 ? '' : `(${fmtPct(worstSwapPct)}%)`\n page.vSwapFeesHigh.textContent = Doc.formatCoinValue(swap.estimate.realisticWorstCase, fromFeeAssetUI)\n\n const swapFeesMaxPct = swap.estimate.maxFees / swappedInParentUnits * 100\n page.vSwapFeesMaxPct.textContent = fromExchangeRate <= 0 ? '' : `(${fmtPct(swapFeesMaxPct)}%)`\n page.vSwapFeesMax.textContent = Doc.formatCoinValue(swap.estimate.maxFees, fromFeeAssetUI)\n\n // Set redemption fee estimates in the details pane.\n const midGap = this.midGap()\n const estRate = midGap || order.rate / rateConversionFactor\n const received = order.sell ? swapped * estRate : swapped / estRate\n const receivedInParentUnits = toExchangeRate > 0 ? received / toExchangeRate : received\n\n const bestRedeemPct = redeem.estimate.realisticBestCase / receivedInParentUnits * 100\n page.vRedeemFeesLowPct.textContent = toExchangeRate <= 0 ? '' : `(${fmtPct(bestRedeemPct)}%)`\n page.vRedeemFeesLow.textContent = Doc.formatCoinValue(redeem.estimate.realisticBestCase, toFeeAssetUI)\n\n const worstRedeemPct = redeem.estimate.realisticWorstCase / receivedInParentUnits * 100\n page.vRedeemFeesHighPct.textContent = toExchangeRate <= 0 ? '' : `(${fmtPct(worstRedeemPct)}%)`\n page.vRedeemFeesHigh.textContent = Doc.formatCoinValue(redeem.estimate.realisticWorstCase, toFeeAssetUI)\n\n if (baseExchangeRate && quoteExchangeRate) {\n Doc.show(page.vFeeSummaryPct)\n Doc.hide(page.vFeeSummary)\n page.vFeeSummaryLow.textContent = fmtPct(bestSwapPct + bestRedeemPct)\n page.vFeeSummaryHigh.textContent = fmtPct(worstSwapPct + worstRedeemPct)\n } else {\n Doc.hide(page.vFeeSummaryPct)\n Doc.show(page.vFeeSummary)\n page.summarySwapFeesLow.textContent = page.vSwapFeesLow.textContent\n page.summarySwapFeesHigh.textContent = page.vSwapFeesHigh.textContent\n page.summaryRedeemFeesLow.textContent = page.vRedeemFeesLow.textContent\n page.summaryRedeemFeesHigh.textContent = page.vRedeemFeesHigh.textContent\n }\n }\n\n async submitCancel () {\n // this will be the page.cancelSubmit button (evt.currentTarget)\n const page = this.page\n const cancelData = this.cancelData\n const order = cancelData.order\n const req = {\n orderID: order.id,\n pw: page.cancelPass.value\n }\n page.cancelPass.value = ''\n // Toggle the loader and submit button.\n const loaded = app().loading(page.cancelSubmit)\n const res = await postJSON('/api/cancel', req)\n loaded()\n // Display error on confirmation modal.\n if (!app().checkResponse(res)) {\n page.cancelErr.textContent = res.msg\n Doc.show(page.cancelErr)\n return\n }\n // Hide confirmation modal only on success.\n Doc.hide(cancelData.bttn, page.forms)\n order.cancelling = true\n }\n\n /* showCancel shows a form to confirm submission of a cancel order. */\n showCancel (row: HTMLElement, orderID: string) {\n const ord = this.metaOrders[orderID].ord\n const page = this.page\n const remaining = ord.qty - ord.filled\n const asset = OrderUtil.isMarketBuy(ord) ? this.market.quote : this.market.base\n page.cancelRemain.textContent = Doc.formatCoinValue(remaining, asset.unitInfo)\n page.cancelUnit.textContent = asset.symbol.toUpperCase()\n Doc.hide(page.cancelErr)\n this.showForm(page.cancelForm)\n page.cancelPass.focus()\n this.cancelData = {\n bttn: Doc.tmplElement(row, 'cancelBttn'),\n order: ord\n }\n }\n\n /* showAccelerate shows the accelerate order form. */\n showAccelerate (order: Order) {\n const loaded = app().loading(this.main)\n this.accelerateOrderForm.refresh(order)\n loaded()\n this.showForm(this.page.accelerateForm)\n }\n\n /* showCreate shows the new wallet creation form. */\n showCreate (asset: SupportedAsset) {\n const page = this.page\n this.currentCreate = asset\n this.newWalletForm.setAsset(asset.id)\n this.showForm(page.newWalletForm)\n }\n\n /*\n * stepSubmit will examine the current state of wallets and step the user\n * through the process of order submission.\n * NOTE: I expect this process will be streamlined soon such that the wallets\n * will attempt to be unlocked in the order submission process, negating the\n * need to unlock ahead of time.\n */\n stepSubmit () {\n const page = this.page\n const market = this.market\n Doc.hide(page.orderErr)\n if (!this.validateOrder(this.parseOrder())) return\n const baseWallet = app().walletMap[market.base.id]\n const quoteWallet = app().walletMap[market.quote.id]\n if (!baseWallet) {\n page.orderErr.textContent = intl.prep(intl.ID_NO_ASSET_WALLET, { asset: market.base.symbol })\n Doc.show(page.orderErr)\n return\n }\n if (!quoteWallet) {\n page.orderErr.textContent = intl.prep(intl.ID_NO_ASSET_WALLET, { asset: market.quote.symbol })\n Doc.show(page.orderErr)\n return\n }\n this.showVerify()\n }\n\n /* Display a deposit address. */\n async showDeposit (assetID: number) {\n this.depositAddrForm.setAsset(assetID)\n this.showForm(this.page.deposit)\n }\n\n /*\n * handlePriceUpdate is the handler for the 'spots' notification.\n */\n handlePriceUpdate (note: SpotPriceNote) {\n if (note.host === this.market.dex.host && note.spots[this.market.cfg.name]) {\n this.setCurrMarketPrice()\n }\n this.marketList.updateSpots(note)\n }\n\n /*\n * handleBondUpdate is the handler for the 'bondpost' notification type.\n * This is used to update the registration status of the current exchange.\n */\n async handleBondUpdate (note: BondNote) {\n const dexAddr = note.dex\n if (dexAddr !== this.market.dex.host) return\n // If we just finished legacy registration, we need to update the Exchange.\n // TODO: Use tier change notification once available.\n if (note.topic === 'AccountRegistered') await app().fetchUser()\n // Update local copy of Exchange.\n this.market.dex = app().exchanges[dexAddr]\n this.setRegistrationStatusVisibility()\n }\n\n handleMatchNote (note: MatchNote) {\n const mord = this.metaOrders[note.orderID]\n if (!mord) return this.refreshActiveOrders()\n if (app().canAccelerateOrder(mord.ord)) Doc.show(mord.details.accelerateBttn)\n else Doc.hide(mord.details.accelerateBttn)\n }\n\n /*\n * handleOrderNote is the handler for the 'order'-type notification, which are\n * used to update a user's order's status.\n */\n handleOrderNote (note: OrderNote) {\n const order = note.order\n const mord = this.metaOrders[order.id]\n // - If metaOrder doesn't exist for the given order it means it was created\n // via dexcctl and the GUI isn't aware of it or it was an inflight order.\n // refreshActiveOrders must be called to grab this order.\n // - If an OrderLoaded notification is recieved, it means an order that was\n // previously not \"ready to tick\" (due to its wallets not being connected\n // and unlocked) has now become ready to tick. The active orders section\n // needs to be refreshed.\n if (!mord || note.topic === 'AsyncOrderFailure' || (note.topic === 'OrderLoaded' && order.readyToTick)) {\n return this.refreshActiveOrders()\n }\n const oldStatus = mord.status\n mord.ord = order\n if (note.topic === 'MissedCancel') Doc.show(mord.details.cancelBttn)\n if (order.filled === order.qty) Doc.hide(mord.details.cancelBttn)\n if (app().canAccelerateOrder(order)) Doc.show(mord.details.accelerateBttn)\n else Doc.hide(mord.details.accelerateBttn)\n this.updateMetaOrder(mord)\n // Only reset markers if there is a change, since the chart is redrawn.\n if ((oldStatus === OrderUtil.StatusEpoch && order.status === OrderUtil.StatusBooked) ||\n (oldStatus === OrderUtil.StatusBooked && order.status > OrderUtil.StatusBooked)) this.setDepthMarkers()\n }\n\n /*\n * handleEpochNote handles notifications signalling the start of a new epoch.\n */\n handleEpochNote (note: EpochNote) {\n app().log('book', 'handleEpochNote:', note)\n if (note.host !== this.market.dex.host || note.marketID !== this.market.sid) return\n if (this.book) {\n this.book.setEpoch(note.epoch)\n this.depthChart.draw()\n }\n\n this.clearOrderTableEpochs()\n for (const { ord, details, header } of Object.values(this.metaOrders)) {\n const alreadyMatched = note.epoch > ord.epoch\n switch (true) {\n case ord.type === OrderUtil.Limit && ord.status === OrderUtil.StatusEpoch && alreadyMatched: {\n const status = ord.tif === OrderUtil.ImmediateTiF ? intl.prep(intl.ID_EXECUTED) : intl.prep(intl.ID_BOOKED)\n details.status.textContent = header.status.textContent = status\n ord.status = ord.tif === OrderUtil.ImmediateTiF ? OrderUtil.StatusExecuted : OrderUtil.StatusBooked\n break\n }\n case ord.type === OrderUtil.Market && ord.status === OrderUtil.StatusEpoch:\n // Technically don't know if this should be 'executed' or 'settling'.\n details.status.textContent = header.status.textContent = intl.prep(intl.ID_EXECUTED)\n ord.status = OrderUtil.StatusExecuted\n break\n }\n }\n }\n\n /*\n * recentMatchesSortCompare returns sort compare function according to the active\n * sort key and direction.\n */\n recentMatchesSortCompare () {\n switch (this.recentMatchesSortKey) {\n case 'rate':\n return (a: RecentMatch, b: RecentMatch) => this.recentMatchesSortDirection * (a.rate - b.rate)\n case 'qty':\n return (a: RecentMatch, b: RecentMatch) => this.recentMatchesSortDirection * (a.qty - b.qty)\n case 'age':\n return (a: RecentMatch, b:RecentMatch) => this.recentMatchesSortDirection * (a.stamp - b.stamp)\n }\n }\n\n refreshRecentMatchesTable () {\n const page = this.page\n const recentMatches = this.recentMatches\n Doc.empty(page.recentMatchesLiveList)\n if (!recentMatches) return\n const compare = this.recentMatchesSortCompare()\n recentMatches.sort(compare)\n for (const match of recentMatches) {\n const row = page.recentMatchesTemplate.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(row)\n app().bindTooltips(row)\n tmpl.rate.textContent = Doc.formatCoinValue(match.rate / this.market.rateConversionFactor)\n tmpl.qty.textContent = Doc.formatCoinValue(match.qty, this.market.baseUnitInfo)\n tmpl.age.textContent = Doc.timeSince(match.stamp)\n tmpl.age.dataset.sinceStamp = String(match.stamp)\n row.classList.add(match.sell ? 'sellcolor' : 'buycolor')\n page.recentMatchesLiveList.append(row)\n }\n }\n\n addRecentMatches (matches: RecentMatch[]) {\n this.recentMatches = [...matches, ...this.recentMatches].slice(0, 100)\n }\n\n setBalanceVisibility () {\n const mkt = this.market\n if (!mkt || !mkt.dex) return\n this.balanceWgt.setBalanceVisibility(mkt.dex.connectionStatus === ConnectionStatus.Connected)\n }\n\n /* handleBalanceNote handles notifications updating a wallet's balance. */\n handleBalanceNote (note: BalanceNote) {\n this.setBalanceVisibility()\n this.preorderCache = {} // invalidate previous preorder results\n // if connection to dex server fails, it is not possible to retrieve\n // markets.\n const mkt = this.market\n if (!mkt || !mkt.dex || mkt.dex.connectionStatus !== ConnectionStatus.Connected) return\n // If there's a balance update, refresh the max order section.\n const avail = note.balance.available\n switch (note.assetID) {\n case mkt.baseCfg.id:\n // If we're not showing the max order panel yet, don't do anything.\n if (!mkt.maxSell) break\n if (typeof mkt.sellBalance === 'number' && mkt.sellBalance !== avail) mkt.maxSell = null\n if (this.isSell()) this.preSell()\n break\n case mkt.quoteCfg.id:\n if (!Object.keys(mkt.maxBuys).length) break\n if (typeof mkt.buyBalance === 'number' && mkt.buyBalance !== avail) mkt.maxBuys = {}\n if (!this.isSell()) this.preBuy()\n }\n }\n\n /*\n * submitOrder is attached to the affirmative button on the order validation\n * form. Clicking the button is the last step in the order submission process.\n */\n async submitOrder () {\n const page = this.page\n Doc.hide(page.orderErr, page.vErr)\n const order = this.currentOrder\n const pw = page.vPass.value\n page.vPass.value = ''\n const req = {\n order: wireOrder(order),\n pw: pw\n }\n if (!this.validateOrder(order)) return\n // Show loader and hide submit button.\n page.vSubmit.classList.add('d-hide')\n page.vLoader.classList.remove('d-hide')\n const res = await postJSON('/api/tradeasync', req)\n // Hide loader and show submit button.\n page.vSubmit.classList.remove('d-hide')\n page.vLoader.classList.add('d-hide')\n // If error, display error on confirmation modal.\n if (!app().checkResponse(res)) {\n page.vErr.textContent = res.msg\n Doc.show(page.vErr)\n return\n }\n // Hide confirmation modal only on success.\n Doc.hide(page.forms)\n this.refreshActiveOrders()\n }\n\n /*\n * createWallet is attached to successful submission of the wallet creation\n * form. createWallet is only called once the form is submitted and a success\n * response is received from the client.\n */\n async createWallet () {\n const user = await app().fetchUser()\n if (!user) return\n const asset = user.assets[this.currentCreate.id]\n Doc.hide(this.page.forms)\n this.balanceWgt.updateAsset(asset.id)\n Doc.setVis(!(this.market.base.wallet && this.market.quote.wallet), this.page.noWallet)\n this.resolveOrderFormVisibility()\n }\n\n /*\n * walletUnlocked is attached to successful submission of the wallet unlock\n * form. walletUnlocked is only called once the form is submitted and a\n * success response is received from the client.\n */\n async walletUnlocked () {\n Doc.hide(this.page.forms)\n this.balanceWgt.updateAsset(this.openAsset.id)\n }\n\n /* lotChanged is attached to the keyup and change events of the lots input. */\n lotChanged () {\n const page = this.page\n const lots = parseInt(page.lotField.value || '0')\n if (lots <= 0) {\n page.lotField.value = '0'\n page.qtyField.value = ''\n this.previewQuoteAmt(false)\n return\n }\n const lotSize = this.market.cfg.lotsize\n page.lotField.value = String(lots)\n // Conversion factor must be a multiple of 10.\n page.qtyField.value = String(lots * lotSize / this.market.baseUnitInfo.conventional.conversionFactor)\n this.previewQuoteAmt(true)\n }\n\n /*\n * quantityChanged is attached to the keyup and change events of the quantity\n * input.\n */\n quantityChanged (finalize: boolean) {\n const page = this.page\n const order = this.parseOrder()\n if (order.qty < 0) {\n page.lotField.value = '0'\n page.qtyField.value = ''\n this.previewQuoteAmt(false)\n return\n }\n const lotSize = this.market.cfg.lotsize\n const lots = Math.floor(order.qty / lotSize)\n const adjusted = lots * lotSize\n page.lotField.value = String(lots)\n if (!order.isLimit && !order.sell) return\n // Conversion factor must be a multiple of 10.\n if (finalize) page.qtyField.value = String(adjusted / this.market.baseUnitInfo.conventional.conversionFactor)\n this.previewQuoteAmt(true)\n }\n\n /*\n * marketBuyChanged is attached to the keyup and change events of the quantity\n * input for the market-buy form.\n */\n marketBuyChanged () {\n const page = this.page\n const qty = convertToAtoms(page.mktBuyField.value || '', this.market.quoteUnitInfo.conventional.conversionFactor)\n const gap = this.midGap()\n if (!gap || !qty) {\n page.mktBuyLots.textContent = '0'\n page.mktBuyScore.textContent = '0'\n return\n }\n const lotSize = this.market.cfg.lotsize\n const received = qty / gap\n page.mktBuyLots.textContent = (received / lotSize).toFixed(1)\n page.mktBuyScore.textContent = Doc.formatCoinValue(received, this.market.baseUnitInfo)\n }\n\n /*\n * rateFieldChanged is attached to the keyup and change events of the rate\n * input.\n */\n rateFieldChanged () {\n // Truncate to rate step. If it is a market buy order, do not adjust.\n const adjusted = this.adjustedRate()\n if (adjusted <= 0) {\n this.depthLines.input = []\n this.drawChartLines()\n this.page.rateField.value = '0'\n return\n }\n const order = this.parseOrder()\n const r = adjusted / this.market.rateConversionFactor\n this.page.rateField.value = String(r)\n this.depthLines.input = [{\n rate: r,\n color: order.sell ? this.depthChart.theme.sellLine : this.depthChart.theme.buyLine\n }]\n this.drawChartLines()\n this.previewQuoteAmt(true)\n }\n\n /*\n * adjustedRate is the current rate field rate, rounded down to a\n * multiple of rateStep.\n */\n adjustedRate (): number {\n const v = this.page.rateField.value\n if (!v) return NaN\n const rate = convertToAtoms(v, this.market.rateConversionFactor)\n const rateStep = this.market.cfg.ratestep\n return rate - (rate % rateStep)\n }\n\n /* loadTable reloads the table from the current order book information. */\n loadTable () {\n this.loadTableSide(true)\n this.loadTableSide(false)\n }\n\n /* binOrdersByRateAndEpoch takes a list of sorted orders and returns the\n same orders grouped into arrays. The orders are grouped by their rate\n and whether or not they are epoch queue orders. Epoch queue orders\n will come after non epoch queue orders with the same rate. */\n binOrdersByRateAndEpoch (orders: MiniOrder[]) {\n if (!orders || !orders.length) return []\n const bins = []\n let currEpochBin = []\n let currNonEpochBin = []\n let currRate = orders[0].msgRate\n if (orders[0].epoch) currEpochBin.push(orders[0])\n else currNonEpochBin.push(orders[0])\n for (let i = 1; i < orders.length; i++) {\n if (orders[i].msgRate !== currRate) {\n bins.push(currNonEpochBin)\n bins.push(currEpochBin)\n currEpochBin = []\n currNonEpochBin = []\n currRate = orders[i].msgRate\n }\n if (orders[i].epoch) currEpochBin.push(orders[i])\n else currNonEpochBin.push(orders[i])\n }\n bins.push(currNonEpochBin)\n bins.push(currEpochBin)\n return bins.filter(bin => bin.length > 0)\n }\n\n /* loadTables loads the order book side into its table. */\n loadTableSide (sell: boolean) {\n const bookSide = sell ? this.book.sells : this.book.buys\n const tbody = sell ? this.page.sellRows : this.page.buyRows\n Doc.empty(tbody)\n if (!bookSide || !bookSide.length) return\n const orderBins = this.binOrdersByRateAndEpoch(bookSide)\n orderBins.forEach(bin => { tbody.appendChild(this.orderTableRow(bin)) })\n }\n\n /* addTableOrder adds a single order to the appropriate table. */\n addTableOrder (order: MiniOrder) {\n const tbody = order.sell ? this.page.sellRows : this.page.buyRows\n let row = tbody.firstChild as OrderRow\n // Handle market order differently.\n if (order.rate === 0) {\n if (order.qtyAtomic === 0) return // a cancel order. TODO: maybe make an indicator on the target order, maybe gray out\n // This is a market order.\n if (row && row.manager.getRate() === 0) {\n row.manager.insertOrder(order)\n } else {\n row = this.orderTableRow([order])\n tbody.insertBefore(row, tbody.firstChild)\n }\n return\n }\n // Must be a limit order. Sort by rate. Skip the market order row.\n if (row && row.manager.getRate() === 0) row = row.nextSibling as OrderRow\n while (row) {\n if (row.manager.compare(order) === 0) {\n row.manager.insertOrder(order)\n return\n } else if (row.manager.compare(order) > 0) {\n const tr = this.orderTableRow([order])\n tbody.insertBefore(tr, row)\n return\n }\n row = row.nextSibling as OrderRow\n }\n const tr = this.orderTableRow([order])\n tbody.appendChild(tr)\n }\n\n /* removeTableOrder removes a single order from its table. */\n removeTableOrder (order: MiniOrder) {\n const token = order.token\n for (const tbody of [this.page.sellRows, this.page.buyRows]) {\n for (const tr of (Array.from(tbody.children) as OrderRow[])) {\n if (tr.manager.removeOrder(token)) {\n return\n }\n }\n }\n }\n\n /* updateTableOrder looks for the order in the table and updates the qty */\n updateTableOrder (u: RemainderUpdate) {\n for (const tbody of [this.page.sellRows, this.page.buyRows]) {\n for (const tr of (Array.from(tbody.children) as OrderRow[])) {\n if (tr.manager.updateOrderQty(u)) {\n return\n }\n }\n }\n }\n\n /*\n * clearOrderTableEpochs removes immediate-tif orders whose epoch has expired.\n */\n clearOrderTableEpochs () {\n this.clearOrderTableEpochSide(this.page.sellRows)\n this.clearOrderTableEpochSide(this.page.buyRows)\n }\n\n /*\n * clearOrderTableEpochs removes immediate-tif orders whose epoch has expired\n * for a single side.\n */\n clearOrderTableEpochSide (tbody: HTMLElement) {\n for (const tr of (Array.from(tbody.children)) as OrderRow[]) {\n tr.manager.removeEpochOrders()\n }\n }\n\n /*\n * orderTableRow creates a new element to insert into an order table.\n Takes a bin of orders with the same rate, and displays the total quantity.\n */\n orderTableRow (orderBin: MiniOrder[]): OrderRow {\n const tr = this.page.rowTemplate.cloneNode(true) as OrderRow\n const { baseUnitInfo, rateConversionFactor } = this.market\n const manager = new OrderTableRowManager(tr, orderBin, baseUnitInfo, rateConversionFactor)\n tr.manager = manager\n bind(tr, 'click', () => {\n this.reportDepthClick(tr.manager.getRate() / rateConversionFactor)\n })\n if (tr.manager.getRate() !== 0) {\n Doc.bind(tr, 'mouseenter', () => {\n const chart = this.depthChart\n this.depthLines.hover = [{\n rate: tr.manager.getRate() / rateConversionFactor,\n color: tr.manager.isSell() ? chart.theme.sellLine : chart.theme.buyLine\n }]\n this.drawChartLines()\n })\n }\n return tr\n }\n\n /* handleConnNote handles the 'conn' notification.\n */\n async handleConnNote (note: ConnEventNote) {\n this.marketList.setConnectionStatus(note)\n if (note.connectionStatus === ConnectionStatus.Connected) {\n // Having been disconnected from a DEX server, anything may have changed,\n // or this may be the first opportunity to get the server's config, so\n // fetch it all before reloading the markets page.\n await app().fetchUser()\n await app().loadPage('markets')\n }\n }\n\n /*\n * filterMarkets sets the display of markets in the markets list based on the\n * value of the search input.\n */\n filterMarkets () {\n const filterTxt = this.page.marketSearchV1.value?.toLowerCase()\n const filter = filterTxt ? (mkt: MarketRow) => mkt.name.includes(filterTxt) : () => true\n this.marketList.setFilter(filter)\n }\n\n /* drawChartLines draws the hover and input lines on the chart. */\n drawChartLines () {\n this.depthChart.setLines([...this.depthLines.hover, ...this.depthLines.input])\n this.depthChart.draw()\n }\n\n /* candleDurationSelected sets the candleDur and loads the candles. */\n candleDurationSelected (dur: string) {\n this.candleDur = dur\n this.loadCandles()\n }\n\n /*\n * loadCandles loads the candles for the current candleDur. If a cache is already\n * active, the cache will be used without a loadcandles request.\n */\n loadCandles () {\n for (const bttn of Doc.kids(this.page.durBttnBox)) {\n if (bttn.textContent === this.candleDur) bttn.classList.add('selected')\n else bttn.classList.remove('selected')\n }\n const { candleCaches, cfg, baseUnitInfo, quoteUnitInfo } = this.market\n const cache = candleCaches[this.candleDur]\n if (cache) {\n // this.depthChart.hide()\n // this.candleChart.show()\n this.candleChart.setCandles(cache, cfg, baseUnitInfo, quoteUnitInfo)\n return\n }\n this.requestCandles()\n }\n\n /* requestCandles sends the loadcandles request. */\n requestCandles () {\n this.candlesLoading = {\n loaded: () => { /* pass */ },\n timer: window.setTimeout(() => {\n if (this.candlesLoading) {\n this.candlesLoading = null\n console.error('candles not received')\n }\n }, 10000)\n }\n const { dex, baseCfg, quoteCfg } = this.market\n ws.request('loadcandles', { host: dex.host, base: baseCfg.id, quote: quoteCfg.id, dur: this.candleDur })\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /markets page.\n */\n unload () {\n ws.request(unmarketRoute, {})\n ws.deregisterRoute(bookRoute)\n ws.deregisterRoute(bookOrderRoute)\n ws.deregisterRoute(unbookOrderRoute)\n ws.deregisterRoute(updateRemainingRoute)\n ws.deregisterRoute(epochOrderRoute)\n ws.deregisterRoute(candlesRoute)\n ws.deregisterRoute(candleUpdateRoute)\n this.depthChart.unattach()\n this.candleChart.unattach()\n Doc.unbind(document, 'keyup', this.keyup)\n clearInterval(this.secondTicker)\n }\n}\n\n/*\n * MarketList represents the list of exchanges and markets on the left side of\n * markets view. The MarketList provides utilities for adjusting the visibility\n * and sort order of markets.\n */\nclass MarketList {\n // xcSections: ExchangeSection[]\n div: PageElement\n rowTmpl: PageElement\n markets: MarketRow[]\n selected: MarketRow\n\n constructor (div: HTMLElement) {\n this.div = div\n this.rowTmpl = Doc.idel(div, 'marketTmplV1')\n Doc.cleanTemplates(this.rowTmpl)\n this.reloadMarketsPane()\n }\n\n updateSpots (note: SpotPriceNote) {\n for (const row of this.markets) {\n if (row.mkt.xc.host !== note.host) continue\n const spot = note.spots[row.mkt.name]\n if (!spot) return\n const xc = app().exchanges[row.mkt.xc.host]\n const mkt = xc.markets[row.mkt.name]\n setPriceAndChange(row.tmpl, xc, mkt)\n }\n }\n\n reloadMarketsPane (): void {\n Doc.empty(this.div)\n this.markets = []\n\n const addMarket = (mkt: ExchangeMarket) => {\n const bui = app().unitInfo(mkt.baseid, mkt.xc)\n const qui = app().unitInfo(mkt.quoteid, mkt.xc)\n const rateConversionFactor = OrderUtil.RateEncodingFactor / bui.conventional.conversionFactor * qui.conventional.conversionFactor\n const row = new MarketRow(this.rowTmpl, mkt, rateConversionFactor)\n this.div.appendChild(row.node)\n return row\n }\n\n for (const mkt of sortedMarkets()) this.markets.push(addMarket(mkt))\n app().bindTooltips(this.div)\n }\n\n find (host: string, baseID: number, quoteID: number): MarketRow | null {\n for (const row of this.markets) {\n if (row.mkt.xc.host === host && row.mkt.baseid === baseID && row.mkt.quoteid === quoteID) return row\n }\n return null\n }\n\n /* exists will be true if the specified market exists. */\n exists (host: string, baseID: number, quoteID: number): boolean {\n return this.find(host, baseID, quoteID) !== null\n }\n\n /* first gets the first market from the first exchange, alphabetically. */\n first (): MarketRow {\n return this.markets[0]\n }\n\n /* select sets the specified market as selected. */\n select (host: string, baseID: number, quoteID: number) {\n const row = this.find(host, baseID, quoteID)\n if (!row) return console.error(`select: no market row for ${host}, ${baseID}-${quoteID}`)\n for (const mkt of this.markets) mkt.node.classList.remove('selected')\n this.selected = row\n this.selected.node.classList.add('selected')\n }\n\n /* setConnectionStatus sets the visibility of the disconnected icon based\n * on the core.ConnEventNote.\n */\n setConnectionStatus (note: ConnEventNote) {\n for (const row of this.markets) {\n if (row.mkt.xc.host !== note.host) continue\n if (note.connectionStatus === ConnectionStatus.Connected) Doc.hide(row.tmpl.disconnectedIco)\n else Doc.show(row.tmpl.disconnectedIco)\n }\n }\n\n /*\n * setFilter sets the visibility of market rows based on the provided filter.\n */\n setFilter (filter: (mkt: MarketRow) => boolean) {\n for (const row of this.markets) {\n if (filter(row)) Doc.show(row.node)\n else Doc.hide(row.node)\n }\n }\n}\n\n/*\n * MarketRow represents one row in the MarketList. A MarketRow is a subsection\n * of the ExchangeSection.\n */\nclass MarketRow {\n node: HTMLElement\n mkt: ExchangeMarket\n name: string\n baseID: number\n quoteID: number\n lotSize: number\n rateStep: number\n tmpl: Record\n rateConversionFactor: number\n\n constructor (template: HTMLElement, mkt: ExchangeMarket, rateConversionFactor: number) {\n this.mkt = mkt\n this.name = mkt.name\n this.baseID = mkt.baseid\n this.quoteID = mkt.quoteid\n this.lotSize = mkt.lotsize\n this.rateStep = mkt.ratestep\n this.rateConversionFactor = rateConversionFactor\n this.node = template.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(this.node)\n tmpl.baseIcon.src = Doc.logoPath(mkt.basesymbol)\n tmpl.quoteIcon.src = Doc.logoPath(mkt.quotesymbol)\n tmpl.baseSymbol.appendChild(Doc.symbolize(mkt.basesymbol))\n tmpl.quoteSymbol.appendChild(Doc.symbolize(mkt.quotesymbol))\n tmpl.baseName.textContent = mkt.baseName\n tmpl.host.textContent = mkt.xc.host\n tmpl.host.style.color = hostColor(mkt.xc.host)\n tmpl.host.dataset.tooltip = mkt.xc.host\n setPriceAndChange(tmpl, mkt.xc, mkt)\n if (this.mkt.xc.connectionStatus !== ConnectionStatus.Connected) Doc.show(tmpl.disconnectedIco)\n }\n}\n\ninterface BalanceWidgetElement {\n id: number\n parentID: number\n cfg: Asset | null\n node: PageElement\n tmpl: Record\n iconBox: PageElement\n stateIcons: WalletIcons\n parentBal?: PageElement\n}\n\n/*\n * BalanceWidget is a display of balance information. Because the wallet can be\n * in any number of states, and because every exchange has different funding\n * coin confirmation requirements, the BalanceWidget displays a number of state\n * indicators and buttons, as well as tabulated balance data with rows for\n * locked and immature balance.\n */\nclass BalanceWidget {\n base: BalanceWidgetElement\n quote: BalanceWidgetElement\n // parentRow: PageElement\n dex: Exchange\n\n constructor (base: HTMLElement, quote: HTMLElement) {\n Doc.hide(base, quote)\n const btmpl = Doc.parseTemplate(base)\n this.base = {\n id: 0,\n parentID: parentIDNone,\n cfg: null,\n node: base,\n tmpl: btmpl,\n iconBox: btmpl.walletState,\n stateIcons: new WalletIcons(btmpl.walletState)\n }\n btmpl.balanceRowTmpl.remove()\n\n const qtmpl = Doc.parseTemplate(quote)\n this.quote = {\n id: 0,\n parentID: parentIDNone,\n cfg: null,\n node: quote,\n tmpl: qtmpl,\n iconBox: qtmpl.walletState,\n stateIcons: new WalletIcons(qtmpl.walletState)\n }\n qtmpl.balanceRowTmpl.remove()\n\n app().registerNoteFeeder({\n balance: (note: BalanceNote) => { this.updateAsset(note.assetID) },\n walletstate: (note: WalletStateNote) => { this.updateAsset(note.wallet.assetID) },\n createwallet: (note: WalletCreationNote) => { this.updateAsset(note.assetID) }\n })\n }\n\n setBalanceVisibility (connected: boolean) {\n if (connected) Doc.show(this.base.node, this.quote.node)\n else Doc.hide(this.base.node, this.quote.node)\n }\n\n /*\n * setWallet sets the balance widget to display data for specified market.\n */\n setWallets (host: string, baseID: number, quoteID: number) {\n const parentID = (assetID: number) => {\n const asset = app().assets[assetID]\n if (asset?.token) return asset.token.parentID\n return parentIDNone\n }\n this.dex = app().user.exchanges[host]\n this.base.id = baseID\n this.base.parentID = parentID(baseID)\n this.base.cfg = this.dex.assets[baseID]\n this.quote.id = quoteID\n this.quote.parentID = parentID(quoteID)\n this.quote.cfg = this.dex.assets[quoteID]\n this.updateWallet(this.base)\n this.updateWallet(this.quote)\n }\n\n /*\n * updateWallet updates the displayed wallet information based on the\n * core.Wallet state.\n */\n updateWallet (side: BalanceWidgetElement) {\n const { cfg, tmpl, iconBox, stateIcons, id: assetID } = side\n if (!cfg) return // no wallet set yet\n const asset = app().assets[assetID]\n // Just hide everything to start.\n Doc.hide(\n tmpl.newWalletRow, tmpl.expired, tmpl.unsupported, tmpl.connect, tmpl.spinner,\n tmpl.walletState, tmpl.balanceRows, tmpl.walletAddr\n )\n tmpl.logo.src = Doc.logoPath(cfg.symbol)\n tmpl.addWalletSymbol.textContent = cfg.symbol.toUpperCase()\n Doc.empty(tmpl.symbol)\n tmpl.symbol.appendChild(Doc.symbolize(cfg.symbol))\n // Handle an unsupported asset.\n if (!asset) {\n Doc.show(tmpl.unsupported)\n return\n }\n Doc.show(iconBox)\n const wallet = asset.wallet\n stateIcons.readWallet(wallet)\n // Handle no wallet configured.\n if (!wallet) {\n if (asset.walletCreationPending) {\n Doc.show(tmpl.spinner)\n return\n }\n Doc.show(tmpl.newWalletRow)\n return\n }\n Doc.show(tmpl.walletAddr)\n // Parent asset\n const bal = wallet.balance\n // Handle not connected and no balance known for the DEX.\n if (!bal && !wallet.running && !wallet.disabled) {\n Doc.show(tmpl.connect)\n return\n }\n // If there is no balance, but the wallet is connected, show the loading\n // icon while we fetch an update.\n if (!bal) {\n app().fetchBalance(assetID)\n Doc.show(tmpl.spinner)\n return\n }\n\n // We have a wallet and a DEX-specific balance. Set all of the fields.\n Doc.show(tmpl.balanceRows)\n Doc.empty(tmpl.balanceRows)\n const addRow = (title: string, bal: number, ui: UnitInfo, icon?: PageElement) => {\n const row = tmpl.balanceRowTmpl.cloneNode(true) as PageElement\n tmpl.balanceRows.appendChild(row)\n const balTmpl = Doc.parseTemplate(row)\n balTmpl.title.textContent = title\n balTmpl.bal.textContent = Doc.formatCoinValue(bal, ui)\n if (icon) {\n balTmpl.bal.append(icon)\n side.parentBal = balTmpl.bal\n }\n }\n\n addRow(intl.prep(intl.ID_AVAILABLE), bal.available, asset.unitInfo)\n addRow(intl.prep(intl.ID_LOCKED), bal.locked + bal.contractlocked + bal.bondlocked, asset.unitInfo)\n addRow(intl.prep(intl.ID_IMMATURE), bal.immature, asset.unitInfo)\n if (asset.token) {\n const { wallet: { balance }, unitInfo, symbol } = app().assets[asset.token.parentID]\n const icon = document.createElement('img')\n icon.src = Doc.logoPath(symbol)\n icon.classList.add('micro-icon')\n addRow(intl.prep(intl.ID_FEE_BALANCE), balance.available, unitInfo, icon)\n }\n\n // If the current balance update time is older than an hour, show the\n // expiration icon. Request a balance update, if possible.\n const expired = new Date().getTime() - new Date(bal.stamp).getTime() > anHour\n if (expired && !wallet.disabled) {\n Doc.show(tmpl.expired)\n if (wallet.running) app().fetchBalance(assetID)\n } else Doc.hide(tmpl.expired)\n }\n\n /* updateParent updates the side's parent asset balance. */\n updateParent (side: BalanceWidgetElement) {\n const { wallet: { balance }, unitInfo } = app().assets[side.parentID]\n if (side.parentBal) side.parentBal.textContent = Doc.formatCoinValue(balance.available, unitInfo)\n }\n\n /*\n * updateAsset updates the info for one side of the existing market. If the\n * specified asset ID is not one of the current market's base or quote assets,\n * it is silently ignored.\n */\n updateAsset (assetID: number) {\n if (assetID === this.base.id) this.updateWallet(this.base)\n else if (assetID === this.quote.id) this.updateWallet(this.quote)\n if (assetID === this.base.parentID) this.updateParent(this.base)\n if (assetID === this.quote.parentID) this.updateParent(this.quote)\n }\n}\n\n/* makeMarket creates a market object that specifies basic market details. */\nfunction makeMarket (host: string, base?: number, quote?: number) {\n return {\n host: host,\n base: base,\n quote: quote\n }\n}\n\n/* marketID creates a DEX-compatible market name from the ticker symbols. */\nexport function marketID (b: string, q: string) { return `${b}_${q}` }\n\n/* convertToAtoms converts the float string to the basic unit of a coin. */\nfunction convertToAtoms (s: string, conversionFactor: number) {\n if (!s) return 0\n return Math.round(parseFloat(s) * conversionFactor)\n}\n\n/* swapBttns changes the 'selected' class of the buttons. */\nfunction swapBttns (before: HTMLElement, now: HTMLElement) {\n before.classList.remove('selected')\n now.classList.add('selected')\n}\n\n/*\n * wireOrder prepares a copy of the order with the options field converted to a\n * string -> string map.\n */\nfunction wireOrder (order: TradeForm) {\n const stringyOptions: Record = {}\n for (const [k, v] of Object.entries(order.options)) stringyOptions[k] = JSON.stringify(v)\n return Object.assign({}, order, { options: stringyOptions })\n}\n\n// OrderTableRowManager manages the data within a row in an order table. Each row\n// represents all the orders in the order book with the same rate, but orders that\n// are booked or still in the epoch queue are displayed in separate rows.\nclass OrderTableRowManager {\n tableRow: HTMLElement\n orderBin: MiniOrder[]\n sell: boolean\n msgRate: number\n epoch: boolean\n baseUnitInfo: UnitInfo\n rateConversionFactor: number\n\n constructor (tableRow: HTMLElement, orderBin: MiniOrder[], baseUnitInfo: UnitInfo, rateConversionFactor: number) {\n this.tableRow = tableRow\n this.orderBin = orderBin\n this.sell = orderBin[0].sell\n this.msgRate = orderBin[0].msgRate\n this.epoch = !!orderBin[0].epoch\n this.baseUnitInfo = baseUnitInfo\n this.rateConversionFactor = rateConversionFactor\n this.setRateEl()\n this.setEpochEl()\n this.updateQtyNumOrdersEl()\n }\n\n // setEpochEl displays a checkmark in the row if the orders represented by\n // this row are in the epoch queue.\n setEpochEl () {\n const epochEl = Doc.tmplElement(this.tableRow, 'epoch')\n if (this.isEpoch()) epochEl.appendChild(check.cloneNode())\n }\n\n // setRateEl popuplates the rate element in the row.\n setRateEl () {\n const rateEl = Doc.tmplElement(this.tableRow, 'rate')\n if (this.msgRate === 0) {\n rateEl.innerText = 'market'\n } else {\n const cssClass = this.isSell() ? 'sellcolor' : 'buycolor'\n rateEl.innerText = Doc.formatFullPrecision(this.msgRate / this.rateConversionFactor)\n rateEl.classList.add(cssClass)\n }\n }\n\n // updateQtyNumOrdersEl populates the quantity element in the row, and also\n // displays the number of orders if there is more than one order in the order\n // bin.\n updateQtyNumOrdersEl () {\n const qty = this.orderBin.reduce((total, curr) => total + curr.qtyAtomic, 0)\n const numOrders = this.orderBin.length\n const qtyEl = Doc.tmplElement(this.tableRow, 'qty')\n const numOrdersEl = Doc.tmplElement(this.tableRow, 'numorders')\n qtyEl.innerText = Doc.formatFullPrecision(qty, this.baseUnitInfo)\n if (numOrders > 1) {\n numOrdersEl.removeAttribute('hidden')\n numOrdersEl.innerText = String(numOrders)\n numOrdersEl.title = `quantity is comprised of ${numOrders} orders`\n } else {\n numOrdersEl.setAttribute('hidden', 'true')\n }\n }\n\n // insertOrder adds an order to the order bin and updates the row elements\n // accordingly.\n insertOrder (order: MiniOrder) {\n this.orderBin.push(order)\n this.updateQtyNumOrdersEl()\n }\n\n // updateOrderQuantity updates the quantity of the order identified by a token,\n // if it exists in the row, and updates the row elements accordingly. The function\n // returns true if the order is in the bin, and false otherwise.\n updateOrderQty (update: RemainderUpdate) {\n const { token, qty, qtyAtomic } = update\n for (let i = 0; i < this.orderBin.length; i++) {\n if (this.orderBin[i].token === token) {\n this.orderBin[i].qty = qty\n this.orderBin[i].qtyAtomic = qtyAtomic\n this.updateQtyNumOrdersEl()\n return true\n }\n }\n return false\n }\n\n // removeOrder removes the order identified by the token, if it exists in the row,\n // and updates the row elements accordingly. If the order bin is empty, the row is\n // removed from the screen. The function returns true if an order was removed, and\n // false otherwise.\n removeOrder (token: string) {\n const index = this.orderBin.findIndex(order => order.token === token)\n if (index < 0) return false\n this.orderBin.splice(index, 1)\n if (!this.orderBin.length) this.tableRow.remove()\n else this.updateQtyNumOrdersEl()\n return true\n }\n\n // removeEpochOrders removes all the orders from the row that are not in the\n // new epoch's epoch queue and updates the elements accordingly.\n removeEpochOrders (newEpoch?: number) {\n this.orderBin = this.orderBin.filter((order) => {\n return !(order.epoch && order.epoch !== newEpoch)\n })\n if (!this.orderBin.length) this.tableRow.remove()\n else this.updateQtyNumOrdersEl()\n }\n\n // getRate returns the rate of the orders in the row.\n getRate () {\n return this.msgRate\n }\n\n // isEpoch returns whether the orders in this row are in the epoch queue.\n isEpoch () {\n return this.epoch\n }\n\n // isSell returns whether the orders in this row are sell orders.\n isSell () {\n return this.sell\n }\n\n // compare takes an order and returns 0 if the order belongs in this row,\n // 1 if the order should go after this row in the table, and -1 if it should\n // be before this row in the table. Sell orders are displayed in ascending order,\n // buy orders are displayed in descending order, and epoch orders always come\n // after booked orders.\n compare (order: MiniOrder) {\n if (this.getRate() === order.msgRate && this.isEpoch() === !!order.epoch) {\n return 0\n } else if (this.getRate() !== order.msgRate) {\n return (this.getRate() > order.msgRate) === order.sell ? 1 : -1\n } else {\n return this.isEpoch() ? 1 : -1\n }\n }\n}\n\ninterface ExchangeMarket extends Market {\n xc: Exchange\n baseName: string\n bui: UnitInfo\n}\n\nfunction sortedMarkets (): ExchangeMarket[] {\n const mkts: ExchangeMarket[] = []\n const assets = app().assets\n const convertMarkets = (xc: Exchange, mkts: Market[]) => {\n return mkts.map((mkt: Market) => {\n const a = assets[mkt.baseid]\n const baseName = a ? a.name : mkt.basesymbol\n const bui = app().unitInfo(mkt.baseid, xc)\n return Object.assign({ xc, baseName, bui }, mkt)\n })\n }\n for (const xc of Object.values(app().exchanges)) mkts.push(...convertMarkets(xc, Object.values(xc.markets || {})))\n mkts.sort((a: ExchangeMarket, b: ExchangeMarket): number => {\n if (!a.spot) {\n if (b.spot) return 1 // put b first, since we have the spot\n // no spots. compare market name then host name\n if (a.name === b.name) return a.xc.host.localeCompare(b.xc.host)\n return a.name.localeCompare(b.name)\n } else if (!b.spot) return -1 // put a first, since we have the spot\n const [aLots, bLots] = [a.spot.vol24 / a.lotsize, b.spot.vol24 / b.lotsize]\n return bLots - aLots // whoever has more volume by lot count\n })\n return mkts\n}\n\nconst FourSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n maximumSignificantDigits: 4\n})\n\nconst OneFractionalDigit = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1\n})\n\nfunction fourSigFigs (v: number) {\n if (v >= 1000 || Math.round(v) === v) return OneFractionalDigit.format(v)\n return FourSigFigs.format(v)\n}\n\nfunction setPriceAndChange (tmpl: Record, xc: Exchange, mkt: Market) {\n if (!mkt.spot) return\n tmpl.price.textContent = fourSigFigs(app().conventionalRate(mkt.baseid, mkt.quoteid, mkt.spot.rate, xc))\n const sign = mkt.spot.change24 > 0 ? '+' : ''\n tmpl.change.classList.add(mkt.spot.change24 >= 0 ? 'buycolor' : 'sellcolor')\n tmpl.change.textContent = `${sign}${(mkt.spot.change24 * 100).toFixed(1)}%`\n}\n\nconst hues = [1 / 2, 1 / 4, 3 / 4, 1 / 8, 5 / 8, 3 / 8, 7 / 8]\n\nfunction generateHue (idx: number): string {\n const h = hues[idx % hues.length]\n return `hsl(${h * 360}, 35%, 50%)`\n}\n\nfunction hostColor (host: string): string {\n const hosts = Object.keys(app().exchanges)\n hosts.sort()\n return generateHue(hosts.indexOf(host))\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport * as OrderUtil from './orderutil'\nimport * as intl from './locales'\nimport { postJSON } from './http'\nimport {\n app,\n PageElement,\n OrderFilter,\n Order\n} from './registry'\n\nconst orderBatchSize = 50\nconst animationLength = 500\n\nexport default class OrdersPage extends BasePage {\n main: HTMLElement\n offset: string\n loading: boolean\n currentForm: PageElement\n orderTmpl: PageElement\n filterState: OrderFilter\n page: Record\n\n constructor (main: HTMLElement) {\n super()\n this.main = main\n // if offset is '', there are no more orders available to auto-load for\n // never-ending scrolling.\n this.offset = ''\n this.loading = false\n const page = this.page = Doc.idDescendants(main)\n this.orderTmpl = page.rowTmpl\n this.orderTmpl.remove()\n\n // filterState will store arrays of strings. The assets and statuses\n // sub-filters will need to be converted to ints for JSON encoding.\n const filterState: OrderFilter = this.filterState = {\n hosts: [],\n assets: [],\n statuses: []\n }\n\n const search = new URLSearchParams(window.location.search)\n const readFilter = (form: HTMLElement, filterKey: string) => {\n const v = search.get(filterKey)\n if (!v || v.length === 0) return\n const subFilter = v.split(',')\n if (v) {\n (filterState as any)[filterKey] = subFilter // Kinda janky\n }\n form.querySelectorAll('input').forEach(bttn => {\n if (subFilter.indexOf(bttn.value) >= 0) bttn.checked = true\n })\n }\n readFilter(page.hostFilter, 'hosts')\n readFilter(page.assetFilter, 'assets')\n readFilter(page.statusFilter, 'statuses')\n\n const applyButtons: HTMLElement[] = []\n const monitorFilter = (form: HTMLElement, filterKey: string) => {\n const applyBttn = form.querySelector('.apply-bttn') as HTMLElement\n applyButtons.push(applyBttn)\n Doc.bind(applyBttn, 'click', () => {\n this.submitFilter()\n applyButtons.forEach(bttn => Doc.hide(bttn))\n })\n form.querySelectorAll('input').forEach(bttn => {\n Doc.bind(bttn, 'change', () => {\n const subFilter = parseSubFilter(form)\n if (compareSubFilter(subFilter, (filterState as any)[filterKey])) {\n // Same as currently loaded. Hide the apply button.\n Doc.hide(applyBttn)\n } else {\n Doc.show(applyBttn)\n }\n })\n })\n }\n\n monitorFilter(page.hostFilter, 'hosts')\n monitorFilter(page.assetFilter, 'assets')\n monitorFilter(page.statusFilter, 'statuses')\n\n Doc.bind(this.main, 'scroll', () => {\n if (this.loading) return\n const belowBottom = page.ordersTable.offsetHeight - this.main.offsetHeight - this.main.scrollTop\n if (belowBottom < 0) {\n this.nextPage()\n }\n })\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => {\n Doc.hide(page.forms)\n })\n })\n\n // If the user clicks outside of a form, it should close the page overlay.\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) {\n Doc.hide(page.forms)\n }\n })\n\n Doc.bind(page.exportOrders, 'click', () => {\n this.exportOrders()\n })\n\n page.showArchivedDateField.addEventListener('change', () => {\n if (page.showArchivedDateField.checked) Doc.show(page.archivedDateField)\n else Doc.hide(page.archivedDateField, page.deleteArchivedRecordsErr)\n })\n\n Doc.bind(page.deleteArchivedRecords, 'click', () => {\n const page = this.page\n page.showArchivedDateField.checked = false\n page.saveMatchesToFile.checked = false\n page.saveOrdersToFile.checked = false\n page.deleteArchivedRecordsErr.textContent = ''\n page.archivedRecordsLocation.textContent = ''\n page.deleteArchivedRecordsMsg.textContent = ''\n Doc.hide(page.deleteArchivedResult, page.deleteArchivedRecordsErr,\n page.deleteArchivedRecordsMsg, page.archivedRecordsLocation, page.archivedDateField)\n this.showForm(page.deleteArchivedRecordsForm)\n })\n\n Doc.bind(page.deleteArchivedRecordsSubmit, 'click', () => {\n let date = 0\n if (page.showArchivedDateField.checked) {\n date = Date.parse(page.olderThan.value || '')\n if (isNaN(date) || date <= 0) {\n Doc.showFormError(page.deleteArchivedRecordsErr, intl.prep(intl.ID_INVALID_DATE_ERR_MSG))\n return\n }\n }\n this.deleteArchivedRecords(date)\n })\n\n this.submitFilter()\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n this.currentForm = form\n const page = this.page\n Doc.hide(page.deleteArchivedRecordsForm)\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0px'\n }\n\n /* setOrders empties the order table and appends the specified orders. */\n setOrders (orders: Order[]) {\n Doc.empty(this.page.tableBody)\n this.appendOrders(orders)\n }\n\n /* appendOrders appends orders to the orders table. */\n appendOrders (orders: Order[]) {\n const tbody = this.page.tableBody\n for (const ord of orders) {\n const tr = this.orderTmpl.cloneNode(true) as HTMLElement\n const set = (tmplID: string, s: string) => { Doc.tmplElement(tr, tmplID).textContent = s }\n const mktID = `${ord.baseSymbol.toUpperCase()}-${ord.quoteSymbol.toUpperCase()}`\n set('host', `${mktID} @ ${ord.host}`)\n let from, to, fromQty\n let toQty = ''\n const [baseUnitInfo, quoteUnitInfo] = [app().unitInfo(ord.baseID), app().unitInfo(ord.quoteID)]\n if (ord.sell) {\n [from, to] = [ord.baseSymbol, ord.quoteSymbol]\n fromQty = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n if (ord.type === OrderUtil.Limit) {\n toQty = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n }\n } else {\n [from, to] = [ord.quoteSymbol, ord.baseSymbol]\n if (ord.type === OrderUtil.Market) {\n fromQty = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n } else {\n fromQty = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n toQty = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n }\n }\n\n set('fromQty', fromQty)\n Doc.tmplElement(tr, 'fromLogo').src = Doc.logoPath(from)\n set('fromSymbol', from)\n set('toQty', toQty)\n Doc.tmplElement(tr, 'toLogo').src = Doc.logoPath(to)\n set('toSymbol', to)\n set('type', `${OrderUtil.typeString(ord)} ${OrderUtil.sellString(ord)}`)\n set('rate', Doc.formatCoinValue(app().conventionalRate(ord.baseID, ord.quoteID, ord.rate)))\n set('status', OrderUtil.statusString(ord))\n set('filled', `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`)\n set('settled', `${(OrderUtil.settled(ord) / ord.qty * 100).toFixed(1)}%`)\n const dateTime = new Date(ord.submitTime).toLocaleString()\n set('time', `${Doc.timeSince(ord.submitTime)} ago, ${dateTime}`)\n const link = Doc.tmplElement(tr, 'link')\n link.href = `order/${ord.id}`\n app().bindInternalNavigation(tr)\n tbody.appendChild(tr)\n }\n if (orders.length === orderBatchSize) {\n this.offset = orders[orders.length - 1].id\n } else {\n this.offset = ''\n }\n }\n\n /* submitFilter submits the current filter and reloads the order table. */\n async submitFilter () {\n const page = this.page\n this.offset = ''\n const filterState = this.filterState\n filterState.hosts = parseSubFilter(page.hostFilter)\n filterState.assets = parseSubFilter(page.assetFilter).map((s: string) => parseInt(s))\n filterState.statuses = parseSubFilter(page.statusFilter).map((s: string) => parseInt(s))\n this.setOrders(await this.fetchOrders())\n }\n\n /* fetchOrders fetches orders using the current filter. */\n async fetchOrders () {\n const loaded = app().loading(this.main)\n const res = await postJSON('/api/orders', this.currentFilter())\n loaded()\n return res.orders\n }\n\n /* exportOrders downloads a csv of the user's orders based on the current filter. */\n exportOrders () {\n this.offset = ''\n const filterState = this.currentFilter()\n const url = new URL(window.location.href)\n const search = new URLSearchParams('')\n const setQuery = (k: string) => {\n const subFilter = (filterState as any)[k]\n subFilter.forEach((v: any) => {\n search.append(k, v)\n })\n }\n setQuery('hosts')\n setQuery('assets')\n setQuery('statuses')\n url.search = search.toString()\n url.pathname = '/orders/export'\n window.open(url.toString())\n }\n\n /* deleteArchivedRecords removes the user's archived orders and matches\n * created before user specified date time in millisecond. Deleted archived\n * records are saved to a CSV file if the user specify so.\n */\n async deleteArchivedRecords (olderThanMs?: number) {\n const page = this.page\n const saveMatchesToFIle = page.saveMatchesToFile.checked || false\n const saveOrdersToFile = page.saveOrdersToFile.checked || false\n const reqBody = {\n olderThanMs: olderThanMs,\n saveMatchesToFile: saveMatchesToFIle,\n saveOrdersToFile: saveOrdersToFile\n }\n const loaded = app().loading(this.main)\n const res = await postJSON('/api/deletearchivedrecords', reqBody)\n loaded()\n if (!app().checkResponse(res)) {\n return Doc.showFormError(page.deleteArchivedRecordsErr, res.msg)\n }\n\n if (res.archivedRecordsDeleted > 0) {\n page.deleteArchivedRecordsMsg.textContent = intl.prep(intl.ID_DELETE_ARCHIVED_RECORDS_RESULT, { nRecords: res.archivedRecordsDeleted })\n if (saveMatchesToFIle || saveOrdersToFile) {\n page.archivedRecordsLocation.textContent = intl.prep(intl.ID_ARCHIVED_RECORDS_PATH, { path: res.archivedRecordsPath })\n Doc.show(page.archivedRecordsLocation)\n }\n // Update the order page.\n this.submitFilter()\n } else {\n page.deleteArchivedRecordsMsg.textContent = intl.prep(intl.ID_NO_ARCHIVED_RECORDS)\n }\n Doc.show(page.deleteArchivedResult, page.deleteArchivedRecordsMsg)\n }\n\n /*\n * currentFilter converts the local filter type (which is all strings) to the\n * server's filter type.\n */\n currentFilter (): OrderFilter {\n const filterState = this.filterState\n return {\n hosts: filterState.hosts,\n assets: filterState.assets.map((s: any) => parseInt(s)),\n statuses: filterState.statuses.map((s: any) => parseInt(s)),\n n: orderBatchSize,\n offset: this.offset\n }\n }\n\n /*\n * nextPage resubmits the filter with the offset set to the last loaded order.\n */\n async nextPage () {\n if (this.offset === '' || this.loading) return\n this.loading = true\n Doc.show(this.page.orderLoader)\n const orders = await this.fetchOrders()\n this.loading = false\n Doc.hide(this.page.orderLoader)\n this.appendOrders(orders)\n }\n}\n\n/*\n * parseSubFilter parses a bool-map from the checkbox inputs in the specified\n * ancestor element.\n */\nfunction parseSubFilter (form: HTMLElement): string[] {\n const entries: string[] = []\n form.querySelectorAll('input').forEach(box => {\n if (box.checked) entries.push(box.value)\n })\n return entries\n}\n\n/* compareSubFilter compares the two filter arrays for unordered equivalence. */\nfunction compareSubFilter (filter1: any[], filter2: any[]): boolean {\n if (filter1.length !== filter2.length) return false\n for (const entry of filter1) {\n if (filter2.indexOf(entry) === -1) return false\n }\n return true\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport * as OrderUtil from './orderutil'\nimport { bind as bindForm, AccelerateOrderForm } from './forms'\nimport { postJSON } from './http'\nimport * as intl from './locales'\nimport {\n app,\n Order,\n PageElement,\n OrderNote,\n MatchNote,\n Match,\n Coin\n} from './registry'\nimport { setOptionTemplates } from './opts'\n\nconst Mainnet = 0\nconst Testnet = 1\n// const Regtest = 3\n\nconst coinIDTakerFoundMakerRedemption = 'TakerFoundMakerRedemption:'\n\nconst animationLength = 500\n\nlet net: number\n\nexport default class OrderPage extends BasePage {\n orderID: string\n order: Order\n page: Record\n currentForm: HTMLElement\n secondTicker: number\n refreshOnPopupClose: boolean\n accelerateOrderForm: AccelerateOrderForm\n stampers: PageElement[]\n\n constructor (main: HTMLElement) {\n super()\n const page = this.page = Doc.idDescendants(main)\n this.stampers = Doc.applySelector(main, '[data-stamp]')\n net = parseInt(main.dataset.net || '')\n // Find the order\n this.orderID = main.dataset.oid || ''\n\n Doc.cleanTemplates(page.matchCardTmpl)\n\n const setStamp = () => {\n for (const span of this.stampers) {\n span.textContent = Doc.timeSince(parseInt(span.dataset.stamp || ''))\n }\n }\n setStamp()\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => {\n if (this.refreshOnPopupClose) {\n window.location.replace(window.location.href)\n return\n }\n Doc.hide(page.forms)\n })\n })\n\n // Some static elements on this page contain assets that can be linked\n // to blockchain explorers (such as Etherscan) so users can easily\n // examine funding/acceleration coins data there. We'd need to set up\n // such hyperlinks here.\n main.querySelectorAll('[data-explorer-id]').forEach((link: PageElement) => {\n const assetID = parseInt(link.dataset.explorerId || '')\n setCoinHref(assetID, link)\n })\n\n if (page.cancelBttn) {\n Doc.bind(page.cancelBttn, 'click', () => {\n this.showForm(page.cancelForm)\n })\n }\n\n Doc.bind(page.accelerateBttn, 'click', () => {\n this.showAccelerateForm()\n })\n\n const success = () => {\n this.refreshOnPopupClose = true\n }\n // Do not call cleanTemplates before creating the AccelerateOrderForm\n setOptionTemplates(page)\n this.accelerateOrderForm = new AccelerateOrderForm(page.accelerateForm, success)\n Doc.cleanTemplates(page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl)\n\n // If the user clicks outside of a form, it should close the page overlay.\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) {\n if (this.refreshOnPopupClose) {\n window.location.reload()\n return\n }\n Doc.hide(page.forms)\n page.cancelPass.value = ''\n }\n })\n\n // Cancel order form\n bindForm(page.cancelForm, page.cancelSubmit, async () => { this.submitCancel() })\n\n this.secondTicker = window.setInterval(() => {\n setStamp()\n }, 10000) // update every 10 seconds\n\n app().registerNoteFeeder({\n order: (note: OrderNote) => { this.handleOrderNote(note) },\n match: (note: MatchNote) => { this.handleMatchNote(note) }\n })\n\n this.start()\n }\n\n async start () {\n let ord = app().order(this.orderID)\n // app().order can only access active orders. If the order is not active,\n // we'll need to get the data from the database.\n if (ord) this.order = ord\n else {\n ord = await this.fetchOrder()\n }\n\n // Swap out the dot-notation symbols with token-aware symbols.\n this.page.mktBaseSymbol.replaceWith(Doc.symbolize(ord.baseSymbol))\n this.page.mktQuoteSymbol.replaceWith(Doc.symbolize(ord.quoteSymbol))\n\n this.setAccelerationButtonVis()\n this.showMatchCards()\n }\n\n unload () {\n clearInterval(this.secondTicker)\n }\n\n /* fetchOrder fetches the order from the client. */\n async fetchOrder (): Promise {\n const res = await postJSON('/api/order', this.orderID)\n if (!app().checkResponse(res)) throw res.msg\n this.order = res.order\n return this.order\n }\n\n /*\n * setImmutableMatchCardElements sets the match card elements that are never\n * changed.\n */\n setImmutableMatchCardElements (matchCard: HTMLElement, match: Match) {\n const tmpl = Doc.parseTemplate(matchCard)\n\n tmpl.matchID.textContent = match.matchID\n\n const time = new Date(match.stamp)\n tmpl.matchTime.textContent = time.toLocaleTimeString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric'\n })\n\n tmpl.matchTimeAgo.dataset.stamp = match.stamp.toString()\n tmpl.matchTimeAgo.textContent = Doc.timeSince(match.stamp)\n this.stampers.push(tmpl.matchTimeAgo)\n\n const orderPortion = OrderUtil.orderPortion(this.order, match)\n const baseSymbol = Doc.bipSymbol(this.order.baseID)\n const quoteSymbol = Doc.bipSymbol(this.order.quoteID)\n const baseUnitInfo = app().unitInfo(this.order.baseID)\n const quoteUnitInfo = app().unitInfo(this.order.quoteID)\n const quoteAmount = OrderUtil.baseToQuote(match.rate, match.qty)\n\n if (match.isCancel) {\n Doc.show(tmpl.cancelInfoDiv)\n Doc.hide(tmpl.infoDiv, tmpl.status, tmpl.statusHdr)\n\n if (this.order.sell) {\n tmpl.cancelAmount.textContent = Doc.formatCoinValue(match.qty, baseUnitInfo)\n tmpl.cancelIcon.src = Doc.logoPathFromID(this.order.baseID)\n } else {\n tmpl.cancelAmount.textContent = Doc.formatCoinValue(quoteAmount, quoteUnitInfo)\n tmpl.cancelIcon.src = Doc.logoPathFromID(this.order.quoteID)\n }\n\n tmpl.cancelOrderPortion.textContent = orderPortion\n\n return\n }\n\n Doc.show(tmpl.infoDiv)\n Doc.hide(tmpl.cancelInfoDiv)\n\n tmpl.orderPortion.textContent = orderPortion\n\n if (match.side === OrderUtil.Maker) {\n tmpl.side.textContent = intl.prep(intl.ID_MAKER)\n Doc.show(\n tmpl.makerSwapYou,\n tmpl.makerRedeemYou,\n tmpl.takerSwapThem,\n tmpl.takerRedeemThem\n )\n Doc.hide(\n tmpl.takerSwapYou,\n tmpl.takerRedeemYou,\n tmpl.makerSwapThem,\n tmpl.makerRedeemThem\n )\n } else {\n tmpl.side.textContent = intl.prep(intl.ID_TAKER)\n Doc.hide(\n tmpl.makerSwapYou,\n tmpl.makerRedeemYou,\n tmpl.takerSwapThem,\n tmpl.takerRedeemThem\n )\n Doc.show(\n tmpl.takerSwapYou,\n tmpl.takerRedeemYou,\n tmpl.makerSwapThem,\n tmpl.makerRedeemThem\n )\n }\n\n if ((match.side === OrderUtil.Maker && this.order.sell) ||\n (match.side === OrderUtil.Taker && !this.order.sell)) {\n tmpl.makerSwapAsset.textContent = baseSymbol\n tmpl.takerSwapAsset.textContent = quoteSymbol\n tmpl.makerRedeemAsset.textContent = quoteSymbol\n tmpl.takerRedeemAsset.textContent = baseSymbol\n } else {\n tmpl.makerSwapAsset.textContent = quoteSymbol\n tmpl.takerSwapAsset.textContent = baseSymbol\n tmpl.makerRedeemAsset.textContent = baseSymbol\n tmpl.takerRedeemAsset.textContent = quoteSymbol\n }\n\n const rate = app().conventionalRate(this.order.baseID, this.order.quoteID, match.rate)\n tmpl.rate.textContent = `${rate} ${baseSymbol}/${quoteSymbol}`\n\n if (this.order.sell) {\n tmpl.refundAsset.textContent = baseSymbol\n tmpl.fromAmount.textContent = Doc.formatCoinValue(match.qty, baseUnitInfo)\n tmpl.toAmount.textContent = Doc.formatCoinValue(quoteAmount, quoteUnitInfo)\n tmpl.fromIcon.src = Doc.logoPathFromID(this.order.baseID)\n tmpl.toIcon.src = Doc.logoPathFromID(this.order.quoteID)\n } else {\n tmpl.refundAsset.textContent = quoteSymbol\n tmpl.fromAmount.textContent = Doc.formatCoinValue(quoteAmount, quoteUnitInfo)\n tmpl.toAmount.textContent = Doc.formatCoinValue(match.qty, baseUnitInfo)\n tmpl.fromIcon.src = Doc.logoPathFromID(this.order.quoteID)\n tmpl.toIcon.src = Doc.logoPathFromID(this.order.baseID)\n }\n }\n\n /*\n * setMutableMatchCardElements sets the match card elements which may get\n * updated on each update to the match.\n */\n setMutableMatchCardElements (matchCard: HTMLElement, m: Match) {\n if (m.isCancel) {\n return\n }\n\n const tmpl = Doc.parseTemplate(matchCard)\n tmpl.status.textContent = OrderUtil.matchStatusString(m)\n\n const setCoin = (pendingName: string, linkName: string, coin: Coin) => {\n const formatCoinID = (cid: string) => {\n if (cid.startsWith(coinIDTakerFoundMakerRedemption)) {\n const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length)\n return intl.prep(intl.ID_TAKER_FOUND_MAKER_REDEMPTION, { makerAddr: makerAddr })\n }\n return cid\n }\n const coinLink = tmpl[linkName]\n const pendingSpan = tmpl[pendingName]\n if (!coin) {\n Doc.show(tmpl[pendingName])\n Doc.hide(tmpl[linkName])\n return\n }\n coinLink.textContent = formatCoinID(coin.stringID)\n coinLink.dataset.explorerCoin = coin.stringID\n setCoinHref(coin.assetID, coinLink)\n Doc.hide(pendingSpan)\n Doc.show(coinLink)\n }\n\n setCoin('makerSwapPending', 'makerSwapCoin', makerSwapCoin(m))\n setCoin('takerSwapPending', 'takerSwapCoin', takerSwapCoin(m))\n setCoin('makerRedeemPending', 'makerRedeemCoin', makerRedeemCoin(m))\n setCoin('takerRedeemPending', 'takerRedeemCoin', takerRedeemCoin(m))\n setCoin('refundPending', 'refundCoin', m.refund)\n\n if (m.status === OrderUtil.MakerSwapCast && !m.revoked && !m.refund) {\n const c = makerSwapCoin(m)\n tmpl.makerSwapMsg.textContent = confirmationString(c)\n Doc.hide(tmpl.takerSwapMsg, tmpl.makerRedeemMsg, tmpl.takerRedeemMsg)\n Doc.show(tmpl.makerSwapMsg)\n } else if (m.status === OrderUtil.TakerSwapCast && !m.revoked && !m.refund) {\n const c = takerSwapCoin(m)\n tmpl.takerSwapMsg.textContent = confirmationString(c)\n Doc.hide(tmpl.makerSwapMsg, tmpl.makerRedeemMsg, tmpl.takerRedeemMsg)\n Doc.show(tmpl.takerSwapMsg)\n } else if (inConfirmingMakerRedeem(m) && !m.revoked && !m.refund) {\n tmpl.makerRedeemMsg.textContent = confirmationString(m.redeem)\n Doc.hide(tmpl.makerSwapMsg, tmpl.takerSwapMsg, tmpl.takerRedeemMsg)\n Doc.show(tmpl.makerRedeemMsg)\n } else if (inConfirmingTakerRedeem(m) && !m.revoked && !m.refund) {\n tmpl.takerRedeemMsg.textContent = confirmationString(m.redeem)\n Doc.hide(tmpl.makerSwapMsg, tmpl.takerSwapMsg, tmpl.makerRedeemMsg)\n Doc.show(tmpl.takerRedeemMsg)\n } else {\n Doc.hide(tmpl.makerSwapMsg, tmpl.takerSwapMsg, tmpl.makerRedeemMsg, tmpl.takerRedeemMsg)\n }\n\n Doc.setVis(!m.isCancel && (makerSwapCoin(m) || !m.revoked), tmpl.makerSwap)\n Doc.setVis(!m.isCancel && (takerSwapCoin(m) || !m.revoked), tmpl.takerSwap)\n Doc.setVis(!m.isCancel && (makerRedeemCoin(m) || !m.revoked), tmpl.makerRedeem)\n // When revoked, there is uncertainty about the taker redeem coin. The taker\n // redeem may be needed if maker redeems while taker is waiting to refund.\n Doc.setVis(!m.isCancel && (takerRedeemCoin(m) || (!m.revoked && m.active) || ((m.side === OrderUtil.Taker) && m.active && (m.counterRedeem || !m.refund))), tmpl.takerRedeem)\n // The refund placeholder should not be shown if there is a counter redeem.\n Doc.setVis(!m.isCancel && (m.refund || (m.revoked && m.active && !m.counterRedeem)), tmpl.refund)\n }\n\n /*\n * addNewMatchCard adds a new card to the list of match cards.\n */\n addNewMatchCard (match: Match) {\n const page = this.page\n const matchCard = page.matchCardTmpl.cloneNode(true) as HTMLElement\n matchCard.dataset.matchID = match.matchID\n this.setImmutableMatchCardElements(matchCard, match)\n this.setMutableMatchCardElements(matchCard, match)\n page.matchBox.appendChild(matchCard)\n }\n\n /*\n * showMatchCards creates cards for each match in the order.\n */\n showMatchCards () {\n const order = this.order\n if (!order) return\n if (!order.matches) return\n order.matches.sort((a, b) => a.stamp - b.stamp)\n order.matches.forEach((match) => this.addNewMatchCard(match))\n }\n\n /* showCancel shows a form to confirm submission of a cancel order. */\n showCancel () {\n const order = this.order\n const page = this.page\n const remaining = order.qty - order.filled\n const asset = OrderUtil.isMarketBuy(order) ? app().assets[order.quoteID] : app().assets[order.baseID]\n page.cancelRemain.textContent = Doc.formatCoinValue(remaining, asset.unitInfo)\n page.cancelUnit.textContent = asset.unitInfo.conventional.unit.toUpperCase()\n this.showForm(page.cancelForm)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n this.currentForm = form\n const page = this.page\n Doc.hide(page.cancelForm, page.accelerateForm)\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0px'\n }\n\n /* submitCancel submits a cancellation for the order. */\n async submitCancel () {\n // this will be the page.cancelSubmit button (evt.currentTarget)\n const page = this.page\n const order = this.order\n const req = {\n orderID: order.id,\n pw: page.cancelPass.value\n }\n page.cancelPass.value = ''\n const loaded = app().loading(page.cancelForm)\n const res = await postJSON('/api/cancel', req)\n loaded()\n if (!app().checkResponse(res)) return\n page.status.textContent = intl.prep(intl.ID_CANCELING)\n Doc.hide(page.forms)\n order.cancelling = true\n }\n\n /*\n * setAccelerationButtonVis shows the acceleration button if the order can\n * be accelerated.\n */\n setAccelerationButtonVis () {\n const order = this.order\n if (!order) return\n const page = this.page\n Doc.setVis(app().canAccelerateOrder(order), page.accelerateBttn, page.actionsLabel)\n }\n\n /* showAccelerateForm shows a form to accelerate an order */\n async showAccelerateForm () {\n const loaded = app().loading(this.page.accelerateBttn)\n this.accelerateOrderForm.refresh(this.order)\n loaded()\n this.showForm(this.page.accelerateForm)\n }\n\n /*\n * handleOrderNote is the handler for the 'order'-type notification, which are\n * used to update an order's status.\n */\n handleOrderNote (note: OrderNote) {\n const page = this.page\n const order = note.order\n if (order.id !== this.orderID) return\n this.order = order\n const bttn = page.cancelBttn\n if (bttn && order.status > OrderUtil.StatusBooked) Doc.hide(bttn)\n page.status.textContent = OrderUtil.statusString(order)\n for (const m of order.matches || []) this.processMatch(m)\n this.setAccelerationButtonVis()\n }\n\n /* handleMatchNote handles a 'match' notification. */\n handleMatchNote (note: MatchNote) {\n if (note.orderID !== this.orderID) return\n this.processMatch(note.match)\n this.setAccelerationButtonVis()\n }\n\n /*\n * processMatch synchronizes a match's card with a match received in a\n * 'order' or 'match' notification.\n */\n processMatch (m: Match) {\n let card: HTMLElement | null = null\n for (const div of Doc.applySelector(this.page.matchBox, '.match-card')) {\n if (div.dataset.matchID === m.matchID) {\n card = div\n break\n }\n }\n if (card) {\n this.setMutableMatchCardElements(card, m)\n } else {\n this.addNewMatchCard(m)\n }\n }\n}\n\n/*\n * confirmationString is a string describing the state of confirmations for a\n * coin\n * */\nfunction confirmationString (coin: Coin) {\n if (!coin.confs || coin.confs.required === 0) return ''\n return `${coin.confs.count} / ${coin.confs.required} ${intl.prep(intl.ID_CONFIRMATIONS)}`\n}\n\n// makerSwapCoin return's the maker's swap coin.\nfunction makerSwapCoin (m: Match) : Coin {\n return (m.side === OrderUtil.Maker) ? m.swap : m.counterSwap\n}\n\n// takerSwapCoin return's the taker's swap coin.\nfunction takerSwapCoin (m: Match) {\n return (m.side === OrderUtil.Maker) ? m.counterSwap : m.swap\n}\n\n// makerRedeemCoin return's the maker's redeem coin.\nfunction makerRedeemCoin (m: Match) {\n return (m.side === OrderUtil.Maker) ? m.redeem : m.counterRedeem\n}\n\n// takerRedeemCoin return's the taker's redeem coin.\nfunction takerRedeemCoin (m: Match) {\n return (m.side === OrderUtil.Maker) ? m.counterRedeem : m.redeem\n}\n\n/*\n* inConfirmingMakerRedeem will be true if we are the maker, and we are waiting\n* on confirmations for our own redeem.\n*/\nfunction inConfirmingMakerRedeem (m: Match) {\n return m.status < OrderUtil.MatchConfirmed && m.side === OrderUtil.Maker && m.status >= OrderUtil.MakerRedeemed\n}\n\n/*\n* inConfirmingTakerRedeem will be true if we are the taker, and we are waiting\n* on confirmations for our own redeem.\n*/\nfunction inConfirmingTakerRedeem (m: Match) {\n return m.status < OrderUtil.MatchConfirmed && m.side === OrderUtil.Taker && m.status >= OrderUtil.MatchComplete\n}\n\n/*\n * setCoinHref sets the hyperlink element's href attribute based on provided\n * assetID and data-explorer-coin value present on supplied link element.\n */\nfunction setCoinHref (assetID: number, link: PageElement) {\n const assetExplorer = CoinExplorers[assetID]\n if (!assetExplorer) return\n const formatter = assetExplorer[net]\n if (!formatter) return\n link.classList.remove('plainlink')\n link.classList.add('subtlelink')\n link.href = formatter(link.dataset.explorerCoin || '')\n}\n\nconst ethExplorers: Record string> = {\n [Mainnet]: (cid: string) => {\n if (cid.startsWith(coinIDTakerFoundMakerRedemption)) {\n const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length)\n return `https://etherscan.io/address/${makerAddr}`\n }\n if (cid.length === 42) {\n return `https://etherscan.io/address/${cid}`\n }\n return `https://etherscan.io/tx/${cid}`\n },\n [Testnet]: (cid: string) => {\n if (cid.startsWith(coinIDTakerFoundMakerRedemption)) {\n const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length)\n return `https://goerli.etherscan.io/address/${makerAddr}`\n }\n if (cid.length === 42) {\n return `https://goerli.etherscan.io/address/${cid}`\n }\n return `https://goerli.etherscan.io/tx/${cid}`\n }\n}\n\nconst CoinExplorers: Record string>> = {\n 42: { // dcr\n [Mainnet]: (cid: string) => {\n const [txid, vout] = cid.split(':')\n return `https://explorer.dcrdata.org/tx/${txid}/out/${vout}`\n },\n [Testnet]: (cid: string) => {\n const [txid, vout] = cid.split(':')\n return `https://testnet.dcrdata.org/tx/${txid}/out/${vout}`\n }\n },\n 0: { // btc\n [Mainnet]: (cid: string) => `https://mempool.space/tx/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://mempool.space/testnet/tx/${cid.split(':')[0]}`\n },\n 2: { // ltc\n [Mainnet]: (cid: string) => `https://ltc.bitaps.com/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://sochain.com/tx/LTCTEST/${cid.split(':')[0]}`\n },\n 60: ethExplorers,\n 60001: ethExplorers,\n 3: { // doge\n [Mainnet]: (cid: string) => `https://dogeblocks.com/tx/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://blockexplorer.one/dogecoin/testnet/tx/${cid.split(':')[0]}`\n },\n 145: { // bch\n [Mainnet]: (cid: string) => `https://bch.loping.net/tx/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://tbch4.loping.net/tx/${cid.split(':')[0]}`\n }\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport State from './state'\nimport { postJSON } from './http'\nimport * as forms from './forms'\nimport * as intl from './locales'\n\nimport {\n app,\n PageElement,\n ConnectionStatus,\n Exchange\n} from './registry'\n\nconst animationLength = 300\n\nexport default class DexSettingsPage extends BasePage {\n body: HTMLElement\n forms: PageElement[]\n currentForm: PageElement\n page: Record\n host: string\n keyup: (e: KeyboardEvent) => void\n dexAddrForm: forms.DEXAddressForm\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n this.host = body.dataset.host ? body.dataset.host : ''\n const page = this.page = Doc.idDescendants(body)\n this.forms = Doc.applySelector(page.forms, ':scope > form')\n\n Doc.bind(page.exportDexBtn, 'click', () => this.prepareAccountExport(page.authorizeAccountExportForm))\n Doc.bind(page.disableAcctBtn, 'click', () => this.prepareAccountDisable(page.disableAccountForm))\n Doc.bind(page.updateBondOptionsBtn, 'click', () => this.prepareUpdateBondOptions())\n Doc.bind(page.updateCertBtn, 'click', () => page.certFileInput.click())\n Doc.bind(page.updateHostBtn, 'click', () => this.prepareUpdateHost())\n Doc.bind(page.certFileInput, 'change', () => this.onCertFileChange())\n\n this.dexAddrForm = new forms.DEXAddressForm(page.dexAddrForm, async (xc: Exchange) => {\n window.location.assign(`/dexsettings/${xc.host}`)\n }, undefined, this.host)\n\n forms.bind(page.updateBondOptionsForm, page.updateBondOptionsConfirm, () => this.updateBondOptions())\n forms.bind(page.authorizeAccountExportForm, page.authorizeExportAccountConfirm, () => this.exportAccount())\n forms.bind(page.disableAccountForm, page.disableAccountConfirm, () => this.disableAccount())\n\n const closePopups = () => {\n Doc.hide(page.forms)\n }\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n closePopups()\n }\n }\n Doc.bind(document, 'keyup', this.keyup)\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { closePopups() })\n })\n\n app().registerNoteFeeder({\n conn: () => { this.setConnectionStatus() }\n })\n\n this.setConnectionStatus()\n }\n\n unload () {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n const page = this.page\n this.currentForm = form\n this.forms.forEach(form => Doc.hide(form))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n // exportAccount exports and downloads the account info.\n async exportAccount () {\n const page = this.page\n const pw = page.exportAccountAppPass.value\n const host = page.exportAccountHost.textContent\n page.exportAccountAppPass.value = ''\n const req = {\n pw,\n host\n }\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/exportaccount', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.exportAccountErr.textContent = res.msg\n Doc.show(page.exportAccountErr)\n return\n }\n res.account.bonds = res.bonds // maintain backward compat of JSON file\n const accountForExport = JSON.parse(JSON.stringify(res.account))\n const a = document.createElement('a')\n a.setAttribute('download', 'dcrAccount-' + host + '.json')\n a.setAttribute('href', 'data:text/json,' + JSON.stringify(accountForExport, null, 2))\n a.click()\n Doc.hide(page.forms)\n }\n\n // disableAccount disables the account associated with the provided host.\n async disableAccount () {\n const page = this.page\n const pw = page.disableAccountAppPW.value\n const host = page.disableAccountHost.textContent\n page.disableAccountAppPW.value = ''\n const req = {\n pw,\n host\n }\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/disableaccount', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.disableAccountErr.textContent = res.msg\n Doc.show(page.disableAccountErr)\n return\n }\n Doc.hide(page.forms)\n window.location.assign('/settings')\n }\n\n async prepareAccountExport (authorizeAccountExportForm: HTMLElement) {\n const page = this.page\n page.exportAccountHost.textContent = this.host\n page.exportAccountErr.textContent = ''\n if (State.passwordIsCached()) {\n this.exportAccount()\n } else {\n this.showForm(authorizeAccountExportForm)\n }\n }\n\n async prepareAccountDisable (disableAccountForm: HTMLElement) {\n const page = this.page\n page.disableAccountHost.textContent = this.host\n page.disableAccountErr.textContent = ''\n this.showForm(disableAccountForm)\n }\n\n // prepareUpdateBondOptions resets and prepares the Update Bond Options form.\n async prepareUpdateBondOptions () {\n const page = this.page\n const xc = app().user.exchanges[this.host]\n page.bondTargetTier.setAttribute('placeholder', xc.bondOptions.targetTier.toString())\n Doc.empty(page.bondAssetSelect)\n for (const [assetSymbol, bondAsset] of Object.entries(xc.bondAssets)) {\n const option = document.createElement('option') as HTMLOptionElement\n option.value = bondAsset.id.toString()\n option.textContent = assetSymbol.toUpperCase()\n if (bondAsset.id === xc.bondOptions.bondAsset) option.selected = true\n page.bondAssetSelect.appendChild(option)\n }\n page.bondOptionsErr.textContent = ''\n Doc.hide(page.bondOptionsErr)\n this.showForm(page.updateBondOptionsForm)\n }\n\n async prepareUpdateHost () {\n const page = this.page\n this.dexAddrForm.refresh()\n this.showForm(page.dexAddrForm)\n }\n\n async onCertFileChange () {\n const page = this.page\n Doc.hide(page.errMsg)\n const files = page.certFileInput.files\n let cert\n if (files && files.length) cert = await files[0].text()\n if (!cert) return\n const req = { host: this.host, cert: cert }\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/updatecert', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.errMsg.textContent = res.msg\n Doc.show(page.errMsg)\n } else {\n Doc.show(page.updateCertMsg)\n setTimeout(() => { Doc.hide(page.updateCertMsg) }, 5000)\n }\n }\n\n setConnectionStatus () {\n const page = this.page\n const exchange = app().user.exchanges[this.host]\n const displayIcons = (connected: boolean) => {\n if (connected) {\n Doc.hide(page.disconnectedIcon)\n Doc.show(page.connectedIcon)\n } else {\n Doc.show(page.disconnectedIcon)\n Doc.hide(page.connectedIcon)\n }\n }\n if (exchange) {\n switch (exchange.connectionStatus) {\n case ConnectionStatus.Connected:\n displayIcons(true)\n page.connectionStatus.textContent = intl.prep(intl.ID_CONNECTED)\n break\n case ConnectionStatus.Disconnected:\n displayIcons(false)\n page.connectionStatus.textContent = intl.prep(intl.ID_DISCONNECTED)\n break\n case ConnectionStatus.InvalidCert:\n displayIcons(false)\n page.connectionStatus.textContent = `${intl.prep(intl.ID_DISCONNECTED)} - ${intl.prep(intl.ID_INVALID_CERTIFICATE)}`\n }\n }\n }\n\n /*\n * updateBondOptions is called when the form to update bond options is\n * submitted.\n */\n async updateBondOptions () {\n const page = this.page\n const targetTier = parseInt(page.bondTargetTier.value ?? '')\n const bondAsset = parseInt(page.bondAssetSelect.value ?? '')\n\n const bondOptions = {\n host: this.host,\n targetTier: targetTier,\n bondAsset: bondAsset\n }\n\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/updatebondoptions', bondOptions)\n loaded()\n if (!app().checkResponse(res)) {\n page.bondOptionsErr.textContent = res.msg\n Doc.show(page.bondOptionsErr)\n } else {\n Doc.hide(page.bondOptionsErr)\n Doc.show(page.bondOptionsMsg)\n setTimeout(() => {\n Doc.hide(page.bondOptionsMsg)\n Doc.hide(page.forms)\n }, 5000)\n // update the in-memory values.\n const xc = app().user.exchanges[this.host]\n xc.bondOptions.bondAsset = bondAsset\n xc.bondOptions.targetTier = targetTier\n }\n }\n}\n","import {\n app,\n PageElement,\n Market,\n Exchange,\n OrderOption,\n XYRange,\n BotReport,\n BotNote,\n MaxSell,\n MaxBuy,\n MarketReport,\n SupportedAsset\n} from './registry'\nimport Doc from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport { setOptionTemplates, XYRangeOption } from './opts'\nimport State from './state'\nimport { bind as bindForm, NewWalletForm } from './forms'\nimport { RateEncodingFactor } from './orderutil'\n\nconst GapStrategyMultiplier = 'multiplier'\nconst GapStrategyAbsolute = 'absolute'\nconst GapStrategyAbsolutePlus = 'absolute-plus'\nconst GapStrategyPercent = 'percent'\nconst GapStrategyPercentPlus = 'percent-plus'\n\ninterface HostedMarket extends Market {\n host: string\n}\n\ninterface LiveProgram extends BotReport {\n tmpl: Record\n div: PageElement\n}\n\ninterface BaseOption {\n key: string\n displayname: string\n description: string\n default: number\n min: number\n max: number\n}\n\n// Oracle Weighting\nconst oracleWeightBaseOption: BaseOption = {\n key: 'oracleWeighting',\n displayname: 'Oracle Weight',\n description: 'Enable the oracle and set its weight in calculating our target price.',\n default: 0.1,\n max: 1.0,\n min: 0.0\n}\nconst oracleWeightRange: XYRange = {\n start: {\n label: '0%',\n x: 0,\n y: 0\n },\n end: {\n label: '100%',\n x: 1,\n y: 100\n },\n xUnit: '',\n yUnit: '%'\n}\nconst oracleWeightOption: OrderOption = createXYRange(oracleWeightBaseOption, oracleWeightRange)\n\nconst oracleBiasBaseOption: BaseOption = {\n key: 'oracleBias',\n displayname: 'Oracle Bias',\n description: 'Apply an adjustment to the oracles rate, up to +/-1%.',\n default: 0.0,\n max: 0.01,\n min: -0.01\n}\nconst oracleBiasRange: XYRange = {\n start: {\n label: '-1%',\n x: -0.01,\n y: -1\n },\n end: {\n label: '1%',\n x: 0.01,\n y: 1\n },\n xUnit: '',\n yUnit: '%'\n}\nconst oracleBiasOption: OrderOption = createXYRange(oracleBiasBaseOption, oracleBiasRange)\n\nconst driftToleranceBaseOption: BaseOption = {\n key: 'driftTolerance',\n displayname: 'Drift Tolerance',\n description: 'How far from the ideal price will we allow orders to drift. Typically a fraction of a percent.',\n default: 0.001,\n max: 0.01,\n min: 0\n}\nconst driftToleranceRange: XYRange = {\n start: {\n label: '0%',\n x: 0,\n y: 0\n },\n end: {\n label: '1%',\n x: 0.01,\n y: 1\n },\n xUnit: '',\n yUnit: '%'\n}\nconst driftToleranceOption: OrderOption = createXYRange(driftToleranceBaseOption, driftToleranceRange)\n\nconst gapMultiplierBaseOption: BaseOption = {\n key: 'gapMultiplier',\n displayname: 'Spread Multiplier',\n description: 'Increase the spread for reduced risk and higher potential profits, but with a lower fill rate. ' +\n 'The baseline value, 1, is the break-even value, where tx fees and profits are equivalent in an otherwise static market.',\n default: 2,\n max: 10,\n min: 1\n}\nconst gapMultiplierRange: XYRange = {\n start: {\n label: '1x',\n x: 1,\n y: 100\n },\n end: {\n label: '100x',\n x: 100,\n y: 10000\n },\n xUnit: 'X',\n yUnit: '%'\n}\nconst gapMultiplierOption: OrderOption = createXYRange(gapMultiplierBaseOption, gapMultiplierRange)\n\nconst gapPercentBaseOption: BaseOption = {\n key: 'gapPercent',\n displayname: 'Percent Spread',\n description: 'The spread is set as a percent of current spot price.',\n default: 0.005,\n max: 1,\n min: 0\n}\nconst gapPercentRange: XYRange = {\n start: {\n label: '0%',\n x: 0,\n y: 0\n },\n end: {\n label: '10%',\n x: 0.1,\n y: 10\n },\n xUnit: 'X',\n yUnit: '%'\n}\nconst gapPercentOption: OrderOption = createXYRange(gapPercentBaseOption, gapPercentRange)\n\nconst animationLength = 300\n\nexport default class MarketMakerPage extends BasePage {\n page: Record\n data: any\n createOpts: Record\n gapRanges: Record\n currentMarket: HostedMarket\n currentForm: PageElement\n keyup: (e: KeyboardEvent) => void\n programs: Record\n editProgram: BotReport | null\n gapMultiplierOpt: XYRangeOption\n gapPercentOpt: XYRangeOption\n driftToleranceOpt: XYRangeOption\n biasOpt: XYRangeOption\n weightOpt: XYRangeOption\n pwHandler: ((pw: string) => Promise) | null\n newWalletForm: NewWalletForm\n specifiedPrice: number\n currentReport: MarketReport | null\n\n constructor (main: HTMLElement, data: any) {\n super()\n const page = this.page = Doc.idDescendants(main)\n this.data = data\n this.programs = {}\n this.editProgram = null\n this.pwHandler = null\n this.createOpts = {\n [oracleBiasBaseOption.key]: oracleBiasBaseOption.default,\n [oracleWeightBaseOption.key]: oracleWeightBaseOption.default,\n [driftToleranceOption.key]: driftToleranceOption.default\n }\n this.gapRanges = {\n [gapMultiplierBaseOption.key]: gapMultiplierBaseOption.default,\n [gapPercentBaseOption.key]: gapPercentBaseOption.default\n }\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { this.closePopups() })\n })\n\n setOptionTemplates(page)\n\n Doc.cleanTemplates(page.assetRowTmpl, page.booleanOptTmpl, page.rangeOptTmpl,\n page.orderOptTmpl, page.runningProgramTmpl, page.oracleTmpl)\n\n const selectClicked = (e: MouseEvent, isBase: boolean): void => {\n e.stopPropagation()\n const select = isBase ? page.baseSelect : page.quoteSelect\n const m = Doc.descendentMetrics(main, select)\n page.assetDropdown.style.left = `${m.bodyLeft}px`\n page.assetDropdown.style.top = `${m.bodyTop}px`\n\n const counterAsset = isBase ? this.currentMarket.quoteid : this.currentMarket.baseid\n const clickedSymbol = isBase ? this.currentMarket.basesymbol : this.currentMarket.quotesymbol\n\n // Look through markets for other base assets for the counter asset.\n const matches: Set = new Set()\n const otherAssets: Set = new Set()\n\n for (const mkt of sortedMarkets()) {\n otherAssets.add(mkt.basesymbol)\n otherAssets.add(mkt.quotesymbol)\n const [firstID, secondID] = isBase ? [mkt.quoteid, mkt.baseid] : [mkt.baseid, mkt.quoteid]\n const [firstSymbol, secondSymbol] = isBase ? [mkt.quotesymbol, mkt.basesymbol] : [mkt.basesymbol, mkt.quotesymbol]\n if (firstID === counterAsset) matches.add(secondSymbol)\n else if (secondID === counterAsset) matches.add(firstSymbol)\n }\n\n const options = Array.from(matches)\n options.sort((a: string, b: string) => a.localeCompare(b))\n for (const symbol of options) otherAssets.delete(symbol)\n const nonOptions = Array.from(otherAssets)\n nonOptions.sort((a: string, b: string) => a.localeCompare(b))\n\n Doc.empty(page.assetDropdown)\n const addOptions = (symbols: string[], avail: boolean): void => {\n for (const symbol of symbols) {\n const row = this.assetRow(symbol)\n Doc.bind(row, 'click', (e: MouseEvent) => {\n e.stopPropagation()\n if (symbol === clickedSymbol) return this.hideAssetDropdown() // no change\n this.leaveEditMode()\n if (isBase) this.setCreationBase(symbol)\n else this.setCreationQuote(symbol)\n })\n if (!avail) row.classList.add('ghost')\n page.assetDropdown.appendChild(row)\n }\n }\n addOptions(options, true)\n addOptions(nonOptions, false)\n Doc.show(page.assetDropdown)\n const clicker = (e: MouseEvent): void => {\n if (Doc.mouseInElement(e, page.assetDropdown)) return\n this.hideAssetDropdown()\n Doc.unbind(document, 'click', clicker)\n }\n Doc.bind(document, 'click', clicker)\n }\n\n Doc.bind(page.baseSelect, 'click', (e: MouseEvent) => selectClicked(e, true))\n Doc.bind(page.quoteSelect, 'click', (e: MouseEvent) => selectClicked(e, false))\n\n Doc.bind(page.marketSelect, 'change', () => {\n const [host, name] = page.marketSelect.value?.split(' ') as string[]\n this.setMarketSubchoice(host, name)\n })\n\n this.gapMultiplierOpt = new XYRangeOption(gapMultiplierOption, '', this.gapRanges, () => this.createOptsUpdated())\n this.gapPercentOpt = new XYRangeOption(gapPercentOption, '', this.gapRanges, () => this.createOptsUpdated())\n this.driftToleranceOpt = new XYRangeOption(driftToleranceOption, '', this.createOpts, () => this.createOptsUpdated())\n this.biasOpt = new XYRangeOption(oracleBiasOption, '', this.createOpts, () => this.createOptsUpdated())\n this.weightOpt = new XYRangeOption(oracleWeightOption, '', this.createOpts, () => this.createOptsUpdated())\n\n page.options.appendChild(this.gapMultiplierOpt.node)\n Doc.hide(this.gapMultiplierOpt.node) // Default is GapStrategyPercentPlus\n page.options.appendChild(this.gapPercentOpt.node)\n page.options.appendChild(this.driftToleranceOpt.node)\n page.options.appendChild(this.weightOpt.node)\n page.options.appendChild(this.biasOpt.node)\n\n Doc.bind(page.showAdvanced, 'click', () => {\n State.storeLocal(State.optionsExpansionLK, true)\n Doc.hide(page.showAdvanced)\n Doc.show(page.hideAdvanced, page.options)\n })\n\n Doc.bind(page.hideAdvanced, 'click', () => {\n State.storeLocal(State.optionsExpansionLK, false)\n Doc.hide(page.hideAdvanced, page.options)\n Doc.show(page.showAdvanced)\n })\n\n Doc.bind(page.runBttn, 'click', this.authedRoute(async (pw: string): Promise => this.createBot(pw)))\n\n Doc.bind(page.lotsInput, 'change', () => {\n page.lotsInput.value = String(Math.round(parseFloat(page.lotsInput.value ?? '0')))\n })\n\n Doc.bind(page.exitEditMode, 'click', () => this.leaveEditMode())\n\n Doc.bind(page.manualPriceBttn, 'click', () => {\n Doc.hide(page.basisPrice, page.manualPriceBttn)\n Doc.show(page.manualPriceInput)\n page.manualPriceInput.focus()\n })\n\n const setManualPrice = () => {\n const v = parseFloat(page.manualPriceInput.value ?? '0')\n page.basisPrice.textContent = Doc.formatFiveSigFigs(v)\n this.specifiedPrice = v\n this.setCurrentBasisPrice(v)\n Doc.show(page.basisPrice, page.manualPriceBttn)\n Doc.hide(page.manualPriceInput, page.noFiatBox)\n }\n\n const hideManualPrice = () => {\n page.manualPriceInput.value = ''\n Doc.show(page.basisPrice, page.manualPriceBttn)\n Doc.hide(page.manualPriceInput)\n }\n\n // Doc.bind(page.manualPriceInput, 'change', () => { setManualPrice() })\n\n Doc.bind(page.manualPriceInput, 'keyup', (e: KeyboardEvent) => {\n switch (e.key) {\n case 'Escape':\n hideManualPrice()\n break\n case 'Enter':\n case 'NumpadEnter':\n setManualPrice()\n }\n })\n\n Doc.bind(page.manualPriceInput, 'blur', () => {\n if (Doc.isHidden(page.basisPrice)) hideManualPrice()\n })\n\n Doc.bind(page.gapStrategySelect, 'change', () => this.updateGapStrategyInputs(this.currentReport))\n\n const submitPasswordForm = async () => {\n const pw = page.pwInput.value ?? ''\n if (pw === '') return\n const handler = this.pwHandler\n if (handler === null) return\n this.pwHandler = null\n await handler(pw)\n if (this.currentForm === page.pwForm) this.closePopups()\n }\n\n bindForm(page.pwForm, page.pwSubmit, () => submitPasswordForm())\n\n Doc.bind(page.pwInput, 'keyup', (e: KeyboardEvent) => {\n if (e.key !== 'Enter' && e.key !== 'NumpadEnter') return\n submitPasswordForm()\n })\n\n this.newWalletForm = new NewWalletForm(\n page.newWalletForm,\n assetID => this.newWalletCreated(assetID)\n )\n\n Doc.bind(page.createBaseWallet, 'click', () => {\n this.newWalletForm.setAsset(this.currentMarket.baseid)\n this.showForm(page.newWalletForm)\n })\n\n Doc.bind(page.createQuoteWallet, 'click', () => {\n this.newWalletForm.setAsset(this.currentMarket.quoteid)\n this.showForm(page.newWalletForm)\n })\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { this.closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n this.closePopups()\n }\n }\n\n const lastMkt = State.fetchLocal(State.lastMMMarketLK) as HostedMarket\n let mkt: HostedMarket | null = null\n if (lastMkt && lastMkt.host) {\n const xc = app().exchanges[lastMkt.host]\n if (xc) {\n const mktID = lastMkt.basesymbol + '_' + lastMkt.quotesymbol\n const xcMkt = xc.markets[mktID]\n if (xcMkt) mkt = Object.assign({ host: xc.host }, xcMkt)\n }\n }\n\n if (State.fetchLocal(State.optionsExpansionLK)) {\n Doc.show(page.hideAdvanced, page.options)\n Doc.hide(page.showAdvanced)\n }\n\n app().registerNoteFeeder({\n bot: (n: BotNote) => { this.handleBotNote(n) }\n })\n\n this.setMarket([mkt ?? sortedMarkets()[0]])\n this.populateRunningPrograms()\n page.createBox.classList.remove('invisible')\n page.programsBox.classList.remove('invisible')\n }\n\n unload (): void {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement): Promise {\n this.currentForm = form\n const page = this.page\n Doc.hide(page.pwForm, page.newWalletForm)\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n closePopups (): void {\n this.pwHandler = null\n this.page.pwInput.value = ''\n Doc.hide(this.page.forms)\n }\n\n hideAssetDropdown (): void {\n const page = this.page\n page.assetDropdown.scrollTop = 0\n Doc.hide(page.assetDropdown)\n }\n\n assetRow (symbol: string): PageElement {\n const row = this.page.assetRowTmpl.cloneNode(true) as PageElement\n const tmpl = Doc.parseTemplate(row)\n tmpl.logo.src = Doc.logoPath(symbol)\n Doc.empty(tmpl.symbol)\n tmpl.symbol.appendChild(Doc.symbolize(symbol))\n return row\n }\n\n setCurrentReport (r: MarketReport | null) {\n this.currentReport = r\n this.setCurrentBasisPrice(r ? r.basisPrice : 0)\n this.updateGapStrategyInputs(r)\n\n // Set max value for absolute input\n const page = this.page\n if (r && r.basisPrice > 0) {\n this.fetchMaxBuy(Math.round(r.basisPrice / this.currentMarket.atomToConv * RateEncodingFactor))\n page.basisPrice.textContent = Doc.formatFiveSigFigs(r.basisPrice)\n Doc.show(page.absMaxBox, page.basisPrice)\n Doc.hide(page.manualPriceInput, page.noFiatBox)\n this.page.gapFactorMax.textContent = Doc.formatFiveSigFigs(r.basisPrice)\n } else {\n page.lotEstQuoteLots.textContent = '[no rate]'\n page.basisPrice.textContent = 'must set manually'\n Doc.hide(page.basisPrice, page.absMaxBox)\n Doc.show(page.manualPriceInput)\n page.manualPriceInput.focus()\n const u = app().user\n if (Object.keys(u.fiatRates).length === 0) Doc.show(page.noFiatBox)\n }\n\n if (r && r.breakEvenSpread > 0) {\n Doc.show(page.breakEvenGapBox)\n page.breakEvenGap.textContent = Doc.formatFiveSigFigs(r.breakEvenSpread)\n } else Doc.hide(page.breakEvenGapBox)\n }\n\n updateGapStrategyInputVisibility () {\n const page = this.page\n const gapStrategy = page.gapStrategySelect.value\n switch (gapStrategy) {\n case GapStrategyMultiplier:\n Doc.show(this.gapMultiplierOpt.node)\n Doc.hide(page.absInputBox, page.absMaxBox, this.gapPercentOpt.node)\n break\n case GapStrategyPercent:\n case GapStrategyPercentPlus:\n Doc.show(this.gapPercentOpt.node)\n Doc.hide(page.absInputBox, page.absMaxBox, this.gapMultiplierOpt.node)\n break\n case GapStrategyAbsolute:\n case GapStrategyAbsolutePlus:\n Doc.hide(this.gapMultiplierOpt.node, this.gapPercentOpt.node)\n Doc.show(page.absInputBox, page.absMaxBox)\n }\n }\n\n updateGapStrategyInputs (r: MarketReport | null) {\n this.updateGapStrategyInputVisibility()\n switch (this.page.gapStrategySelect.value) {\n case GapStrategyMultiplier: {\n if (r && r.breakEvenSpread > 0) {\n gapMultiplierRange.start.y = r.breakEvenSpread\n gapMultiplierRange.end.y = r.breakEvenSpread * 100\n gapMultiplierRange.yUnit = this.rateUnit()\n } else {\n gapMultiplierRange.start.y = 100\n gapMultiplierRange.end.y = 10000\n gapMultiplierRange.yUnit = '%'\n }\n this.gapMultiplierOpt.handler.setConfig(gapMultiplierRange)\n break\n }\n case GapStrategyPercent:\n case GapStrategyPercentPlus: {\n if (r && r.breakEvenSpread > 0) {\n gapPercentRange.end.y = r.basisPrice\n gapPercentRange.yUnit = this.rateUnit()\n this.gapPercentOpt.handler.setConfig(gapPercentRange)\n } else {\n gapPercentRange.end.y = 10\n gapPercentRange.yUnit = '%'\n }\n }\n }\n }\n\n setCurrentBasisPrice (p: number) {\n if (p > 0) {\n const rateUnit = this.rateUnit()\n oracleBiasRange.start.y = -0.01 * p\n oracleBiasRange.end.y = 0.01 * p\n oracleBiasRange.yUnit = rateUnit\n driftToleranceRange.start.y = 0\n driftToleranceRange.end.y = p * 0.01\n driftToleranceRange.yUnit = rateUnit\n } else {\n oracleBiasRange.start.y = -1\n oracleBiasRange.end.y = 1\n oracleBiasRange.yUnit = '%'\n driftToleranceRange.start.y = 0\n driftToleranceRange.end.y = 1\n driftToleranceRange.yUnit = '%'\n }\n this.biasOpt.handler.setConfig(oracleBiasRange)\n this.driftToleranceOpt.handler.setConfig(driftToleranceRange)\n }\n\n rateUnit (): string {\n const quoteSymbol = this.currentMarket.quotesymbol.split('.')[0]\n const baseSymbol = this.currentMarket.basesymbol.split('.')[0]\n return `${quoteSymbol}/${baseSymbol}`\n }\n\n async setMarket (mkts: HostedMarket[], skipMarketUpdate?: boolean): Promise {\n const page = this.page\n\n const mkt = mkts[0]\n this.currentMarket = mkt\n this.specifiedPrice = 0\n this.setCurrentReport(null)\n page.manualPriceInput.value = ''\n\n State.storeLocal(State.lastMMMarketLK, mkt)\n\n Doc.empty(page.baseSelect, page.quoteSelect)\n page.baseSelect.appendChild(this.assetRow(mkt.basesymbol))\n page.quoteSelect.appendChild(this.assetRow(mkt.quotesymbol))\n this.hideAssetDropdown()\n\n const addMarketSelect = (mkt: HostedMarket, el: PageElement) => {\n Doc.setContent(el,\n Doc.symbolize(mkt.basesymbol),\n new Text('-') as any,\n Doc.symbolize(mkt.quotesymbol),\n new Text(' @ ') as any,\n new Text(mkt.host) as any\n )\n }\n\n Doc.hide(page.marketSelect, page.marketOneChoice)\n if (mkts.length === 1) {\n Doc.show(page.marketOneChoice)\n addMarketSelect(mkt, page.marketOneChoice)\n } else {\n Doc.show(page.marketSelect)\n Doc.empty(page.marketSelect)\n for (const mkt of mkts) {\n const opt = document.createElement('option')\n page.marketSelect.appendChild(opt)\n opt.value = `${mkt.host} ${mkt.name}`\n addMarketSelect(mkt, opt)\n }\n }\n\n if (skipMarketUpdate) return\n\n Doc.setContent(page.lotEstBaseSymbol, Doc.symbolize(mkt.basesymbol))\n Doc.setContent(page.lotEstQuoteSymbol, Doc.symbolize(mkt.quotesymbol))\n\n const setNoWallet = (a: SupportedAsset, noWalletBox: PageElement): boolean => {\n Doc.show(noWalletBox)\n const tmpl = Doc.parseTemplate(noWalletBox)\n tmpl.assetLogo.src = Doc.logoPath(a.symbol)\n tmpl.assetName.textContent = a.name\n return false\n }\n\n Doc.hide(\n page.lotEstQuoteBox, page.lotEstQuoteNoWallet, page.lotEstBaseBox, page.lotEstBaseNoWallet, page.availHeader,\n page.lotEstimateBox, page.marketInfo, page.oraclesBox, page.lotsBox, page.options, page.advancedBox\n )\n const [b, q] = [app().assets[mkt.baseid], app().assets[mkt.quoteid]]\n if (b.wallet && q.wallet) {\n Doc.show(\n page.lotEstQuoteBox, page.lotEstBaseBox, page.availHeader, page.fetchingMarkets,\n page.lotsBox, page.advancedBox\n )\n if (State.fetchLocal(State.optionsExpansionLK)) Doc.show(page.options)\n const loaded = app().loading(page.options)\n const buy = this.fetchOracleAndMaxBuy()\n const sell = this.fetchMaxSell()\n await buy\n await sell\n loaded()\n Doc.show(page.lotEstimateBox, page.marketInfo, page.oraclesBox)\n Doc.hide(page.fetchingMarkets)\n return\n }\n Doc.show(page.lotEstimateBox)\n if (q.wallet) {\n page.lotEstQuoteLots.textContent = '0'\n } else setNoWallet(q, page.lotEstQuoteNoWallet)\n\n if (b.wallet) {\n page.lotEstBaseLots.textContent = '0'\n } else setNoWallet(b, page.lotEstBaseNoWallet)\n }\n\n async fetchMaxSell (): Promise {\n const { currentMarket: mkt, page } = this\n const res = await postJSON('/api/maxsell', {\n host: mkt.host,\n base: mkt.baseid,\n quote: mkt.quoteid\n }) as MaxSell\n if (this.currentMarket !== mkt) return\n if (!app().checkResponse(res as any)) {\n page.lotEstBaseLots.textContent = '0'\n console.error(res)\n return\n }\n page.lotEstBaseLots.textContent = String(res.maxSell.swap.lots)\n }\n\n async fetchOracleAndMaxBuy (): Promise {\n const { currentMarket: mkt, page } = this\n const res = await postJSON('/api/marketreport', { host: mkt.host, baseID: mkt.baseid, quoteID: mkt.quoteid })\n if (!app().checkResponse(res)) {\n page.lotEstQuoteLots.textContent = '0'\n console.error(res)\n return\n }\n const r = res.report as MarketReport\n Doc.hide(page.manualPriceBttn)\n this.setCurrentReport(r)\n\n if (!r.oracles || r.oracles.length === 0) return\n\n Doc.empty(page.oracles)\n let weight = 0\n let weightedSum = 0\n for (const o of r.oracles ?? []) {\n const tr = page.oracleTmpl.cloneNode(true) as PageElement\n page.oracles.appendChild(tr)\n const tmpl = Doc.parseTemplate(tr)\n tmpl.logo.src = 'img/' + o.host + '.png'\n tmpl.host.textContent = ExchangeNames[o.host]\n tmpl.volume.textContent = Doc.formatThreeSigFigs(o.usdVol)\n const price = (o.bestBuy + o.bestSell) / 2\n weightedSum += o.usdVol * price\n weight += o.usdVol\n tmpl.price.textContent = Doc.formatThreeSigFigs((o.bestBuy + o.bestSell) / 2)\n }\n page.avgPrice.textContent = Doc.formatFiveSigFigs(weightedSum / weight)\n }\n\n async fetchMaxBuy (rate: number): Promise {\n const { currentMarket: mkt, page } = this\n const res = await postJSON('/api/maxbuy', {\n host: mkt.host,\n base: mkt.baseid,\n quote: mkt.quoteid,\n rate: rate ?? 0\n }) as MaxBuy\n if (this.currentMarket !== mkt) return\n page.lotEstQuoteLots.textContent = String(res.maxBuy.swap.lots)\n }\n\n setEditProgram (report: BotReport): void {\n const { createOpts, page } = this\n const pgm = report.program\n const [b, q] = [app().assets[pgm.baseID], app().assets[pgm.quoteID]]\n const mkt = app().exchanges[pgm.host].markets[`${b.symbol}_${q.symbol}`]\n this.setMarket([{\n host: pgm.host,\n ...mkt\n }], true)\n for (const p of Object.values(this.programs)) {\n if (p.programID !== report.programID) Doc.hide(p.div)\n }\n this.editProgram = report\n page.createBox.classList.add('edit')\n page.programsBox.classList.add('edit')\n page.lotsInput.value = String(pgm.lots)\n createOpts.oracleWeighting = pgm.oracleWeighting\n this.weightOpt.setValue(pgm.oracleWeighting)\n createOpts.oracleBias = pgm.oracleBias\n this.biasOpt.setValue(pgm.oracleBias)\n createOpts.driftTolerance = pgm.driftTolerance\n this.driftToleranceOpt.setValue(pgm.driftTolerance)\n page.gapStrategySelect.value = pgm.gapStrategy\n this.updateGapStrategyInputVisibility()\n this.createOptsUpdated()\n\n switch (pgm.gapStrategy) {\n case GapStrategyPercent:\n case GapStrategyPercentPlus:\n this.gapPercentOpt.setValue(pgm.gapFactor)\n break\n case GapStrategyMultiplier:\n this.gapMultiplierOpt.setValue(pgm.gapFactor)\n }\n\n Doc.bind(page.programsBox, 'click', () => this.leaveEditMode())\n }\n\n leaveEditMode (): void {\n const page = this.page\n Doc.unbind(page.programsBox, 'click', () => this.leaveEditMode())\n for (const p of Object.values(this.programs)) Doc.show(p.div)\n page.createBox.classList.remove('edit')\n page.programsBox.classList.remove('edit')\n this.editProgram = null\n this.setMarket([this.currentMarket])\n }\n\n populateRunningPrograms (): void {\n const page = this.page\n const bots = app().user.bots\n Doc.empty(page.runningPrograms)\n this.programs = {}\n for (const report of bots) page.runningPrograms.appendChild(this.programDiv(report))\n this.setProgramsHeader()\n }\n\n setProgramsHeader (): void {\n const page = this.page\n if (page.runningPrograms.children.length > 0) {\n Doc.show(page.programsHeader)\n Doc.hide(page.noProgramsMessage)\n } else {\n Doc.hide(page.programsHeader)\n Doc.show(page.noProgramsMessage)\n }\n }\n\n programDiv (report: BotReport): PageElement {\n const page = this.page\n const div = page.runningProgramTmpl.cloneNode(true) as PageElement\n const tmpl = Doc.parseTemplate(div)\n const startStop = async (endpoint: string, pw?: string): Promise => {\n Doc.hide(tmpl.startErr)\n const loaded = app().loading(div)\n const res = await postJSON(endpoint, { programID: report.programID, appPW: pw })\n loaded()\n if (!app().checkResponse(res)) {\n tmpl.startErr.textContent = res.msg\n Doc.show(tmpl.startErr)\n }\n }\n Doc.bind(tmpl.pauseBttn, 'click', () => startStop('/api/stopbot'))\n Doc.bind(tmpl.startBttn, 'click', this.authedRoute(async (pw: string) => startStop('/api/startbot', pw)))\n Doc.bind(tmpl.retireBttn, 'click', () => startStop('/api/retirebot'))\n Doc.bind(tmpl.configureBttn, 'click', (e: MouseEvent) => {\n e.stopPropagation()\n this.setEditProgram(this.programs[report.programID])\n })\n const [b, q] = [app().assets[report.program.baseID], app().assets[report.program.quoteID]]\n tmpl.base.appendChild(this.assetRow(b.symbol))\n tmpl.quote.appendChild(this.assetRow(q.symbol))\n tmpl.baseSymbol.textContent = b.symbol.toUpperCase()\n tmpl.quoteSymbol.textContent = q.symbol.toUpperCase()\n tmpl.host.textContent = report.program.host\n this.updateProgramDiv(tmpl, report)\n this.programs[report.programID] = Object.assign({ tmpl, div }, report)\n return div\n }\n\n authedRoute (handler: (pw: string) => Promise): () => void {\n return async () => {\n if (State.passwordIsCached()) return await handler('')\n this.pwHandler = handler\n await this.showForm(this.page.pwForm)\n }\n }\n\n updateProgramDiv (tmpl: Record, report: BotReport): void {\n const pgm = report.program\n Doc.hide(tmpl.programRunning, tmpl.programPaused)\n if (report.running) Doc.show(tmpl.programRunning)\n else Doc.show(tmpl.programPaused)\n tmpl.lots.textContent = String(pgm.lots)\n tmpl.boost.textContent = `${(pgm.gapFactor * 100).toFixed(1)}%`\n tmpl.driftTolerance.textContent = `${(pgm.driftTolerance * 100).toFixed(2)}%`\n tmpl.oracleWeight.textContent = `${(pgm.oracleWeighting * 100).toFixed(0)}%`\n tmpl.oracleBias.textContent = `${(pgm.oracleBias * 100).toFixed(1)}%`\n }\n\n setMarketSubchoice (host: string, name: string): void {\n if (host !== this.currentMarket.host || name !== this.currentMarket.name) this.leaveEditMode()\n this.currentMarket = Object.assign({ host }, app().exchanges[host].markets[name])\n }\n\n createOptsUpdated (): void {\n const opts = this.createOpts\n if (opts.oracleWeighting) Doc.show(this.biasOpt.node)\n else Doc.hide(this.biasOpt.node)\n }\n\n handleBotNote (n: BotNote): void {\n const page = this.page\n const r = n.report\n switch (n.topic) {\n case 'BotCreated':\n page.runningPrograms.prepend(this.programDiv(r))\n this.setProgramsHeader()\n break\n case 'BotRetired':\n this.programs[r.programID].div.remove()\n delete this.programs[r.programID]\n this.setProgramsHeader()\n break\n default: {\n const p = this.programs[r.programID]\n Object.assign(p, r)\n this.updateProgramDiv(p.tmpl, r)\n }\n }\n }\n\n setCreationBase (symbol: string) {\n const counterAsset = this.currentMarket.quotesymbol\n const markets = sortedMarkets()\n const options: HostedMarket[] = []\n // Best option: find an exact match.\n for (const mkt of markets) if (mkt.basesymbol === symbol && mkt.quotesymbol === counterAsset) options.push(mkt)\n // Next best option: same assets, reversed order.\n for (const mkt of markets) if (mkt.quotesymbol === symbol && mkt.basesymbol === counterAsset) options.push(mkt)\n // If we have exact matches, we're done.\n if (options.length > 0) return this.setMarket(options)\n // No exact matches. Must have selected a ghost-class market. Next best\n // option will be the first market where the selected asset is a base asset.\n for (const mkt of markets) if (mkt.basesymbol === symbol) return this.setMarket([mkt])\n // Last option: Market where this is the quote asset.\n for (const mkt of markets) if (mkt.quotesymbol === symbol) return this.setMarket([mkt])\n }\n\n setCreationQuote (symbol: string) {\n const counterAsset = this.currentMarket.basesymbol\n const markets = sortedMarkets()\n const options: HostedMarket[] = []\n for (const mkt of markets) if (mkt.quotesymbol === symbol && mkt.basesymbol === counterAsset) options.push(mkt)\n for (const mkt of markets) if (mkt.basesymbol === symbol && mkt.quotesymbol === counterAsset) options.push(mkt)\n if (options.length > 0) return this.setMarket(options)\n for (const mkt of markets) if (mkt.quotesymbol === symbol) return this.setMarket([mkt])\n for (const mkt of markets) if (mkt.basesymbol === symbol) return this.setMarket([mkt])\n }\n\n async createBot (appPW: string): Promise {\n const { page, currentMarket, currentReport } = this\n\n Doc.hide(page.createErr)\n const setError = (s: string) => {\n page.createErr.textContent = s\n Doc.show(page.createErr)\n }\n\n const lots = parseInt(page.lotsInput.value || '0')\n if (lots === 0) return setError('must specify > 0 lots')\n const makerProgram = Object.assign({\n host: currentMarket.host,\n baseID: currentMarket.baseid,\n quoteID: currentMarket.quoteid\n }, this.createOpts, { lots, gapStrategy: '' })\n\n const strategy = page.gapStrategySelect.value\n makerProgram.gapStrategy = strategy ?? ''\n\n const req = {\n botType: 'MakerV0',\n program: makerProgram,\n programID: 0,\n appPW: appPW,\n manualRate: 0\n }\n\n switch (strategy) {\n case GapStrategyAbsolute:\n case GapStrategyAbsolutePlus: {\n const r = parseFloat(page.absInput.value || '0')\n if (r === 0) return setError('gap must be specified for strategy = absolute')\n else if (currentReport?.basisPrice && r >= currentReport.basisPrice) return setError('gap width cannot be > current spot price')\n makerProgram.gapFactor = r\n break\n }\n case GapStrategyPercent:\n case GapStrategyPercentPlus:\n makerProgram.gapFactor = this.gapRanges.gapPercent\n break\n default:\n makerProgram.gapFactor = this.gapRanges.gapMultiplier\n }\n\n let endpoint = '/api/createbot'\n\n if (this.editProgram !== null) {\n req.programID = this.editProgram.programID\n endpoint = '/api/updatebotprogram'\n } else {\n if (!this.currentReport || this.currentReport.basisPrice === 0) {\n if (this.specifiedPrice === 0) {\n setError('price must be set manually')\n return\n }\n req.program.manualRate = this.specifiedPrice\n }\n }\n\n const loaded = app().loading(page.botCreator)\n const res = await postJSON(endpoint, req)\n loaded()\n\n if (!app().checkResponse(res)) {\n page.createErr.textContent = res.msg\n Doc.show(page.createErr)\n return\n }\n\n this.leaveEditMode()\n\n page.lotsInput.value = ''\n }\n\n newWalletCreated (assetID: number) {\n const m = this.currentMarket\n if (assetID === m.baseid) this.setCreationBase(m.basesymbol)\n else if (assetID === m.quoteid) this.setCreationQuote(m.quotesymbol)\n else return\n this.closePopups()\n }\n}\n\nfunction sortedMarkets (): HostedMarket[] {\n const mkts: HostedMarket[] = []\n const convertMarkets = (xc: Exchange): HostedMarket[] => {\n return Object.values(xc.markets).map((mkt: Market) => Object.assign({ host: xc.host }, mkt))\n }\n for (const xc of Object.values(app().user.exchanges)) mkts.push(...convertMarkets(xc))\n mkts.sort((a: Market, b: Market) => {\n if (!a.spot) {\n if (!b.spot) return a.name.localeCompare(b.name)\n return -1\n }\n if (!b.spot) return 1\n // Sort by lots.\n return b.spot.vol24 / b.lotsize - a.spot.vol24 / a.lotsize\n })\n return mkts\n}\n\nfunction createOption (opt: BaseOption): OrderOption {\n return {\n key: opt.key,\n displayname: opt.displayname,\n description: opt.description,\n default: opt.default,\n max: opt.max,\n min: opt.min,\n noecho: false,\n isboolean: false,\n isdate: false,\n disablewhenactive: false,\n isBirthdayConfig: false,\n noauth: false\n }\n}\n\nfunction createXYRange (baseOpt: BaseOption, xyRange: XYRange): OrderOption {\n const opt = createOption(baseOpt)\n opt.xyRange = xyRange\n return opt\n}\n\nconst ExchangeNames: Record = {\n 'binance.com': 'Binance',\n 'coinbase.com': 'Coinbase',\n 'bittrex.com': 'Bittrex',\n 'hitbtc.com': 'HitBTC',\n 'exmo.com': 'EXMO'\n}\n","import Doc from './doc'\nimport State from './state'\nimport RegistrationPage from './register'\nimport LoginPage from './login'\nimport WalletsPage from './wallets'\nimport SettingsPage from './settings'\nimport MarketsPage from './markets'\nimport OrdersPage from './orders'\nimport OrderPage from './order'\nimport DexSettingsPage from './dexsettings'\nimport MarketMakerPage from './mm'\nimport { RateEncodingFactor, StatusExecuted, hasActiveMatches } from './orderutil'\nimport { getJSON, postJSON, Errors } from './http'\nimport * as ntfn from './notifications'\nimport ws from './ws'\nimport * as intl from './locales'\nimport {\n User,\n SupportedAsset,\n Exchange,\n WalletState,\n BondNote,\n CoreNote,\n OrderNote,\n Market,\n Order,\n Match,\n BalanceNote,\n WalletConfigNote,\n MatchNote,\n ConnEventNote,\n SpotPriceNote,\n BotNote,\n UnitInfo,\n WalletDefinition,\n WalletBalance,\n LogMessage,\n NoteElement,\n BalanceResponse,\n APIResponse,\n RateNote,\n BotReport,\n InFlightOrder\n} from './registry'\n\nconst idel = Doc.idel // = element by id\nconst bind = Doc.bind\nconst unbind = Doc.unbind\n\nconst notificationRoute = 'notify'\nconst noteCacheSize = 100\n\ninterface Page {\n unload (): void\n}\n\ninterface PageClass {\n new (main: HTMLElement, data: any): Page;\n}\n\ninterface CoreNotePlus extends CoreNote {\n el: HTMLElement // Added in app\n}\n\n/* constructors is a map to page constructors. */\nconst constructors: Record = {\n login: LoginPage,\n register: RegistrationPage,\n markets: MarketsPage,\n wallets: WalletsPage,\n settings: SettingsPage,\n orders: OrdersPage,\n order: OrderPage,\n dexsettings: DexSettingsPage,\n mm: MarketMakerPage\n}\n\n// Application is the main javascript web application for the Decred DEX client.\nexport default class Application {\n notes: CoreNotePlus[]\n pokes: CoreNotePlus[]\n user: User\n seedGenTime: number\n commitHash: string\n showPopups: boolean\n loggers: Record\n recorders: Record\n main: HTMLElement\n header: HTMLElement\n headerSpace: HTMLElement\n assets: Record\n exchanges: Record\n walletMap: Record\n fiatRatesMap: Record\n tooltip: HTMLElement\n page: Record\n loadedPage: Page | null\n popupNotes: HTMLElement\n popupTmpl: HTMLElement\n noteReceivers: Record void>[]\n\n constructor () {\n this.notes = []\n this.pokes = []\n this.seedGenTime = 0\n this.commitHash = process.env.COMMITHASH || ''\n this.noteReceivers = []\n this.fiatRatesMap = {}\n this.showPopups = State.fetchLocal(State.popupsLK) === '1'\n\n console.log('Decred DEX Client App, Build', this.commitHash.substring(0, 7))\n\n // Loggers can be enabled by setting a truthy value to the loggerID using\n // enableLogger. Settings are stored across sessions. See docstring for the\n // log method for more info.\n this.loggers = State.fetchLocal(State.loggersLK) || {}\n window.enableLogger = (loggerID, state) => {\n if (state) this.loggers[loggerID] = true\n else delete this.loggers[loggerID]\n State.storeLocal(State.loggersLK, this.loggers)\n return `${loggerID} logger ${state ? 'enabled' : 'disabled'}`\n }\n // Enable logging from anywhere.\n window.log = (loggerID, ...a) => { this.log(loggerID, ...a) }\n\n // Recorders can record log messages, and then save them to file on request.\n const recorderKeys = State.fetchLocal(State.recordersLK) || []\n this.recorders = {}\n for (const loggerID of recorderKeys) {\n console.log('recording', loggerID)\n this.recorders[loggerID] = []\n }\n window.recordLogger = (loggerID, on) => {\n if (on) this.recorders[loggerID] = []\n else delete this.recorders[loggerID]\n State.storeLocal(State.recordersLK, Object.keys(this.recorders))\n return `${loggerID} recorder ${on ? 'enabled' : 'disabled'}`\n }\n window.dumpLogger = loggerID => {\n const record = this.recorders[loggerID]\n if (!record) return `no recorder for logger ${loggerID}`\n const a = document.createElement('a')\n a.href = `data:application/octet-stream;base64,${window.btoa(JSON.stringify(record, null, 4))}`\n a.download = `${loggerID}.json`\n document.body.appendChild(a)\n a.click()\n setTimeout(() => {\n document.body.removeChild(a)\n }, 0)\n }\n\n // use user current locale set by backend\n intl.setLocale()\n }\n\n /**\n * Start the application. This is the only thing done from the index.js entry\n * point. Read the id = main element and attach handlers.\n */\n async start () {\n // Handle back navigation from the browser.\n bind(window, 'popstate', (e: PopStateEvent) => {\n const page = e.state.page\n if (!page && page !== '') return\n this.loadPage(page, e.state.data, true)\n })\n // The main element is the interchangeable part of the page that doesn't\n // include the header. Main should define a data-handler attribute\n // associated with one of the available constructors.\n this.main = idel(document, 'main')\n const handler = this.main.dataset.handler\n // Don't fetch the user until we know what page we're on.\n await this.fetchUser()\n // The application is free to respond with a page that differs from the\n // one requested in the omnibox, e.g. routing though a login page. Set the\n // current URL state based on the actual page.\n const url = new URL(window.location.href)\n if (handlerFromPath(url.pathname) !== handler) {\n url.pathname = `/${handler}`\n url.search = ''\n window.history.replaceState({ page: handler }, '', url)\n }\n // Attach stuff.\n this.attachHeader()\n this.attachCommon(this.header)\n this.attach({})\n // Load recent notifications from Window.localStorage.\n const notes = State.fetchLocal(State.notificationsLK)\n this.setNotes(notes || [])\n // Connect the websocket and register the notification route.\n ws.connect(getSocketURI(), this.reconnected)\n ws.registerRoute(notificationRoute, (note: CoreNote) => {\n this.notify(note)\n })\n }\n\n /*\n * reconnected is called by the websocket client when a reconnection is made.\n */\n reconnected () {\n window.location.reload() // This triggers another websocket disconnect/connect (!)\n // a fetchUser() and loadPage(window.history.state.page) might work\n }\n\n /*\n * Fetch and save the user, which is the primary core state that must be\n * maintained by the Application.\n */\n async fetchUser (): Promise {\n const resp: APIResponse = await getJSON('/api/user')\n if (!this.checkResponse(resp)) return\n const user = (resp as any) as User\n this.seedGenTime = user.seedgentime\n this.user = user\n this.assets = user.assets\n this.exchanges = user.exchanges\n this.walletMap = {}\n this.fiatRatesMap = user.fiatRates\n for (const [assetID, asset] of (Object.entries(user.assets) as [any, SupportedAsset][])) {\n if (asset.wallet) {\n this.walletMap[assetID] = asset.wallet\n }\n }\n\n this.updateMenuItemsDisplay()\n return user\n }\n\n authed () {\n return this.user && this.user.authed\n }\n\n /* Load the page from the server. Insert and bind the DOM. */\n async loadPage (page: string, data?: any, skipPush?: boolean): Promise {\n // Close some menus and tooltips.\n this.tooltip.style.left = '-10000px'\n Doc.hide(this.page.noteBox, this.page.profileBox)\n // Parse the request.\n const url = new URL(`/${page}`, window.location.origin)\n const requestedHandler = handlerFromPath(page)\n // Fetch and parse the page.\n const response = await window.fetch(url.toString())\n if (!response.ok) return false\n const html = await response.text()\n const doc = Doc.noderize(html)\n const main = idel(doc, 'main')\n const delivered = main.dataset.handler\n // Append the request to the page history.\n if (!skipPush) {\n const path = delivered === requestedHandler ? url.toString() : `/${delivered}`\n window.history.pushState({ page: page, data: data }, '', path)\n }\n // Insert page and attach handlers.\n document.title = doc.title\n this.main.replaceWith(main)\n this.main = main\n this.noteReceivers = []\n Doc.empty(this.headerSpace)\n this.attach(data)\n return true\n }\n\n /* attach binds the common handlers and calls the page constructor. */\n attach (data: any) {\n const handlerID = this.main.dataset.handler\n if (!handlerID) {\n console.error('cannot attach to content with no specified handler')\n return\n }\n this.attachCommon(this.main)\n if (this.loadedPage) this.loadedPage.unload()\n const constructor = constructors[handlerID]\n if (constructor) this.loadedPage = new constructor(this.main, data)\n else this.loadedPage = null\n\n // Bind the tooltips.\n this.bindTooltips(this.main)\n }\n\n bindTooltips (ancestor: HTMLElement) {\n ancestor.querySelectorAll('[data-tooltip]').forEach((el: HTMLElement) => {\n bind(el, 'mouseenter', () => {\n this.tooltip.textContent = el.dataset.tooltip || ''\n const lyt = Doc.layoutMetrics(el)\n let left = lyt.centerX - this.tooltip.offsetWidth / 2\n if (left < 0) left = 5\n if (left + this.tooltip.offsetWidth > document.body.offsetWidth) {\n left = document.body.offsetWidth - this.tooltip.offsetWidth - 5\n }\n this.tooltip.style.left = `${left}px`\n this.tooltip.style.top = `${lyt.bodyTop - this.tooltip.offsetHeight - 5}px`\n })\n bind(el, 'mouseleave', () => {\n this.tooltip.style.left = '-10000px'\n })\n })\n }\n\n /* attachHeader attaches the header element, which unlike the main element,\n * isn't replaced during page navigation.\n */\n attachHeader () {\n this.header = idel(document.body, 'header')\n this.headerSpace = Doc.idel(this.header, 'headerSpace')\n this.popupNotes = idel(document.body, 'popupNotes')\n this.popupTmpl = Doc.tmplElement(this.popupNotes, 'note')\n if (this.popupTmpl) this.popupTmpl.remove()\n else console.error('popupTmpl element not found')\n this.tooltip = idel(document.body, 'tooltip')\n const page = this.page = Doc.idDescendants(this.header)\n page.noteTmpl.removeAttribute('id')\n page.noteTmpl.remove()\n page.pokeTmpl.removeAttribute('id')\n page.pokeTmpl.remove()\n page.loader.remove()\n Doc.show(page.loader)\n\n bind(page.noteBell, 'click', async () => {\n Doc.hide(page.pokeList)\n Doc.show(page.noteList)\n this.ackNotes()\n page.noteCat.classList.add('active')\n page.pokeCat.classList.remove('active')\n this.showDropdown(page.noteBell, page.noteBox)\n Doc.hide(page.noteIndicator)\n for (const note of this.notes) {\n if (note.acked) {\n note.el.classList.remove('firstview')\n }\n }\n this.setNoteTimes(page.noteList)\n this.setNoteTimes(page.pokeList)\n this.storeNotes()\n })\n\n bind(page.burgerIcon, 'click', () => {\n Doc.hide(page.logoutErr)\n this.showDropdown(page.burgerIcon, page.profileBox)\n })\n\n bind(page.innerNoteIcon, 'click', () => { Doc.hide(page.noteBox) })\n bind(page.innerBurgerIcon, 'click', () => { Doc.hide(page.profileBox) })\n\n bind(page.profileSignout, 'click', async () => await this.signOut())\n\n bind(page.pokeCat, 'click', () => {\n this.setNoteTimes(page.pokeList)\n page.pokeCat.classList.add('active')\n page.noteCat.classList.remove('active')\n Doc.hide(page.noteList)\n Doc.show(page.pokeList)\n this.ackNotes()\n })\n\n bind(page.noteCat, 'click', () => {\n this.setNoteTimes(page.noteList)\n page.noteCat.classList.add('active')\n page.pokeCat.classList.remove('active')\n Doc.hide(page.pokeList)\n Doc.show(page.noteList)\n this.ackNotes()\n })\n }\n\n /*\n * showDropdown sets the position and visibility of the specified dropdown\n * dialog according to the position of its icon button.\n */\n showDropdown (icon: HTMLElement, dialog: HTMLElement) {\n const ico = icon.getBoundingClientRect()\n Doc.hide(this.page.noteBox, this.page.profileBox)\n Doc.show(dialog)\n dialog.style.right = `${window.innerWidth - ico.left - ico.width + 5}px`\n dialog.style.top = `${ico.top - 4}px`\n\n const hide = (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, dialog)) {\n Doc.hide(dialog)\n unbind(document, 'click', hide)\n if (dialog === this.page.noteBox && Doc.isDisplayed(this.page.noteList)) {\n this.ackNotes()\n }\n }\n }\n bind(document, 'click', hide)\n }\n\n ackNotes () {\n const acks = []\n for (const note of this.notes) {\n if (note.acked) {\n note.el.classList.remove('firstview')\n } else {\n note.acked = true\n if (note.id && note.severity > ntfn.POKE) acks.push(note.id)\n }\n }\n if (acks.length) ws.request('acknotes', acks)\n Doc.hide(this.page.noteIndicator)\n }\n\n setNoteTimes (noteList: HTMLElement) {\n for (const el of (Array.from(noteList.children) as NoteElement[])) {\n Doc.safeSelector(el, 'span.note-time').textContent = Doc.timeSince(el.note.stamp)\n }\n }\n\n /*\n * bindInternalNavigation hijacks navigation by click on any local links that\n * are descendants of ancestor.\n */\n bindInternalNavigation (ancestor: HTMLElement) {\n const pageURL = new URL(window.location.href)\n ancestor.querySelectorAll('a').forEach(a => {\n if (!a.href) return\n const url = new URL(a.href)\n if (url.origin === pageURL.origin) {\n const token = url.pathname.substring(1)\n const params: Record = {}\n if (url.search) {\n url.searchParams.forEach((v, k) => {\n params[k] = v\n })\n }\n Doc.bind(a, 'click', (e: Event) => {\n e.preventDefault()\n this.loadPage(token, params)\n })\n }\n })\n }\n\n /*\n * storeNotes stores the list of notifications in Window.localStorage. The\n * actual stored list is stripped of information not necessary for display.\n */\n storeNotes () {\n State.storeLocal(State.notificationsLK, this.notes.map(n => {\n return {\n subject: n.subject,\n details: n.details,\n severity: n.severity,\n stamp: n.stamp,\n id: n.id,\n acked: n.acked\n }\n }))\n }\n\n /*\n * updateMenuItemsDisplay should be called when the user has signed in or out,\n * and when the user registers a DEX.\n */\n updateMenuItemsDisplay () {\n const { page, user } = this\n if (!page) {\n // initial page load, header elements not yet attached but menu items\n // would already be hidden/displayed as appropriate.\n return\n }\n const authed = user && user.authed\n if (authed) page.profileBox.classList.add('authed')\n else {\n page.profileBox.classList.remove('authed')\n Doc.hide(page.noteBell, page.walletsMenuEntry, page.marketsMenuEntry)\n return\n }\n Doc.show(page.noteBell, page.walletsMenuEntry)\n if (Object.keys(user.exchanges).length > 0) {\n Doc.show(page.marketsMenuEntry)\n } else {\n Doc.hide(page.marketsMenuEntry)\n }\n }\n\n /* attachCommon scans the provided node and handles some common bindings. */\n attachCommon (node: HTMLElement) {\n this.bindInternalNavigation(node)\n }\n\n /*\n * updateBondConfs updates the information for a pending bond.\n */\n updateBondConfs (dexAddr: string, coinID: string, confs: number, assetID: number) {\n const dex = this.exchanges[dexAddr]\n const symbol = this.assets[assetID].symbol\n dex.pendingBonds[coinID] = { confs, assetID, symbol }\n }\n\n updateTier (host: string, tier: number) {\n this.exchanges[host].tier = tier\n }\n\n /*\n * handleBondNote is the handler for the 'bondpost'-type notification, which\n * is used to update the dex tier and registration status.\n */\n handleBondNote (note: BondNote) {\n switch (note.topic) {\n case 'RegUpdate':\n if (note.coinID !== null) { // should never be null for RegUpdate\n this.updateBondConfs(note.dex, note.coinID, note.confirmations, note.asset)\n }\n break\n case 'BondConfirmed':\n if (note.tier !== null) { // should never be null for BondConfirmed\n this.updateTier(note.dex, note.tier)\n }\n break\n default:\n break\n }\n }\n\n /*\n * setNotes sets the current notification cache and populates the notification\n * display.\n */\n setNotes (notes: CoreNote[]) {\n this.log('notes', 'setNotes', notes)\n this.notes = []\n Doc.empty(this.page.noteList)\n for (let i = 0; i < notes.length; i++) {\n this.prependNoteElement(notes[i], true)\n }\n this.storeNotes()\n }\n\n updateUser (note: CoreNote) {\n const { user, assets, walletMap } = this\n if (note.type === 'fiatrateupdate') {\n this.fiatRatesMap = (note as RateNote).fiatRates\n return\n }\n // Some notes can be received before we get a User during login.\n if (!user) return\n switch (note.type) {\n case 'order': {\n const orderNote = note as OrderNote\n const order = orderNote.order\n const mkt = user.exchanges[order.host].markets[order.market]\n const tempID = orderNote.tempID\n\n // Ensure market's inflight orders list is updated.\n if (note.topic === 'AsyncOrderSubmitted') {\n const inFlight = order as InFlightOrder\n inFlight.tempID = tempID\n if (!mkt.inflight) mkt.inflight = [inFlight]\n else mkt.inflight.push(inFlight)\n break\n } else if (note.topic === 'AsyncOrderFailure') {\n mkt.inflight = mkt.inflight.filter(ord => ord.tempID !== tempID)\n break\n } else {\n for (const i in mkt.inflight || []) {\n if (!(mkt.inflight[i].tempID === tempID)) continue\n mkt.inflight = mkt.inflight.filter(ord => ord.tempID !== tempID)\n break\n }\n }\n\n // Updates given order in market's orders list if it finds it.\n // Returns a bool which indicates if order was found.\n const updateOrder = (mkt: Market, ord: Order) => {\n for (const i in mkt.orders || []) {\n if (mkt.orders[i].id === ord.id) {\n mkt.orders[i] = ord\n return true\n }\n }\n return false\n }\n // If the notification order already exists we update it.\n // In case market's orders list is empty or the notification order isn't\n // part of it we add it manually as this means the order was\n // just placed.\n if (!mkt.orders) mkt.orders = [order]\n else if (!updateOrder(mkt, order)) mkt.orders.push(order)\n break\n }\n case 'balance': {\n const n: BalanceNote = note as BalanceNote\n const asset = user.assets[n.assetID]\n // Balance updates can come before the user is fetched after login.\n if (!asset) break\n const w = asset.wallet\n if (w) w.balance = n.balance\n break\n }\n case 'bondpost':\n this.handleBondNote(note as BondNote)\n break\n case 'walletstate':\n case 'walletconfig': {\n // assets can be null if failed to connect to dex server.\n if (!assets) return\n const wallet = (note as WalletConfigNote).wallet\n const asset = assets[wallet.assetID]\n asset.wallet = wallet\n walletMap[wallet.assetID] = wallet\n break\n }\n case 'match': {\n const n = note as MatchNote\n const ord = this.order(n.orderID)\n if (ord) updateMatch(ord, n.match)\n break\n }\n case 'conn': {\n const n = note as ConnEventNote\n const xc = user.exchanges[n.host]\n if (xc) xc.connectionStatus = n.connectionStatus\n break\n }\n case 'spots': {\n const n = note as SpotPriceNote\n const xc = user.exchanges[n.host]\n // Spots can come before the user is fetched after login and before/while the\n // markets page reload when it receives a dex conn note.\n if (!xc || !xc.markets) break\n for (const [mktName, spot] of Object.entries(n.spots)) xc.markets[mktName].spot = spot\n break\n }\n case 'fiatrateupdate': {\n this.fiatRatesMap = (note as RateNote).fiatRates\n break\n }\n case 'bot': {\n const n = note as BotNote\n const [r, bots] = [n.report, this.user.bots]\n const idx = bots.findIndex((report: BotReport) => report.programID === r.programID)\n switch (n.topic) {\n case 'BotRetired':\n if (idx >= 0) bots.splice(idx, 1)\n break\n default:\n if (idx >= 0) bots[idx] = n.report\n else bots.push(n.report)\n }\n }\n }\n }\n\n /*\n * notify is the top-level handler for notifications received from the client.\n * Notifications are propagated to the loadedPage.\n */\n notify (note: CoreNote) {\n // Handle type-specific updates.\n this.log('notes', 'notify', note)\n this.updateUser(note)\n // Inform the page.\n for (const feeder of this.noteReceivers) {\n const f = feeder[note.type]\n if (!f) continue\n try {\n f(note)\n } catch (error) {\n console.error('note feeder error:', error.message ? error.message : error)\n console.log(note)\n console.log(error.stack)\n }\n }\n // Discard data notifications.\n if (note.severity < ntfn.POKE) return\n // Poke notifications have their own display.\n const { popupTmpl, popupNotes, showPopups } = this\n if (showPopups) {\n const span = popupTmpl.cloneNode(true) as HTMLElement\n Doc.tmplElement(span, 'text').textContent = `${note.subject}: ${note.details}`\n const indicator = Doc.tmplElement(span, 'indicator')\n if (note.severity === ntfn.POKE) {\n Doc.hide(indicator)\n } else setSeverityClass(indicator, note.severity)\n popupNotes.appendChild(span)\n // These take up screen space. Only show max 5 at a time.\n while (popupNotes.children.length > 5) popupNotes.removeChild(popupNotes.firstChild as Node)\n setTimeout(async () => {\n await Doc.animate(500, (progress: number) => {\n span.style.opacity = String(1 - progress)\n })\n span.remove()\n }, 6000)\n }\n // Success and higher severity go to the bell dropdown.\n if (note.severity === ntfn.POKE) this.prependPokeElement(note)\n else this.prependNoteElement(note)\n }\n\n /*\n * registerNoteFeeder registers a feeder for core notifications. The feeder\n * will be de-registered when a new page is loaded.\n */\n registerNoteFeeder (receivers: Record void>) {\n this.noteReceivers.push(receivers)\n }\n\n /*\n * log prints to the console if a logger has been enabled. Loggers are created\n * implicitly by passing a loggerID to log. i.e. you don't create a logger,\n * you just log to it. Loggers are enabled by invoking a global function,\n * enableLogger(loggerID, onOffBoolean), from the browser's js console. Your\n * choices are stored across sessions. Some common and useful loggers are\n * listed below, but this list is not meant to be comprehensive.\n *\n * LoggerID Description\n * -------- -----------\n * notes Notifications of all levels.\n * book Order book feed.\n * ws.........Websocket connection status changes.\n */\n log (loggerID: string, ...msg: any) {\n if (this.loggers[loggerID]) console.log(`${nowString()}[${loggerID}]:`, ...msg)\n if (this.recorders[loggerID]) {\n this.recorders[loggerID].push({\n time: nowString(),\n msg: msg\n })\n }\n }\n\n prependPokeElement (cn: CoreNote) {\n const [el, note] = this.makePoke(cn)\n this.pokes.push(note)\n while (this.pokes.length > noteCacheSize) this.pokes.shift()\n this.prependListElement(this.page.pokeList, note, el)\n }\n\n prependNoteElement (cn: CoreNote, skipSave?: boolean) {\n const [el, note] = this.makeNote(cn)\n this.notes.push(note)\n while (this.notes.length > noteCacheSize) this.notes.shift()\n const noteList = this.page.noteList\n this.prependListElement(noteList, note, el)\n if (!skipSave) this.storeNotes()\n // Set the indicator color.\n if (this.notes.length === 0 || (Doc.isDisplayed(this.page.noteBox) && Doc.isDisplayed(noteList))) return\n let unacked = 0\n const severity = this.notes.reduce((s, note) => {\n if (!note.acked) unacked++\n if (!note.acked && note.severity > s) return note.severity\n return s\n }, ntfn.IGNORE)\n const ni = this.page.noteIndicator\n setSeverityClass(ni, severity)\n if (unacked) {\n ni.textContent = String((unacked > noteCacheSize - 1) ? `${noteCacheSize - 1}+` : unacked)\n Doc.show(ni)\n } else Doc.hide(ni)\n }\n\n prependListElement (noteList: HTMLElement, note: CoreNotePlus, el: NoteElement) {\n el.note = note\n noteList.prepend(el)\n while (noteList.children.length > noteCacheSize) noteList.removeChild(noteList.lastChild as Node)\n this.setNoteTimes(noteList)\n }\n\n /*\n * makeNote constructs a single notification element for the drop-down\n * notification list.\n */\n makeNote (note: CoreNote): [NoteElement, CoreNotePlus] {\n const el = this.page.noteTmpl.cloneNode(true) as NoteElement\n if (note.severity > ntfn.POKE) {\n const cls = note.severity === ntfn.SUCCESS ? 'good' : note.severity === ntfn.WARNING ? 'warn' : 'bad'\n Doc.safeSelector(el, 'div.note-indicator').classList.add(cls)\n }\n\n Doc.safeSelector(el, 'div.note-subject').textContent = note.subject\n Doc.safeSelector(el, 'div.note-details').textContent = note.details\n const np: CoreNotePlus = { el, ...note }\n return [el, np]\n }\n\n makePoke (note: CoreNote): [NoteElement, CoreNotePlus] {\n const el = this.page.pokeTmpl.cloneNode(true) as NoteElement\n const d = new Date(note.stamp)\n Doc.tmplElement(el, 'dateTime').textContent = `${d.toLocaleDateString()}, ${d.toLocaleTimeString()}`\n Doc.tmplElement(el, 'details').textContent = `${note.subject}: ${note.details}`\n const np: CoreNotePlus = { el, ...note }\n return [el, np]\n }\n\n /*\n * loading appends the loader to the specified element and displays the\n * loading icon. The loader will block all interaction with the specified\n * element until Application.loaded is called.\n */\n loading (el: HTMLElement): () => void {\n const loader = this.page.loader.cloneNode(true) as HTMLElement\n el.appendChild(loader)\n return () => { loader.remove() }\n }\n\n /* orders retrieves a list of orders for the specified dex and market\n * including inflight orders.\n */\n orders (host: string, mktID: string): Order[] {\n let orders: Order[] = []\n const mkt = this.user.exchanges[host].markets[mktID]\n if (mkt.orders) orders = orders.concat(mkt.orders)\n if (mkt.inflight) orders = orders.concat(mkt.inflight)\n return orders\n }\n\n /*\n * haveActiveOrders returns whether or not there are active orders involving a\n * certain asset.\n */\n haveActiveOrders (assetID: number): boolean {\n for (const xc of Object.values(this.user.exchanges)) {\n if (!xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n if (!market.orders) continue\n for (const ord of market.orders) {\n if ((ord.baseID === assetID || ord.quoteID === assetID) &&\n (ord.status < StatusExecuted || hasActiveMatches(ord))) return true\n }\n }\n }\n return false\n }\n\n /* order attempts to locate an order by order ID. */\n order (oid: string): Order | null {\n for (const xc of Object.values(this.user.exchanges)) {\n if (!xc || !xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n if (!market.orders) continue\n for (const ord of market.orders) {\n if (ord.id === oid) return ord\n }\n }\n }\n return null\n }\n\n /*\n * canAccelerateOrder returns true if the \"from\" wallet of the order\n * supports acceleration, and if the order has unconfirmed swap\n * transactions.\n */\n canAccelerateOrder (order: Order): boolean {\n const walletTraitAccelerator = 1 << 4\n let fromAssetID\n if (order.sell) fromAssetID = order.baseID\n else fromAssetID = order.quoteID\n const wallet = this.walletMap[fromAssetID]\n if (!wallet || !(wallet.traits & walletTraitAccelerator)) return false\n if (order.matches) {\n for (let i = 0; i < order.matches.length; i++) {\n const match = order.matches[i]\n if (match.swap && match.swap.confs && match.swap.confs.count === 0) {\n return true\n }\n }\n }\n return false\n }\n\n /*\n * unitInfo fetches unit info [dex.UnitInfo] for the asset. If xc\n * [core.Exchange] is provided, and this is not a SupportedAsset, the UnitInfo\n * sent from the exchange's assets map [dex.Asset] will be used.\n */\n unitInfo (assetID: number, xc?: Exchange): UnitInfo {\n const supportedAsset = this.assets[assetID]\n if (supportedAsset) return supportedAsset.unitInfo\n if (!xc || !xc.assets) {\n throw Error(intl.prep(intl.ID_UNSUPPORTED_ASSET_INFO_ERR_MSG, { assetID: `${assetID}` }))\n }\n return xc.assets[assetID].unitInfo\n }\n\n /* conventionalRate converts the encoded atomic rate to a conventional rate */\n conventionalRate (baseID: number, quoteID: number, encRate: number, xc?: Exchange): number {\n const [b, q] = [this.unitInfo(baseID, xc), this.unitInfo(quoteID, xc)]\n\n const r = b.conventional.conversionFactor / q.conventional.conversionFactor\n return encRate * r / RateEncodingFactor\n }\n\n walletDefinition (assetID: number, walletType: string): WalletDefinition {\n const asset = this.assets[assetID]\n if (asset.token) return asset.token.definition\n if (!asset.info) throw Error('where\\'s the wallet info?')\n if (walletType === '') return asset.info.availablewallets[asset.info.emptyidx]\n return asset.info.availablewallets.filter(def => def.type === walletType)[0]\n }\n\n currentWalletDefinition (assetID: number): WalletDefinition {\n const asset = this.assets[assetID]\n if (asset.token) {\n return asset.token.definition\n }\n return this.walletDefinition(assetID, this.assets[assetID].wallet.type)\n }\n\n /*\n * fetchBalance requests a balance update from the API. The API response does\n * include the balance, but we're ignoring it, since a balance update\n * notification is received via the Application anyways.\n */\n async fetchBalance (assetID: number): Promise {\n const res: BalanceResponse = await postJSON('/api/balance', { assetID: assetID })\n if (!this.checkResponse(res)) {\n throw new Error(`failed to fetch balance for asset ID ${assetID}`)\n }\n return res.balance\n }\n\n /*\n * checkResponse checks the response object as returned from the functions in\n * the http module. If the response indicates that the request failed, it\n * returns false, otherwise, true.\n */\n checkResponse (resp: APIResponse): boolean {\n return (resp.requestSuccessful && resp.ok)\n }\n\n /**\n * signOut call to /api/logout, if response with no errors occurred remove auth\n * and other privacy-critical cookies/locals and reload the page, otherwise\n * show a notification.\n */\n async signOut () {\n const res = await postJSON('/api/logout')\n if (!this.checkResponse(res)) {\n if (res.code === Errors.activeOrdersErr) {\n this.page.logoutErr.textContent = intl.prep(intl.ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG)\n } else {\n this.page.logoutErr.textContent = res.msg\n }\n Doc.show(this.page.logoutErr)\n return\n }\n State.removeCookie(State.authCK)\n State.removeCookie(State.pwKeyCK)\n State.removeLocal(State.notificationsLK)\n window.location.href = '/login'\n }\n}\n\n/* getSocketURI returns the websocket URI for the client. */\nfunction getSocketURI (): string {\n const protocol = (window.location.protocol === 'https:') ? 'wss' : 'ws'\n return `${protocol}://${window.location.host}/ws`\n}\n\n/*\n * severityClassMap maps a notification severity level to a CSS class that\n * assigns a background color.\n */\nconst severityClassMap: Record = {\n [ntfn.SUCCESS]: 'good',\n [ntfn.ERROR]: 'bad',\n [ntfn.WARNING]: 'warn'\n}\n\n/* handlerFromPath parses the handler name from the path. */\nfunction handlerFromPath (path: string): string {\n return path.replace(/^\\//, '').split('/')[0].split('?')[0].split('#')[0]\n}\n\n/* nowString creates a string formatted like HH:MM:SS.xxx */\nfunction nowString (): string {\n const stamp = new Date()\n const h = stamp.getHours().toString().padStart(2, '0')\n const m = stamp.getMinutes().toString().padStart(2, '0')\n const s = stamp.getSeconds().toString().padStart(2, '0')\n const ms = stamp.getMilliseconds().toString().padStart(3, '0')\n return `${h}:${m}:${s}.${ms}`\n}\n\nfunction setSeverityClass (el: HTMLElement, severity: number) {\n el.classList.remove('bad', 'warn', 'good')\n el.classList.add(severityClassMap[severity])\n}\n\n/* updateMatch updates the match in or adds the match to the order. */\nfunction updateMatch (order: Order, match: Match) {\n for (const i in order.matches) {\n const m = order.matches[i]\n if (m.matchID === match.matchID) {\n order.matches[i] = match\n return\n }\n }\n order.matches = order.matches || []\n order.matches.push(match)\n}\n","import { CoreNote } from './registry'\n\nexport const IGNORE = 0\nexport const DATA = 1\nexport const POKE = 2\nexport const SUCCESS = 3\nexport const WARNING = 4\nexport const ERROR = 5\n\n/*\n * make constructs a new notification. The notification structure is a mirror of\n * the structure of notifications sent from the web server.\n * NOTE: I'm hoping to make this function obsolete, since errors generated in\n * javascript should usually be displayed/cached somewhere better. For example,\n * if the error is generated during submission of a form, the error should be\n * displayed on or near the form itself, not in the notifications.\n */\nexport function make (subject: string, details: string, severity: number): CoreNote {\n return {\n subject: subject,\n details: details,\n severity: severity,\n stamp: new Date().getTime(),\n acked: false,\n type: 'internal',\n topic: 'internal',\n id: ''\n }\n}\n","import Application from './js/app'\nimport { registerApplication } from './js/registry'\nimport './css/application.scss'\n\nconst app = new Application()\nregisterApplication(app)\napp.start()\n"],"names":["_typeof","_regeneratorRuntime","module","exports","__esModule","Op","Object","prototype","hasOwn","hasOwnProperty","defineProperty","obj","key","desc","value","$Symbol","Symbol","iteratorSymbol","iterator","asyncIteratorSymbol","asyncIterator","toStringTagSymbol","toStringTag","define","enumerable","configurable","writable","err","wrap","innerFn","outerFn","self","tryLocsList","protoGenerator","Generator","generator","create","context","Context","makeInvokeMethod","tryCatch","fn","arg","type","call","ContinueSentinel","GeneratorFunction","GeneratorFunctionPrototype","IteratorPrototype","this","getProto","getPrototypeOf","NativeIteratorPrototype","values","Gp","defineIteratorMethods","forEach","method","_invoke","AsyncIterator","PromiseImpl","invoke","resolve","reject","record","result","__await","then","unwrapped","error","previousPromise","callInvokeWithMethodAndArg","state","Error","undefined","done","delegate","delegateResult","maybeInvokeDelegate","sent","_sent","dispatchException","abrupt","methodName","TypeError","info","resultName","next","nextLoc","pushTryEntry","locs","entry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","iterable","iteratorMethod","isNaN","length","i","doneResult","displayName","isGeneratorFunction","genFun","ctor","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","iter","keys","val","object","reverse","pop","skipTempReset","prev","charAt","slice","stop","rootRecord","rval","exception","handle","loc","caught","hasCatch","hasFinally","finallyEntry","complete","finish","thrown","delegateYield","runtime","regeneratorRuntime","accidentalStrictMode","globalThis","Function","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","d","a","definition","o","get","prop","_arrayLikeToArray","arr","len","arr2","Array","_unsupportedIterableToArray","minLen","toString","from","test","_slicedToArray","isArray","_i","_s","_e","_x","_r","_arr","_n","_d","asyncGeneratorStep","gen","_next","_throw","_asyncToGenerator","args","arguments","apply","_classCallCheck","instance","Constructor","_toPropertyKey","input","hint","prim","toPrimitive","res","String","_defineProperties","target","props","descriptor","_createClass","protoProps","staticProps","_defineProperty","locale","ID_NO_PASS_ERROR_MSG","ID_NO_APP_PASS_ERROR_MSG","ID_SET_BUTTON_BUY","ID_SET_BUTTON_SELL","ID_OFF","ID_READY","ID_NO_WALLET","ID_DISABLED_MSG","ID_WALLET_SYNC_PROGRESS","ID_HIDE_ADDITIONAL_SETTINGS","ID_SHOW_ADDITIONAL_SETTINGS","ID_BUY","ID_SELL","ID_NOT_SUPPORTED","ID_VERSION_NOT_SUPPORTED","ID_CONNECTION_FAILED","ID_ORDER_PREVIEW","ID_CALCULATING","ID_ESTIMATE_UNAVAILABLE","ID_NO_ZERO_RATE","ID_NO_ZERO_QUANTITY","ID_TRADE","ID_NO_ASSET_WALLET","ID_EXECUTED","ID_BOOKED","ID_CANCELING","ID_PASSWORD_NOT_MATCH","ID_ACCT_UNDEFINED","ID_KEEP_WALLET_PASS","ID_NEW_WALLET_PASS","ID_LOT","ID_LOTS","ID_UNKNOWN","ID_EPOCH","ID_ORDER_SUBMITTING","ID_SETTLING","ID_NO_MATCH","ID_CANCELED","ID_REVOKED","ID_WAITING_FOR_CONFS","ID_NONE_SELECTED","ID_REGISTRATION_FEE_SUCCESS","ID_API_ERROR","ID_ADD","ID_CREATE","ID_SETUP_WALLET","ID_WALLET_READY","ID_CHANGE_WALLET_TYPE","ID_KEEP_WALLET_TYPE","WALLET_READY","WALLET_PENDING","SETUP_NEEDED","ID_SEND_SUCCESS","ID_RECONFIG_SUCCESS","ID_RESCAN_STARTED","ID_NEW_WALLET_SUCCESS","ID_WALLET_UNLOCKED","ID_SELLING","ID_BUYING","ID_WALLET_DISABLED_MSG","ID_WALLET_ENABLED_MSG","ID_ACTIVE_ORDERS_ERR_MSG","ID_AVAILABLE","ID_LOCKED","ID_IMMATURE","ID_FEE_BALANCE","ID_CANDLES_LOADING","ID_DEPTH_LOADING","ID_INVALID_ADDRESS_MSG","ID_TXFEE_ERR_MSG","ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG","ID_INVALID_DATE_ERR_MSG","ID_NO_ARCHIVED_RECORDS","ID_DELETE_ARCHIVED_RECORDS_RESULT","ID_ARCHIVED_RECORDS_PATH","ID_DEFAULT","ID_ADDED","ID_DISCOVERED","ID_UNSUPPORTED_ASSET_INFO_ERR_MSG","ID_LIMIT_ORDER","ID_LIMIT_ORDER_IMMEDIATE_TIF","ID_MARKET_ORDER","ID_MATCH_STATUS_NEWLY_MATCHED","ID_MATCH_STATUS_MAKER_SWAP_CAST","ID_MATCH_STATUS_TAKER_SWAP_CAST","ID_MATCH_STATUS_MAKER_REDEEMED","ID_MATCH_STATUS_REDEMPTION_SENT","ID_MATCH_STATUS_REDEMPTION_CONFIRMED","ID_MATCH_STATUS_REVOKED","ID_MATCH_STATUS_REFUNDED","ID_MATCH_STATUS_REFUND_PENDING","ID_MATCH_STATUS_REDEEM_PENDING","ID_MATCH_STATUS_COMPLETE","ID_TAKER_FOUND_MAKER_REDEMPTION","ID_OPEN_WALLET_ERR_MSG","ID_ORDER_ACCELERATION_FEE_ERR_MSG","ID_ORDER_ACCELERATION_ERR_MSG","ID_CONNECTED","ID_DISCONNECTED","ID_INVALID_CERTIFICATE","ID_CONFIRMATIONS","ID_TAKER","ID_MAKER","ID_EMPTY_DEX_ADDRESS_MSG","ID_SELECT_WALLET_FOR_FEE_PAYMENT","ID_UNAVAILABLE","ID_WALLET_SYNC_FINISHING_UP","enUS","ptBR","zhCN","plPL","deDE","ar","localesMap","defaultLocale","prep","k","expression","replace","_","stringTemplateParser","window","localeDiscrepancies","ref","entries","lang","dict","s","console","log","parser","DOMParser","BipIDs","BipSymbols","intFormatter","Intl","NumberFormat","navigator","languages","threeSigFigs","minimumSignificantDigits","maximumSignificantDigits","fiveSigFigs","decimalFormatters","fullPrecisionFormatters","fullPrecisionFormatter","prec","formatter","formatters","min","max","fmt","minimumFractionDigits","maximumFractionDigits","convertToConventional","v","unitInfo","f","conventional","conversionFactor","Math","round","log10","Doc","el","id","querySelector","ev","addEventListener","removeEventListener","html","parseFromString","e","contains","rect","getBoundingClientRect","pageX","left","right","pageY","top","bottom","box","docEl","document","documentElement","scrollTop","scrollLeft","w","offsetWidth","h","offsetHeight","bodyTop","bodyLeft","width","height","centerX","centerY","parent","kid","parentMetrics","layoutMetrics","kidMetrics","els","firstChild","removeChild","ancestor","empty","kids","appendChild","classList","add","remove","vis","show","hide","duration","easingAlgo","Animation","wait","querySelectorAll","children","warn","createElement","applySelector","vAtomic","Number","isInteger","format","decimalFormatter","rate","intl","symbol","indexOf","substring","assetID","logoPath","parts","split","assetSymbol","textContent","toUpperCase","span","tmpls","tmpl","removeAttribute","dataset","t","formatDuration","Date","getTime","dur","y","mo","m","seconds","floor","count","timeMod","aYear","aMonth","aDay","anHour","aMinute","inputFields","inputField","preventDefault","msg","thread","run","easer","Easing","linear","start","end","Forever","MAX_SAFE_INTEGER","range","frameDuration","now","endAnimation","runCompletionFunction","sleep","easeIn","easeOut","easeInHard","easeOutHard","easeOutElastic","c4","PI","pow","sin","WalletIcons","stateElement","icons","sleeping","locked","unlocked","nowallet","syncing","nopeers","disabled","status","tooltip","wallet","syncIcon","running","peerCount","synced","syncProgress","toFixed","setSyncing","open","ms","setTimeout","State","cname","cvalue","setTime","expires","toUTCString","cookie","trim","cKey","filter","item","includes","darkModeCK","getCookie","pwKeyCK","localStorage","setItem","JSON","stringify","getItem","parse","removeItem","_assertThisInitialized","ReferenceError","_setPrototypeOf","p","bind","_inherits","subClass","superClass","_possibleConstructorReturn","_getPrototypeOf","setCookie","fetchLocal","popupsLK","storeLocal","leftMarketDockLK","Errors","ConnectionStatus","PeerSource","application","orderOptTmpl","booleanOptTmpl","rangeOptTmpl","BasePage","requestJSON","addr","reqBody","fetch","headers","Headers","body","response","json","requestSuccessful","text","postJSON","data","getJSON","_toConsumableArray","app","setOptionTemplates","page","Option","opt","report","node","cloneNode","parseTemplate","optName","displayname","description","chainIcon","src","on","enable","toggle","stopPropagation","disable","BooleanOption","changed","cfg","control","controls","reason","store","XYRangeOption","xyRange","setVal","x","handler","XYRangeHandler","setValue","initVal","updated","selected","roundY","Boolean","slider","rangeX","rangeY","normalizeX","setConfig","newCfg","rangeLblStart","label","rangeLblEnd","xUnit","yUnit","r","scrollingX","accept","clickOutX","xInput","xx","parseFloat","clamp","unbind","focus","clickOutY","yInput","yy","button","startX","clientWidth","startLeft","trackMouse","ee","mouseUp","skipUpdate","style","RateEncodingFactor","sellString","ord","sell","toLowerCase","toLocaleLowerCase","typeString","tif","isMarketBuy","hasActiveMatches","order","matches","active","statusString","isLive","cancelling","filled","qty","reduce","match","isCancel","settled","side","baseToQuote","base","revokedMatchStatus","matchStatus","PIPI","plusChar","fromCharCode","minusChar","darkTheme","axisLabel","gridBorder","gridLines","gapLine","zoom","zoomHover","sellLine","buyLine","sellFill","buyFill","crosshairs","legendFill","legendText","lightTheme","Chart","reporters","theme","isDark","canvas","visible","ctx","getContext","textAlign","textBaseline","mousePos","clientX","clientY","draw","ResizeObserver","resize","observe","wheelLimiter","wheel","click","setVis","visibilityState","renderScheduled","unattachers","clearRect","render","deltaY","clientHeight","plotExtents","Extents","xLblExtents","yLblExtents","plotRegion","Region","xRegion","yRegion","requestAnimationFrame","bigger","u","fontSize","font","fillStyle","labels","minX","maxX","unitLines","extents","plot","tools","applyLabelStyle","lastX","unitCenter","lbls","lbl","fillText","txt","lineWidth","strokeStyle","line","region","minY","maxY","unit","lastY","step","valFmt","yLabels","makeLabels","dataExtents","yAxisWidth","widest","plotYLabels","beginPath","dataCoords","moveTo","lineTo","stroke","DepthChart","resized","clicked","zoomed","zoomLevel","lines","markers","buys","sells","setZoomBttns","zoomInBttn","zoomOutBttn","book","wheeled","translator","unx","lotSize","rateStep","baseUnitInfo","quoteUnitInfo","qFactor","bFactor","baseUnit","quoteUnit","gap","midGap","gapWidth","minZoom","clear","halfWindow","high","low","buyMarkers","sellMarkers","sort","b","buyDepth","buyEpoch","sellDepth","sellEpoch","volumeReport","buyBase","buyQuote","sellBase","sellQuote","sum","epochSum","epoch","floatCompare","shift","buySum","last","epochBuySum","sellSum","epochSellSum","growthFactor","doYLabels","xLabels","plotXLabels","mouseData","drawFrame","formatLabelValue","topCenterX","midX","topCenterY","zoomPct","xRange","zoomText","measureText","bttnLeft","bttnTop","bttnSize","setExtents","hover","midY","drawLine","color","save","setLineDash","restore","tolerance","hoverMarkers","marker","hovered","withinTolerance","size","tip","sqrt","closePath","fill","dataX","evalSide","trigger","ptX","dotColor","bestDepth","pt","depth","drawDepth","radius","arc","dot","volume","mouse","firstPt","globalAlpha","bestGapBuy","bestGapSell","CandleChart","numToShow","ext","candleExtents","yRange","candleRegion","volumeExtents","volumeRegion","resizeTimer","clearTimeout","idx","zoomLevels","candles","candleWidth","allCandles","c","truncate","endStamp","paddedStart","paddedWidth","first","highRate","lowRate","matchVolume","highVol","market","ratestep","rFactor","rateConversionFactor","quotesymbol","screenW","spacingGuess","diff","tick","zoneOffset","getTimezoneOffset","dayStamp","lastDay","lastYear","pts","months","getMonth","getDate","getHours","getMinutes","padStart","year","getFullYear","makeCandleTimeLabels","mouseCandle","selectedStartStamp","fillRect","yExt","rangeTxt","toLocaleString","rangeWidth","xExt","rectArgs","rangeHeight","strokeRect","volDataExtents","startRate","endRate","cx","maxCandles","Wave","opts","zIndex","random","colorShift","amplitudes","ks","speeds","phases","single","angularX","angularTime","cos","ani","drawValues","cw","ch","l","message","ypad","halfH","msgRegion","hsl","bg","backgroundColor","getComputedStyle","getPropertyValue","lineCap","grad","createLinearGradient","addColorStop","prog","xMin","xMax","yMin","yMax","screenMinX","screenMaxY","screenH","xFactor","yFactor","uny","drawFunc","skipMask","clip","tx","ty","transform","tickGuess","absMax","abs","sigFigs","toPrecision","unitW","x0","y0","x1","y1","skipStroke","labelSpecs","NewWalletForm","form","success","pwCache","backFunc","pwHiders","refresh","goBack","walletTabTmpl","subform","WalletConfigForm","walletSettings","submitAdd","submit","oneBttn","registerNoteFeeder","walletstate","note","reportWalletState","createwallet","reportCreationUpdate","parentSyncer","createUpdater","passwordIsCached","pw","walletType","parentForm","createForm","pass","newWalletPass","config","map","appPass","mainForm","newWalletErr","current","asset","parentAsset","selectedDef","createWallet","checkResponse","setError","runParentSync","parentSyncPct","parentName","parentLogo","childName","childLogo","parentSyncing","syncParent","assets","topic","subject","details","parseAsset","tabs","walletTypeTabs","winfo","assetName","header","assetLogo","pinfo","walletDefs","availablewallets","wDef","tab","update","bindTooltips","walletCreationPending","token","user","parentID","walletDef","appPwCached","auth","oneBttnBox","newWalletPassBox","configOpts","configopts","isBirthdayConfig","seedGenTime","toUnixDate","containsRequired","required","noWalletPWNeeded","seeded","noauth","parentAndTokenOpts","regAsset","tokenOpts","tokenOptsCopy","dynamicOpts","defaultSettings","walletSettingsHeader","fileSelector","loadDefaults","errMsg","configpath","configID","loaded","loading","setLoadedConfig","repeatableCounter","sectionize","configElements","allSettings","tmplElement","textInputTmpl","dateInputTmpl","checkboxTmpl","repeatableTmpl","fileInput","showOther","showIcon","hideIcon","showHideMsg","otherSettings","loadedSettingsMsg","loadedSettings","defaultSettingsMsg","fileInputChanged","setOtherSettingsViz","files","configtext","append","reorder","defaultOpts","loadedOpts","insertAfter","elID","isboolean","isdate","repeatable","addOpt","configKey","safeSelector","htmlFor","prepend","logo","Image","logoPathFromID","after","noecho","autocomplete","checked","getMinMaxVal","minMax","dateToString","date","disablewhenactive","assetHasActiveOrders","activeOrders","defaultedOpts","finds","handledRepeatables","removes","vals","firstVal","newEl","parentElement","parseInt","splice","minDate","MIN_SAFE_INTEGER","maxDate","inputs","ConfirmRegistrationForm","certFile","bondStrengthField","bondAssetID","ui","bondAsset","xc","bondAssets","bondAmt","formatCoinValue","totalBondAmount","amount","submitForm","passBox","host","bondUnit","singleBondAmount","animate","opacity","offset","regErr","innerText","cert","dexAddr","postBondForm","bond","FeeAssetSelectionForm","cleanTemplates","marketTmpl","assetTmpl","walletCreated","assetTmpls","allMarkets","cFactor","marketNode","mkt","excludeIcon","baseAsset","baseid","quoteAsset","quoteid","excludeBase","otherSymbol","otherLogo","parentNode","insertBefore","nextSibling","baseSymbol","quoteSymbol","baseName","replaceWith","symbolize","quoteName","lotsize","lotSizeSymbol","spot","quoteLotSize","quoteLot","OrderUtil","assetNode","fee","feeAmt","feeSymbol","confs","setReadyMessage","ready","markets","fader","setExchange","how","regAssetElements","allmkts","marginTop","paddingTop","WalletWaitForm","progressCache","progressed","funded","balance","reportBalance","bondFeeBuffer","parentAssetSynced","depoAddr","address","syncUncheck","syncCheck","balUncheck","balCheck","syncRemainBox","balanceBox","totalForBond","sendEnough","txFeeBox","sendEnoughForToken","txFeeBalanceBox","sendEnoughWithEst","txFee","parentFees","tokenFees","txFeeUnit","parentUnit","parentBalUnit","parentBal","available","syncSpinner","progress","reportProgress","avail","parentAvail","syncRemaining","syncFinishingUp","cache","stamp","progDelta","progRate","toGoTime","syncRemain","UnlockWalletForm","idDescendants","submitUnlock","currentAsset","uwAssetLogo","uwAssetName","uwAppPass","unlockErr","uwAppPassBox","submitUnlockDiv","AccelerateOrderForm","accelerateSubmit","submitEarlyConfirm","sendAccelerateRequest","earlyAcceleration","recentAccelerationTime","timePast","recentSwapTime","wasAcceleration","recentAccelerationMsg","recentSwapMsg","configureAccelerationDiv","accelerateErr","earlyAccelerationDiv","req","acceleratePass","orderID","newRate","acceleratedRate","accelerateMainDiv","accelerateTxID","txID","preAccelerateErr","accelerateMsgDiv","accelerateSuccess","displayEarlyAccelerationMsg","feeEstimateDiv","preAccelerate","currencyUnit","suggestedRange","accelerateAvgFeeRate","swapRate","accelerateCurrentFeeRate","suggestedRate","updateRate","newY","rangeHandler","updateAccelerationEstimate","sliderContainer","feeRateEstimate","baseID","quoteID","feeEstimate","DEXAddressForm","dexToUpdate","selectedCert","skipRegistration","showOrHidePWBox","onCertFileChange","removeCert","clearCertFile","addCert","showCustom","customBox","knownExchanges","knownXCs","div","checkDEX","appPW","addDexHdr","skipRegistrationBox","updateDexHdr","pickServerMsg","addCustomMsg","passwordRequired","appPWBox","isDisplayed","endpoint","newHost","oldHost","needCert","paid","pendingBonds","fetchUser","loadPage","DiscoverAccountForm","dexHost","LoginForm","headerTxt","login","handleLoginNote","loginMsg","idel","rememberPass","notes","setNotes","DepositAddress","newDepAddrBttn","newDepositAddress","copyAddressBtn","copyAddress","depositErr","depositLogo","walletMap","depositName","depositAddress","qrcode","traits","clipboard","writeText","copyAlert","animationLength","slideSwap","form1","form2","submitBttn","wrapper","dateApplyOffset","toISOString","RegistrationPage","dexAddrForm","discoverAcctForm","bindForm","appPWForm","appPWSubmit","setAppPass","showSeedRestore","seedRestore","loginForm","newWalletForm","newWalletCreated","animateRegAsset","requestFeepayment","regAssetForm","confirmRegisterForm","setAsset","currentDEX","animateConfirmForm","getBondsFeeBuffer","bondsFeeBuffer","walletWaitForm","setWallet","walletWait","confirmRegForm","registerDEXSuccess","currentForm","forms","authed","oldForm","feeBuffer","appPWErrMsg","pwAgain","appPWAgain","seed","seedInput","updateMenuItemsDisplay","LoginPage","loggedIn","WalletsPage","restoreInfoCard","connectedIconTmpl","disconnectedIconTmpl","removeIconTmpl","closePopups","cancelForce","selectedAssetID","iconSelectTmpl","balanceDetailRow","recentOrderTmpl","showNewWallet","connectBttn","doConnect","send","showSendForm","receive","showDeposit","unlockBttn","openWallet","lockBttn","lock","reconfigureBttn","showReconfig","rescanWallet","fmtParams","assetUpdated","sortAssetButtons","reconfigForm","reconfigInputs","unlockForm","unlockWalletForm","openWalletSuccess","sendForm","submitSendForm","stepSend","vSendForm","vSend","vCancelSend","cancelSend","submitReconfig","reconfig","mouseInElement","keyup","downloadLogs","exportWallet","displayExportWalletAuth","recoverWallet","showRecoverWallet","exportWalletAuth","exportWalletAuthSubmit","recoverWalletConfirm","recoverWalletSubmit","confirmForce","confirmForceSubmit","disableWallet","showToggleWalletStatus","enableWallet","toggleWalletStatusConfirm","toggleWalletStatusSubmit","toggleWalletStatus","managePeers","showManagePeersForm","addPeerSubmit","submitAddPeer","depositAddrForm","deposit","walletBal","populateMaxSend","sendAmt","amt","showFiatValue","sendValue","maxSend","sendAddr","validAddr","validateSendAddress","showChangePW","changeWalletPW","setPWSettingViz","changeWalletTypeSelect","changeWalletType","showChangeType","isHidden","changeTypeHideIcon","changeTypeShowIcon","changeTypeMsg","fiatrateupdate","handleRatesNote","handleBalanceNote","handleWalletStateNote","walletconfig","handleCreateWalletNote","selectedAsset","assetIDStr","selectedAssetLK","setSelectedAsset","animation","vSendErr","sendErr","vSendEstimates","txFeeNotAvailable","subtract","subtractCheckBox","showFormError","txfee","ok","validaddress","vSendSymbol","vSendLogo","feeUI","vSendFee","formatFullPrecision","vSendFeeFiat","vSendDestinationAmt","vTotalSend","vTotalSendFiat","vSendAddr","bal","balanceAfterSend","balanceAfterSendFiat","approxSign","totalSend","showForm","resp","changePW","switchPWMsg","peerSpinner","managePeersErr","peersTableBody","peers","source","defaultText","addedText","discoveredText","peer","connectionIcon","row","peerTableRow","WalletDefault","UserAdded","Discovered","connected","removeIcon","spinUntilPeersUpdate","updateWalletPeersTable","managePeersForm","addPeerInput","toggleWalletStatusErr","walletStatusDisable","disableWalletMsg","walletStatusEnable","enableWalletMsg","code","activeOrdersErr","successMsg","focuser","displayed","successMessage","checkmarkForm","checkmark","startR","startG","startB","diffR","diffG","diffB","defaultsLoaded","assetButtons","assetSelect","sortedAssets","localeCompare","bttn","updateAssetButton","fiat","noWallet","img","totalBalance","immature","fiatRatesMap","formatFiatConversion","updateDisplayedAsset","showAvailableMarkets","showRecentActivity","fiatBalanceBox","createWalletBox","walletDetails","sendReceive","connectBttnBox","statusLocked","statusReady","statusOff","unlockBttnBox","lockBttnBox","peerCountBox","syncProgressBox","statusDisabled","updateDisplayedAssetBalance","walletDefinition","defs","zerothOpts","assetIsConfigurable","passwordWrapper","haveActiveOrders","encrypted","walletDetailsBox","totalLocked","contractlocked","bondlocked","balanceUnit","fiatBalance","firstOther","balanceDetailBox","addSubBalance","category","subBalance","sortedCats","other","exchanges","spotVolume","vol24","hostA","mktA","hostB","mktB","availableMarkets","basesymbol","marketRow","baseLogo","quoteLogo","convRate","conventionalRate","price","fourSigFigs","priceQuoteUnit","priceBaseUnit","volumeUnit","priceBox","volumeBox","quote","marketsOverviewBox","orderActivityBox","hosts","statuses","noActivity","orderActivity","orders","recentOrders","to","fromQty","toQty","fromLogo","fromSymbol","toSymbol","toLogo","age","timeSince","submitTime","link","href","bindInternalNavigation","reconfigErr","url","forceUrl","forceReq","showConfirmForce","confirmForceErr","recoverWalletErr","showOpen","errorMsg","showErrorOnly","skipAnimation","currentDef","currentWalletDefinition","option","traitLogFiler","otherActionsLabel","recfgAssetLogo","recfgAssetName","updateDisplayedReconfigFields","toggleSubtract","isWithdrawer","maxSendDisplay","sendLogo","sendName","feeReq","canSend","maxSendFiat","maxSendFee","maxSendFeeFiat","is","showSuccess","vSendPw","newWalletPW","newPW","search","URLSearchParams","URL","location","pathname","exportWalletErr","exportWalletPW","displayRestoreWalletInfo","restorationinfo","restoreInfoCardsList","wr","card","seedName","instructions","restoreWalletInfo","recoverWalletPW","force","fiatRates","display","FourSigFigs","OneFractionalDigit","_objectWithoutProperties","excluded","sourceKeys","getOwnPropertySymbols","sourceSymbolKeys","propertyIsEnumerable","SettingsPage","fiatRateSources","darkMode","showPokes","showPopups","commitHash","addADex","getRegistrationTxFeeEstimate","importAccount","prepareAccountImport","authorizeAccountImportForm","authorizeImportAccountConfirm","changeAppPW","changeAppPWForm","submitNewPW","accountFile","onAccountFileChange","removeAccount","clearAccountFile","addAccount","exportSeed","exportSeedAuth","exportSeedSubmit","submitExportSeedReq","exportSeedPW","seedDiv","getCertFile","selectedAccount","importAccountErr","importAccountAppPass","accountString","account","bonds","acctInf","importResponse","reload","exportAccountErr","exportSeedE","authorizeSeedDisplay","changePWErrMsg","clearValues","newAppPW","confirmNewPW","OrderBook","mktBook","qtyAtomic","less","findIdx","removeFromSide","findOrder","updateRemainingSide","epochIdx","approve","best","bestGapOrder","forward","route","payload","handlers","queue","maxQlength","connection","readyState","WebSocket","OPEN","close","uri","reloader","retrys","go","conn","timeout","onmessage","evt","onclose","delay","onopen","request","onerror","bookRoute","bookOrderRoute","unbookOrderRoute","updateRemainingRoute","epochOrderRoute","candlesRoute","candleUpdateRoute","check","buyBtnClass","sellBtnClass","fiveMinBinKey","percentFormatter","parentIDNone","MarketsPage","main","maxOrderUpdateCounter","metaOrders","recentMatches","preorderCache","depthLines","hovers","recentMatchesSortKey","recentMatchesSortDirection","ogTitle","title","depthReporters","reportDepthClick","reportDepthVolume","reportDepthMouse","z","reportDepthZoom","depthChart","depthZoomLK","candleReporters","reportMouseCandle","candleChart","candlesChart","accelerateOrderForm","accelerateForm","candleDur","registerBttn","notRegistered","dex","walletInfoTmpl","bWidget","qWidget","wgt","balanceWgt","BalanceWidget","baseIcons","stateIcons","quoteIcons","connect","walletUnlocked","expired","newWalletBttn","showCreate","walletAddr","rowTemplate","durBttnTemplate","userOrderTmpl","quoteUnits","baseUnits","buyBttn","swapBttns","sellBttn","maxLbl","setOrderBttnText","setOrderVisibility","drawChartLines","limitBttn","marketBttn","rateField","isSell","setMarketBuyOrderEstimate","maxOrd","maxSell","lotField","swap","lots","maxBuys","adjustedRate","lotChanged","disableMouseWheel","qtyField","mktBuyField","ws","handleBookRoute","handleBookOrderRoute","handleUnbookOrderRoute","handleUpdateRemainingRoute","handleEpochOrderRoute","handleCandlesRoute","handleCandleUpdateRoute","handleEpochMatchSummary","openFunc","orderForm","stepSubmit","verifyForm","vSubmit","submitOrder","vUnlockSubmit","submitEstimateUnlock","cancelForm","cancelSubmit","submitCancel","vFeeDetails","vDetailPane","closeDetailPane","showVerifyForm","recentMatchesTable","th","setRecentMatchesSortCol","ordercol","unsetRecentMatchesSortColClasses","refreshRecentMatchesTable","setRecentMatchesSortColClasses","sortCls","vPass","cancelPass","quantityChanged","marketBuyChanged","rateFieldChanged","previewQuoteAmt","marketSearchV1","filterMarkets","setDisclaimerAckViz","acked","disclaimer","disclaimerAck","showDisclaimer","orderDisclaimerAckedLK","clearChartLines","buyRows","sellRows","userOrders","activeMarkerRate","setDepthMarkers","stats0","marketStatsV1","stats1","headerSpace","stats","closeMarketsList","leftMarketDock","openMarketsList","leftHider","marketReopener","marketSelect","marketList","MarketList","marketListV1","startLoadingAnimations","setMarket","handleOrderNote","handleMatchNote","handleEpochNote","handleConnNote","bondpost","handleBondUpdate","spots","handlePriceUpdate","loadingAnimations","secondTicker","setInterval","mord","recentMatchesLiveList","td","sinceStamp","init","makeMarket","lastMarketLK","exists","setRegistrationStatusVisibility","setBalanceVisibility","anis","bconv","setPriceAndChange","obPrice","change24","obUp","obDown","baseIcon","quoteIcon","candleCaches","qconv","baseCfg","quoteCfg","isSupported","bVers","versions","qVers","version","isLimit","tifBox","qtyBox","maxBox","mktBuyBox","orderTypeBttns","assetsAreSupported","tier","loaderMsg","titleContent","confStatusMsg","titleClass","regStatusTitle","regStatusConfsDisplay","registrationStatus","regStatusDex","setRegistrationStatusView","pending","confirmationsRequired","join","connectionStatus","Connected","updateRegistrationStatusView","bondRequired","resolveOrderFormVisibility","viewOnly","unregisteredDex","hasPendingBonds","acctTier","dexSettingsLink","shortSymbol","durBttnBox","candleDurs","candleDurationSelected","chartErrMsg","qui","bui","maxEstimateTimer","mktId","marketID","sid","maxSellRequested","sellBalance","buyBalance","setMarketDetails","setCurrMarketPrice","loadCandles","setLoaderMsgVisibility","setCandleDurBttns","q","sellBookedBase","sellBookedQuote","buyBookedBase","buyBookedQuote","hoverData","depthHoverData","hoverPrice","hoverVolume","candle","candleHoverData","candleStart","candleEnd","candleHigh","candleLow","candleVol","limit","qtyConv","convertToAtoms","tifnow","tifNow","options","parseOrder","adjusted","orderErr","preSell","preBuy","orderPreview","quoteQty","total","baseWallet","setMaxOrder","scheduleMaxEstimate","quoteWallet","aLot","maxBuy","path","maxLoaded","bid","qid","bWallet","qWallet","maxLotBox","maxAboveZero","maxFromLots","maxFromLotsLbl","counter","maxOrder","fromAsset","maxFromAmt","maxFromTicker","loadTable","addTableOrder","epochLine","set","msgRate","buffer","buybuffer","midGapConventional","minMktBuy","oid","userNoOrders","unreadyOrders","headerEl","detailsDiv","readyToTick","sideLight","updateMetaOrder","cancelBttn","showCancel","accelerateBttn","showAccelerate","canAccelerateOrder","expander","unreadyOrdersMsg","activeLight","rateFactor","setMarkers","midGapValue","baseSymb","quoteSymb","refreshActiveOrders","handleBook","updateTitle","select","setWallets","removeTableOrder","updateRemaining","updateTableOrder","candlesLoading","timer","setHighLow","setCandles","addRecentMatches","startStamp","openAsset","updateAsset","currentOrder","toAsset","setIcon","icon","vFeeSummary","vUnlockPreorder","vPreorderErr","vPreorder","vBuySell","buySellStr","vSideSubmit","vOrderHost","verifyLimit","verifyMarket","orderDesc","vOrderType","vRate","vQty","vTotal","vFiatTotal","vmFromTotal","vmFromAsset","vmFromTotalFiat","vMarketEstimate","received","vmToTotal","vmToAsset","vmTotalFiat","vHeader","preOrder","unlockWalletsForEstimates","vErr","vUnlockPass","attemptWalletUnlock","setPreorderErr","assetIDs","cacheKey","cached","wireOrder","estimate","vPreorderErrTip","showAdvancedOptions","hideAdvancedOptions","vOtherOrderOpts","redeem","vDefaultOrderOpts","addOption","isSwap","change","isBaseChain","showByDefault","refreshPreorder","fetchPreorder","est","setFeeEstimates","showPreOrderAdvancedOptions","hidePreOrderAdvancedOptions","reloadOrderOpts","vPreorderEstimates","fmtPct","baseExchangeRate","quoteExchangeRate","baseFeeAssetUI","quoteFeeAssetUI","tokenFiatRate","parentFiatRate","toFeeAssetUI","fromFeeAssetUI","toExchangeRate","fromExchangeRate","swapped","swappedInParentUnits","bestSwapPct","realisticBestCase","vSwapFeesLowPct","vSwapFeesLow","worstSwapPct","realisticWorstCase","vSwapFeesHighPct","vSwapFeesHigh","swapFeesMaxPct","maxFees","vSwapFeesMaxPct","vSwapFeesMax","estRate","receivedInParentUnits","bestRedeemPct","vRedeemFeesLowPct","vRedeemFeesLow","worstRedeemPct","vRedeemFeesHighPct","vRedeemFeesHigh","vFeeSummaryPct","vFeeSummaryLow","vFeeSummaryHigh","summarySwapFeesLow","summarySwapFeesHigh","summaryRedeemFeesLow","summaryRedeemFeesHigh","cancelData","cancelErr","remaining","cancelRemain","cancelUnit","currentCreate","validateOrder","showVerify","updateSpots","oldStatus","setEpoch","clearOrderTableEpochs","alreadyMatched","compare","recentMatchesSortCompare","recentMatchesTemplate","vLoader","finalize","mktBuyLots","mktBuyScore","NaN","loadTableSide","bins","currEpochBin","currNonEpochBin","currRate","bin","bookSide","tbody","binOrdersByRateAndEpoch","orderTableRow","manager","getRate","insertOrder","tr","removeOrder","updateOrderQty","clearOrderTableEpochSide","removeEpochOrders","orderBin","OrderTableRowManager","chart","setConnectionStatus","filterTxt","setFilter","setLines","requestCandles","unattach","clearInterval","rowTmpl","reloadMarketsPane","mkts","convertMarkets","assign","aLots","sortedMarkets","MarketRow","find","disconnectedIco","template","hues","btmpl","iconBox","walletState","balanceRowTmpl","qtmpl","updateWallet","newWalletRow","unsupported","spinner","balanceRows","addWalletSymbol","readWallet","fetchBalance","addRow","balTmpl","updateParent","before","stringyOptions","tableRow","setRateEl","setEpochEl","updateQtyNumOrdersEl","epochEl","isEpoch","rateEl","cssClass","curr","numOrders","qtyEl","numOrdersEl","setAttribute","index","findIndex","newEpoch","sign","OrdersPage","orderTmpl","filterState","readFilter","filterKey","subFilter","hostFilter","assetFilter","statusFilter","applyButtons","monitorFilter","applyBttn","submitFilter","filter1","filter2","compareSubFilter","parseSubFilter","ordersTable","nextPage","exportOrders","showArchivedDateField","archivedDateField","deleteArchivedRecordsErr","deleteArchivedRecords","saveMatchesToFile","saveOrdersToFile","archivedRecordsLocation","deleteArchivedRecordsMsg","deleteArchivedResult","deleteArchivedRecordsForm","deleteArchivedRecordsSubmit","olderThan","tableBody","appendOrders","tmplID","mktID","dateTime","fetchOrders","setOrders","currentFilter","setQuery","olderThanMs","saveMatchesToFIle","archivedRecordsDeleted","nRecords","archivedRecordsPath","orderLoader","net","coinIDTakerFoundMakerRedemption","OrderPage","stampers","matchCardTmpl","setStamp","refreshOnPopupClose","setCoinHref","explorerId","showAccelerateForm","fetchOrder","mktBaseSymbol","mktQuoteSymbol","setAccelerationButtonVis","showMatchCards","matchCard","matchID","time","matchTime","toLocaleTimeString","month","day","matchTimeAgo","orderPortion","matchQty","bipSymbol","quoteAmount","cancelInfoDiv","infoDiv","statusHdr","cancelAmount","cancelIcon","cancelOrderPortion","makerSwapYou","makerRedeemYou","takerSwapThem","takerRedeemThem","takerSwapYou","takerRedeemYou","makerSwapThem","makerRedeemThem","makerSwapAsset","takerSwapAsset","makerRedeemAsset","takerRedeemAsset","refundAsset","fromAmount","toAmount","fromIcon","toIcon","revoked","counterRedeem","refund","setCoin","pendingName","linkName","coin","coinLink","pendingSpan","cid","startsWith","makerAddr","formatCoinID","stringID","explorerCoin","makerSwapCoin","takerSwapCoin","makerRedeemCoin","takerRedeemCoin","inConfirmingMakerRedeem","inConfirmingTakerRedeem","makerSwapMsg","takerSwapMsg","makerRedeemMsg","takerRedeemMsg","confirmationString","makerSwap","takerSwap","makerRedeem","takerRedeem","setImmutableMatchCardElements","setMutableMatchCardElements","matchBox","addNewMatchCard","actionsLabel","processMatch","counterSwap","assetExplorer","CoinExplorers","ethExplorers","txid","vout","DexSettingsPage","exportDexBtn","prepareAccountExport","authorizeAccountExportForm","disableAcctBtn","prepareAccountDisable","disableAccountForm","updateBondOptionsBtn","prepareUpdateBondOptions","updateCertBtn","certFileInput","updateHostBtn","prepareUpdateHost","updateBondOptionsForm","updateBondOptionsConfirm","updateBondOptions","authorizeExportAccountConfirm","exportAccount","disableAccountConfirm","disableAccount","exportAccountAppPass","exportAccountHost","accountForExport","disableAccountAppPW","disableAccountHost","disableAccountErr","bondTargetTier","bondOptions","targetTier","bondAssetSelect","bondOptionsErr","updateCertMsg","exchange","displayIcons","disconnectedIcon","connectedIcon","Disconnected","InvalidCert","bondOptionsMsg","GapStrategyMultiplier","GapStrategyAbsolute","GapStrategyAbsolutePlus","GapStrategyPercent","GapStrategyPercentPlus","oracleWeightBaseOption","default","oracleWeightOption","createXYRange","oracleBiasBaseOption","oracleBiasRange","oracleBiasOption","driftToleranceRange","driftToleranceOption","gapMultiplierBaseOption","gapMultiplierRange","gapMultiplierOption","gapPercentBaseOption","gapPercentRange","gapPercentOption","MarketMakerPage","programs","editProgram","pwHandler","createOpts","gapRanges","assetRowTmpl","runningProgramTmpl","oracleTmpl","selectClicked","isBase","baseSelect","quoteSelect","descendentMetrics","assetDropdown","counterAsset","currentMarket","clickedSymbol","Set","otherAssets","firstID","secondID","firstSymbol","secondSymbol","nonOptions","addOptions","symbols","assetRow","hideAssetDropdown","leaveEditMode","setCreationBase","setCreationQuote","clicker","setMarketSubchoice","gapMultiplierOpt","createOptsUpdated","gapPercentOpt","driftToleranceOpt","biasOpt","weightOpt","showAdvanced","optionsExpansionLK","hideAdvanced","runBttn","authedRoute","createBot","lotsInput","exitEditMode","manualPriceBttn","basisPrice","manualPriceInput","hideManualPrice","formatFiveSigFigs","specifiedPrice","setCurrentBasisPrice","noFiatBox","gapStrategySelect","updateGapStrategyInputs","currentReport","submitPasswordForm","pwInput","pwForm","pwSubmit","createBaseWallet","createQuoteWallet","lastMkt","lastMMMarketLK","xcMkt","bot","handleBotNote","populateRunningPrograms","createBox","programsBox","fetchMaxBuy","atomToConv","absMaxBox","gapFactorMax","lotEstQuoteLots","breakEvenSpread","breakEvenGapBox","breakEvenGap","absInputBox","updateGapStrategyInputVisibility","rateUnit","skipMarketUpdate","setCurrentReport","addMarketSelect","setContent","Text","marketOneChoice","lotEstBaseSymbol","lotEstQuoteSymbol","setNoWallet","noWalletBox","lotEstQuoteBox","lotEstQuoteNoWallet","lotEstBaseBox","lotEstBaseNoWallet","availHeader","lotEstimateBox","marketInfo","oraclesBox","lotsBox","advancedBox","fetchingMarkets","buy","fetchOracleAndMaxBuy","fetchMaxSell","lotEstBaseLots","oracles","weight","weightedSum","ExchangeNames","formatThreeSigFigs","usdVol","bestBuy","bestSell","avgPrice","pgm","program","programID","oracleWeighting","oracleBias","driftTolerance","gapStrategy","gapFactor","bots","runningPrograms","programDiv","setProgramsHeader","programsHeader","noProgramsMessage","startStop","startErr","pauseBttn","startBttn","retireBttn","configureBttn","setEditProgram","updateProgramDiv","programRunning","programPaused","boost","oracleWeight","createErr","makerProgram","strategy","botType","manualRate","absInput","gapPercent","gapMultiplier","botCreator","baseOpt","createOption","constructors","register","wallets","settings","dexsettings","mm","Application","pokes","process","noteReceivers","loggers","loggersLK","enableLogger","loggerID","recorderKeys","recordersLK","recorders","recordLogger","dumpLogger","btoa","download","handlerFromPath","history","replaceState","attachHeader","attachCommon","attach","notificationsLK","protocol","reconnected","notify","seedgentime","skipPush","noteBox","profileBox","origin","requestedHandler","doc","noderize","delivered","pushState","handlerID","loadedPage","unload","lyt","popupNotes","popupTmpl","noteTmpl","pokeTmpl","loader","noteBell","pokeList","noteList","ackNotes","noteCat","pokeCat","showDropdown","noteIndicator","setNoteTimes","storeNotes","burgerIcon","logoutErr","innerNoteIcon","innerBurgerIcon","profileSignout","signOut","dialog","ico","innerWidth","acks","severity","pageURL","params","searchParams","walletsMenuEntry","marketsMenuEntry","coinID","updateBondConfs","confirmations","updateTier","prependNoteElement","orderNote","tempID","inFlight","inflight","updateOrder","handleBondNote","updateMatch","mktName","updateUser","stack","indicator","setSeverityClass","prependPokeElement","receivers","nowString","cn","makePoke","prependListElement","skipSave","makeNote","unacked","ni","noteCacheSize","lastChild","cls","toLocaleDateString","concat","fromAssetID","supportedAsset","encRate","emptyidx","def","removeCookie","authCK","removeLocal","severityClassMap","getSeconds","getMilliseconds"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"entry.js","mappings":"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","sources":["webpack://dexclient/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/typeof.js","webpack://dexclient/./node_modules/@babel/runtime/regenerator/index.js","webpack://dexclient/webpack/bootstrap","webpack://dexclient/webpack/runtime/compat get default export","webpack://dexclient/webpack/runtime/define property getters","webpack://dexclient/webpack/runtime/hasOwnProperty shorthand","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/arrayLikeToArray.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/slicedToArray.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/iterableToArrayLimit.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/nonIterableRest.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/classCallCheck.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/typeof.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/toPropertyKey.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/toPrimitive.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/createClass.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/defineProperty.js","webpack://dexclient/./src/js/locales.ts","webpack://dexclient/./src/js/doc.ts","webpack://dexclient/./src/js/state.ts","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/inherits.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/possibleConstructorReturn.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/getPrototypeOf.js","webpack://dexclient/./src/js/http.ts","webpack://dexclient/./src/js/registry.ts","webpack://dexclient/./src/js/opts.ts","webpack://dexclient/./src/js/basepage.ts","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/arrayWithoutHoles.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/iterableToArray.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/nonIterableSpread.js","webpack://dexclient/./src/js/orderutil.ts","webpack://dexclient/./src/js/charts.ts","webpack://dexclient/./src/js/forms.ts","webpack://dexclient/./src/js/register.ts","webpack://dexclient/./src/js/login.ts","webpack://dexclient/./src/js/wallets.ts","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/objectWithoutProperties.js","webpack://dexclient/./node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js","webpack://dexclient/./src/js/settings.ts","webpack://dexclient/./src/js/orderbook.ts","webpack://dexclient/./src/js/ws.ts","webpack://dexclient/./src/js/markets.ts","webpack://dexclient/./src/js/orders.ts","webpack://dexclient/./src/js/order.ts","webpack://dexclient/./src/js/dexsettings.ts","webpack://dexclient/./src/js/mm.ts","webpack://dexclient/./src/js/app.ts","webpack://dexclient/./src/js/notifications.ts","webpack://dexclient/./src/index.ts"],"sourcesContent":["var _typeof = require(\"./typeof.js\")[\"default\"];\nfunction _regeneratorRuntime() {\n \"use strict\"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */\n module.exports = _regeneratorRuntime = function _regeneratorRuntime() {\n return exports;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;\n var exports = {},\n Op = Object.prototype,\n hasOwn = Op.hasOwnProperty,\n defineProperty = Object.defineProperty || function (obj, key, desc) {\n obj[key] = desc.value;\n },\n $Symbol = \"function\" == typeof Symbol ? Symbol : {},\n iteratorSymbol = $Symbol.iterator || \"@@iterator\",\n asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\",\n toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n function define(obj, key, value) {\n return Object.defineProperty(obj, key, {\n value: value,\n enumerable: !0,\n configurable: !0,\n writable: !0\n }), obj[key];\n }\n try {\n define({}, \"\");\n } catch (err) {\n define = function define(obj, key, value) {\n return obj[key] = value;\n };\n }\n function wrap(innerFn, outerFn, self, tryLocsList) {\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator,\n generator = Object.create(protoGenerator.prototype),\n context = new Context(tryLocsList || []);\n return defineProperty(generator, \"_invoke\", {\n value: makeInvokeMethod(innerFn, self, context)\n }), generator;\n }\n function tryCatch(fn, obj, arg) {\n try {\n return {\n type: \"normal\",\n arg: fn.call(obj, arg)\n };\n } catch (err) {\n return {\n type: \"throw\",\n arg: err\n };\n }\n }\n exports.wrap = wrap;\n var ContinueSentinel = {};\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n var IteratorPrototype = {};\n define(IteratorPrototype, iteratorSymbol, function () {\n return this;\n });\n var getProto = Object.getPrototypeOf,\n NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype);\n var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);\n function defineIteratorMethods(prototype) {\n [\"next\", \"throw\", \"return\"].forEach(function (method) {\n define(prototype, method, function (arg) {\n return this._invoke(method, arg);\n });\n });\n }\n function AsyncIterator(generator, PromiseImpl) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (\"throw\" !== record.type) {\n var result = record.arg,\n value = result.value;\n return value && \"object\" == _typeof(value) && hasOwn.call(value, \"__await\") ? PromiseImpl.resolve(value.__await).then(function (value) {\n invoke(\"next\", value, resolve, reject);\n }, function (err) {\n invoke(\"throw\", err, resolve, reject);\n }) : PromiseImpl.resolve(value).then(function (unwrapped) {\n result.value = unwrapped, resolve(result);\n }, function (error) {\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n reject(record.arg);\n }\n var previousPromise;\n defineProperty(this, \"_invoke\", {\n value: function value(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new PromiseImpl(function (resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();\n }\n });\n }\n function makeInvokeMethod(innerFn, self, context) {\n var state = \"suspendedStart\";\n return function (method, arg) {\n if (\"executing\" === state) throw new Error(\"Generator is already running\");\n if (\"completed\" === state) {\n if (\"throw\" === method) throw arg;\n return doneResult();\n }\n for (context.method = method, context.arg = arg;;) {\n var delegate = context.delegate;\n if (delegate) {\n var delegateResult = maybeInvokeDelegate(delegate, context);\n if (delegateResult) {\n if (delegateResult === ContinueSentinel) continue;\n return delegateResult;\n }\n }\n if (\"next\" === context.method) context.sent = context._sent = context.arg;else if (\"throw\" === context.method) {\n if (\"suspendedStart\" === state) throw state = \"completed\", context.arg;\n context.dispatchException(context.arg);\n } else \"return\" === context.method && context.abrupt(\"return\", context.arg);\n state = \"executing\";\n var record = tryCatch(innerFn, self, context);\n if (\"normal\" === record.type) {\n if (state = context.done ? \"completed\" : \"suspendedYield\", record.arg === ContinueSentinel) continue;\n return {\n value: record.arg,\n done: context.done\n };\n }\n \"throw\" === record.type && (state = \"completed\", context.method = \"throw\", context.arg = record.arg);\n }\n };\n }\n function maybeInvokeDelegate(delegate, context) {\n var methodName = context.method,\n method = delegate.iterator[methodName];\n if (undefined === method) return context.delegate = null, \"throw\" === methodName && delegate.iterator[\"return\"] && (context.method = \"return\", context.arg = undefined, maybeInvokeDelegate(delegate, context), \"throw\" === context.method) || \"return\" !== methodName && (context.method = \"throw\", context.arg = new TypeError(\"The iterator does not provide a '\" + methodName + \"' method\")), ContinueSentinel;\n var record = tryCatch(method, delegate.iterator, context.arg);\n if (\"throw\" === record.type) return context.method = \"throw\", context.arg = record.arg, context.delegate = null, ContinueSentinel;\n var info = record.arg;\n return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, \"return\" !== context.method && (context.method = \"next\", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = \"throw\", context.arg = new TypeError(\"iterator result is not an object\"), context.delegate = null, ContinueSentinel);\n }\n function pushTryEntry(locs) {\n var entry = {\n tryLoc: locs[0]\n };\n 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry);\n }\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\", delete record.arg, entry.completion = record;\n }\n function Context(tryLocsList) {\n this.tryEntries = [{\n tryLoc: \"root\"\n }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0);\n }\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) return iteratorMethod.call(iterable);\n if (\"function\" == typeof iterable.next) return iterable;\n if (!isNaN(iterable.length)) {\n var i = -1,\n next = function next() {\n for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next;\n return next.value = undefined, next.done = !0, next;\n };\n return next.next = next;\n }\n }\n return {\n next: doneResult\n };\n }\n function doneResult() {\n return {\n value: undefined,\n done: !0\n };\n }\n return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, \"constructor\", {\n value: GeneratorFunctionPrototype,\n configurable: !0\n }), defineProperty(GeneratorFunctionPrototype, \"constructor\", {\n value: GeneratorFunction,\n configurable: !0\n }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, \"GeneratorFunction\"), exports.isGeneratorFunction = function (genFun) {\n var ctor = \"function\" == typeof genFun && genFun.constructor;\n return !!ctor && (ctor === GeneratorFunction || \"GeneratorFunction\" === (ctor.displayName || ctor.name));\n }, exports.mark = function (genFun) {\n return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, \"GeneratorFunction\")), genFun.prototype = Object.create(Gp), genFun;\n }, exports.awrap = function (arg) {\n return {\n __await: arg\n };\n }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () {\n return this;\n }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {\n void 0 === PromiseImpl && (PromiseImpl = Promise);\n var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);\n return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) {\n return result.done ? result.value : iter.next();\n });\n }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, \"Generator\"), define(Gp, iteratorSymbol, function () {\n return this;\n }), define(Gp, \"toString\", function () {\n return \"[object Generator]\";\n }), exports.keys = function (val) {\n var object = Object(val),\n keys = [];\n for (var key in object) keys.push(key);\n return keys.reverse(), function next() {\n for (; keys.length;) {\n var key = keys.pop();\n if (key in object) return next.value = key, next.done = !1, next;\n }\n return next.done = !0, next;\n };\n }, exports.values = values, Context.prototype = {\n constructor: Context,\n reset: function reset(skipTempReset) {\n if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = \"next\", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) \"t\" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined);\n },\n stop: function stop() {\n this.done = !0;\n var rootRecord = this.tryEntries[0].completion;\n if (\"throw\" === rootRecord.type) throw rootRecord.arg;\n return this.rval;\n },\n dispatchException: function dispatchException(exception) {\n if (this.done) throw exception;\n var context = this;\n function handle(loc, caught) {\n return record.type = \"throw\", record.arg = exception, context.next = loc, caught && (context.method = \"next\", context.arg = undefined), !!caught;\n }\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i],\n record = entry.completion;\n if (\"root\" === entry.tryLoc) return handle(\"end\");\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\"),\n hasFinally = hasOwn.call(entry, \"finallyLoc\");\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);\n if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);\n } else {\n if (!hasFinally) throw new Error(\"try statement without catch or finally\");\n if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);\n }\n }\n }\n },\n abrupt: function abrupt(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev && hasOwn.call(entry, \"finallyLoc\") && this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n finallyEntry && (\"break\" === type || \"continue\" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null);\n var record = finallyEntry ? finallyEntry.completion : {};\n return record.type = type, record.arg = arg, finallyEntry ? (this.method = \"next\", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record);\n },\n complete: function complete(record, afterLoc) {\n if (\"throw\" === record.type) throw record.arg;\n return \"break\" === record.type || \"continue\" === record.type ? this.next = record.arg : \"return\" === record.type ? (this.rval = this.arg = record.arg, this.method = \"return\", this.next = \"end\") : \"normal\" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel;\n },\n finish: function finish(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel;\n }\n },\n \"catch\": function _catch(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (\"throw\" === record.type) {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n throw new Error(\"illegal catch attempt\");\n },\n delegateYield: function delegateYield(iterable, resultName, nextLoc) {\n return this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n }, \"next\" === this.method && (this.arg = undefined), ContinueSentinel;\n }\n }, exports;\n}\nmodule.exports = _regeneratorRuntime, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","function _typeof(obj) {\n \"@babel/helpers - typeof\";\n\n return (module.exports = _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) {\n return typeof obj;\n } : function (obj) {\n return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports), _typeof(obj);\n}\nmodule.exports = _typeof, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;","// TODO(Babel 8): Remove this file.\n\nvar runtime = require(\"../helpers/regeneratorRuntime\")();\nmodule.exports = runtime;\n\n// Copied from https://github.com/facebook/regenerator/blob/main/packages/runtime/runtime.js#L736=\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n if (typeof globalThis === \"object\") {\n globalThis.regeneratorRuntime = runtime;\n } else {\n Function(\"r\", \"regeneratorRuntime = r\")(runtime);\n }\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export default function _arrayLikeToArray(arr, len) {\n if (len == null || len > arr.length) len = arr.length;\n for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];\n return arr2;\n}","import arrayLikeToArray from \"./arrayLikeToArray.js\";\nexport default function _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);\n}","import arrayWithHoles from \"./arrayWithHoles.js\";\nimport iterableToArrayLimit from \"./iterableToArrayLimit.js\";\nimport unsupportedIterableToArray from \"./unsupportedIterableToArray.js\";\nimport nonIterableRest from \"./nonIterableRest.js\";\nexport default function _slicedToArray(arr, i) {\n return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || unsupportedIterableToArray(arr, i) || nonIterableRest();\n}","export default function _arrayWithHoles(arr) {\n if (Array.isArray(arr)) return arr;\n}","export default function _iterableToArrayLimit(arr, i) {\n var _i = null == arr ? null : \"undefined\" != typeof Symbol && arr[Symbol.iterator] || arr[\"@@iterator\"];\n if (null != _i) {\n var _s,\n _e,\n _x,\n _r,\n _arr = [],\n _n = !0,\n _d = !1;\n try {\n if (_x = (_i = _i.call(arr)).next, 0 === i) {\n if (Object(_i) !== _i) return;\n _n = !1;\n } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);\n } catch (err) {\n _d = !0, _e = err;\n } finally {\n try {\n if (!_n && null != _i[\"return\"] && (_r = _i[\"return\"](), Object(_r) !== _r)) return;\n } finally {\n if (_d) throw _e;\n }\n }\n return _arr;\n }\n}","export default function _nonIterableRest() {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}","function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {\n try {\n var info = gen[key](arg);\n var value = info.value;\n } catch (error) {\n reject(error);\n return;\n }\n if (info.done) {\n resolve(value);\n } else {\n Promise.resolve(value).then(_next, _throw);\n }\n}\nexport default function _asyncToGenerator(fn) {\n return function () {\n var self = this,\n args = arguments;\n return new Promise(function (resolve, reject) {\n var gen = fn.apply(self, args);\n function _next(value) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value);\n }\n function _throw(err) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err);\n }\n _next(undefined);\n });\n };\n}","export default function _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n}","export default function _typeof(obj) {\n \"@babel/helpers - typeof\";\n\n return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) {\n return typeof obj;\n } : function (obj) {\n return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n }, _typeof(obj);\n}","import _typeof from \"./typeof.js\";\nimport toPrimitive from \"./toPrimitive.js\";\nexport default function _toPropertyKey(arg) {\n var key = toPrimitive(arg, \"string\");\n return _typeof(key) === \"symbol\" ? key : String(key);\n}","import _typeof from \"./typeof.js\";\nexport default function _toPrimitive(input, hint) {\n if (_typeof(input) !== \"object\" || input === null) return input;\n var prim = input[Symbol.toPrimitive];\n if (prim !== undefined) {\n var res = prim.call(input, hint || \"default\");\n if (_typeof(res) !== \"object\") return res;\n throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n }\n return (hint === \"string\" ? String : Number)(input);\n}","import toPropertyKey from \"./toPropertyKey.js\";\nfunction _defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor);\n }\n}\nexport default function _createClass(Constructor, protoProps, staticProps) {\n if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n if (staticProps) _defineProperties(Constructor, staticProps);\n Object.defineProperty(Constructor, \"prototype\", {\n writable: false\n });\n return Constructor;\n}","import toPropertyKey from \"./toPropertyKey.js\";\nexport default function _defineProperty(obj, key, value) {\n key = toPropertyKey(key);\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n return obj;\n}","type Locale = Record\n\nexport const ID_NO_PASS_ERROR_MSG = 'ID_NO_PASS_ERROR_MSG'\nexport const ID_NO_APP_PASS_ERROR_MSG = 'ID_NO_APP_PASS_ERROR_MSG'\nexport const ID_SET_BUTTON_BUY = 'ID_SET_BUTTON_BUY'\nexport const ID_SET_BUTTON_SELL = 'ID_SET_BUTTON_SELL'\nexport const ID_OFF = 'ID_OFF'\nexport const ID_READY = 'ID_READY'\nexport const ID_NO_WALLET = 'ID_NO_WALLET'\nexport const ID_DISABLED_MSG = 'ID_DISABLED_MSG'\nexport const ID_WALLET_SYNC_PROGRESS = 'ID_WALLET_SYNC_PROGRESS'\nexport const ID_HIDE_ADDITIONAL_SETTINGS = 'ID_HIDE_ADDITIONAL_SETTINGS'\nexport const ID_SHOW_ADDITIONAL_SETTINGS = 'ID_SHOW_ADDITIONAL_SETTINGS'\nexport const ID_BUY = 'ID_BUY'\nexport const ID_SELL = 'ID_SELL'\nexport const ID_NOT_SUPPORTED = 'ID_NOT_SUPPORTED'\nexport const ID_VERSION_NOT_SUPPORTED = 'ID_VERSION_NOT_SUPPORTED'\nexport const ID_CONNECTION_FAILED = 'ID_CONNECTION_FAILED'\nexport const ID_ORDER_PREVIEW = 'ID_ORDER_PREVIEW'\nexport const ID_CALCULATING = 'ID_CALCULATING'\nexport const ID_ESTIMATE_UNAVAILABLE = 'ID_ESTIMATE_UNAVAILABLE'\nexport const ID_NO_ZERO_RATE = 'ID_NO_ZERO_RATE'\nexport const ID_NO_ZERO_QUANTITY = 'ID_NO_ZERO_QUANTITY'\nexport const ID_TRADE = 'ID_TRADE'\nexport const ID_NO_ASSET_WALLET = 'ID_NO_ASSET_WALLET'\nexport const ID_EXECUTED = 'ID_EXECUTED'\nexport const ID_BOOKED = 'ID_BOOKED'\nexport const ID_CANCELING = 'ID_CANCELING'\nexport const ID_PASSWORD_NOT_MATCH = 'ID_PASSWORD_NOT_MATCH'\nexport const ID_ACCT_UNDEFINED = 'ID_ACCT_UNDEFINED'\nexport const ID_KEEP_WALLET_PASS = 'ID_KEEP_WALLET_PASS'\nexport const ID_NEW_WALLET_PASS = 'ID_NEW_WALLET_PASS'\nexport const ID_LOT = 'ID_LOT'\nexport const ID_LOTS = 'ID_LOTS'\nexport const ID_UNKNOWN = 'ID_UNKNOWN'\nexport const ID_EPOCH = 'ID_EPOCH'\nexport const ID_ORDER_SUBMITTING = 'ID_ORDER_SUBMITTING'\nexport const ID_SETTLING = 'ID_SETTLING'\nexport const ID_NO_MATCH = 'ID_NO_MATCH'\nexport const ID_CANCELED = 'ID_CANCELED'\nexport const ID_REVOKED = 'ID_REVOKED'\nexport const ID_WAITING_FOR_CONFS = 'ID_WAITING_FOR_CONFS'\nexport const ID_NONE_SELECTED = 'ID_NONE_SELECTED' // unused?\nexport const ID_REGISTRATION_FEE_SUCCESS = 'ID_REGISTRATION_FEE_SUCCESS'\nexport const ID_API_ERROR = 'ID_API_ERROR'\nexport const ID_ADD = 'ID_ADD'\nexport const ID_CREATE = 'ID_CREATE'\nexport const ID_SETUP_WALLET = 'ID_SETUP_WALLET'\nexport const ID_WALLET_READY = 'ID_WALLET_READY'\nexport const ID_CHANGE_WALLET_TYPE = 'ID_CHANGE_WALLET_TYPE'\nexport const ID_KEEP_WALLET_TYPE = 'ID_KEEP_WALLET_TYPE'\nexport const WALLET_READY = 'WALLET_READY'\nexport const WALLET_PENDING = 'WALLET_PENDING'\nexport const SETUP_NEEDED = 'SETUP_NEEDED'\nexport const ID_SEND_SUCCESS = 'SEND_SUCCESS'\nexport const ID_RECONFIG_SUCCESS = 'RECONFIG_SUCCESS'\nexport const ID_RESCAN_STARTED = 'RESCAN_STARTED'\nexport const ID_NEW_WALLET_SUCCESS = 'NEW_WALLET_SUCCESS'\nexport const ID_WALLET_UNLOCKED = 'WALLET_UNLOCKED'\nexport const ID_SELLING = 'ID_SELLING'\nexport const ID_BUYING = 'ID_BUYING'\nexport const ID_WALLET_DISABLED_MSG = 'WALLET_DISABLED'\nexport const ID_WALLET_ENABLED_MSG = 'WALLET_ENABLED'\nexport const ID_ACTIVE_ORDERS_ERR_MSG = 'ACTIVE_ORDERS_ERR_MSG'\nexport const ID_AVAILABLE = 'AVAILABLE'\nexport const ID_LOCKED = 'LOCKED'\nexport const ID_IMMATURE = 'IMMATURE'\nexport const ID_FEE_BALANCE = 'FEE_BALANCE'\nexport const ID_CANDLES_LOADING = 'CANDLES_LOADING'\nexport const ID_DEPTH_LOADING = 'DEPTH_LOADING'\nexport const ID_INVALID_ADDRESS_MSG = 'INVALID_ADDRESS_MSG'\nexport const ID_TXFEE_UNSUPPORTED = 'TXFEE_UNSUPPORTED'\nexport const ID_TXFEE_ERR_MSG = 'TXFEE_ERR_MSG'\nexport const ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG = 'ACTIVE_ORDERS_LOGOUT_ERR_MSG'\nexport const ID_INVALID_DATE_ERR_MSG = 'INVALID_DATE_ERR_MSG'\nexport const ID_NO_ARCHIVED_RECORDS = 'NO_ARCHIVED_RECORDS'\nexport const ID_DELETE_ARCHIVED_RECORDS_RESULT = 'DELETE_ARCHIVED_RECORDS_RESULT'\nexport const ID_ARCHIVED_RECORDS_PATH = 'ARCHIVED_RECORDS_PATH'\nexport const ID_DEFAULT = 'DEFAULT'\nexport const ID_ADDED = 'USER_ADDED'\nexport const ID_DISCOVERED = 'DISCOVERED'\nexport const ID_UNSUPPORTED_ASSET_INFO_ERR_MSG = 'UNSUPPORTED_ASSET_INFO_ERR_MSG'\nexport const ID_LIMIT_ORDER = 'LIMIT_ORDER'\nexport const ID_LIMIT_ORDER_IMMEDIATE_TIF = 'LIMIT_ORDER_IMMEDIATE_TIF'\nexport const ID_MARKET_ORDER = 'MARKET_ORDER'\nexport const ID_MATCH_STATUS_NEWLY_MATCHED = 'MATCH_STATUS_NEWLY_MATCHED'\nexport const ID_MATCH_STATUS_MAKER_SWAP_CAST = 'MATCH_STATUS_MAKER_SWAP_CAST'\nexport const ID_MATCH_STATUS_TAKER_SWAP_CAST = 'MATCH_STATUS_TAKER_SWAP_CAST'\nexport const ID_MATCH_STATUS_MAKER_REDEEMED = 'MATCH_STATUS_MAKER_REDEEMED'\nexport const ID_MATCH_STATUS_REDEMPTION_SENT = 'MATCH_STATUS_REDEMPTION_SENT'\nexport const ID_MATCH_STATUS_REDEMPTION_CONFIRMED = 'MATCH_REDEMPTION_CONFIRMED'\nexport const ID_MATCH_STATUS_REVOKED = 'MATCH_STATUS_REVOKED'\nexport const ID_MATCH_STATUS_REFUNDED = 'MATCH_STATUS_REFUNDED'\nexport const ID_MATCH_STATUS_REFUND_PENDING = 'MATCH_STATUS_REFUND_PENDING'\nexport const ID_MATCH_STATUS_REDEEM_PENDING = 'MATCH_STATUS_REDEEM_PENDING'\nexport const ID_MATCH_STATUS_COMPLETE = 'MATCH_STATUS_COMPLETE'\nexport const ID_TAKER_FOUND_MAKER_REDEMPTION = 'TAKER_FOUND_MAKER_REDEMPTION'\nexport const ID_OPEN_WALLET_ERR_MSG = 'OPEN_WALLET_ERR_MSG'\nexport const ID_ORDER_ACCELERATION_FEE_ERR_MSG = 'ORDER_ACCELERATION_FEE_ERR_MSG'\nexport const ID_ORDER_ACCELERATION_ERR_MSG = 'ORDER_ACCELERATION_FEE_ERR_MSG'\nexport const ID_CONNECTED = 'CONNECTED'\nexport const ID_DISCONNECTED = 'DISCONNECTED'\nexport const ID_INVALID_CERTIFICATE = 'INVALID_CERTIFICATE'\nexport const ID_CONFIRMATIONS = 'ID_CONFIRMATIONS'\nexport const ID_TAKER = 'TAKER'\nexport const ID_MAKER = 'MAKER'\nexport const ID_EMPTY_DEX_ADDRESS_MSG = 'EMPTY_DEX_ADDRESS_MSG'\nexport const ID_SELECT_WALLET_FOR_FEE_PAYMENT = 'SELECT_WALLET_FOR_FEE_PAYMENT'\nexport const ID_UNAVAILABLE = 'UNAVAILABLE'\nexport const ID_WALLET_SYNC_FINISHING_UP = 'WALLET_SYNC_FINISHING_UP'\n\nexport const enUS: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'password cannot be empty',\n [ID_NO_APP_PASS_ERROR_MSG]: 'app password cannot be empty',\n [ID_PASSWORD_NOT_MATCH]: 'passwords do not match',\n [ID_SET_BUTTON_BUY]: 'Place order to buy {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Place order to sell {{ asset }}',\n [ID_OFF]: 'off',\n [ID_READY]: 'ready',\n [ID_LOCKED]: 'locked',\n [ID_NO_WALLET]: 'no wallet',\n [ID_WALLET_SYNC_PROGRESS]: 'wallet is {{ syncProgress }}% synced',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'hide additional settings',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'show additional settings',\n [ID_BUY]: 'Buy',\n [ID_SELL]: 'Sell',\n [ID_NOT_SUPPORTED]: '{{ asset }} is not supported',\n [ID_VERSION_NOT_SUPPORTED]: '{{ asset }} (v{{version}}) is not supported',\n [ID_CONNECTION_FAILED]: 'Connection to dex server failed. You can close dexc and try again later or wait for it to reconnect.',\n [ID_ORDER_PREVIEW]: 'Total: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'calculating...',\n [ID_ESTIMATE_UNAVAILABLE]: 'estimate unavailable',\n [ID_NO_ZERO_RATE]: 'zero rate not allowed',\n [ID_NO_ZERO_QUANTITY]: 'zero quantity not allowed',\n [ID_TRADE]: 'trade',\n [ID_NO_ASSET_WALLET]: 'No {{ asset }} wallet',\n [ID_EXECUTED]: 'executed',\n [ID_BOOKED]: 'booked',\n [ID_CANCELING]: 'canceling',\n [ID_ACCT_UNDEFINED]: 'Account undefined.',\n [ID_KEEP_WALLET_PASS]: 'keep current wallet password',\n [ID_NEW_WALLET_PASS]: 'set a new wallet password',\n [ID_LOT]: 'lot',\n [ID_LOTS]: 'lots',\n [ID_UNKNOWN]: 'unknown',\n [ID_EPOCH]: 'epoch',\n [ID_SETTLING]: 'settling',\n [ID_NO_MATCH]: 'no match',\n [ID_CANCELED]: 'canceled',\n [ID_REVOKED]: 'revoked',\n [ID_WAITING_FOR_CONFS]: 'Waiting for confirmations...',\n [ID_NONE_SELECTED]: 'none selected',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Fidelity bond accepted!',\n [ID_API_ERROR]: 'API error',\n [ID_ADD]: 'Add',\n [ID_CREATE]: 'Create',\n [ID_WALLET_READY]: 'Ready',\n [ID_SETUP_WALLET]: 'Setup',\n [ID_CHANGE_WALLET_TYPE]: 'change the wallet type',\n [ID_KEEP_WALLET_TYPE]: 'don\\'t change the wallet type',\n [WALLET_READY]: 'Wallet Ready',\n [SETUP_NEEDED]: 'Setup Needed',\n [WALLET_PENDING]: 'Creating Wallet',\n [ID_SEND_SUCCESS]: '{{ assetName }} Sent!',\n [ID_RECONFIG_SUCCESS]: 'Wallet Reconfigured!',\n [ID_RESCAN_STARTED]: 'Wallet Rescan Running',\n [ID_NEW_WALLET_SUCCESS]: '{{ assetName }} Wallet Created!',\n [ID_WALLET_UNLOCKED]: 'Wallet Unlocked',\n [ID_SELLING]: 'Selling',\n [ID_BUYING]: 'Buying',\n [ID_WALLET_ENABLED_MSG]: '{{ assetName }} Wallet Enabled',\n [ID_WALLET_DISABLED_MSG]: '{{ assetName }} Wallet Disabled',\n [ID_DISABLED_MSG]: 'wallet is disabled',\n [ID_ACTIVE_ORDERS_ERR_MSG]: '{{ assetName }} wallet is actively managing orders',\n [ID_AVAILABLE]: 'available',\n [ID_IMMATURE]: 'immature',\n [ID_FEE_BALANCE]: 'fee balance',\n [ID_CANDLES_LOADING]: 'waiting for candlesticks',\n [ID_DEPTH_LOADING]: 'retrieving depth data',\n [ID_INVALID_ADDRESS_MSG]: 'invalid address: {{ address }}',\n [ID_TXFEE_UNSUPPORTED]: 'fee estimation is not supported for this wallet type',\n [ID_TXFEE_ERR_MSG]: 'fee estimation failed: {{ err }}',\n [ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG]: 'cannot logout with active orders',\n [ID_INVALID_DATE_ERR_MSG]: 'error: invalid date or time',\n [ID_NO_ARCHIVED_RECORDS]: 'No archived records found',\n [ID_DELETE_ARCHIVED_RECORDS_RESULT]: 'Message: {{ nRecords }} archived records has been deleted',\n [ID_ARCHIVED_RECORDS_PATH]: 'File Location: {{ path }}',\n [ID_ORDER_SUBMITTING]: 'submitting',\n [ID_DEFAULT]: 'Default',\n [ID_ADDED]: 'Added',\n [ID_DISCOVERED]: 'Discovered',\n [ID_UNSUPPORTED_ASSET_INFO_ERR_MSG]: 'no supported asset info for id = {{ assetID }}, and no exchange info provided',\n [ID_LIMIT_ORDER]: 'limit',\n [ID_LIMIT_ORDER_IMMEDIATE_TIF]: 'limit (i)',\n [ID_MARKET_ORDER]: 'market',\n [ID_MATCH_STATUS_NEWLY_MATCHED]: 'Newly Matched',\n [ID_MATCH_STATUS_MAKER_SWAP_CAST]: 'Maker Swap Sent',\n [ID_MATCH_STATUS_TAKER_SWAP_CAST]: 'Taker Swap Sent',\n [ID_MATCH_STATUS_MAKER_REDEEMED]: 'Maker Redeemed',\n [ID_MATCH_STATUS_REDEMPTION_SENT]: 'Redemption Sent',\n [ID_MATCH_STATUS_REVOKED]: 'Revoked - {{ status }}',\n [ID_MATCH_STATUS_REFUND_PENDING]: 'Refund PENDING',\n [ID_MATCH_STATUS_REFUNDED]: 'Refunded',\n [ID_MATCH_STATUS_REDEEM_PENDING]: 'Redeem PENDING',\n [ID_MATCH_STATUS_REDEMPTION_CONFIRMED]: 'Redemption Confirmed',\n [ID_MATCH_STATUS_COMPLETE]: 'Complete',\n [ID_TAKER_FOUND_MAKER_REDEMPTION]: 'redeemed by {{ makerAddr }}',\n [ID_OPEN_WALLET_ERR_MSG]: 'Error opening wallet: {{ msg }}',\n [ID_ORDER_ACCELERATION_FEE_ERR_MSG]: 'Error estimating acceleration fee: {{ msg }}',\n [ID_ORDER_ACCELERATION_ERR_MSG]: 'Error accelerating order: {{ msg }}',\n [ID_CONNECTED]: 'Connected',\n [ID_DISCONNECTED]: 'Disconnected',\n [ID_INVALID_CERTIFICATE]: 'Invalid Certificate',\n [ID_CONFIRMATIONS]: 'confirmations',\n [ID_TAKER]: 'Taker',\n [ID_MAKER]: 'Maker',\n [ID_UNAVAILABLE]: 'unavailable',\n [ID_EMPTY_DEX_ADDRESS_MSG]: 'DEX address cannot be empty',\n [ID_SELECT_WALLET_FOR_FEE_PAYMENT]: 'Select a valid wallet to post a bond',\n [ID_WALLET_SYNC_FINISHING_UP]: 'finishing up'\n}\n\nexport const ptBR: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'senha não pode ser vazia',\n [ID_NO_APP_PASS_ERROR_MSG]: 'senha do app não pode ser vazia',\n [ID_PASSWORD_NOT_MATCH]: 'senhas diferentes',\n [ID_SET_BUTTON_BUY]: 'Ordem de compra de {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Ordem de venda de {{ asset }}',\n [ID_OFF]: 'desligar',\n [ID_READY]: 'pronto',\n [ID_LOCKED]: 'trancado',\n [ID_NO_WALLET]: 'sem carteira',\n [ID_WALLET_SYNC_PROGRESS]: 'carteira está {{ syncProgress }}% sincronizada',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'esconder configurações adicionais',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'mostrar configurações adicionais',\n [ID_BUY]: 'Comprar',\n [ID_SELL]: 'Vender',\n [ID_NOT_SUPPORTED]: '{{ asset }} não tem suporte',\n [ID_CONNECTION_FAILED]: 'Conexão ao server dex falhou. Pode fechar dexc e tentar novamente depois ou esperar para tentar se reconectar.',\n [ID_ORDER_PREVIEW]: 'Total: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'calculando...',\n [ID_ESTIMATE_UNAVAILABLE]: 'estimativa indisponível',\n [ID_NO_ZERO_RATE]: 'taxa não pode ser zero',\n [ID_NO_ZERO_QUANTITY]: 'quantidade não pode ser zero',\n [ID_TRADE]: 'troca',\n [ID_NO_ASSET_WALLET]: 'Sem carteira {{ asset }}',\n [ID_EXECUTED]: 'executado',\n [ID_BOOKED]: 'reservado',\n [ID_CANCELING]: 'cancelando',\n [ID_ACCT_UNDEFINED]: 'conta não definida.',\n [ID_KEEP_WALLET_PASS]: 'manter senha da carteira',\n [ID_NEW_WALLET_PASS]: 'definir nova senha para carteira',\n [ID_LOT]: 'lote',\n [ID_LOTS]: 'lotes',\n [ID_UNKNOWN]: 'desconhecido',\n [ID_EPOCH]: 'epoque',\n [ID_SETTLING]: 'assentando',\n [ID_NO_MATCH]: 'sem combinações',\n [ID_CANCELED]: 'cancelado',\n [ID_REVOKED]: 'revocado',\n [ID_WAITING_FOR_CONFS]: 'Esperando confirmações...',\n [ID_NONE_SELECTED]: 'nenhuma selecionado',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Sucesso no pagamento da taxa de registro!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'Erro de API',\n [ID_ADD]: 'Adicionar',\n [ID_CREATE]: 'Criar',\n [ID_WALLET_READY]: 'Escolher',\n [ID_SETUP_WALLET]: 'Configurar',\n [ID_CHANGE_WALLET_TYPE]: 'trocar o tipo de carteira',\n [ID_KEEP_WALLET_TYPE]: 'Não trocara tipo de carteira',\n [WALLET_READY]: 'Carteira Pronta',\n [SETUP_NEEDED]: 'Configuração Necessária',\n [ID_AVAILABLE]: 'disponível',\n [ID_IMMATURE]: 'imaturo'\n}\n\nexport const zhCN: Locale = {\n [ID_NO_PASS_ERROR_MSG]: '密码不能为空',\n [ID_NO_APP_PASS_ERROR_MSG]: '应用密码不能为空',\n [ID_PASSWORD_NOT_MATCH]: '密码不相同',\n [ID_SET_BUTTON_BUY]: '来自{{ asset }}的买入订单',\n [ID_SET_BUTTON_SELL]: '来自{{ asset }}的卖出订单',\n [ID_OFF]: '关闭',\n [ID_READY]: '准备就绪', // alt. 准备好\n [ID_LOCKED]: '锁',\n [ID_NO_WALLET]: '未连接钱包', // alt. 没有钱包\n [ID_WALLET_SYNC_PROGRESS]: '钱包同步进度{{ syncProgress }}%',\n [ID_HIDE_ADDITIONAL_SETTINGS]: '隐藏其它设置',\n [ID_SHOW_ADDITIONAL_SETTINGS]: '显示其它设置',\n [ID_BUY]: '买入',\n [ID_SELL]: '卖出',\n [ID_NOT_SUPPORTED]: '{{ asset }}不受支持',\n [ID_CONNECTION_FAILED]: '连接到服务器 dex 失败。您可以关闭 dexc 并稍后重试或等待尝试重新连接。',\n [ID_ORDER_PREVIEW]: '总计: {{ total }} {{ asset }}',\n [ID_CALCULATING]: '计算中...',\n [ID_ESTIMATE_UNAVAILABLE]: '估计不可用',\n [ID_NO_ZERO_RATE]: '汇率不能为零',\n [ID_NO_ZERO_QUANTITY]: '数量不能为零',\n [ID_TRADE]: '交易',\n [ID_NO_ASSET_WALLET]: '没有钱包 {{ asset }}',\n [ID_EXECUTED]: '执行',\n [ID_BOOKED]: '保留',\n [ID_CANCELING]: '取消',\n [ID_ACCT_UNDEFINED]: '帐户未定义。',\n [ID_KEEP_WALLET_PASS]: '保留钱包密码',\n [ID_NEW_WALLET_PASS]: '设置新的钱包密码',\n [ID_LOT]: '批处理',\n [ID_LOTS]: '批', // alt. 很多\n [ID_EPOCH]: '时间',\n [ID_API_ERROR]: '接口错误',\n [ID_ADD]: '添加',\n [ID_CREATE]: '创建',\n [ID_AVAILABLE]: '可用',\n [ID_IMMATURE]: '不成熟'\n}\n\nexport const plPL: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'hasło nie może być puste',\n [ID_NO_APP_PASS_ERROR_MSG]: 'hasło aplikacji nie może być puste',\n [ID_PASSWORD_NOT_MATCH]: 'hasła nie są jednakowe',\n [ID_SET_BUTTON_BUY]: 'Złóż zlecenie, aby kupić {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Złóż zlecenie, aby sprzedać {{ asset }}',\n [ID_OFF]: 'wyłączony',\n [ID_READY]: 'gotowy',\n [ID_LOCKED]: 'zablokowany',\n [ID_NO_WALLET]: 'brak portfela',\n [ID_WALLET_SYNC_PROGRESS]: 'portfel zsynchronizowany w {{ syncProgress }}%',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'ukryj dodatkowe ustawienia',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'pokaż dodatkowe ustawienia',\n [ID_BUY]: 'Kup',\n [ID_SELL]: 'Sprzedaj',\n [ID_NOT_SUPPORTED]: '{{ asset }} nie jest wspierany',\n [ID_CONNECTION_FAILED]: 'Połączenie z serwerem dex nie powiodło się. Możesz zamknąć dexc i spróbować ponownie później, lub poczekać na wznowienie połączenia.',\n [ID_ORDER_PREVIEW]: 'W sumie: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'obliczanie...',\n [ID_ESTIMATE_UNAVAILABLE]: 'brak szacunkowego wyliczenia',\n [ID_NO_ZERO_RATE]: 'zero nie może być ceną',\n [ID_NO_ZERO_QUANTITY]: 'zero nie może być ilością',\n [ID_TRADE]: 'handluj',\n [ID_NO_ASSET_WALLET]: 'Brak portfela {{ asset }}',\n [ID_EXECUTED]: 'wykonano',\n [ID_BOOKED]: 'zapisano',\n [ID_CANCELING]: 'anulowanie',\n [ID_ACCT_UNDEFINED]: 'Niezdefiniowane konto.',\n [ID_KEEP_WALLET_PASS]: 'zachowaj obecne hasło portfela',\n [ID_NEW_WALLET_PASS]: 'ustaw nowe hasło portfela',\n [ID_LOT]: 'lot',\n [ID_LOTS]: 'loty(ów)',\n [ID_UNKNOWN]: 'nieznane',\n [ID_EPOCH]: 'epoka',\n [ID_SETTLING]: 'rozliczanie',\n [ID_NO_MATCH]: 'brak spasowania',\n [ID_CANCELED]: 'anulowano',\n [ID_REVOKED]: 'unieważniono',\n [ID_WAITING_FOR_CONFS]: 'Oczekiwanie na potwierdzenia...',\n [ID_NONE_SELECTED]: 'brak zaznaczenia',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Płatność rejestracyjna powiodła się!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'błąd API',\n [ID_ADD]: 'Dodaj',\n [ID_CREATE]: 'Utwórz',\n [ID_WALLET_READY]: 'Gotowy',\n [ID_SETUP_WALLET]: 'Konfiguracja',\n [ID_CHANGE_WALLET_TYPE]: 'zmień typ portfela',\n [ID_KEEP_WALLET_TYPE]: 'nie zmieniaj typu portfela',\n [WALLET_READY]: 'Portfel jest gotowy',\n [SETUP_NEEDED]: 'Potrzebna konfiguracja',\n [ID_AVAILABLE]: 'dostępne',\n [ID_IMMATURE]: 'niedojrzałe'\n}\n\nexport const deDE: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'Passwort darf nicht leer sein',\n [ID_NO_APP_PASS_ERROR_MSG]: 'App-Passwort darf nicht leer sein',\n [ID_PASSWORD_NOT_MATCH]: 'Passwörter stimmen nicht überein',\n [ID_SET_BUTTON_BUY]: 'Platziere Auftrag zum Kauf von {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'Platziere Auftrag zum Verkauf von {{ asset }}',\n [ID_OFF]: 'aus',\n [ID_READY]: 'bereit',\n [ID_LOCKED]: 'gesperrt',\n [ID_NO_WALLET]: 'kein Wallet',\n [ID_WALLET_SYNC_PROGRESS]: 'Wallet ist zu {{ syncProgress }}% synchronisiert',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'zusätzliche Einstellungen ausblenden',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'zusätzliche Einstellungen anzeigen',\n [ID_BUY]: 'Kaufen',\n [ID_SELL]: 'Verkaufen',\n [ID_NOT_SUPPORTED]: '{{ asset }} wird nicht unterstützt',\n [ID_CONNECTION_FAILED]: 'Die Verbindung zum Dex-Server fehlgeschlagen. Du kannst dexc schließen und es später erneut versuchen oder warten bis die Verbindung wiederhergestellt ist.',\n [ID_ORDER_PREVIEW]: 'Insgesamt: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'kalkuliere...',\n [ID_ESTIMATE_UNAVAILABLE]: 'Schätzung nicht verfügbar',\n [ID_NO_ZERO_RATE]: 'Null-Satz nicht erlaubt',\n [ID_NO_ZERO_QUANTITY]: 'Null-Menge nicht erlaubt',\n [ID_TRADE]: 'Handel',\n [ID_NO_ASSET_WALLET]: 'Kein {{ asset }} Wallet',\n [ID_EXECUTED]: 'ausgeführt',\n [ID_BOOKED]: 'gebucht',\n [ID_CANCELING]: 'Abbruch',\n [ID_ACCT_UNDEFINED]: 'Account undefiniert.',\n [ID_KEEP_WALLET_PASS]: 'aktuelles Passwort für das Wallet behalten',\n [ID_NEW_WALLET_PASS]: 'ein neues Passwort für das Wallet festlegen',\n [ID_LOT]: 'Lot',\n [ID_LOTS]: 'Lots',\n [ID_UNKNOWN]: 'unbekannt',\n [ID_EPOCH]: 'Epoche',\n [ID_SETTLING]: 'Abwicklung',\n [ID_NO_MATCH]: 'kein Match',\n [ID_CANCELED]: 'abgebrochen',\n [ID_REVOKED]: 'widerrufen',\n [ID_WAITING_FOR_CONFS]: 'Warten auf Bestätigungen...',\n [ID_NONE_SELECTED]: 'keine ausgewählt',\n [ID_REGISTRATION_FEE_SUCCESS]: 'Zahlung der Registrierungsgebühr erfolgreich!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'API Fehler',\n [ID_ADD]: 'Hinzufügen',\n [ID_CREATE]: 'Erstellen',\n [ID_WALLET_READY]: 'Bereit',\n [ID_SETUP_WALLET]: 'Einrichten',\n [ID_CHANGE_WALLET_TYPE]: 'den Wallet-Typ ändern',\n [ID_KEEP_WALLET_TYPE]: 'den Wallet-Typ nicht ändern',\n [WALLET_READY]: 'Wallet bereit',\n [SETUP_NEEDED]: 'Einrichtung erforderlich',\n [WALLET_PENDING]: 'Erstelle Wallet',\n [ID_SEND_SUCCESS]: '{{ assetName }} gesendet!',\n [ID_RECONFIG_SUCCESS]: 'Wallet neu konfiguriert!',\n [ID_RESCAN_STARTED]: 'Wallet Rescan läuft',\n [ID_NEW_WALLET_SUCCESS]: '{{ assetName }} Wallet erstellt!',\n [ID_WALLET_UNLOCKED]: 'Wallet entsperrt'\n}\n\nexport const ar: Locale = {\n [ID_NO_PASS_ERROR_MSG]: 'لا يمكن أن تكون كلمة المرور فارغة',\n [ID_NO_APP_PASS_ERROR_MSG]: 'لا يمكن أن تكون كلمة مرور التطبيق فارغة',\n [ID_PASSWORD_NOT_MATCH]: 'كلمات المرور غير متطابقة',\n [ID_SET_BUTTON_BUY]: 'ضع طلبًا للشراء {{ asset }}',\n [ID_SET_BUTTON_SELL]: 'ضع طلبًا للبيع {{ asset }}',\n [ID_OFF]: 'إيقاف',\n [ID_READY]: 'متوقف',\n [ID_LOCKED]: 'مقفل',\n [ID_NO_WALLET]: 'لا توجد أي محفظة',\n [ID_WALLET_SYNC_PROGRESS]: 'تمت مزامنة {{ syncProgress }}% المحفظة',\n [ID_HIDE_ADDITIONAL_SETTINGS]: 'إخفاء الإعدادات الإضافية',\n [ID_SHOW_ADDITIONAL_SETTINGS]: 'عرض الإعدادات الإضافية',\n [ID_BUY]: 'شراء',\n [ID_SELL]: 'بيع',\n [ID_NOT_SUPPORTED]: '{{ asset }} غير مدعوم',\n [ID_CONNECTION_FAILED]: 'فشل الاتصال بخادم dex. يمكنك إغلاق dexc والمحاولة مرة أخرى لاحقًا أو انتظار إعادة الاتصال.',\n [ID_ORDER_PREVIEW]: 'إجمالي: {{ total }} {{ asset }}',\n [ID_CALCULATING]: 'جاري الحساب ...',\n [ID_ESTIMATE_UNAVAILABLE]: 'التقديرات غير متاحة',\n [ID_NO_ZERO_RATE]: 'معدل الصفر غير مسموح به',\n [ID_NO_ZERO_QUANTITY]: 'غير مسموح بالكمية الصفرية',\n [ID_TRADE]: 'التداول',\n [ID_NO_ASSET_WALLET]: 'لا توجد {{ asset }} محفظة',\n [ID_EXECUTED]: 'تم تنفيذها',\n [ID_BOOKED]: 'تم الحجز',\n [ID_CANCELING]: 'جارٍ الإلغاء',\n [ID_ACCT_UNDEFINED]: 'حساب غير محدد.',\n [ID_KEEP_WALLET_PASS]: 'احتفظ بكلمة مرور المحفظة الحالية',\n [ID_NEW_WALLET_PASS]: 'قم بتعيين كلمة مرور جديدة للمحفظة',\n [ID_LOT]: 'الحصة',\n [ID_LOTS]: 'الحصص',\n [ID_UNKNOWN]: 'غير معروف',\n [ID_EPOCH]: 'الحقبة الزمنية',\n [ID_SETTLING]: 'الإعدادات',\n [ID_NO_MATCH]: 'غير متطابقة',\n [ID_CANCELED]: 'ملغاة',\n [ID_REVOKED]: 'مستعادة',\n [ID_WAITING_FOR_CONFS]: 'في انتظار التأكيدات ...',\n [ID_NONE_SELECTED]: 'لم يتم تحديد أي شيء',\n [ID_REGISTRATION_FEE_SUCCESS]: 'تم دفع رسوم التسجيل بنجاح!', // TODO: reword from fee => bond\n [ID_API_ERROR]: 'خطأ في واجهة برمجة التطبيقات',\n [ID_ADD]: 'إضافة',\n [ID_CREATE]: 'إنشاء',\n [ID_WALLET_READY]: 'جاهزة',\n [ID_SETUP_WALLET]: 'إعداد',\n [ID_CHANGE_WALLET_TYPE]: 'تغيير نوع المحفظة',\n [ID_KEEP_WALLET_TYPE]: 'لا تغير نوع المحفظة',\n [WALLET_READY]: 'المحفظة جاهزة',\n [SETUP_NEEDED]: 'الإعداد مطلوب',\n [WALLET_PENDING]: 'إنشاء المحفظة',\n [ID_SEND_SUCCESS]: '{{ assetName }} تم الإرسال!',\n [ID_RECONFIG_SUCCESS]: 'تمت إعادة تهيئة المحفظة!!',\n [ID_RESCAN_STARTED]: 'إعادة فحص المحفظة قيد التشغيل',\n [ID_NEW_WALLET_SUCCESS]: '{{ assetName }} تم إنشاء المحفظة!',\n [ID_WALLET_UNLOCKED]: 'المحفظة غير مقفلة',\n [ID_SELLING]: 'البيع',\n [ID_BUYING]: 'Bالشراء',\n [ID_WALLET_ENABLED_MSG]: '{{ assetName }} المحفظة ممكنة',\n [ID_WALLET_DISABLED_MSG]: '{{ assetName }} المحفظة معطلة',\n [ID_DISABLED_MSG]: 'تم تعطيل المحفظة',\n [ID_ACTIVE_ORDERS_ERR_MSG]: '{{ assetName }} تدير المحفظة الطلبات بفعالية'\n}\n\nconst localesMap: Record = {\n 'en-us': enUS,\n 'pt-br': ptBR,\n 'zh-cn': zhCN,\n 'pl-pl': plPL,\n 'de-de': deDE,\n 'ar': ar\n}\n\n/* locale will hold the locale loaded via setLocale. */\nlet locale: Locale\n\nconst defaultLocale = enUS\n\n/*\n * setLocale read the language tag from the current document's html element lang\n * attribute and sets the locale. setLocale should be called once by the\n * application before prep is used.\n*/\nexport function setLocale () { locale = localesMap[document.documentElement.lang.toLowerCase()] }\n\n/* prep will format the message to the current locale. */\nexport function prep (k: string, args?: Record) {\n return stringTemplateParser(locale[k] || defaultLocale[k], args || {})\n}\n\n/*\n * stringTemplateParser is a template string matcher, where expression is any\n * text. It switches what is inside double brackets (e.g. 'buy {{ asset }}')\n * for the value described into args. args is an object with keys\n * equal to the placeholder keys. (e.g. {\"asset\": \"dcr\"}).\n * So that will be switched for: 'asset dcr'.\n */\nfunction stringTemplateParser (expression: string, args: Record) {\n // templateMatcher matches any text which:\n // is some {{ text }} between two brackets, and a space between them.\n // It is global, therefore it will change all occurrences found.\n // text can be anything, but brackets '{}' and space '\\s'\n const templateMatcher = /{{\\s?([^{}\\s]*)\\s?}}/g\n return expression.replace(templateMatcher, (_, value) => args[value])\n}\n\nwindow.localeDiscrepancies = () => {\n const ref = enUS\n for (const [lang, dict] of Object.entries(localesMap)) {\n if (dict === ref) continue\n for (const [k, s] of Object.entries(ref)) {\n if (!dict[k]) console.log(`${lang} needs a tranlation for: ${s}`)\n }\n }\n}\n","import * as intl from './locales'\nimport {\n UnitInfo,\n LayoutMetrics,\n WalletState,\n PageElement\n} from './registry'\n\nconst parser = new window.DOMParser()\n\nconst FPS = 30\n\nconst BipIDs: Record = {\n 0: 'btc',\n 42: 'dcr',\n 2: 'ltc',\n 22: 'mona',\n 28: 'vtc',\n 3: 'doge',\n 145: 'bch',\n 60: 'eth',\n 133: 'zec',\n 60000: 'dextt.eth',\n 60001: 'usdc.eth'\n}\n\nconst BipSymbols = Object.values(BipIDs)\n\nconst intFormatter = new Intl.NumberFormat((navigator.languages as string[]))\n\nconst threeSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 3,\n maximumSignificantDigits: 3\n})\n\nconst fiveSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 5,\n maximumSignificantDigits: 5\n})\n\n/* A cache for formatters used for Doc.formatCoinValue. */\nconst decimalFormatters = {}\n\n/*\n * decimalFormatter gets the formatCoinValue formatter for the specified decimal\n * precision.\n */\nfunction decimalFormatter (prec: number) {\n return formatter(decimalFormatters, 2, prec)\n}\n\n/* A cache for formatters used for Doc.formatFullPrecision. */\nconst fullPrecisionFormatters = {}\n\n/*\n * fullPrecisionFormatter gets the formatFullPrecision formatter for the\n * specified decimal precision.\n */\nfunction fullPrecisionFormatter (prec: number) {\n return formatter(fullPrecisionFormatters, prec, prec)\n}\n\n/*\n * formatter gets the formatter from the supplied cache if it already exists,\n * else creates it.\n */\nfunction formatter (formatters: Record, min: number, max: number): Intl.NumberFormat {\n const k = `${min}-${max}`\n let fmt = formatters[k]\n if (!fmt) {\n fmt = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumFractionDigits: min,\n maximumFractionDigits: max\n })\n formatters[k] = fmt\n }\n return fmt\n}\n\n/*\n * convertToConventional converts the value in atomic units to conventional\n * units.\n */\nfunction convertToConventional (v: number, unitInfo?: UnitInfo) {\n let prec = 8\n if (unitInfo) {\n const f = unitInfo.conventional.conversionFactor\n v /= f\n prec = Math.round(Math.log10(f))\n }\n return [v, prec]\n}\n\n// Helpers for working with the DOM.\nexport default class Doc {\n /*\n * idel is the element with the specified id that is the descendent of the\n * specified node.\n */\n static idel (el: Document | Element, id: string): HTMLElement {\n return el.querySelector(`#${id}`) as HTMLElement\n }\n\n /* bind binds the function to the event for the element. */\n static bind (el: EventTarget, ev: string, f: EventListenerOrEventListenerObject): void {\n el.addEventListener(ev, f)\n }\n\n /* unbind removes the handler for the event from the element. */\n static unbind (el: EventTarget, ev: string, f: (e: Event) => void): void {\n el.removeEventListener(ev, f)\n }\n\n /* noderize creates a Document object from a string of HTML. */\n static noderize (html: string): Document {\n return parser.parseFromString(html, 'text/html')\n }\n\n /*\n * mouseInElement returns true if the position of mouse event, e, is within\n * the bounds of the specified element or any of its descendents.\n */\n static mouseInElement (e: MouseEvent, el: HTMLElement): boolean {\n if (el.contains(e.target as Node)) return true\n const rect = el.getBoundingClientRect()\n return e.pageX >= rect.left && e.pageX <= rect.right &&\n e.pageY >= rect.top && e.pageY <= rect.bottom\n }\n\n /*\n * layoutMetrics gets information about the elements position on the page.\n */\n static layoutMetrics (el: HTMLElement): LayoutMetrics {\n const box = el.getBoundingClientRect()\n const docEl = document.documentElement\n const top = box.top + docEl.scrollTop\n const left = box.left + docEl.scrollLeft\n const w = el.offsetWidth\n const h = el.offsetHeight\n return {\n bodyTop: top,\n bodyLeft: left,\n width: w,\n height: h,\n centerX: left + w / 2,\n centerY: top + h / 2\n }\n }\n\n static descendentMetrics (parent: PageElement, kid: PageElement): LayoutMetrics {\n const parentMetrics = Doc.layoutMetrics(parent)\n const kidMetrics = Doc.layoutMetrics(kid)\n return {\n bodyTop: kidMetrics.bodyTop - parentMetrics.bodyTop,\n bodyLeft: kidMetrics.bodyLeft - parentMetrics.bodyLeft,\n width: kidMetrics.width,\n height: kidMetrics.height,\n centerX: kidMetrics.centerX - parentMetrics.bodyLeft,\n centerY: kidMetrics.centerY - parentMetrics.bodyTop\n }\n }\n\n /* empty removes all child nodes from the specified element. */\n static empty (...els: Element[]) {\n for (const el of els) while (el.firstChild) el.removeChild(el.firstChild)\n }\n\n /*\n * setContent removes all child nodes from the specified element and appends\n * passed elements.\n */\n static setContent (ancestor: PageElement, ...kids: PageElement[]) {\n Doc.empty(ancestor)\n for (const k of kids) ancestor.appendChild(k)\n }\n\n /*\n * hide hides the specified elements. This is accomplished by adding the\n * bootstrap d-hide class to the element. Use Doc.show to undo.\n */\n static hide (...els: Element[]) {\n for (const el of els) el.classList.add('d-hide')\n }\n\n /*\n * show shows the specified elements. This is accomplished by removing the\n * bootstrap d-hide class as added with Doc.hide.\n */\n static show (...els: Element[]) {\n for (const el of els) el.classList.remove('d-hide')\n }\n\n /*\n * show or hide the specified elements, based on value of the truthiness of\n * vis.\n */\n static setVis (vis: any, ...els: Element[]) {\n if (vis) Doc.show(...els)\n else Doc.hide(...els)\n }\n\n /* isHidden returns true if the specified element is hidden */\n static isHidden (el: Element): boolean {\n return el.classList.contains('d-hide')\n }\n\n /* isDisplayed returns true if the specified element is not hidden */\n static isDisplayed (el: Element): boolean {\n return !el.classList.contains('d-hide')\n }\n\n /*\n * animate runs the supplied function, which should be a \"progress\" function\n * accepting one argument. The progress function will be called repeatedly\n * with the argument varying from 0.0 to 1.0. The exact path that animate\n * takes from 0.0 to 1.0 will vary depending on the choice of easing\n * algorithm. See the Easing object for the available easing algo choices. The\n * default easing algorithm is linear.\n */\n static async animate (duration: number, f: (progress: number) => void, easingAlgo?: string) {\n await new Animation(duration, f, easingAlgo).wait()\n }\n\n static applySelector (ancestor: HTMLElement, k: string): PageElement[] {\n return Array.from(ancestor.querySelectorAll(k)) as PageElement[]\n }\n\n static kids (ancestor: HTMLElement): PageElement[] {\n return Array.from(ancestor.children) as PageElement[]\n }\n\n static safeSelector (ancestor: HTMLElement, k: string): PageElement {\n const el = ancestor.querySelector(k)\n if (el) return el as PageElement\n console.warn(`no element found for selector '${k}' on element ->`, ancestor)\n return document.createElement('div')\n }\n\n /*\n * idDescendants creates an object mapping to elements which are descendants\n * of the ancestor and have id attributes. Elements are keyed by their id\n * value.\n */\n static idDescendants (ancestor: HTMLElement): Record {\n const d: Record = {}\n for (const el of Doc.applySelector(ancestor, '[id]')) d[el.id] = el\n return d\n }\n\n /*\n * formatCoinValue formats the value in atomic units into a string\n * representation in conventional units. If the value happens to be an\n * integer, no decimals are displayed. Trailing zeros may be truncated.\n */\n static formatCoinValue (vAtomic: number, unitInfo?: UnitInfo): string {\n const [v, prec] = convertToConventional(vAtomic, unitInfo)\n if (Number.isInteger(v)) return intFormatter.format(v)\n return decimalFormatter(prec).format(v)\n }\n\n static formatThreeSigFigs (v: number): string {\n if (v >= 1000) return intFormatter.format(Math.round(v))\n return threeSigFigs.format(v)\n }\n\n static formatFiveSigFigs (v: number, prec?: number): string {\n if (v >= 10000) return intFormatter.format(Math.round(v))\n else if (v < 1e5) return fullPrecisionFormatter(prec ?? 8 /* rate encoding factor */).format(v)\n return fiveSigFigs.format(v)\n }\n\n /*\n * formatFullPrecision formats the value in atomic units into a string\n * representation in conventional units using the full decimal precision\n * associated with the conventional unit's conversion factor.\n */\n static formatFullPrecision (vAtomic: number, unitInfo?: UnitInfo): string {\n const [v, prec] = convertToConventional(vAtomic, unitInfo)\n return fullPrecisionFormatter(prec).format(v)\n }\n\n /*\n * formatFiatConversion formats the value in atomic units to its representation in\n * conventional units and returns the fiat value as a string.\n */\n static formatFiatConversion (vAtomic: number, rate: number, unitInfo?: UnitInfo): string {\n if (!rate || rate === 0) return intl.prep(intl.ID_UNAVAILABLE)\n const prec = 2\n const [v] = convertToConventional(vAtomic, unitInfo)\n const value = v * rate\n return fullPrecisionFormatter(prec).format(value)\n }\n\n /*\n * logoPath creates a path to a png logo for the specified ticker symbol. If\n * the symbol is not a supported asset, the generic letter logo will be\n * requested instead.\n */\n static logoPath (symbol: string): string {\n if (BipSymbols.indexOf(symbol) === -1) symbol = symbol.substring(0, 1)\n return `/img/coins/${symbol}.png`\n }\n\n static bipSymbol (assetID: number): string {\n return BipIDs[assetID]\n }\n\n static logoPathFromID (assetID: number): string {\n return Doc.logoPath(BipIDs[assetID])\n }\n\n /*\n * symbolize creates a token-aware symbol element for the asset's symbol. For\n * non-token assets, this is simply a SYMBOL. For tokens, it'll\n * be SYMBOLPARENT.\n */\n static symbolize (symbol: string): PageElement {\n const parts = symbol.split('.')\n const assetSymbol = document.createElement('span')\n assetSymbol.textContent = parts[0].toUpperCase()\n if (parts.length === 1) return assetSymbol\n const span = document.createElement('span')\n span.classList.add('token-aware-symbol')\n span.appendChild(assetSymbol)\n const parent = document.createElement('sup')\n parent.textContent = parts[1].toUpperCase()\n span.appendChild(parent)\n return span\n }\n\n /*\n * shortSymbol removes the short format of a symbol, with any parent chain\n * identifier removed\n */\n static shortSymbol (symbol: string): string {\n return symbol.split('.')[0].toUpperCase()\n }\n\n /*\n * cleanTemplates removes the elements from the DOM and deletes the id\n * attribute.\n */\n static cleanTemplates (...tmpls: HTMLElement[]) {\n tmpls.forEach(tmpl => {\n tmpl.remove()\n tmpl.removeAttribute('id')\n })\n }\n\n /*\n * tmplElement is a helper function for grabbing sub-elements of the market list\n * template.\n */\n static tmplElement (ancestor: Document | HTMLElement, s: string): PageElement {\n return ancestor.querySelector(`[data-tmpl=\"${s}\"]`) || document.createElement('div')\n }\n\n /*\n * parseTemplate returns an object of data-tmpl elements, keyed by their\n * data-tmpl values.\n */\n static parseTemplate (ancestor: HTMLElement): Record {\n const d: Record = {}\n for (const el of Doc.applySelector(ancestor, '[data-tmpl]')) d[el.dataset.tmpl || ''] = el\n return d\n }\n\n /*\n * timeSince returns a string representation of the duration since the\n * specified unix timestamp.\n */\n static timeSince (t: number): string {\n return Doc.formatDuration((new Date().getTime()) - t)\n }\n\n /* formatDuration returns a string representation of the duration */\n static formatDuration (dur: number): string {\n let seconds = Math.floor(dur)\n let result = ''\n let count = 0\n const add = (n: number, s: string) => {\n if (n > 0 || count > 0) count++\n if (n > 0) result += `${n} ${s} `\n return count >= 2\n }\n let y, mo, d, h, m, s\n [y, seconds] = timeMod(seconds, aYear)\n if (add(y, 'y')) { return result }\n [mo, seconds] = timeMod(seconds, aMonth)\n if (add(mo, 'mo')) { return result }\n [d, seconds] = timeMod(seconds, aDay)\n if (add(d, 'd')) { return result }\n [h, seconds] = timeMod(seconds, anHour)\n if (add(h, 'h')) { return result }\n [m, seconds] = timeMod(seconds, aMinute)\n if (add(m, 'm')) { return result }\n [s, seconds] = timeMod(seconds, 1000)\n add(s, 's')\n return result || '0 s'\n }\n\n /*\n * disableMouseWheel can be used to disable the mouse wheel for any\n * input. It is very easy to unknowingly scroll up on a number input\n * and then submit an unexpected value. This function prevents the\n * scroll increment/decrement behavior for a wheel action on a\n * number input.\n */\n static disableMouseWheel (...inputFields: Element[]) {\n for (const inputField of inputFields) {\n inputField.addEventListener('wheel', (ev) => {\n ev.preventDefault()\n })\n }\n }\n\n // showFormError can be used to set and display error message on forms.\n static showFormError (el: PageElement, msg: any) {\n el.textContent = msg\n Doc.show(el)\n }\n}\n\n/*\n * Animation is a handler for starting and stopping animations.\n */\nexport class Animation {\n done: (() => void) | undefined\n endAnimation: boolean\n thread: Promise\n static Forever: number\n\n constructor (duration: number, f: (progress: number) => void, easingAlgo?: string, done?: () => void) {\n this.done = done\n this.thread = this.run(duration, f, easingAlgo)\n }\n\n /*\n * run runs the animation function, increasing progress from 0 to 1 in a\n * manner dictated by easingAlgo.\n */\n async run (duration: number, f: (progress: number) => void, easingAlgo?: string) {\n duration = duration >= 0 ? duration : 1000 * 86400 * 365 * 10 // 10 years, in ms\n const easer = easingAlgo ? Easing[easingAlgo] : Easing.linear\n const start = new Date().getTime()\n const end = (duration === Animation.Forever) ? Number.MAX_SAFE_INTEGER : start + duration\n const range = end - start\n const frameDuration = 1000 / FPS\n let now = start\n this.endAnimation = false\n while (now < end) {\n if (this.endAnimation) return this.runCompletionFunction()\n f(easer((now - start) / range))\n await sleep(frameDuration)\n now = new Date().getTime()\n }\n f(1)\n this.runCompletionFunction()\n }\n\n /* wait returns a promise that will resolve when the animation completes. */\n async wait () {\n await this.thread\n }\n\n /* stop schedules the animation to exit at its next frame. */\n stop () {\n this.endAnimation = true\n }\n\n /*\n * stopAndWait stops the animations and returns a promise that will resolve\n * when the animation exits.\n */\n async stopAndWait () {\n this.stop()\n await this.wait()\n }\n\n /* runCompletionFunction runs any registered callback function */\n runCompletionFunction () {\n if (this.done) this.done()\n }\n}\nAnimation.Forever = -1\n\n/* Easing algorithms for animations. */\nexport const Easing: Record number> = {\n linear: t => t,\n easeIn: t => t * t,\n easeOut: t => t * (2 - t),\n easeInHard: t => t * t * t,\n easeOutHard: t => (--t) * t * t + 1,\n easeOutElastic: t => {\n const c4 = (2 * Math.PI) / 3\n return t === 0\n ? 0\n : t === 1\n ? 1\n : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1\n }\n}\n\n/* WalletIcons are used for controlling wallets in various places. */\nexport class WalletIcons {\n icons: Record\n status: Element\n\n constructor (box: HTMLElement) {\n const stateElement = (name: string) => box.querySelector(`[data-state=${name}]`) as HTMLElement\n this.icons = {}\n this.icons.sleeping = stateElement('sleeping')\n this.icons.locked = stateElement('locked')\n this.icons.unlocked = stateElement('unlocked')\n this.icons.nowallet = stateElement('nowallet')\n this.icons.syncing = stateElement('syncing')\n this.icons.nopeers = stateElement('nopeers')\n this.icons.disabled = stateElement('disabled')\n this.status = stateElement('status')\n }\n\n /* sleeping sets the icons to indicate that the wallet is not connected. */\n sleeping () {\n const i = this.icons\n Doc.hide(i.locked, i.unlocked, i.nowallet, i.syncing, i.disabled)\n Doc.show(i.sleeping)\n if (this.status) this.status.textContent = intl.prep(intl.ID_OFF)\n }\n\n /*\n * locked sets the icons to indicate that the wallet is connected, but locked.\n */\n locked () {\n const i = this.icons\n Doc.hide(i.unlocked, i.nowallet, i.sleeping, i.disabled)\n Doc.show(i.locked)\n if (this.status) this.status.textContent = intl.prep(intl.ID_LOCKED)\n }\n\n /*\n * unlocked sets the icons to indicate that the wallet is connected and\n * unlocked.\n */\n unlocked () {\n const i = this.icons\n Doc.hide(i.locked, i.nowallet, i.sleeping, i.disabled)\n Doc.show(i.unlocked)\n if (this.status) this.status.textContent = intl.prep(intl.ID_READY)\n }\n\n /* nowallet sets the icons to indicate that no wallet exists. */\n nowallet () {\n const i = this.icons\n Doc.hide(i.locked, i.unlocked, i.sleeping, i.syncing, i.disabled)\n Doc.show(i.nowallet)\n if (this.status) this.status.textContent = intl.prep(intl.ID_NO_WALLET)\n }\n\n /* set the icons to indicate that the wallet is disabled */\n disabled () {\n const i = this.icons\n Doc.hide(i.locked, i.unlocked, i.sleeping, i.syncing, i.nowallet, i.nopeers)\n Doc.show(i.disabled)\n i.disabled.dataset.tooltip = intl.prep(intl.ID_DISABLED_MSG)\n }\n\n setSyncing (wallet: WalletState | null) {\n const syncIcon = this.icons.syncing\n if (!wallet || !wallet.running || wallet.disabled) {\n Doc.hide(syncIcon)\n return\n }\n\n if (wallet.peerCount === 0) {\n Doc.show(this.icons.nopeers)\n Doc.hide(syncIcon) // potentially misleading with no peers\n return\n }\n Doc.hide(this.icons.nopeers)\n\n if (!wallet.synced) {\n Doc.show(syncIcon)\n syncIcon.dataset.tooltip = intl.prep(intl.ID_WALLET_SYNC_PROGRESS, { syncProgress: (wallet.syncProgress * 100).toFixed(1) })\n return\n }\n Doc.hide(syncIcon)\n }\n\n /* reads the core.Wallet state and sets the icon visibility. */\n readWallet (wallet: WalletState | null) {\n this.setSyncing(wallet)\n if (!wallet) return this.nowallet()\n switch (true) {\n case (wallet.disabled):\n this.disabled()\n break\n case (!wallet.running):\n this.sleeping()\n break\n case (!wallet.open):\n this.locked()\n break\n case (wallet.open):\n this.unlocked()\n break\n default:\n console.error('wallet in unknown state', wallet)\n }\n }\n}\n\n/* sleep can be used by async functions to pause for a specified period. */\nfunction sleep (ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nconst aYear = 31536000000\nconst aMonth = 2592000000\nconst aDay = 86400000\nconst anHour = 3600000\nconst aMinute = 60000\n\n/* timeMod returns the quotient and remainder of t / dur. */\nfunction timeMod (t: number, dur: number) {\n const n = Math.floor(t / dur)\n return [n, t - n * dur]\n}\n","// State is a set of static methods for working with the user state. It has\n// utilities for setting and retrieving cookies and storing user configuration\n// to localStorage.\nexport default class State {\n // Cookie keys.\n static darkModeCK = 'darkMode'\n static authCK = 'dexauth'\n static pwKeyCK = 'sessionkey'\n // Local storage keys (for data that we don't need at the server).\n static popupsLK = 'popups'\n static loggersLK = 'loggers'\n static recordersLK = 'recorders'\n static lastMarketLK = 'selectedMarket'\n static depthZoomLK = 'depthZoom'\n static lastMMMarketLK = 'mmMarket'\n static optionsExpansionLK = 'mmOptsExpand'\n static leftMarketDockLK = 'leftmarketdock'\n static selectedAssetLK = 'selectedasset'\n static notificationsLK = 'notifications'\n static orderDisclaimerAckedLK = 'ordAck'\n\n static setCookie (cname: string, cvalue: string) {\n const d = new Date()\n // Set cookie to expire in ten years.\n d.setTime(d.getTime() + (86400 * 365 * 10 * 1000))\n const expires = 'expires=' + d.toUTCString()\n document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/'\n }\n\n /*\n * getCookie returns the value at the specified cookie name, otherwise null.\n */\n static getCookie (cname: string) {\n for (const cstr of document.cookie.split(';')) {\n const [k, v] = cstr.split('=')\n if (k.trim() === cname) return v\n }\n return null\n }\n\n /*\n * removeCookie tells the browser to stop using cookie. It's not enough to simply\n * erase cookie value because browser will still send it to the server (with empty\n * value), and that's not what server expects.\n */\n static removeCookie (cKey: string) {\n document.cookie = `${cKey}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`\n }\n\n /*\n * isDark returns true if the dark-mode cookie is currently set to '1' = true.\n */\n static isDark () {\n return document.cookie.split(';').filter((item) => item.includes(`${State.darkModeCK}=1`)).length\n }\n\n /* passwordIsCached returns whether or not there is a cached password in the cookies. */\n static passwordIsCached () {\n return !!this.getCookie(State.pwKeyCK)\n }\n\n /* storeLocal puts the key-value pair into Window.localStorage. */\n static storeLocal (k: string, v: any) {\n window.localStorage.setItem(k, JSON.stringify(v))\n }\n\n /*\n * fetchLocal the value associated with the key in Window.localStorage, or\n * null if the no value exists for the key.\n */\n static fetchLocal (k: string) {\n const v = window.localStorage.getItem(k)\n if (v !== null) {\n return JSON.parse(v)\n }\n return null\n }\n\n /* removeLocal removes the key-value pair from Window.localStorage. */\n static removeLocal (k: string) {\n window.localStorage.removeItem(k)\n }\n}\n\n// Setting defaults here, unless specific cookie (or local storage) value was already chosen by the user.\nif (State.getCookie(State.darkModeCK) === null) State.setCookie(State.darkModeCK, '1')\nif (State.fetchLocal(State.popupsLK) === null) State.storeLocal(State.popupsLK, '1')\nif (State.fetchLocal(State.leftMarketDockLK) === null) State.storeLocal(State.leftMarketDockLK, '1')\n","export default function _assertThisInitialized(self) {\n if (self === void 0) {\n throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n }\n return self;\n}","export default function _setPrototypeOf(o, p) {\n _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\n return _setPrototypeOf(o, p);\n}","import setPrototypeOf from \"./setPrototypeOf.js\";\nexport default function _inherits(subClass, superClass) {\n if (typeof superClass !== \"function\" && superClass !== null) {\n throw new TypeError(\"Super expression must either be null or a function\");\n }\n subClass.prototype = Object.create(superClass && superClass.prototype, {\n constructor: {\n value: subClass,\n writable: true,\n configurable: true\n }\n });\n Object.defineProperty(subClass, \"prototype\", {\n writable: false\n });\n if (superClass) setPrototypeOf(subClass, superClass);\n}","import _typeof from \"./typeof.js\";\nimport assertThisInitialized from \"./assertThisInitialized.js\";\nexport default function _possibleConstructorReturn(self, call) {\n if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) {\n return call;\n } else if (call !== void 0) {\n throw new TypeError(\"Derived constructors may only return object or undefined\");\n }\n return assertThisInitialized(self);\n}","export default function _getPrototypeOf(o) {\n _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {\n return o.__proto__ || Object.getPrototypeOf(o);\n };\n return _getPrototypeOf(o);\n}","/*\n * requestJSON encodes the object and sends the JSON to the specified address.\n */\nexport async function requestJSON (method: string, addr: string, reqBody?: any): Promise {\n try {\n const response = await window.fetch(addr, {\n method: method,\n headers: new window.Headers({ 'content-type': 'application/json' }),\n // credentials: \"same-origin\",\n body: reqBody\n })\n if (response.status !== 200) { throw response }\n const obj = await response.json()\n obj.requestSuccessful = true\n return obj\n } catch (response) {\n response.requestSuccessful = false\n response.msg = await response.text()\n return response\n }\n}\n\n/*\n * postJSON sends a POST request with JSON-formatted data and returns the\n * response.\n */\nexport async function postJSON (addr: string, data?: any) {\n return requestJSON('POST', addr, JSON.stringify(data))\n}\n\n/*\n * getJSON sends a GET request and returns the response.\n */\nexport async function getJSON (addr: string): Promise {\n return requestJSON('GET', addr)\n}\n\nexport enum Errors {\n walletErr,\n walletAuthErr,\n walletBalanceErr,\n dupeDEXErr,\n assetSupportErr,\n registerErr,\n signatureErr,\n zeroFeeErr,\n feeMismatchErr,\n feeSendErr,\n passwordErr,\n emptyHostErr,\n connectionErr,\n acctKeyErr,\n unknownOrderErr,\n orderParamsErr,\n dbErr,\n authErr,\n connectWalletErr,\n missingWalletErr,\n encryptionErr,\n decodeErr,\n accountVerificationErr,\n accountProofErr,\n parseKeyErr,\n marketErr,\n addressParseErr,\n addrErr,\n fileReadErr,\n unknownDEXErr,\n accountRetrieveErr,\n accountDisableErr,\n suspendedAcctErr,\n existenceCheckErr,\n createWalletErr,\n activeOrdersErr,\n newAddrErr,\n}\n","declare global {\n interface Window {\n log: (...args: any) => void\n enableLogger: (loggerID: string, enable: boolean) => void\n recordLogger: (loggerID: string, enable: boolean) => void\n dumpLogger: (loggerID: string) => void\n localeDiscrepancies: () => void\n }\n}\n\nexport enum ConnectionStatus {\n Disconnected = 0,\n Connected = 1,\n InvalidCert = 2,\n}\n\nexport interface BondOptions {\n bondAsset: number\n targetTier: number\n maxBondedAmt: number\n}\n\nexport interface Exchange {\n host: string\n acctID: string\n markets: Record\n assets: Record\n connectionStatus: ConnectionStatus\n viewOnly: boolean\n bondAssets: Record\n tier: number\n bondOptions: BondOptions\n pendingBonds: Record\n candleDurs: string[]\n}\n\nexport interface Candle {\n startStamp: number\n endStamp: number\n matchVolume: number\n quoteVolume: number\n highRate: number\n lowRate: number\n startRate: number\n endRate: number\n}\n\nexport interface CandlesPayload {\n dur: string\n ms: number\n candles: Candle[]\n}\n\nexport interface Market {\n name: string\n baseid: number\n basesymbol: string\n quoteid: number\n quotesymbol: string\n lotsize: number\n ratestep: number\n epochlen: number\n startepoch: number\n buybuffer: number\n orders: Order[]\n spot: Spot | undefined\n atomToConv: number\n inflight: InFlightOrder[]\n}\n\nexport interface InFlightOrder extends Order {\n tempID: number\n}\n\nexport interface Order {\n host: string\n baseID: number\n baseSymbol: string\n quoteID: number\n quoteSymbol: string\n market: string\n type: number\n id: string\n stamp: number\n submitTime: number\n sig: string\n status: number\n epoch: number\n qty: number\n sell: boolean\n filled: number\n matches: Match[]\n cancelling: boolean\n canceled: boolean\n feesPaid: FeeBreakdown\n fundingCoins: Coin[]\n accelerationCoins: Coin[]\n lockedamt: number\n rate: number // limit only\n tif: number // limit only\n targetOrderID: string // cancel only\n readyToTick: boolean\n}\n\nexport interface Match {\n matchID: string\n status: number\n active: boolean\n revoked: boolean\n rate: number\n qty: number\n side: number\n feeRate: number\n swap: Coin\n counterSwap: Coin\n redeem: Coin\n counterRedeem: Coin\n refund: Coin\n stamp: number\n isCancel: boolean\n}\n\nexport interface Spot {\n stamp: number\n baseID: number\n quoteID: number\n rate: number\n bookVolume: number // Unused?\n change24: number\n vol24: number\n}\n\nexport interface Asset {\n id: number\n symbol: string\n version: number\n maxFeeRate: number\n swapSize: number\n swapSizeBase: number\n redeemSize: number\n swapConf: number\n unitInfo: UnitInfo\n}\n\nexport interface BondAsset {\n ver: number\n id: number\n confs: number\n amount: number\n}\n\nexport interface PendingBondState {\n symbol: string\n assetID: number\n confs: number\n}\n\nexport interface FeeBreakdown {\n swap: number\n redemption: number\n}\n\nexport interface SupportedAsset {\n id: number\n symbol: string\n name: string\n wallet: WalletState\n info?: WalletInfo\n token?: Token\n unitInfo: UnitInfo\n walletCreationPending: boolean\n}\n\nexport interface Token {\n parentID: number\n name: string\n unitInfo: UnitInfo\n definition: WalletDefinition\n}\n\nexport interface WalletState {\n symbol: string\n assetID: number\n version: number\n type: string\n traits: number\n open: boolean\n running: boolean\n disabled: boolean\n balance: WalletBalance\n address: string\n units: string\n encrypted: boolean\n peerCount: number\n synced: boolean\n syncProgress: number\n}\n\nexport interface WalletInfo {\n name: string\n version: number\n availablewallets: WalletDefinition[]\n versions: number[]\n emptyidx: number\n unitinfo: UnitInfo\n}\n\nexport interface WalletBalance {\n available: number\n immature: number\n locked: number\n stamp: string // time.Time\n orderlocked: number\n contractlocked: number\n bondlocked: number\n other: Record\n}\n\nexport interface WalletDefinition {\n seeded: boolean\n type: string\n tab: string\n description: string\n configpath: string\n configopts: ConfigOption[]\n noauth: boolean\n}\n\nexport interface ConfigOption {\n key: string\n displayname: string\n description: string\n default: any\n max: any\n min: any\n noecho: boolean\n isboolean: boolean\n isdate: boolean\n disablewhenactive: boolean\n isBirthdayConfig: boolean\n repeatable?: string\n noauth: boolean\n regAsset?: number\n required?: boolean\n}\n\nexport interface Coin {\n id: string\n stringID: string\n assetID: number\n symbol: string\n confs: Confirmations\n}\n\nexport interface Confirmations {\n required: number\n count: number\n}\n\nexport interface UnitInfo {\n atomicUnit: string\n conventional: Denomination\n denominations: Denomination[]\n}\n\nexport interface Denomination {\n unit: string\n conversionFactor: number\n}\n\nexport interface User {\n exchanges: Record\n inited: boolean\n seedgentime: number\n assets: Record\n fiatRates: Record\n authed: boolean // added by webserver\n ok: boolean // added by webserver\n bots: BotReport[]\n}\n\nexport interface CoreNote {\n type: string\n topic: string\n subject: string\n details: string\n severity: number\n stamp: number\n acked: boolean\n id: string\n}\n\nexport interface BondNote extends CoreNote {\n asset: number\n confirmations: number\n dex: string\n coinID: string | null\n tier: number | null\n}\n\nexport interface BalanceNote extends CoreNote {\n assetID: number\n balance: WalletBalance\n}\n\nexport interface RateNote extends CoreNote {\n fiatRates: Record\n}\n\nexport interface WalletConfigNote extends CoreNote {\n wallet: WalletState\n}\n\nexport type WalletStateNote = WalletConfigNote\n\nexport interface WalletCreationNote extends CoreNote {\n assetID: number\n}\n\nexport interface SpotPriceNote extends CoreNote {\n host: string\n spots: Record\n}\n\nexport interface BotNote extends CoreNote {\n report: BotReport\n}\n\nexport interface MatchNote extends CoreNote {\n orderID: string\n match: Match\n host: string\n marketID: string\n}\n\nexport interface ConnEventNote extends CoreNote {\n host: string\n connectionStatus: ConnectionStatus\n}\n\nexport interface OrderNote extends CoreNote {\n order: Order\n tempID: number\n}\n\nexport interface RecentMatch {\n rate: number\n qty: number\n stamp: number\n sell: boolean\n}\n\nexport interface EpochNote extends CoreNote {\n host: string\n marketID: string\n epoch: number\n}\n\nexport interface APIResponse {\n requestSuccessful: boolean\n ok: boolean\n msg: string\n err?: string\n}\n\nexport interface LogMessage {\n time: string\n msg: string\n}\n\nexport interface NoteElement extends HTMLElement {\n note: CoreNote\n}\n\nexport interface BalanceResponse extends APIResponse {\n balance: WalletBalance\n}\n\nexport interface LayoutMetrics {\n bodyTop: number\n bodyLeft: number\n width: number\n height: number\n centerX: number\n centerY: number\n}\n\nexport interface PasswordCache {\n pw: string\n}\n\nexport interface PageElement extends HTMLElement {\n value?: string\n src?: string\n files?: FileList\n checked?: boolean\n href?: string\n htmlFor?: string\n}\n\nexport interface BooleanConfig {\n reason: string\n}\n\nexport interface XYRangePoint {\n label: string\n x: number\n y: number\n}\n\nexport interface XYRange {\n start: XYRangePoint\n end: XYRangePoint\n xUnit: string\n yUnit: string\n}\n\nexport interface OrderOption extends ConfigOption {\n boolean?: BooleanConfig\n xyRange?: XYRange\n showByDefault?: boolean\n}\n\nexport interface SwapEstimate {\n lots: number\n value: number\n maxFees: number\n realisticWorstCase: number\n realisticBestCase: number\n}\n\nexport interface RedeemEstimate {\n realisticBestCase: number\n realisticWorstCase: number\n}\n\nexport interface PreSwap {\n estimate: SwapEstimate\n options: OrderOption[]\n}\n\nexport interface PreRedeem {\n estimate: RedeemEstimate\n options: OrderOption[]\n}\n\nexport interface OrderEstimate {\n swap: PreSwap\n redeem: PreRedeem\n}\n\nexport interface MaxOrderEstimate {\n swap: SwapEstimate\n redeem: RedeemEstimate\n}\n\nexport interface MaxSell {\n maxSell: MaxOrderEstimate\n}\n\nexport interface MaxBuy {\n maxBuy: MaxOrderEstimate\n}\n\nexport interface TradeForm {\n host: string\n isLimit: boolean\n sell: boolean\n base: number\n quote: number\n qty: number\n rate: number\n tifnow: boolean\n options: Record\n}\n\nexport interface BookUpdate {\n action: string\n host: string\n marketID: string\n matchesSummary: RecentMatch[]\n payload: any\n}\n\nexport interface MiniOrder {\n qty: number\n qtyAtomic: number\n rate: number\n msgRate: number\n epoch: number\n sell: boolean\n token: string\n}\n\nexport interface CoreOrderBook {\n sells: MiniOrder[]\n buys: MiniOrder[]\n epoch: MiniOrder[]\n recentMatches: RecentMatch[]\n}\n\nexport interface MarketOrderBook {\n base: number\n quote: number\n book: CoreOrderBook\n}\n\nexport interface RemainderUpdate {\n token: string\n qty: number\n qtyAtomic: number\n}\n\nexport interface OrderFilter {\n n?: number\n offset?: string\n hosts: string[]\n assets: number[]\n statuses: number[]\n}\n\nexport interface MakerProgram {\n host: string\n baseID: number\n quoteID: number\n lots: number\n oracleWeighting: number\n oracleBias: number\n driftTolerance: number\n gapFactor: number\n gapStrategy: string\n}\n\nexport interface BotOrder {\n host: string\n marketID: string\n orderID: string\n}\n\nexport interface BotReport {\n programID: number\n program: MakerProgram\n running: boolean\n orders: BotOrder\n}\n\nexport interface MarketReport {\n basisPrice: number\n price: number\n oracles: OracleReport[]\n breakEvenSpread: number\n}\n\nexport interface OracleReport {\n host: string\n usdVol: number\n bestBuy: number\n bestSell: number\n}\n\n// changing the order of the elements in this enum will affect\n// the sorting of the peers table in wallets.ts.\nexport enum PeerSource {\n WalletDefault,\n UserAdded,\n Discovered,\n}\n\nexport interface WalletPeer {\n addr: string\n source: PeerSource\n connected: boolean\n}\n\nexport interface Application {\n assets: Record\n seedGenTime: number\n user: User\n header: HTMLElement\n headerSpace: HTMLElement\n walletMap: Record\n exchanges: Record\n fiatRatesMap: Record\n showPopups: boolean\n commitHash: string\n authed(): boolean\n start (): Promise\n reconnected (): void\n fetchUser (): Promise\n loadPage (page: string, data?: any, skipPush?: boolean): Promise\n attach (data: any): void\n bindTooltips (ancestor: HTMLElement): void\n attachHeader (): void\n showDropdown (icon: HTMLElement, dialog: HTMLElement): void\n ackNotes (): void\n setNoteTimes (noteList: HTMLElement): void\n bindInternalNavigation (ancestor: HTMLElement): void\n storeNotes (): void\n updateMenuItemsDisplay (): void\n attachCommon (node: HTMLElement): void\n updateBondConfs (dexAddr: string, coinID: string, confs: number, assetID: number): void\n handleBondNote (note: BondNote): void\n setNotes (notes: CoreNote[]): void\n notify (note: CoreNote): void\n log (loggerID: string, ...msg: any): void\n prependPokeElement (note: CoreNote): void\n prependNoteElement (note: CoreNote, skipSave?: boolean): void\n prependListElement (noteList: HTMLElement, note: CoreNote, el: NoteElement): void\n loading (el: HTMLElement): () => void\n orders (host: string, mktID: string): Order[]\n haveActiveOrders (assetID: number): boolean\n order (oid: string): Order | null\n canAccelerateOrder(order: Order): boolean\n unitInfo (assetID: number, xc?: Exchange): UnitInfo\n conventionalRate (baseID: number, quoteID: number, encRate: number, xc?: Exchange): number\n walletDefinition (assetID: number, walletType: string): WalletDefinition\n currentWalletDefinition (assetID: number): WalletDefinition\n fetchBalance (assetID: number): Promise\n checkResponse (resp: APIResponse): boolean\n signOut (): Promise\n registerNoteFeeder (receivers: Record void>): void\n}\n\n// TODO: Define an interface for Application?\nlet application: Application\n\nexport function registerApplication (a: Application) {\n application = a\n}\n\nexport function app (): Application {\n return application\n}\n","import Doc from './doc'\nimport {\n PageElement,\n XYRange,\n OrderOption\n} from './registry'\n\ninterface OptionsReporters {\n enable: () => void\n disable: () => void\n}\n\n// Having the caller set these vars on load using an exported function makes\n// life easier.\nlet orderOptTmpl: HTMLElement, booleanOptTmpl: HTMLElement, rangeOptTmpl: HTMLElement\n\n// setOptionTemplates sets the package vars for the templates and application.\nexport function setOptionTemplates (page: Record): void {\n [booleanOptTmpl, rangeOptTmpl, orderOptTmpl] = [page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl]\n}\n\nconst threeSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 3,\n maximumSignificantDigits: 3\n})\n\n/*\n * Option is a base class for option elements. Option stores some common\n * parameters and monitors the toggle switch, calling the child class's\n * enable/disable methods when the user manually turns the option on or off.\n */\nexport class Option {\n opt: OrderOption\n node: HTMLElement\n tmpl: Record\n on: boolean\n\n constructor (opt: OrderOption, symbol: string, report: OptionsReporters) {\n this.opt = opt\n const node = this.node = orderOptTmpl.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(node)\n\n tmpl.optName.textContent = opt.displayname\n tmpl.tooltip.dataset.tooltip = opt.description\n\n // const isBaseChain = (isSwapOption && order.sell) || (!isSwapOption && !order.sell)\n // const symbol = isBaseChain ? this.baseSymbol() : this.quoteSymbol()\n if (symbol) tmpl.chainIcon.src = Doc.logoPath(symbol)\n else Doc.hide(tmpl.chainIcon)\n\n this.on = false\n Doc.bind(node, 'click', () => {\n if (this.on) return\n this.on = true\n node.classList.add('selected')\n report.enable()\n })\n Doc.bind(tmpl.toggle, 'click', e => {\n if (!this.on) return\n e.stopPropagation()\n this.on = false\n node.classList.remove('selected')\n report.disable()\n })\n }\n}\n\n/*\n * BooleanOption is a simple on/off option with a short summary of it's effects.\n * BooleanOrderOption is the handler for a *BooleanConfig from client/asset.\n */\nexport class BooleanOption extends Option {\n control: HTMLElement\n changed: () => void\n dict: Record\n\n constructor (opt: OrderOption, symbol: string, dict: Record, changed: () => void) {\n super(opt, symbol, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.dict = dict\n this.changed = () => changed()\n if (opt.boolean === undefined) throw Error('not a boolean opt')\n const cfg = opt.boolean\n const control = this.control = booleanOptTmpl.cloneNode(true) as HTMLElement\n // Append to parent's options div.\n this.tmpl.controls.appendChild(control)\n const tmpl = Doc.parseTemplate(control)\n tmpl.reason.textContent = cfg.reason\n this.on = typeof dict[opt.key] !== 'undefined' ? dict[opt.key] : opt.default\n if (this.on) this.node.classList.add('selected')\n }\n\n store (): void {\n if (this.on === this.opt.default) delete this.dict[this.opt.key]\n else this.dict[this.opt.key] = this.on\n this.changed()\n }\n\n enable (): void {\n this.store()\n }\n\n disable (): void {\n this.store()\n }\n}\n\n/*\n * XYRangeOption is an order option that contains an XYRangeHandler. The logic\n * for handling the slider to is defined in XYRangeHandler so that the slider\n * can be used without being contained in an order option.\n */\nexport class XYRangeOption extends Option {\n handler: XYRangeHandler\n x: number\n changed: () => void\n dict: Record\n\n constructor (opt: OrderOption, symbol: string, dict: Record, changed: () => void) {\n super(opt, symbol, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.dict = dict\n this.changed = changed\n if (opt.xyRange === undefined) throw Error('not an xy range opt')\n const cfg = opt.xyRange\n const setVal = dict[opt.key]\n this.on = typeof setVal !== 'undefined'\n if (this.on) {\n this.node.classList.add('selected')\n this.x = setVal\n } else {\n this.x = opt.default\n }\n const onUpdate = (x: number) => {\n this.x = x\n this.dict[this.opt.key] = x\n }\n const onChange = () => { this.changed() }\n const selected = () => { this.node.classList.add('selected') }\n this.handler = new XYRangeHandler(cfg, this.x, onUpdate, onChange, selected)\n this.tmpl.controls.appendChild(this.handler.control)\n }\n\n enable (): void {\n this.dict[this.opt.key] = this.x\n this.changed()\n }\n\n disable (): void {\n delete this.dict[this.opt.key]\n this.changed()\n }\n\n setValue (x: number): void {\n this.handler.setValue(x)\n this.on = true\n this.node.classList.add('selected')\n }\n}\n\n/*\n * XYRangeHandler is the handler for an *XYRange from client/asset. XYRange\n * has a slider which allows adjusting the x and y, linearly between two limits.\n * The user can also manually enter values for x or y.\n */\nexport class XYRangeHandler {\n control: HTMLElement\n cfg: XYRange\n tmpl: Record\n x: number\n scrollingX: number\n y: number\n r: number\n roundY: boolean\n updated: (x:number, y:number) => void\n changed: () => void\n selected: () => void\n setConfig: (cfg: XYRange) => void\n\n constructor (cfg: XYRange, initVal: number, updated: (x:number, y:number) => void, changed: () => void, selected: () => void, roundY?: boolean) {\n const control = this.control = rangeOptTmpl.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(control)\n this.roundY = Boolean(roundY)\n this.cfg = cfg\n\n this.changed = changed\n this.selected = selected\n this.updated = updated\n\n const { slider, handle } = tmpl\n\n let rangeX = cfg.end.x - cfg.start.x\n let rangeY = cfg.end.y - cfg.start.y\n const normalizeX = (x: number) => (x - cfg.start.x) / rangeX\n\n const setConfig = (newCfg: XYRange) => {\n rangeX = newCfg.end.x - newCfg.start.x\n rangeY = newCfg.end.y - newCfg.start.y\n cfg = this.cfg = newCfg\n tmpl.rangeLblStart.textContent = cfg.start.label\n tmpl.rangeLblEnd.textContent = cfg.end.label\n tmpl.xUnit.textContent = cfg.xUnit\n tmpl.yUnit.textContent = cfg.yUnit\n this.y = this.r * rangeY + cfg.start.y\n this.r = (this.y - cfg.start.y) / rangeY\n this.scrollingX = this.r * rangeX + cfg.start.x\n }\n setConfig(cfg)\n\n this.setConfig = (cfg: XYRange) => {\n setConfig(cfg)\n this.accept(this.scrollingX)\n }\n\n // r, x, and y will be updated by the various input event handlers. r is\n // x (or y) normalized on its range, e.g. [x_min, x_max] -> [0, 1]\n this.r = normalizeX(initVal)\n this.scrollingX = this.x = initVal\n this.y = this.r * rangeY + cfg.start.y\n\n // Set up the handlers for the x and y text input fields.\n const clickOutX = (e: MouseEvent) => {\n if (e.type !== 'change' && e.target === tmpl.xInput) return\n const s = tmpl.xInput.value\n if (s) {\n const xx = parseFloat(s)\n if (!isNaN(xx)) {\n this.scrollingX = clamp(xx, cfg.start.x, cfg.end.x)\n this.r = normalizeX(this.scrollingX)\n this.y = this.r * rangeY + cfg.start.y\n this.accept(this.scrollingX)\n }\n }\n Doc.hide(tmpl.xInput)\n Doc.show(tmpl.x)\n Doc.unbind(document, 'click', clickOutX)\n this.changed()\n }\n\n Doc.bind(tmpl.x, 'click', e => {\n Doc.hide(tmpl.x)\n Doc.show(tmpl.xInput)\n tmpl.xInput.focus()\n tmpl.xInput.value = threeSigFigs.format(this.scrollingX)\n Doc.bind(document, 'click', clickOutX)\n e.stopPropagation()\n })\n\n Doc.bind(tmpl.xInput, 'change', clickOutX)\n\n const clickOutY = (e: MouseEvent) => {\n if (e.type !== 'change' && e.target === tmpl.yInput) return\n const s = tmpl.yInput.value\n if (s) {\n const yy = parseFloat(s)\n if (!isNaN(yy)) {\n this.y = clamp(yy, cfg.start.y, cfg.end.y)\n this.r = (this.y - cfg.start.y) / rangeY\n this.scrollingX = cfg.start.x + this.r * rangeX\n this.accept(this.scrollingX)\n }\n }\n Doc.hide(tmpl.yInput)\n Doc.show(tmpl.y)\n Doc.unbind(document, 'click', clickOutY)\n this.changed()\n }\n\n Doc.bind(tmpl.y, 'click', e => {\n Doc.hide(tmpl.y)\n Doc.show(tmpl.yInput)\n tmpl.yInput.focus()\n tmpl.yInput.value = threeSigFigs.format(this.y)\n Doc.bind(document, 'click', clickOutY)\n e.stopPropagation()\n })\n\n Doc.bind(tmpl.yInput, 'change', clickOutY)\n\n // Read the slider.\n Doc.bind(handle, 'mousedown', (e: MouseEvent) => {\n if (e.button !== 0) return\n e.preventDefault()\n this.selected()\n const startX = e.pageX\n const w = slider.clientWidth - handle.offsetWidth\n const startLeft = normalizeX(this.scrollingX) * w\n const left = (ee: MouseEvent) => Math.max(Math.min(startLeft + (ee.pageX - startX), w), 0)\n const trackMouse = (ee: MouseEvent) => {\n ee.preventDefault()\n this.r = left(ee) / w\n this.scrollingX = this.r * rangeX + cfg.start.x\n this.y = this.r * rangeY + cfg.start.y\n this.accept(this.scrollingX)\n }\n const mouseUp = (ee: MouseEvent) => {\n trackMouse(ee)\n Doc.unbind(document, 'mousemove', trackMouse)\n Doc.unbind(document, 'mouseup', mouseUp)\n this.changed()\n }\n Doc.bind(document, 'mousemove', trackMouse)\n Doc.bind(document, 'mouseup', mouseUp)\n })\n\n this.accept(this.scrollingX, true)\n }\n\n accept (x: number, skipUpdate?: boolean): void {\n const tmpl = this.tmpl\n if (this.roundY) this.y = Math.round(this.y)\n tmpl.x.textContent = threeSigFigs.format(x)\n tmpl.y.textContent = threeSigFigs.format(this.y)\n if (this.roundY) tmpl.y.textContent = `${this.y}`\n tmpl.handle.style.left = `calc(${this.r * 100}% - ${this.r * 14}px)`\n this.x = x\n this.scrollingX = x\n if (!skipUpdate) this.updated(x, this.y)\n }\n\n setValue (x: number) {\n const cfg = this.cfg\n this.r = (x - cfg.start.x) / (cfg.end.x - cfg.start.x)\n this.y = cfg.start.y + this.r * (cfg.end.y - cfg.start.y)\n this.accept(x, true)\n }\n}\n\nconst clamp = (v: number, min: number, max: number): number => v < min ? min : v > max ? max : v\n","export default class BasePage {\n /* unload is called when the user navigates away from the page. */\n unload () {\n // should be implemented by inheriting class.\n }\n}\n","import arrayWithoutHoles from \"./arrayWithoutHoles.js\";\nimport iterableToArray from \"./iterableToArray.js\";\nimport unsupportedIterableToArray from \"./unsupportedIterableToArray.js\";\nimport nonIterableSpread from \"./nonIterableSpread.js\";\nexport default function _toConsumableArray(arr) {\n return arrayWithoutHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableSpread();\n}","import arrayLikeToArray from \"./arrayLikeToArray.js\";\nexport default function _arrayWithoutHoles(arr) {\n if (Array.isArray(arr)) return arrayLikeToArray(arr);\n}","export default function _iterableToArray(iter) {\n if (typeof Symbol !== \"undefined\" && iter[Symbol.iterator] != null || iter[\"@@iterator\"] != null) return Array.from(iter);\n}","export default function _nonIterableSpread() {\n throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}","import * as intl from './locales'\nimport {\n app,\n Order,\n TradeForm,\n OrderOption,\n Match\n} from './registry'\nimport { BooleanOption, XYRangeOption } from './opts'\n\nexport const Limit = 1\nexport const Market = 2\nexport const Cancel = 3\n\n/* The time-in-force specifiers are a mirror of dex/order.TimeInForce. */\nexport const ImmediateTiF = 0\nexport const StandingTiF = 1\n\n/* The order statuses are a mirror of dex/order.OrderStatus. */\nexport const StatusUnknown = 0\nexport const StatusEpoch = 1\nexport const StatusBooked = 2\nexport const StatusExecuted = 3\nexport const StatusCanceled = 4\nexport const StatusRevoked = 5\n\n/* The match statuses are a mirror of dex/order.MatchStatus. */\nexport const NewlyMatched = 0\nexport const MakerSwapCast = 1\nexport const TakerSwapCast = 2\nexport const MakerRedeemed = 3\nexport const MatchComplete = 4\nexport const MatchConfirmed = 5\n\n/* The match sides are a mirror of dex/order.MatchSide. */\nexport const Maker = 0\nexport const Taker = 1\n\n/*\n * RateEncodingFactor is used when encoding an atomic exchange rate as an\n * integer. See docs on message-rate encoding @\n * https://github.com/decred/dcrdex/blob/master/spec/comm.mediawiki#Rate_Encoding\n */\nexport const RateEncodingFactor = 1e8\n\nexport function sellString (ord: Order) {\n const key = ord.sell ? intl.ID_SELL : intl.ID_BUY\n const lang = document.documentElement.lang.toLowerCase()\n return intl.prep(key).toLocaleLowerCase(lang)\n}\n\nexport function typeString (ord: Order) {\n return ord.type === Limit ? (ord.tif === ImmediateTiF ? intl.prep(intl.ID_LIMIT_ORDER_IMMEDIATE_TIF) : intl.prep(intl.ID_LIMIT_ORDER)) : intl.prep(intl.ID_MARKET_ORDER)\n}\n\n/* isMarketBuy will return true if the order is a market buy order. */\nexport function isMarketBuy (ord: Order) {\n return ord.type === Market && !ord.sell\n}\n\n/*\n * hasActiveMatches returns true if the order has matches that have not completed\n * settlement yet.\n */\nexport function hasActiveMatches (order: Order) {\n if (!order.matches) return false\n for (const match of order.matches) {\n if (match.active) return true\n }\n return false\n}\n\n/**\n * statusString converts the order status to a string.\n *\n * IMPORTANT: we have similar function in Golang, it must match this one exactly,\n * when updating make sure to update both!\n */\nexport function statusString (order: Order): string {\n if (!order.id) return intl.prep(intl.ID_ORDER_SUBMITTING) // order ID is empty.\n const isLive = hasActiveMatches(order)\n switch (order.status) {\n case StatusUnknown: return intl.prep(intl.ID_UNKNOWN)\n case StatusEpoch: return intl.prep(intl.ID_EPOCH)\n case StatusBooked:\n if (order.cancelling) return intl.prep(intl.ID_CANCELING)\n return isLive ? `${intl.prep(intl.ID_BOOKED)}/${intl.prep(intl.ID_SETTLING)}` : intl.prep(intl.ID_BOOKED)\n case StatusExecuted:\n if (isLive) return intl.prep(intl.ID_SETTLING)\n if (order.filled === 0 && order.type !== Cancel) return intl.prep(intl.ID_NO_MATCH)\n return intl.prep(intl.ID_EXECUTED)\n case StatusCanceled:\n return isLive ? `${intl.prep(intl.ID_CANCELED)}/${intl.prep(intl.ID_SETTLING)}` : intl.prep(intl.ID_CANCELED)\n case StatusRevoked:\n return isLive ? `${intl.prep(intl.ID_REVOKED)}/${intl.prep(intl.ID_SETTLING)}` : intl.prep(intl.ID_REVOKED)\n }\n return intl.prep(intl.ID_UNKNOWN)\n}\n\n/* filled sums the quantities of non-cancel matches available. */\nexport function filled (order: Order) {\n if (!order.matches) return 0\n const qty = isMarketBuy(order) ? (m: Match) => m.qty * m.rate / RateEncodingFactor : (m: Match) => m.qty\n return order.matches.reduce((filled, match) => {\n if (match.isCancel) return filled\n return filled + qty(match)\n }, 0)\n}\n\n/* settled sums the quantities of the matches that have completed. */\nexport function settled (order: Order) {\n if (!order.matches) return 0\n const qty = isMarketBuy(order) ? (m: Match) => m.qty * m.rate / RateEncodingFactor : (m: Match) => m.qty\n return order.matches.reduce((settled, match) => {\n if (match.isCancel) return settled\n const redeemed = (match.side === Maker && match.status >= MakerRedeemed) ||\n (match.side === Taker && match.status >= MatchComplete)\n return redeemed ? settled + qty(match) : settled\n }, 0)\n}\n\n/* baseToQuote returns the quantity of the quote asset. */\nexport function baseToQuote (rate: number, base: number) : number {\n return rate * base / RateEncodingFactor\n}\n\n/* orderPortion returns a string stating the percentage of the order a match\n makes up. */\nexport function orderPortion (order: Order, match: Match) : string {\n let matchQty = match.qty\n if (isMarketBuy(order)) {\n matchQty = baseToQuote(match.rate, match.qty)\n }\n return ((matchQty / order.qty) * 100).toFixed(1) + ' %'\n}\n\n/*\n * matchStatusString is a string used to create a displayable string describing\n * describing the match status.\n */\nexport function matchStatusString (m: Match) {\n if (m.revoked) {\n // When revoked, match status is less important than pending action if still\n // active, or the outcome if inactive.\n if (m.active) {\n if (m.redeem) return revokedMatchStatus(intl.ID_MATCH_STATUS_REDEMPTION_SENT) // must require confirmation if active\n // If maker and we have not redeemed, waiting to refund, assuming it's not\n // revoked while waiting for confs on an unspent/unexpired taker swap.\n if (m.side === Maker) return revokedMatchStatus(intl.ID_MATCH_STATUS_REFUND_PENDING)\n // As taker, resolution depends on maker's actions while waiting to refund.\n if (m.counterRedeem) return revokedMatchStatus(intl.ID_MATCH_STATUS_REDEEM_PENDING) // this should be very brief if we see the maker's redeem\n return revokedMatchStatus(intl.ID_MATCH_STATUS_REFUND_PENDING) // may switch to redeem if maker redeems on the sly\n }\n if (m.refund) {\n return revokedMatchStatus(intl.ID_MATCH_STATUS_REFUNDED)\n }\n if (m.redeem) {\n return revokedMatchStatus(intl.ID_MATCH_STATUS_REDEMPTION_CONFIRMED)\n }\n return revokedMatchStatus(intl.ID_MATCH_STATUS_COMPLETE) // i.e. we sent no swap\n }\n\n switch (m.status) {\n case NewlyMatched:\n return intl.prep(intl.ID_MATCH_STATUS_NEWLY_MATCHED)\n case MakerSwapCast:\n return intl.prep(intl.ID_MATCH_STATUS_MAKER_SWAP_CAST)\n case TakerSwapCast:\n return intl.prep(intl.ID_MATCH_STATUS_TAKER_SWAP_CAST)\n case MakerRedeemed:\n if (m.side === Maker) {\n return intl.prep(intl.ID_MATCH_STATUS_REDEMPTION_SENT)\n }\n return intl.prep(intl.ID_MATCH_STATUS_MAKER_REDEEMED)\n case MatchComplete:\n return intl.prep(intl.ID_MATCH_STATUS_REDEMPTION_SENT)\n case MatchConfirmed:\n return intl.prep(intl.ID_MATCH_STATUS_REDEMPTION_CONFIRMED)\n }\n return intl.prep(intl.ID_UNKNOWN)\n}\n\n// revokedMatchStatus is a helper function that returns the revoked match status\n// string.\nfunction revokedMatchStatus (matchStatus: string): string {\n return intl.prep(intl.ID_MATCH_STATUS_REVOKED, { status: intl.prep(matchStatus) })\n}\n\n/*\n * optionElement is a getter for an element matching the *OrderOption from\n * client/asset. change is a function with no arguments that is called when the\n * returned option's value has changed.\n */\nexport function optionElement (opt: OrderOption, order: TradeForm, change: () => void, isSwap: boolean): HTMLElement {\n const isBaseChain = (isSwap && order.sell) || (!isSwap && !order.sell)\n const symbol = isBaseChain ? dexAssetSymbol(order.host, order.base) : dexAssetSymbol(order.host, order.quote)\n\n switch (true) {\n case !!opt.boolean:\n return new BooleanOption(opt, symbol, order.options, change).node\n case !!opt.xyRange:\n return new XYRangeOption(opt, symbol, order.options, change).node\n default:\n console.error('no option type specified', opt)\n }\n console.error('unknown option type', opt)\n return document.createElement('div')\n}\n\nfunction dexAssetSymbol (host: string, assetID: number): string {\n return app().exchanges[host].assets[assetID].symbol\n}\n","import Doc, { Animation } from './doc'\nimport { RateEncodingFactor } from './orderutil'\nimport OrderBook from './orderbook'\nimport State from './state'\nimport { UnitInfo, Market, Candle, CandlesPayload } from './registry'\n\nconst bind = Doc.bind\nconst PIPI = 2 * Math.PI\nconst plusChar = String.fromCharCode(59914)\nconst minusChar = String.fromCharCode(59915)\n\ninterface Point {\n x: number\n y: number\n}\n\ninterface MinMax {\n min: number\n max: number\n}\n\ninterface Label {\n val: number\n txt: string\n}\n\ninterface LabelSet {\n widest?: number\n lbls: Label[]\n}\n\ninterface Translator {\n x: (x: number) => number\n y: (y: number) => number\n unx: (x: number) => number\n uny: (y: number) => number\n w: (w: number) => number\n h: (h: number) => number\n dataCoords: (f: () => void) => void\n}\n\nexport interface MouseReport {\n rate: number\n depth: number\n dotColor: string\n hoverMarkers: number[]\n}\n\nexport interface VolumeReport {\n buyBase: number\n buyQuote: number\n sellBase: number\n sellQuote: number\n}\n\nexport interface DepthReporters {\n mouse: (r: MouseReport | null) => void\n click: (x: number) => void\n volume: (r: VolumeReport) => void\n zoom: (z: number) => void\n}\n\nexport interface CandleReporters {\n mouse: (r: Candle | null) => void\n}\n\nexport interface ChartReporters {\n resize: () => void,\n click: (e: MouseEvent) => void,\n zoom: (bigger: boolean) => void\n}\n\nexport interface DepthLine {\n rate: number\n color: string\n}\n\nexport interface DepthMarker {\n rate: number\n active: boolean\n}\n\ninterface DepthMark extends DepthMarker {\n qty: number\n sell: boolean\n}\n\ninterface Theme {\n axisLabel: string\n gridBorder: string\n gridLines: string\n gapLine: string\n value: string\n zoom: string\n zoomHover: string\n sellLine: string\n buyLine: string\n sellFill: string\n buyFill: string\n crosshairs: string\n legendFill: string\n legendText: string\n}\n\nconst darkTheme: Theme = {\n axisLabel: '#b1b1b1',\n gridBorder: '#383f4b',\n gridLines: '#383f4b',\n gapLine: '#6b6b6b',\n value: '#9a9a9a',\n zoom: '#5b5b5b',\n zoomHover: '#aaa',\n sellLine: '#ae3333',\n buyLine: '#05a35a',\n sellFill: '#591a1a',\n buyFill: '#02572f',\n crosshairs: '#888',\n legendFill: 'black',\n legendText: '#d5d5d5'\n}\n\nconst lightTheme: Theme = {\n axisLabel: '#1b1b1b',\n gridBorder: '#ddd',\n gridLines: '#ddd',\n gapLine: '#595959',\n value: '#4d4d4d',\n zoom: '#777',\n zoomHover: '#333',\n sellLine: '#99302b',\n buyLine: '#207a46',\n sellFill: '#bd5959',\n buyFill: '#4cad75',\n crosshairs: '#595959',\n legendFill: '#e6e6e6',\n legendText: '#1b1b1b'\n}\n\n// Chart is the base class for charts.\nexport class Chart {\n parent: HTMLElement\n report: ChartReporters\n theme: Theme\n canvas: HTMLCanvasElement\n visible: boolean\n renderScheduled: boolean\n ctx: CanvasRenderingContext2D\n mousePos: Point | null\n rect: DOMRect\n wheelLimiter: number | null\n boundResizer: () => void\n plotRegion: Region\n xRegion: Region\n yRegion: Region\n dataExtents: Extents\n unattachers: (() => void)[]\n\n constructor (parent: HTMLElement, reporters: ChartReporters) {\n this.parent = parent\n this.report = reporters\n this.theme = State.isDark() ? darkTheme : lightTheme\n this.canvas = document.createElement('canvas')\n this.visible = true\n parent.appendChild(this.canvas)\n const ctx = this.canvas.getContext('2d')\n if (!ctx) {\n console.error('error getting canvas context')\n return\n }\n this.ctx = ctx\n this.ctx.textAlign = 'center'\n this.ctx.textBaseline = 'middle'\n // Mouse handling\n this.mousePos = null\n bind(this.canvas, 'mousemove', (e: MouseEvent) => {\n // this.rect will be set in resize().\n if (!this.rect) return\n this.mousePos = {\n x: e.clientX - this.rect.left,\n y: e.clientY - this.rect.y\n }\n this.draw()\n })\n bind(this.canvas, 'mouseleave', () => {\n this.mousePos = null\n this.draw()\n })\n\n // Bind resize.\n const resizeObserver = new ResizeObserver(() => this.resize())\n resizeObserver.observe(this.parent)\n\n // Scrolling by wheel is smoother when the rate is slightly limited.\n this.wheelLimiter = null\n bind(this.canvas, 'wheel', (e: WheelEvent) => { this.wheel(e) })\n bind(this.canvas, 'click', (e: MouseEvent) => { this.click(e) })\n const setVis = () => {\n this.visible = document.visibilityState !== 'hidden'\n if (this.visible && this.renderScheduled) {\n this.renderScheduled = false\n this.draw()\n }\n }\n bind(document, 'visibilitychange', setVis)\n this.unattachers = [() => { Doc.unbind(document, 'visibilitychange', setVis) }]\n }\n\n wheeled () {\n this.wheelLimiter = window.setTimeout(() => { this.wheelLimiter = null }, 100)\n }\n\n /* clear the canvas. */\n clear () {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)\n }\n\n /* draw calls the child class's render method. */\n draw () {\n this.render()\n }\n\n /* click is the handler for a click event on the canvas. */\n click (e: MouseEvent) {\n this.report.click(e)\n }\n\n /* wheel is a mousewheel event handler. */\n wheel (e: WheelEvent) {\n this.zoom(e.deltaY < 0)\n e.preventDefault()\n }\n\n /*\n * resize updates the chart size. The parentHeight is an argument to support\n * updating the height programmatically after the caller sets a style.height\n * but before the clientHeight has been updated.\n */\n resize () {\n this.canvas.width = this.parent.clientWidth\n this.canvas.height = this.parent.clientHeight\n const xLblHeight = 30\n const yGuess = 40 // y label width guess. Will be adjusted when drawn.\n const plotExtents = new Extents(yGuess, this.canvas.width, 10, this.canvas.height - xLblHeight)\n const xLblExtents = new Extents(yGuess, this.canvas.width, this.canvas.height - xLblHeight, this.canvas.height)\n const yLblExtents = new Extents(0, yGuess, 10, this.canvas.height - xLblHeight)\n this.plotRegion = new Region(this.ctx, plotExtents)\n this.xRegion = new Region(this.ctx, xLblExtents)\n this.yRegion = new Region(this.ctx, yLblExtents)\n // After changing the visibility, this.canvas.getBoundingClientRect will\n // return nonsense until a render.\n window.requestAnimationFrame(() => {\n this.rect = this.canvas.getBoundingClientRect()\n this.report.resize()\n })\n }\n\n /* zoom is called when the user scrolls the mouse wheel on the canvas. */\n zoom (bigger: boolean) {\n if (this.wheelLimiter) return\n this.report.zoom(bigger)\n }\n\n /* The market handler will call unattach when the markets page is unloaded. */\n unattach () {\n for (const u of this.unattachers) u()\n this.unattachers = []\n }\n\n /* render must be implemented by the child class. */\n render () {\n console.error('child class must override render method')\n }\n\n /* applyLabelStyle applies the style used for axis tick labels. */\n applyLabelStyle (fontSize?: number) {\n this.ctx.textAlign = 'center'\n this.ctx.textBaseline = 'middle'\n this.ctx.font = `${fontSize ?? '14'}px 'sans', sans-serif`\n this.ctx.fillStyle = this.theme.axisLabel\n }\n\n /* plotXLabels applies the provided labels to the x axis and draws the grid. */\n plotXLabels (labels: LabelSet, minX: number, maxX: number, unitLines: string[]) {\n const extents = new Extents(minX, maxX, 0, 1)\n this.xRegion.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n this.applyLabelStyle()\n const centerX = (maxX + minX) / 2\n let lastX = minX\n let unitCenter = centerX\n labels.lbls.forEach(lbl => {\n ctx.fillText(lbl.txt, tools.x(lbl.val), tools.y(0.5))\n if (centerX >= lastX && centerX < lbl.val) {\n unitCenter = (lastX + lbl.val) / 2\n }\n lastX = lbl.val\n })\n ctx.font = '11px \\'sans\\', sans-serif'\n if (unitLines.length === 2) {\n ctx.fillText(unitLines[0], tools.x(unitCenter), tools.y(0.63))\n ctx.fillText(unitLines[1], tools.x(unitCenter), tools.y(0.23))\n } else if (unitLines.length === 1) {\n ctx.fillText(unitLines[0], tools.x(unitCenter), tools.y(0.5))\n }\n }, true)\n this.plotRegion.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n ctx.lineWidth = 1\n ctx.strokeStyle = this.theme.gridLines\n labels.lbls.forEach(lbl => {\n line(ctx, tools.x(lbl.val), tools.y(0), tools.x(lbl.val), tools.y(1))\n })\n }, true)\n }\n\n /*\n * plotYLabels applies the y labels based on the provided plot region, and\n * draws the grid.\n */\n plotYLabels (region: Region, labels: LabelSet, minY: number, maxY: number, unit: string) {\n const extents = new Extents(0, 1, minY, maxY)\n this.yRegion.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n this.applyLabelStyle()\n const centerY = maxY / 2\n let lastY = 0\n let unitCenter = centerY\n labels.lbls.forEach(lbl => {\n ctx.fillText(lbl.txt, tools.x(0.5), tools.y(lbl.val))\n if (centerY >= lastY && centerY < lbl.val) {\n unitCenter = (lastY + lbl.val) / 2\n }\n lastY = lbl.val\n })\n ctx.fillText(unit, tools.x(0.5), tools.y(unitCenter))\n }, true)\n region.plot(extents, (ctx: CanvasRenderingContext2D, tools: Translator) => {\n ctx.lineWidth = 1\n ctx.strokeStyle = this.theme.gridLines\n labels.lbls.forEach(lbl => {\n line(ctx, tools.x(0), tools.y(lbl.val), tools.x(1), tools.y(lbl.val))\n })\n }, true)\n }\n\n /*\n * doYLabels generates and applies the y-axis labels, based upon the\n * provided plot region.\n */\n doYLabels (region: Region, step: number, unit: string, valFmt?: (v: number) => string) {\n this.applyLabelStyle()\n const yLabels = makeLabels(this.ctx, region.height(), this.dataExtents.y.min,\n this.dataExtents.y.max, 50, step, unit, valFmt)\n\n // Reassign the width of the y-label column to accommodate the widest text.\n const yAxisWidth = (yLabels.widest || 0) + 20 /* x padding */\n this.yRegion.extents.x.max = yAxisWidth\n this.yRegion.extents.y.max = region.extents.y.max\n\n this.plotRegion.extents.x.min = yAxisWidth\n this.xRegion.extents.x.min = yAxisWidth\n // Print the y labels.\n this.plotYLabels(region, yLabels, this.dataExtents.y.min, this.dataExtents.y.max, unit)\n return yLabels\n }\n\n // drawFrame draws an outline around the plotRegion.\n drawFrame () {\n this.plotRegion.plot(new Extents(0, 1, 0, 1), (ctx: CanvasRenderingContext2D, tools: Translator) => {\n ctx.lineWidth = 1\n ctx.strokeStyle = this.theme.gridBorder\n ctx.beginPath()\n tools.dataCoords(() => {\n ctx.moveTo(0, 0)\n ctx.lineTo(0, 1)\n ctx.lineTo(1, 1)\n ctx.lineTo(1, 0)\n ctx.lineTo(0, 0)\n })\n ctx.stroke()\n })\n }\n}\n\n/* DepthChart is a javascript Canvas-based depth chart renderer. */\nexport class DepthChart extends Chart {\n reporters: DepthReporters\n book: OrderBook\n zoomLevel: number\n lotSize: number\n rateStep: number\n lines: DepthLine[]\n markers: Record\n zoomInBttn: Region\n zoomOutBttn: Region\n baseUnit: string\n quoteUnit: string\n\n constructor (parent: HTMLElement, reporters: DepthReporters, zoom: number) {\n super(parent, {\n resize: () => this.resized(),\n click: (e: MouseEvent) => this.clicked(e),\n zoom: (bigger: boolean) => this.zoomed(bigger)\n })\n this.reporters = reporters\n this.zoomLevel = zoom\n this.lines = []\n this.markers = {\n buys: [],\n sells: []\n }\n this.setZoomBttns() // can't wait for requestAnimationFrame -> resized\n this.resize()\n }\n\n // setZoomBttns creates new regions for zoom in and zoom out buttons. It is\n // used in initiation of the buttons and resizing.\n setZoomBttns () {\n this.zoomInBttn = new Region(this.ctx, new Extents(0, 0, 0, 0))\n this.zoomOutBttn = new Region(this.ctx, new Extents(0, 0, 0, 0))\n }\n\n /* resized is called when the window or parent element are resized. */\n resized () {\n // The button region extents are set during drawing.\n this.setZoomBttns()\n if (this.book) this.draw()\n }\n\n /* zoomed zooms the current view in or out. bigger=true is zoom in. */\n zoomed (bigger: boolean) {\n if (!this.zoomLevel) return\n if (!this.book.buys || !this.book.sells) return\n this.wheeled()\n // Zoom in to 66%, but out to 150% = 1 / (2/3) so that the same zoom levels\n // are hit when reversing direction.\n this.zoomLevel *= bigger ? 2 / 3 : 3 / 2\n this.zoomLevel = clamp(this.zoomLevel, 0.005, 2)\n this.draw()\n this.reporters.zoom(this.zoomLevel)\n }\n\n /* clicked is the canvas 'click' event handler. */\n clicked (e: MouseEvent) {\n if (!this.dataExtents) return\n const x = e.clientX - this.rect.left\n const y = e.clientY - this.rect.y\n if (this.zoomInBttn.contains(x, y)) { this.zoom(true); return }\n if (this.zoomOutBttn.contains(x, y)) { this.zoom(false); return }\n const translator = this.plotRegion.translator(this.dataExtents)\n this.reporters.click(translator.unx(x))\n }\n\n // clear the canvas.\n clear () {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)\n }\n\n // set sets the current data set and draws.\n set (book: OrderBook, lotSize: number, rateStep: number, baseUnitInfo: UnitInfo, quoteUnitInfo: UnitInfo) {\n this.book = book\n this.lotSize = lotSize / baseUnitInfo.conventional.conversionFactor\n const [qFactor, bFactor] = [quoteUnitInfo.conventional.conversionFactor, baseUnitInfo.conventional.conversionFactor]\n this.rateStep = rateStep / RateEncodingFactor * qFactor / bFactor\n this.baseUnit = baseUnitInfo.conventional.unit\n this.quoteUnit = quoteUnitInfo.conventional.unit\n if (!this.zoomLevel) {\n const [midGap, gapWidth] = this.gap()\n // Default to 5% zoom, but with a minimum of 5 * midGap, but still observing\n // the hard cap of 200%.\n const minZoom = Math.max(gapWidth / midGap * 5, 0.05)\n this.zoomLevel = Math.min(minZoom, 2)\n }\n this.draw()\n }\n\n /*\n * render draws the chart.\n * 1. Calculate the data extents and translate the order book data to a\n * cumulative form.\n * 2. Draw axis ticks and grid, mid-gap line and value, zoom buttons, mouse\n * position indicator...\n * 4. Tick labels.\n * 5. Data.\n * 6. Epoch line legend.\n * 7. Hover legend.\n */\n render () {\n // if connection fails it is not possible to get book.\n if (!this.book || !this.visible || this.canvas.width === 0) {\n this.renderScheduled = true\n return\n }\n\n this.clear()\n // if (!this.book || this.book.empty()) return\n const ctx = this.ctx\n const mousePos = this.mousePos\n const buys = this.book.buys\n const sells = this.book.sells\n const [midGap, gapWidth] = this.gap()\n\n const halfWindow = this.zoomLevel * midGap / 2\n const high = midGap + halfWindow\n const low = midGap - halfWindow\n\n // Get a sorted copy of the markers list.\n const buyMarkers = [...this.markers.buys]\n const sellMarkers = [...this.markers.sells]\n buyMarkers.sort((a, b) => b.rate - a.rate)\n sellMarkers.sort((a, b) => a.rate - b.rate)\n const markers: DepthMark[] = []\n\n const buyDepth: [number, number][] = []\n const buyEpoch: [number, number][] = []\n const sellDepth: [number, number][] = []\n const sellEpoch: [number, number][] = []\n const volumeReport = {\n buyBase: 0,\n buyQuote: 0,\n sellBase: 0,\n sellQuote: 0\n }\n let sum = 0\n // The epoch line is above the non-epoch region, so the epochSum y value\n // must account for non-epoch orders too.\n let epochSum = 0\n\n for (let i = 0; i < buys.length; i++) {\n const ord = buys[i]\n epochSum += ord.qty\n if (ord.rate >= low) buyEpoch.push([ord.rate, epochSum])\n if (ord.epoch) continue\n sum += ord.qty\n buyDepth.push([ord.rate, sum])\n volumeReport.buyBase += ord.qty\n volumeReport.buyQuote += ord.qty * ord.rate\n while (buyMarkers.length && floatCompare(buyMarkers[0].rate, ord.rate)) {\n const mark = buyMarkers.shift()\n if (!mark) continue\n markers.push({\n rate: mark.rate,\n qty: ord.epoch ? epochSum : sum,\n sell: ord.sell,\n active: mark.active\n })\n }\n }\n const buySum = buyDepth.length ? last(buyDepth)[1] : 0\n buyDepth.push([low, buySum])\n const epochBuySum = buyEpoch.length ? last(buyEpoch)[1] : 0\n buyEpoch.push([low, epochBuySum])\n\n epochSum = sum = 0\n for (let i = 0; i < sells.length; i++) {\n const ord = sells[i]\n epochSum += ord.qty\n if (ord.rate <= high) sellEpoch.push([ord.rate, epochSum])\n if (ord.epoch) continue\n sum += ord.qty\n sellDepth.push([ord.rate, sum])\n volumeReport.sellBase += ord.qty\n volumeReport.sellQuote += ord.qty * ord.rate\n while (sellMarkers.length && floatCompare(sellMarkers[0].rate, ord.rate)) {\n const mark = sellMarkers.shift()\n if (!mark) continue\n markers.push({\n rate: mark.rate,\n qty: ord.epoch ? epochSum : sum,\n sell: ord.sell,\n active: mark.active\n })\n }\n }\n // Add a data point going to the left so that the data doesn't end with a\n // vertical line.\n const sellSum = sellDepth.length ? last(sellDepth)[1] : 0\n sellDepth.push([high, sellSum])\n const epochSellSum = sellEpoch.length ? last(sellEpoch)[1] : 0\n sellEpoch.push([high, epochSellSum])\n\n // Add ~30px padding to the top of the chart.\n const h = this.xRegion.extents.y.min\n const growthFactor = (h + 30) / h\n const maxY = (epochSellSum && epochBuySum ? Math.max(epochBuySum, epochSellSum) : epochSellSum || epochBuySum || 1) * growthFactor\n\n const dataExtents = new Extents(low, high, 0, maxY)\n this.dataExtents = dataExtents\n\n this.doYLabels(this.plotRegion, this.lotSize, this.baseUnit)\n\n // Print the x labels\n const xLabels = makeLabels(ctx, this.plotRegion.width(), dataExtents.x.min,\n dataExtents.x.max, 100, this.rateStep, '')\n\n this.plotXLabels(xLabels, low, high, [`${this.quoteUnit}/`, this.baseUnit])\n\n // A function to be run at the end if there is legend data to display.\n let mouseData: MouseReport | null = null\n\n // Draw the grid.\n this.drawFrame()\n this.plotRegion.plot(dataExtents, (ctx, tools) => {\n ctx.lineWidth = 1\n // first, a square around the plot area.\n ctx.strokeStyle = this.theme.gridBorder\n // draw a line to indicate mid-gap\n ctx.lineWidth = 2.5\n ctx.strokeStyle = this.theme.gapLine\n line(ctx, tools.x(midGap), tools.y(0), tools.x(midGap), tools.y(0.3 * dataExtents.y.max))\n\n ctx.font = '30px \\'demi-sans\\', sans-serif'\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.fillStyle = this.theme.value\n const y = 0.5 * dataExtents.y.max\n ctx.fillText(formatLabelValue(midGap), tools.x(midGap), tools.y(y))\n ctx.font = '12px \\'sans\\', sans-serif'\n // ctx.fillText('mid-market price', tools.x(midGap), tools.y(y) + 24)\n ctx.fillText(`${(gapWidth / midGap * 100).toFixed(2)}% spread`,\n tools.x(midGap), tools.y(y) + 24)\n\n // Draw zoom buttons.\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n const topCenterX = this.plotRegion.extents.midX\n const topCenterY = tools.y(maxY * 0.9)\n const zoomPct = dataExtents.xRange / midGap * 100\n const zoomText = `${zoomPct.toFixed(1)}%`\n const w = ctx.measureText(zoomText).width\n ctx.font = '13px \\'sans\\', sans-serif'\n ctx.fillText(zoomText, topCenterX, topCenterY + 1)\n // define the region for the zoom button\n const bttnSize = 20\n const xPad = 10\n let bttnLeft = topCenterX - w / 2 - xPad - bttnSize\n const bttnTop = topCenterY - bttnSize / 2\n this.zoomOutBttn.setExtents(\n bttnLeft,\n bttnLeft + bttnSize,\n bttnTop,\n bttnTop + bttnSize\n )\n let hover = mousePos && this.zoomOutBttn.contains(mousePos.x, mousePos.y)\n this.zoomOutBttn.plot(new Extents(0, 1, 0, 1), ctx => {\n ctx.font = '12px \\'icomoon\\''\n ctx.fillStyle = this.theme.zoom\n if (hover) {\n ctx.fillStyle = this.theme.zoomHover\n ctx.font = '13px \\'icomoon\\''\n }\n ctx.fillText(minusChar, this.zoomOutBttn.extents.midX, this.zoomOutBttn.extents.midY)\n })\n bttnLeft = topCenterX + w / 2 + xPad\n this.zoomInBttn.setExtents(\n bttnLeft,\n bttnLeft + bttnSize,\n bttnTop,\n bttnTop + bttnSize\n )\n hover = mousePos && this.zoomInBttn.contains(mousePos.x, mousePos.y)\n this.zoomInBttn.plot(new Extents(0, 1, 0, 1), ctx => {\n ctx.font = '12px \\'icomoon\\''\n ctx.fillStyle = this.theme.zoom\n if (hover) {\n ctx.fillStyle = this.theme.zoomHover\n ctx.font = '14px \\'icomoon\\''\n }\n ctx.fillText(plusChar, this.zoomInBttn.extents.midX, this.zoomInBttn.extents.midY)\n })\n\n // Draw a dotted vertical line where the mouse is, and a dot at the level\n // of the depth line.\n const drawLine = (x: number, color: string) => {\n if (x > high || x < low) return\n ctx.save()\n ctx.setLineDash([3, 5])\n ctx.lineWidth = 1.5\n ctx.strokeStyle = color\n line(ctx, tools.x(x), tools.y(0), tools.x(x), tools.y(maxY))\n ctx.restore()\n }\n\n for (const line of this.lines || []) {\n drawLine(line.rate, line.color)\n }\n\n const tolerance = (high - low) * 0.005\n const hoverMarkers = []\n for (const marker of markers || []) {\n const hovered = (mousePos && withinTolerance(marker.rate, tools.unx(mousePos.x), tolerance))\n if (hovered) hoverMarkers.push(marker.rate)\n ctx.save()\n ctx.lineWidth = (hovered || marker.active) ? 5 : 3\n ctx.strokeStyle = marker.sell ? this.theme.sellLine : this.theme.buyLine\n ctx.fillStyle = marker.sell ? this.theme.sellFill : this.theme.buyFill\n const size = (hovered || marker.active) ? 10 : 8\n ctx.beginPath()\n const tip = {\n x: tools.x(marker.rate),\n y: tools.y(marker.qty) - 8\n }\n const top = tip.y - (Math.sqrt(3) * size / 2) // cos(30)\n ctx.moveTo(tip.x, tip.y)\n ctx.lineTo(tip.x - size / 2, top)\n ctx.lineTo(tip.x + size / 2, top)\n ctx.closePath()\n ctx.stroke()\n ctx.fill()\n ctx.restore()\n }\n\n // If the mouse is in the chart area, draw the crosshairs.\n if (!mousePos) return\n if (!this.plotRegion.contains(mousePos.x, mousePos.y)) return\n // The mouse is in the plot region. Get the data coordinates and find the\n // side and depth for the x value.\n const dataX = tools.unx(mousePos.x)\n let evalSide = sellDepth\n let trigger = (ptX: number) => ptX >= dataX\n let dotColor = this.theme.sellLine\n if (dataX < midGap) {\n evalSide = buyDepth\n trigger = (ptX) => ptX <= dataX\n dotColor = this.theme.buyLine\n }\n let bestDepth = evalSide[0]\n for (let i = 0; i < evalSide.length; i++) {\n const pt = evalSide[i]\n if (trigger(pt[0])) break\n bestDepth = pt\n }\n drawLine(dataX, this.theme.crosshairs)\n mouseData = {\n rate: dataX,\n depth: bestDepth[1],\n dotColor: dotColor,\n hoverMarkers: hoverMarkers\n }\n })\n\n // Draw the epoch lines\n ctx.lineWidth = 1.5\n ctx.setLineDash([3, 3])\n // epoch sells\n ctx.fillStyle = this.theme.sellFill\n ctx.strokeStyle = this.theme.sellLine\n this.drawDepth(sellEpoch)\n // epoch buys\n ctx.fillStyle = this.theme.buyFill\n ctx.strokeStyle = this.theme.buyLine\n this.drawDepth(buyEpoch)\n\n // Draw the book depth.\n ctx.lineWidth = 2.5\n ctx.setLineDash([])\n // book sells\n ctx.fillStyle = this.theme.sellFill\n ctx.strokeStyle = this.theme.sellLine\n this.drawDepth(sellDepth)\n // book buys\n ctx.fillStyle = this.theme.buyFill\n ctx.strokeStyle = this.theme.buyLine\n this.drawDepth(buyDepth)\n\n // Display the dot at the intersection of the mouse hover line and the depth\n // line. This should be drawn after the depths.\n if (mouseData) {\n this.plotRegion.plot(dataExtents, (ctx, tools) => {\n if (!mouseData) return // For TypeScript. Duh.\n dot(ctx, tools.x(mouseData.rate), tools.y(mouseData.depth), mouseData.dotColor, 5)\n })\n }\n\n // Report the book volumes.\n this.reporters.volume(volumeReport)\n this.reporters.mouse(mouseData)\n }\n\n /* drawDepth draws a single side's depth chart data. */\n drawDepth (depth: [number, number][]) {\n const firstPt = depth[0]\n let y = firstPt[1]\n let x: number\n this.plotRegion.plot(this.dataExtents, (ctx, tools) => {\n tools.dataCoords(() => {\n ctx.beginPath()\n ctx.moveTo(firstPt[0], firstPt[1])\n for (let i = 0; i < depth.length; i++) {\n // Set x, but don't set y until we draw the horizontal line.\n x = depth[i][0]\n ctx.lineTo(x, y)\n // If this is past the render edge, quit drawing.\n y = depth[i][1]\n ctx.lineTo(x, y)\n }\n })\n ctx.stroke()\n tools.dataCoords(() => {\n ctx.lineTo(x, 0)\n ctx.lineTo(firstPt[0], 0)\n })\n ctx.closePath()\n ctx.globalAlpha = 0.25\n ctx.fill()\n })\n }\n\n /* returns the mid-gap rate and gap width as a tuple. */\n gap () {\n const [b, s] = [this.book.bestGapBuy(), this.book.bestGapSell()]\n if (!b) {\n if (!s) return [1, 0]\n return [s.rate, 0]\n } else if (!s) return [b.rate, 0]\n return [(s.rate + b.rate) / 2, s.rate - b.rate]\n }\n\n /* setLines stores the indicator lines to draw. */\n setLines (lines: DepthLine[]) {\n this.lines = lines\n }\n\n /* setMarkers sets the indicator markers to draw. */\n setMarkers (markers: Record) {\n this.markers = markers\n }\n}\n\n/* CandleChart is a candlestick data renderer. */\nexport class CandleChart extends Chart {\n reporters: CandleReporters\n data: CandlesPayload\n zoomLevel: number\n numToShow: number\n candleRegion: Region\n volumeRegion: Region\n resizeTimer: number\n zoomLevels: number[]\n market: Market\n rateConversionFactor: number\n\n constructor (parent: HTMLElement, reporters: CandleReporters) {\n super(parent, {\n resize: () => this.resized(),\n click: (/* e: MouseEvent */) => { this.clicked() },\n zoom: (bigger: boolean) => this.zoomed(bigger)\n })\n this.reporters = reporters\n this.zoomLevel = 1\n this.numToShow = 100\n this.resize()\n }\n\n /* resized is called when the window or parent element are resized. */\n resized () {\n const ext = this.plotRegion.extents\n const candleExtents = new Extents(ext.x.min, ext.x.max, ext.y.min, ext.y.min + ext.yRange * 0.85)\n this.candleRegion = new Region(this.ctx, candleExtents)\n const volumeExtents = new Extents(ext.x.min, ext.x.max, ext.y.min + 0.85 * ext.yRange, ext.y.max)\n this.volumeRegion = new Region(this.ctx, volumeExtents)\n // Set a delay on the render to prevent lag.\n if (this.resizeTimer) clearTimeout(this.resizeTimer)\n this.resizeTimer = window.setTimeout(() => this.draw(), 100)\n }\n\n clicked (/* e: MouseEvent */) {\n // handle clicks\n }\n\n /* zoomed zooms the current view in or out. bigger=true is zoom in. */\n zoomed (bigger: boolean) {\n // bigger actually means fewer candles -> reduce zoomLevels index.\n const idx = this.zoomLevels.indexOf(this.numToShow)\n if (bigger) {\n if (idx === 0) return\n this.numToShow = this.zoomLevels[idx - 1]\n } else {\n if (this.zoomLevels.length <= idx + 1 || this.numToShow > this.data.candles.length) return\n this.numToShow = this.zoomLevels[idx + 1]\n }\n this.draw()\n }\n\n /* render draws the chart */\n render () {\n const data = this.data\n if (!data || !this.visible || this.canvas.width === 0) {\n this.renderScheduled = true\n return\n }\n const candleWidth = data.ms\n const mousePos = this.mousePos\n const allCandles = data.candles || []\n\n const n = Math.min(this.numToShow, allCandles.length)\n const candles = allCandles.slice(allCandles.length - n)\n\n this.clear()\n\n // If there are no candles. just don't draw anything.\n if (n === 0) return\n\n // padding definition and some helper functions to parse candles.\n const candleWidthPadding = 0.2\n const start = (c: Candle) => truncate(c.endStamp, candleWidth)\n const end = (c: Candle) => start(c) + candleWidth\n const paddedStart = (c: Candle) => start(c) + candleWidthPadding * candleWidth\n const paddedWidth = (1 - 2 * candleWidthPadding) * candleWidth\n\n const first = candles[0]\n const last = candles[n - 1]\n\n let [high, low, highVol] = [first.highRate, first.lowRate, first.matchVolume]\n for (const c of candles) {\n if (c.highRate > high) high = c.highRate\n if (c.lowRate < low) low = c.lowRate\n if (c.matchVolume > highVol) highVol = c.matchVolume\n }\n\n // Calculate data extents and store them. They are used to apply labels.\n const rateStep = this.market.ratestep\n const dataExtents = new Extents(start(first), end(last), low, high)\n if (low === high) {\n // If there is no price movement at all in the window, show a little more\n // top and bottom so things render nicely.\n dataExtents.y.min -= rateStep\n dataExtents.y.max += rateStep\n }\n this.dataExtents = dataExtents\n\n // Apply labels.\n const rFactor = this.rateConversionFactor\n this.doYLabels(this.candleRegion, rateStep, this.market.quotesymbol, v => formatLabelValue(v / rFactor))\n this.candleRegion.extents.x.min = this.yRegion.extents.x.max\n this.volumeRegion.extents.x.min = this.yRegion.extents.x.max\n\n const xLabels = makeCandleTimeLabels(candles, candleWidth, this.plotRegion.width(), 100)\n\n this.plotXLabels(xLabels, start(first), end(last), [])\n\n this.drawFrame()\n\n // Highlight the candle if the user mouse is over the canvas.\n let mouseCandle: Candle | null = null\n if (mousePos) {\n this.plotRegion.plot(new Extents(dataExtents.x.min, dataExtents.x.max, 0, 1), (ctx, tools) => {\n const selectedStartStamp = truncate(tools.unx(mousePos.x), candleWidth)\n for (const c of candles) {\n if (start(c) === selectedStartStamp) {\n mouseCandle = c\n ctx.fillStyle = this.theme.gridLines\n ctx.fillRect(tools.x(start(c)), tools.y(0), tools.w(candleWidth), tools.h(1))\n break\n }\n }\n })\n if (mouseCandle) {\n const yExt = this.xRegion.extents.y\n this.xRegion.plot(new Extents(dataExtents.x.min, dataExtents.x.max, yExt.min, yExt.max), (ctx, tools) => {\n if (!mouseCandle) return // For TypeScript. Duh.\n this.applyLabelStyle()\n const rangeTxt = `${new Date(start(mouseCandle)).toLocaleString()} - ${new Date(end(mouseCandle)).toLocaleString()}`\n const [xPad, yPad] = [25, 2]\n const rangeWidth = ctx.measureText(rangeTxt).width + 2 * xPad\n const rangeHeight = 16\n let centerX = tools.x((start(mouseCandle) + end(mouseCandle)) / 2)\n let left = centerX - rangeWidth / 2\n const xExt = this.xRegion.extents.x\n if (left < xExt.min) left = xExt.min\n else if (left + rangeWidth > xExt.max) left = xExt.max - rangeWidth\n centerX = left + rangeWidth / 2\n const top = yExt.min + (this.xRegion.height() - rangeHeight) / 2\n ctx.fillStyle = this.theme.legendFill\n ctx.strokeStyle = this.theme.gridBorder\n const rectArgs: [number, number, number, number] = [left - xPad, top - yPad, rangeWidth + 2 * xPad, rangeHeight + 2 * yPad]\n ctx.fillRect(...rectArgs)\n ctx.strokeRect(...rectArgs)\n this.applyLabelStyle()\n ctx.fillText(rangeTxt, centerX, this.xRegion.extents.midY, rangeWidth)\n })\n }\n }\n\n // Draw the volume bars.\n const volDataExtents = new Extents(start(first), end(last), 0, highVol)\n this.volumeRegion.plot(volDataExtents, (ctx, tools) => {\n ctx.fillStyle = this.theme.gridBorder\n for (const c of candles) {\n ctx.fillRect(tools.x(paddedStart(c)), tools.y(0), tools.w(paddedWidth), tools.h(c.matchVolume))\n }\n })\n\n // Draw the candles.\n this.candleRegion.plot(dataExtents, (ctx, tools) => {\n ctx.lineWidth = 1\n for (const c of candles) {\n const desc = c.startRate > c.endRate\n const [x, y, w, h] = [tools.x(paddedStart(c)), tools.y(c.startRate), tools.w(paddedWidth), tools.h(c.endRate - c.startRate)]\n const [high, low, cx] = [tools.y(c.highRate), tools.y(c.lowRate), w / 2 + x]\n ctx.strokeStyle = desc ? this.theme.sellLine : this.theme.buyLine\n ctx.fillStyle = desc ? this.theme.sellFill : this.theme.buyFill\n\n ctx.beginPath()\n ctx.moveTo(cx, high)\n ctx.lineTo(cx, low)\n ctx.stroke()\n\n ctx.fillRect(x, y, w, h)\n ctx.strokeRect(x, y, w, h)\n }\n })\n\n // Report the mouse candle.\n this.reporters.mouse(mouseCandle)\n }\n\n /* setCandles sets the candle data and redraws the chart. */\n setCandles (data: CandlesPayload, market: Market, baseUnitInfo: UnitInfo, quoteUnitInfo: UnitInfo) {\n this.data = data\n if (!data.candles) return\n this.market = market\n const [qFactor, bFactor] = [quoteUnitInfo.conventional.conversionFactor, baseUnitInfo.conventional.conversionFactor]\n this.rateConversionFactor = RateEncodingFactor * qFactor / bFactor\n let n = 25\n this.zoomLevels = []\n const maxCandles = Math.max(data.candles.length, 1000)\n while (n < maxCandles) {\n this.zoomLevels.push(n)\n n *= 2\n }\n this.numToShow = 100\n this.draw()\n }\n}\n\ninterface WaveOpts {\n message?: string\n backgroundColor?: string | boolean // true for background color\n}\n\n/* Wave is a loading animation that displays a colorful line that oscillates */\nexport class Wave extends Chart {\n ani: Animation\n size: [number, number]\n region: Region\n colorShift: number\n opts: WaveOpts\n msgRegion: Region\n fontSize: number\n\n constructor (parent: HTMLElement, opts?: WaveOpts) {\n super(parent, {\n resize: () => this.resized(),\n click: (/* e: MouseEvent */) => { /* pass */ },\n zoom: (/* bigger: boolean */) => { /* pass */ }\n })\n this.canvas.classList.add('fill-abs')\n this.canvas.style.zIndex = '5'\n\n this.opts = opts ?? {}\n\n const period = 1500 // ms\n const start = Math.random() * period\n this.colorShift = Math.random() * 360\n\n // y = A*cos(k*x + theta*t + c)\n // combine three waves with different periods and speeds and phases.\n const amplitudes = [1, 0.65, 0.75]\n const ks = [3, 3, 2]\n const speeds = [Math.PI, Math.PI * 10 / 9, Math.PI / 2.5]\n const phases = [0, 0, Math.PI * 1.5]\n const n = 75\n const single = (n: number, angularX: number, angularTime: number): number => {\n return amplitudes[n] * Math.cos(ks[n] * angularX + speeds[n] * angularTime + phases[n])\n }\n const value = (x: number, angularTime: number): number => {\n const angularX = x * Math.PI * 2\n return (single(0, angularX, angularTime) + single(1, angularX, angularTime) + single(2, angularX, angularTime)) / 3\n }\n this.resize()\n this.ani = new Animation(Animation.Forever, () => {\n const angularTime = (new Date().getTime() - start) / period * Math.PI * 2\n const values = []\n for (let i = 0; i < n; i++) {\n values.push(value(i / (n - 1), angularTime))\n }\n this.drawValues(values)\n })\n }\n\n resized () {\n const opts = this.opts\n const [maxW, maxH] = [150, 100]\n const [cw, ch] = [this.canvas.width, this.canvas.height]\n let [w, h] = [cw * 0.8, ch * 0.8]\n if (w > maxW) w = maxW\n if (h > maxH) h = maxH\n let [l, t] = [(cw - w) / 2, (ch - h) / 2]\n if (opts.message) {\n this.fontSize = clamp(h * 0.15, 10, 14)\n this.applyLabelStyle(this.fontSize)\n const ypad = this.fontSize * 0.5\n const halfH = (this.fontSize / 2) + ypad\n t -= halfH\n this.msgRegion = new Region(this.ctx, new Extents(0, cw, t + h, t + h + 2 * halfH))\n }\n this.region = new Region(this.ctx, new Extents(l, l + w, t, t + h))\n }\n\n drawValues (values: number[]) {\n if (!this.region) return\n this.clear()\n const hsl = (h: number) => `hsl(${h}, 35%, 50%)`\n\n const { region, msgRegion, canvas: { width: w, height: h }, opts: { backgroundColor: bg, message: msg }, colorShift, ctx } = this\n\n if (bg) {\n if (bg === true) ctx.fillStyle = window.getComputedStyle(document.body, null).getPropertyValue('background-color')\n else ctx.fillStyle = bg\n ctx.fillRect(0, 0, w, h)\n }\n\n region.plot(new Extents(0, 1, -1, 1), (ctx: CanvasRenderingContext2D, t: Translator) => {\n ctx.lineWidth = 4\n ctx.lineCap = 'round'\n\n const shift = colorShift + (new Date().getTime() % 2000) / 2000 * 360 // colors move with frequency 1 / 2s\n const grad = ctx.createLinearGradient(t.x(0), 0, t.x(1), 0)\n grad.addColorStop(0, hsl(shift))\n ctx.strokeStyle = grad\n\n ctx.beginPath()\n ctx.moveTo(t.x(0), t.y(values[0]))\n for (let i = 1; i < values.length; i++) {\n const prog = i / (values.length - 1)\n grad.addColorStop(prog, hsl(prog * 300 + shift))\n ctx.lineTo(t.x(prog), t.y(values[i]))\n }\n ctx.stroke()\n })\n if (!msg) return\n msgRegion.plot(new Extents(0, 1, 0, 1), (ctx: CanvasRenderingContext2D, t: Translator) => {\n ctx.fillText(msg, t.x(0.5), t.y(0.5), this.msgRegion.width())\n })\n }\n\n render () { /* pass */ }\n\n stop () {\n this.ani.stop()\n this.canvas.remove()\n }\n}\n\n/*\n * Extents holds a min and max in both the x and y directions, and provides\n * getters for related data.\n */\nclass Extents {\n x: MinMax\n y: MinMax\n\n constructor (xMin: number, xMax: number, yMin: number, yMax: number) {\n this.setExtents(xMin, xMax, yMin, yMax)\n }\n\n setExtents (xMin: number, xMax: number, yMin: number, yMax: number) {\n this.x = {\n min: xMin,\n max: xMax\n }\n this.y = {\n min: yMin,\n max: yMax\n }\n }\n\n get xRange (): number {\n return this.x.max - this.x.min\n }\n\n get midX (): number {\n return (this.x.max + this.x.min) / 2\n }\n\n get yRange (): number {\n return this.y.max - this.y.min\n }\n\n get midY (): number {\n return (this.y.max + this.y.min) / 2\n }\n}\n\n/*\n * Region applies an Extents to the canvas, providing utilities for coordinate\n * transformations and restricting drawing to a specified region of the canvas.\n */\nclass Region {\n context: CanvasRenderingContext2D\n extents: Extents\n\n constructor (context: CanvasRenderingContext2D, extents: Extents) {\n this.context = context\n this.extents = extents\n }\n\n setExtents (xMin: number, xMax: number, yMin: number, yMax: number) {\n this.extents.setExtents(xMin, xMax, yMin, yMax)\n }\n\n width (): number {\n return this.extents.xRange\n }\n\n height (): number {\n return this.extents.yRange\n }\n\n contains (x: number, y: number): boolean {\n const ext = this.extents\n return (x < ext.x.max && x > ext.x.min &&\n y < ext.y.max && y > ext.y.min)\n }\n\n /*\n * A translator provides 4 function for coordinate transformations. x and y\n * translate data coordinates to canvas coordinates for the specified data\n * Extents. unx and uny translate canvas coordinates to data coordinates.\n */\n translator (dataExtents: Extents): Translator {\n const region = this.extents\n const xMin = dataExtents.x.min\n // const xMax = dataExtents.x.max\n const yMin = dataExtents.y.min\n // const yMax = dataExtents.y.max\n const yRange = dataExtents.yRange\n const xRange = dataExtents.xRange\n const screenMinX = region.x.min\n const screenW = region.x.max - screenMinX\n const screenMaxY = region.y.max\n const screenH = screenMaxY - region.y.min\n const xFactor = screenW / xRange\n const yFactor = screenH / yRange\n return {\n x: (x: number) => (x - xMin) * xFactor + screenMinX,\n y: (y: number) => screenMaxY - (y - yMin) * yFactor,\n unx: (x: number) => (x - screenMinX) / xFactor + xMin,\n uny: (y: number) => yMin - (y - screenMaxY) / yFactor,\n w: (w: number) => w / xRange * screenW,\n h: (h: number) => -h / yRange * screenH,\n dataCoords: () => { /* Added when using plot() */ }\n }\n }\n\n /* clear clears the region. */\n clear () {\n const ext = this.extents\n this.context.clearRect(ext.x.min, ext.y.min, ext.xRange, ext.yRange)\n }\n\n /* plot prepares tools for drawing using data coordinates. */\n plot (dataExtents: Extents, drawFunc: (ctx: CanvasRenderingContext2D, tools: Translator) => void, skipMask?: boolean) {\n const ctx = this.context\n const region = this.extents\n ctx.save() // Save the original state\n if (!skipMask) {\n ctx.beginPath()\n ctx.rect(region.x.min, region.y.min, region.xRange, region.yRange)\n ctx.clip()\n }\n\n // The drawFunc will be passed a set of tool that can be used to assist\n // drawing. The tools start with the transformation functions.\n const tools = this.translator(dataExtents)\n\n // Create a transformation that allows drawing in data coordinates. It's\n // not advisable to stroke or add text with this transform in place, as the\n // result will be distorted. You can however use ctx.moveTo and ctx.lineTo\n // with this transform in place using data coordinates, and remove the\n // transform before stroking. The dataCoords method of the supplied tool\n // provides this functionality.\n const yRange = dataExtents.yRange\n const xFactor = region.xRange / dataExtents.xRange\n const yFactor = region.yRange / yRange\n const xMin = dataExtents.x.min\n const yMin = dataExtents.y.min\n // These translation factors are complicated because the (0, 0) of the\n // region is not necessarily the (0, 0) of the canvas.\n const tx = (region.x.min + xMin) - xMin * xFactor\n const ty = -region.y.min - (yRange - yMin) * yFactor\n const setTransform = () => {\n // Data coordinates are flipped about y. Flip the coordinates and\n // translate top left corner to canvas (0, 0).\n ctx.transform(1, 0, 0, -1, -xMin, yMin)\n // Scale to data coordinates and shift into place for the region's offset\n // on the canvas.\n ctx.transform(xFactor, 0, 0, yFactor, tx, ty)\n }\n // dataCoords allows some drawing to be performed directly in data\n // coordinates. Most actual drawing functions like ctx.stroke and\n // ctx.fillRect should not be called from inside dataCoords, but\n // ctx.moveTo and ctx.LineTo are fine.\n tools.dataCoords = f => {\n ctx.save()\n setTransform()\n f()\n ctx.restore()\n }\n\n drawFunc(this.context, tools)\n ctx.restore()\n }\n}\n\n/*\n * makeLabels attempts to create the appropriate labels for the specified\n * screen size, context, and label spacing.\n */\nfunction makeLabels (\n ctx: CanvasRenderingContext2D,\n screenW: number,\n min: number,\n max: number,\n spacingGuess: number,\n step: number,\n unit: string,\n valFmt?: (v: number) => string\n): LabelSet {\n valFmt = valFmt || formatLabelValue\n const n = screenW / spacingGuess\n const diff = max - min\n if (n < 1 || diff <= 0) return { lbls: [] }\n const tickGuess = diff / n\n // make the tick spacing a multiple of the step\n const tick = tickGuess + step - (tickGuess % step)\n let x = min + tick - (min % tick)\n const absMax = Math.max(Math.abs(max), Math.abs(min))\n // The Math.round part is the minimum precision required to see the change in the numbers.\n // The 2 accounts for the precision of the tick.\n const sigFigs = Math.round(Math.log10(absMax / tick)) + 2\n const pts: Label[] = []\n let widest = 0\n while (x < max) {\n x = Number(x.toPrecision(sigFigs))\n const lbl = valFmt(x)\n widest = Math.max(widest, ctx.measureText(lbl).width)\n pts.push({\n val: x,\n txt: lbl\n })\n x += tick\n }\n const unitW = ctx.measureText(unit).width\n if (unitW > widest) widest = unitW\n return {\n widest: widest,\n lbls: pts\n }\n}\n\nconst months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']\n\n/* makeCandleTimeLabels prepares labels for candlestick data. */\nfunction makeCandleTimeLabels (candles: Candle[], dur: number, screenW: number, spacingGuess: number): LabelSet {\n const first = candles[0]\n const last = candles[candles.length - 1]\n const start = truncate(first.endStamp, dur)\n const end = truncate(last.endStamp, dur) + dur\n const diff = end - start\n const n = Math.min(candles.length, screenW / spacingGuess)\n const tick = truncate(diff / n, dur)\n if (tick === 0) {\n console.error('zero tick', dur, diff, n) // probably won't happen, but it'd suck if it did\n return { lbls: [] }\n }\n let x = start\n const zoneOffset = new Date().getTimezoneOffset()\n const dayStamp = (x: number) => {\n x = x - zoneOffset * 60000\n return x - (x % 86400000)\n }\n let lastDay = dayStamp(start)\n let lastYear = 0 // new Date(start).getFullYear()\n if (dayStamp(first.endStamp) === dayStamp(last.endStamp)) lastDay = 0 // Force at least one day stamp.\n const pts = []\n let label\n if (dur < 86400000) {\n label = (d: Date, x: number) => {\n const day = dayStamp(x)\n if (day !== lastDay) return `${months[d.getMonth()]}${d.getDate()} ${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`\n else return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`\n }\n } else {\n label = (d: Date) => {\n const year = d.getFullYear()\n if (year !== lastYear) return `${months[d.getMonth()]}${d.getDate()} '${String(year).slice(2, 4)}`\n else return `${months[d.getMonth()]}${d.getDate()}`\n }\n }\n while (x <= end) {\n const d = new Date(x)\n pts.push({\n val: x,\n txt: label(d, x)\n })\n lastDay = dayStamp(x)\n lastYear = d.getFullYear()\n x += tick\n }\n return { lbls: pts }\n}\n\n/* The last element of an array. */\nfunction last (arr: any[]): any {\n return arr[arr.length - 1]\n}\n\n/* line draws a line with the provided context. */\nfunction line (ctx: CanvasRenderingContext2D, x0: number, y0: number, x1: number, y1: number, skipStroke?: boolean) {\n ctx.beginPath()\n ctx.moveTo(x0, y0)\n ctx.lineTo(x1, y1)\n if (!skipStroke) ctx.stroke()\n}\n\n/* dot draws a circle with the provided context. */\nfunction dot (ctx: CanvasRenderingContext2D, x: number, y: number, color: string, radius: number) {\n ctx.fillStyle = color\n ctx.beginPath()\n ctx.arc(x, y, radius, 0, PIPI)\n ctx.fill()\n}\n\n/* clamp returns v if min <= v <= max, else min or max. */\nfunction clamp (v: number, min: number, max: number): number {\n if (v < min) return min\n if (v > max) return max\n return v\n}\n\n/* labelSpecs is specifications for axis tick labels. */\nconst labelSpecs = {\n minimumSignificantDigits: 4,\n maximumSignificantDigits: 5\n}\n\n/* formatLabelValue formats the provided value using the labelSpecs format. */\nfunction formatLabelValue (x: number) {\n return x.toLocaleString('en-us', labelSpecs)\n}\n\n/* floatCompare compares two floats to within a tolerance of 1e-8. */\nfunction floatCompare (a: number, b: number) {\n return withinTolerance(a, b, 1e-8)\n}\n\n/*\n * withinTolerance returns true if the difference between a and b are with\n * the specified tolerance.\n */\nfunction withinTolerance (a: number, b: number, tolerance: number) {\n return Math.abs(a - b) < Math.abs(tolerance)\n}\n\nfunction truncate (v: number, w: number): number {\n return v - (v % w)\n}\n","import Doc from './doc'\nimport { postJSON } from './http'\nimport State from './state'\nimport * as intl from './locales'\nimport * as OrderUtil from './orderutil'\nimport { Wave } from './charts'\nimport {\n app,\n PasswordCache,\n SupportedAsset,\n PageElement,\n WalletDefinition,\n ConfigOption,\n Exchange,\n Market,\n UnitInfo,\n BondAsset,\n WalletState,\n BalanceNote,\n Order,\n XYRange,\n WalletStateNote,\n WalletInfo,\n Token,\n WalletCreationNote,\n CoreNote\n} from './registry'\nimport { XYRangeHandler } from './opts'\n\ninterface ConfigOptionInput extends HTMLInputElement {\n configOpt: ConfigOption\n}\n\ninterface ProgressPoint {\n stamp: number\n progress: number\n}\n\ninterface CurrentAsset {\n asset: SupportedAsset\n parentAsset?: SupportedAsset\n winfo: WalletInfo | Token\n // selectedDef is used in a strange way for tokens. If a token's parent wallet\n // already exists, then selectedDef is going to be the Token.definition.\n // BUT, if the token's parent wallet doesn't exist yet, the NewWalletForm\n // operates in a combined configuration mode, and the selectedDef will be the\n // currently selected parent asset definition. There is no loss of info\n // in such a case, because the token wallet only has one definition.\n selectedDef: WalletDefinition\n}\n\ninterface WalletConfig {\n assetID: number\n config: Record\n walletType: string\n}\n\n/*\n * NewWalletForm should be used with the \"newWalletForm\" template. The enclosing\n * element should be the first argument of the constructor.\n */\nexport class NewWalletForm {\n page: Record\n form: HTMLElement\n pwCache: PasswordCache | null\n success: (assetID: number) => void\n current: CurrentAsset\n pwHiders: HTMLElement[]\n subform: WalletConfigForm\n currentWalletType: string\n parentSyncer: null | ((w: WalletState) => void)\n createUpdater: null | ((note: WalletCreationNote) => void)\n\n constructor (form: HTMLElement, success: (assetID: number) => void, pwCache?: PasswordCache, backFunc?: () => void) {\n this.form = form\n this.success = success\n this.pwCache = pwCache || null\n const page = this.page = Doc.parseTemplate(form)\n this.pwHiders = Array.from(form.querySelectorAll('.hide-pw'))\n this.refresh()\n\n if (backFunc) {\n Doc.show(page.goBack)\n Doc.bind(page.goBack, 'click', () => { backFunc() })\n }\n\n Doc.empty(page.walletTabTmpl)\n page.walletTabTmpl.removeAttribute('id')\n\n // WalletConfigForm will set the global app variable.\n this.subform = new WalletConfigForm(page.walletSettings, true)\n\n bind(form, page.submitAdd, () => this.submit())\n bind(form, page.oneBttn, () => this.submit())\n\n app().registerNoteFeeder({\n walletstate: (note: WalletStateNote) => { this.reportWalletState(note.wallet) },\n createwallet: (note: WalletCreationNote) => { this.reportCreationUpdate(note) }\n })\n }\n\n /*\n * reportWalletState should be called when a 'walletstate' notification is\n * received.\n * TODO: Let form classes register for notifications.\n */\n reportWalletState (w: WalletState): void {\n if (this.parentSyncer) this.parentSyncer(w)\n }\n\n /*\n * reportWalletState should be called when a 'createwallet' notification is\n * received.\n */\n reportCreationUpdate (note: WalletCreationNote) {\n if (this.createUpdater) this.createUpdater(note)\n }\n\n refresh () {\n const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(...this.pwHiders)\n else Doc.show(...this.pwHiders)\n }\n\n async createWallet (assetID: number, walletType: string, pw: string, parentForm?: WalletConfig) {\n const createForm = {\n assetID: assetID,\n pass: this.page.newWalletPass.value || '',\n config: this.subform.map(assetID),\n appPass: pw,\n walletType: walletType,\n parentForm: parentForm\n }\n\n const ani = new Wave(this.page.mainForm, { backgroundColor: true })\n const res = await postJSON('/api/newwallet', createForm)\n ani.stop()\n return res\n }\n\n async submit () {\n const page = this.page\n const appPass = page.appPass as HTMLInputElement\n const newWalletPass = page.newWalletPass as HTMLInputElement\n const pw = appPass.value || (this.pwCache ? this.pwCache.pw : '')\n if (!pw && !State.passwordIsCached()) {\n page.newWalletErr.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.newWalletErr)\n return\n }\n Doc.hide(page.newWalletErr)\n\n const { asset, parentAsset } = this.current\n const selectedDef = this.current.selectedDef\n let parentForm\n if (parentAsset) {\n parentForm = {\n assetID: parentAsset.id,\n config: this.subform.map(parentAsset.id),\n walletType: selectedDef.type\n }\n }\n // Register the selected asset.\n const res = await this.createWallet(asset.id, selectedDef.type, pw, parentForm)\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n page.appPass.value = ''\n newWalletPass.value = ''\n if (parentAsset) await this.runParentSync()\n else this.success(this.current.asset.id)\n }\n\n /*\n * runParentSync shows a syncing sub-dialog that tracks the parent asset's\n * syncProgress and informs the user that the token wallet will be created\n * after sync is complete.\n */\n async runParentSync () {\n const { page, current: { parentAsset, asset } } = this\n if (!parentAsset) return\n\n page.parentSyncPct.textContent = '0'\n page.parentName.textContent = parentAsset.name\n page.parentLogo.src = Doc.logoPath(parentAsset.symbol)\n page.childName.textContent = asset.name\n page.childLogo.src = Doc.logoPath(asset.symbol)\n Doc.hide(page.mainForm)\n Doc.show(page.parentSyncing)\n\n try {\n await this.syncParent(parentAsset)\n this.success(this.current.asset.id)\n } catch (error) {\n this.setError(error.message || error)\n }\n Doc.show(page.mainForm)\n Doc.hide(page.parentSyncing)\n }\n\n /*\n * syncParent monitors the sync progress of a token's parent asset, generating\n * an Error if the token wallet creation does not complete successfully.\n */\n syncParent (parentAsset: SupportedAsset): Promise {\n const { page, current: { asset } } = this\n return new Promise((resolve, reject) => {\n // First, check if it's already synced.\n const w = app().assets[parentAsset.id].wallet\n if (w && w.synced) return resolve()\n // Not synced, so create a syncer to update the parent sync pane.\n this.parentSyncer = (w: WalletState) => {\n if (w.assetID !== parentAsset.id) return\n page.parentSyncPct.textContent = (w.syncProgress * 100).toFixed(1)\n }\n // Handle the async result.\n this.createUpdater = (note: WalletCreationNote) => {\n if (note.assetID !== asset.id) return\n switch (note.topic) {\n case 'QueuedCreationFailed':\n reject(new Error(`${note.subject}: ${note.details}`))\n break\n case 'QueuedCreationSuccess':\n resolve()\n break\n default:\n return\n }\n this.parentSyncer = null\n this.createUpdater = null\n }\n })\n }\n\n /* setAsset sets the current asset of the NewWalletForm */\n async setAsset (assetID: number) {\n if (!this.parseAsset(assetID)) return // nothing to change\n const page = this.page\n const tabs = page.walletTypeTabs\n const { winfo, asset, parentAsset } = this.current\n page.assetName.textContent = winfo.name\n page.newWalletPass.value = ''\n\n Doc.empty(tabs)\n Doc.hide(tabs, page.newWalletErr)\n page.header.classList.remove('bordertop')\n this.page.assetLogo.src = Doc.logoPath(asset.symbol)\n\n const pinfo = parentAsset ? parentAsset.info : null\n const walletDefs = pinfo ? pinfo.availablewallets : (winfo as WalletInfo).availablewallets ? (winfo as WalletInfo).availablewallets : [(winfo as Token).definition]\n\n if (walletDefs.length > 1) {\n Doc.show(tabs)\n for (const wDef of walletDefs) {\n const tab = page.walletTabTmpl.cloneNode(true) as HTMLElement\n tab.dataset.tooltip = wDef.description\n tab.textContent = wDef.tab\n tabs.appendChild(tab)\n Doc.bind(tab, 'click', () => {\n for (const t of Doc.kids(tabs)) t.classList.remove('selected')\n tab.classList.add('selected')\n this.update(wDef)\n })\n }\n app().bindTooltips(tabs)\n const first = tabs.firstChild as HTMLElement\n first.classList.add('selected')\n }\n\n await this.update(this.current.selectedDef)\n if (asset.walletCreationPending) await this.runParentSync()\n }\n\n /*\n * parseAsset parses the current data for the asset ID.\n */\n parseAsset (assetID: number) {\n if (this.current && this.current.asset.id === assetID) return false\n const asset = app().assets[assetID]\n const token = asset.token\n if (!token) {\n if (!asset.info) throw Error('this non-token asset has no wallet info!')\n this.current = { asset, winfo: asset.info, selectedDef: asset.info.availablewallets[0] }\n return true\n }\n const parentAsset = app().user.assets[token.parentID]\n if (parentAsset.wallet) {\n // If the parent asset already has a wallet, there's no need to configure\n // the parent too. Just configure the token.\n this.current = { asset, winfo: token, selectedDef: token.definition }\n return true\n }\n if (!parentAsset.info) throw Error('this parent has no wallet info!')\n this.current = { asset, parentAsset, winfo: token, selectedDef: parentAsset.info.availablewallets[0] }\n return true\n }\n\n async update (walletDef: WalletDefinition) {\n const page = this.page\n this.current.selectedDef = walletDef\n const appPwCached = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n Doc.hide(page.auth, page.oneBttnBox, page.newWalletPassBox)\n const configOpts = walletDef.configopts || []\n // If a config represents a wallet's birthday, we update the default\n // selection to the current date if this installation of the client\n // generated a seed.\n configOpts.map((opt) => {\n if (opt.isBirthdayConfig && app().seedGenTime > 0) {\n opt.default = toUnixDate(new Date())\n }\n return opt\n })\n // Either this is a walletDef for a token's uncreated parent asset, or this\n // is the definition for the token.\n let containsRequired = false\n for (const opt of configOpts) {\n if (opt.required) {\n containsRequired = true\n break\n }\n }\n const noWalletPWNeeded = !containsRequired && (walletDef.seeded || Boolean(this.current.asset.token))\n if (appPwCached && noWalletPWNeeded) {\n Doc.show(page.oneBttnBox)\n } else if (noWalletPWNeeded) {\n Doc.show(page.auth)\n page.newWalletPass.value = ''\n page.submitAdd.textContent = intl.prep(intl.ID_CREATE)\n } else {\n Doc.show(page.auth)\n if (!walletDef.noauth) Doc.show(page.newWalletPassBox)\n page.submitAdd.textContent = intl.prep(intl.ID_ADD)\n }\n\n const { asset, parentAsset, winfo } = this.current\n\n if (parentAsset) {\n const parentAndTokenOpts = JSON.parse(JSON.stringify(configOpts))\n // Add the regAsset field to the configurations so proper logos will be displayed\n // next to them, and map can filter them out. The opts are copied here so the originals\n // do not have the regAsset field added to them.\n for (const opt of parentAndTokenOpts) opt.regAsset = parentAsset.id\n const tokenOpts = (winfo as Token).definition.configopts || []\n if (tokenOpts.length > 0) {\n const tokenOptsCopy = JSON.parse(JSON.stringify(tokenOpts))\n for (const opt of tokenOptsCopy) opt.regAsset = asset.id\n parentAndTokenOpts.push(...tokenOptsCopy)\n }\n this.subform.update(parentAndTokenOpts, false)\n } else this.subform.update(configOpts, false)\n\n if (this.subform.dynamicOpts.children.length || this.subform.defaultSettings.children.length) {\n Doc.show(page.walletSettingsHeader)\n } else Doc.hide(page.walletSettingsHeader)\n // A seeded or token wallet is internal to the dex client and as such does\n // not have an external config file to select.\n if (walletDef.seeded || Boolean(this.current.asset.token)) Doc.hide(this.subform.fileSelector)\n else Doc.show(this.subform.fileSelector)\n\n this.refresh()\n await this.loadDefaults()\n }\n\n /* setError sets and shows the in-form error message. */\n async setError (errMsg: string) {\n this.page.newWalletErr.textContent = errMsg\n Doc.show(this.page.newWalletErr)\n }\n\n /*\n * loadDefaults attempts to load the ExchangeWallet configuration from the\n * default wallet config path on the server and will auto-fill the page on\n * the subform if settings are found.\n */\n async loadDefaults () {\n // No default config files for seeded assets right now.\n const { asset, parentAsset, selectedDef } = this.current\n if (!selectedDef.configpath) return\n let configID = asset.id\n if (parentAsset) {\n if (selectedDef.seeded) return\n configID = parentAsset.id\n }\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/defaultwalletcfg', {\n assetID: configID,\n type: selectedDef.type\n })\n loaded()\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n this.subform.setLoadedConfig(res.config)\n }\n}\n\nlet repeatableCounter = 0\n\n/*\n * WalletConfigForm is a dynamically generated sub-form for setting\n * asset-specific wallet configuration options.\n*/\nexport class WalletConfigForm {\n form: HTMLElement\n configElements: [ConfigOption, HTMLElement][]\n configOpts: ConfigOption[]\n sectionize: boolean\n allSettings: PageElement\n dynamicOpts: PageElement\n textInputTmpl: PageElement\n dateInputTmpl: PageElement\n checkboxTmpl: PageElement\n repeatableTmpl: PageElement\n fileSelector: PageElement\n fileInput: PageElement\n errMsg: PageElement\n showOther: PageElement\n showIcon: PageElement\n hideIcon: PageElement\n showHideMsg: PageElement\n otherSettings: PageElement\n loadedSettingsMsg: PageElement\n loadedSettings: PageElement\n defaultSettingsMsg: PageElement\n defaultSettings: PageElement\n assetHasActiveOrders: boolean\n\n constructor (form: HTMLElement, sectionize: boolean) {\n this.form = form\n // A configElement is a div containing an input and its label.\n this.configElements = []\n // configOpts is the wallet options provided by core.\n this.configOpts = []\n this.sectionize = sectionize\n\n // Get template elements\n this.allSettings = Doc.tmplElement(form, 'allSettings')\n this.dynamicOpts = Doc.tmplElement(form, 'dynamicOpts')\n this.textInputTmpl = Doc.tmplElement(form, 'textInput')\n this.textInputTmpl.remove()\n this.dateInputTmpl = Doc.tmplElement(form, 'dateInput')\n this.dateInputTmpl.remove()\n this.checkboxTmpl = Doc.tmplElement(form, 'checkbox')\n this.checkboxTmpl.remove()\n this.repeatableTmpl = Doc.tmplElement(form, 'repeatableInput')\n this.repeatableTmpl.remove()\n this.fileSelector = Doc.tmplElement(form, 'fileSelector')\n this.fileInput = Doc.tmplElement(form, 'fileInput')\n this.errMsg = Doc.tmplElement(form, 'errMsg')\n this.showOther = Doc.tmplElement(form, 'showOther')\n this.showIcon = Doc.tmplElement(form, 'showIcon')\n this.hideIcon = Doc.tmplElement(form, 'hideIcon')\n this.showHideMsg = Doc.tmplElement(form, 'showHideMsg')\n this.otherSettings = Doc.tmplElement(form, 'otherSettings')\n this.loadedSettingsMsg = Doc.tmplElement(form, 'loadedSettingsMsg')\n this.loadedSettings = Doc.tmplElement(form, 'loadedSettings')\n this.defaultSettingsMsg = Doc.tmplElement(form, 'defaultSettingsMsg')\n this.defaultSettings = Doc.tmplElement(form, 'defaultSettings')\n\n if (!sectionize) Doc.hide(this.showOther)\n\n Doc.bind(this.fileSelector, 'click', () => this.fileInput.click())\n\n // config file upload\n Doc.bind(this.fileInput, 'change', async () => this.fileInputChanged())\n\n Doc.bind(this.showOther, 'click', () => {\n this.setOtherSettingsViz(this.hideIcon.classList.contains('d-hide'))\n })\n }\n\n /*\n * fileInputChanged will read the selected file and attempt to load the\n * configuration settings. All loaded settings will be made visible for\n * inspection by the user.\n */\n async fileInputChanged () {\n Doc.hide(this.errMsg)\n if (!this.fileInput.value) return\n const files = this.fileInput.files\n if (!files || files.length === 0) return\n const loaded = app().loading(this.form)\n const config = await files[0].text()\n if (!config) return\n const res = await postJSON('/api/parseconfig', {\n configtext: config\n })\n loaded()\n if (!app().checkResponse(res)) {\n this.errMsg.textContent = res.msg\n Doc.show(this.errMsg)\n return\n }\n if (Object.keys(res.map).length === 0) return\n this.dynamicOpts.append(...this.setConfig(res.map))\n this.reorder(this.dynamicOpts)\n const [loadedOpts, defaultOpts] = [this.loadedSettings.children.length, this.defaultSettings.children.length]\n if (loadedOpts === 0) Doc.hide(this.loadedSettings, this.loadedSettingsMsg)\n if (defaultOpts === 0) Doc.hide(this.defaultSettings, this.defaultSettingsMsg)\n if (loadedOpts + defaultOpts === 0) Doc.hide(this.showOther, this.otherSettings)\n }\n\n addOpt (box: HTMLElement, opt: ConfigOption, insertAfter?: PageElement, n?: number): PageElement {\n const elID = 'wcfg-' + opt.key + (n ? String(n) : '')\n let el: HTMLElement\n if (opt.isboolean) el = this.checkboxTmpl.cloneNode(true) as HTMLElement\n else if (opt.isdate) el = this.dateInputTmpl.cloneNode(true) as HTMLElement\n else if (opt.repeatable) {\n el = this.repeatableTmpl.cloneNode(true) as HTMLElement\n el.classList.add('repeatable')\n Doc.bind(Doc.tmplElement(el, 'add'), 'click', () => {\n repeatableCounter++\n this.addOpt(box, opt, el, repeatableCounter)\n })\n } else el = this.textInputTmpl.cloneNode(true) as HTMLElement\n this.configElements.push([opt, el])\n const input = el.querySelector('input') as ConfigOptionInput\n input.dataset.configKey = opt.key\n input.id = elID\n const label = Doc.safeSelector(el, 'label')\n label.htmlFor = elID // 'for' attribute, but 'for' is a keyword\n label.prepend(opt.displayname)\n if (opt.regAsset !== undefined) {\n const logo = new window.Image(15, 15)\n logo.src = Doc.logoPathFromID(opt.regAsset || -1)\n label.prepend(logo)\n }\n if (insertAfter) insertAfter.after(el)\n else box.appendChild(el)\n if (opt.noecho) {\n input.type = 'password'\n input.autocomplete = 'off'\n }\n if (opt.description) label.dataset.tooltip = opt.description\n if (opt.isboolean) input.checked = opt.default\n else if (opt.isdate) {\n const getMinMaxVal = (minMax: string | number) => {\n if (!minMax) return ''\n if (minMax === 'now') return dateToString(new Date())\n return dateToString(new Date((minMax as number) * 1000))\n }\n input.max = getMinMaxVal(opt.max)\n input.min = getMinMaxVal(opt.min)\n const date = opt.default ? new Date(opt.default * 1000) : new Date()\n // UI shows Dates in valueAsDate as UTC, but user interprets local. Set a\n // local date string so the UI displays what the user expects. alt:\n // input.valueAsDate = dateApplyOffset(date)\n input.value = dateToString(date)\n } else input.value = opt.default !== null ? opt.default : ''\n input.disabled = Boolean(opt.disablewhenactive && this.assetHasActiveOrders)\n return el\n }\n\n /*\n * update creates the dynamic form.\n */\n update (configOpts: ConfigOption[] | null, activeOrders: boolean) {\n this.assetHasActiveOrders = activeOrders\n this.configElements = []\n this.configOpts = configOpts || []\n Doc.empty(this.dynamicOpts, this.defaultSettings, this.loadedSettings)\n\n // If there are no options, just hide the entire form.\n if (this.configOpts.length === 0) return Doc.hide(this.form)\n Doc.show(this.form)\n\n this.setOtherSettingsViz(false)\n Doc.hide(\n this.loadedSettingsMsg, this.loadedSettings, this.defaultSettingsMsg,\n this.defaultSettings, this.errMsg\n )\n const defaultedOpts = []\n for (const opt of this.configOpts) {\n if (this.sectionize && opt.default !== null) defaultedOpts.push(opt)\n else this.addOpt(this.dynamicOpts, opt)\n }\n if (defaultedOpts.length) {\n for (const opt of defaultedOpts) {\n this.addOpt(this.defaultSettings, opt)\n }\n Doc.show(this.showOther, this.defaultSettingsMsg, this.defaultSettings)\n } else {\n Doc.hide(this.showOther)\n }\n app().bindTooltips(this.allSettings)\n if (this.dynamicOpts.children.length) Doc.show(this.dynamicOpts)\n else Doc.hide(this.dynamicOpts)\n }\n\n /*\n * setOtherSettingsViz sets the visibility of the additional settings section.\n */\n setOtherSettingsViz (visible: boolean) {\n if (visible) {\n Doc.hide(this.showIcon)\n Doc.show(this.hideIcon, this.otherSettings)\n this.showHideMsg.textContent = intl.prep(intl.ID_HIDE_ADDITIONAL_SETTINGS)\n return\n }\n Doc.hide(this.hideIcon, this.otherSettings)\n Doc.show(this.showIcon)\n this.showHideMsg.textContent = intl.prep(intl.ID_SHOW_ADDITIONAL_SETTINGS)\n }\n\n /*\n * setConfig looks for inputs with configOpt keys matching the cfg object, and\n * sets the inputs value to the corresponding cfg value. A list of matching\n * configElements is returned.\n */\n setConfig (cfg: Record): HTMLElement[] {\n const finds: HTMLElement[] = []\n const handledRepeatables: Record = {}\n const removes: [ConfigOption, PageElement][] = []\n for (const r of [...this.configElements]) {\n const [opt, el] = r\n const v = cfg[opt.key]\n if (v === undefined) continue\n if (opt.repeatable) {\n if (handledRepeatables[opt.key]) {\n removes.push(r)\n continue\n }\n handledRepeatables[opt.key] = true\n const vals = v.split(opt.repeatable)\n const firstVal = vals[0]\n finds.push(el)\n Doc.safeSelector(el, 'input').value = firstVal\n for (let i = 1; i < vals.length; i++) {\n repeatableCounter++\n const newEl = this.addOpt(el.parentElement as PageElement, opt, el, repeatableCounter)\n Doc.safeSelector(newEl, 'input').value = vals[i]\n finds.push(newEl)\n }\n continue\n }\n finds.push(el)\n const input = Doc.safeSelector(el, 'input') as HTMLInputElement\n if (opt.isboolean) input.checked = isTruthyString(v)\n else if (opt.isdate) {\n input.value = dateToString(new Date(parseInt(v) * 1000))\n // alt: input.valueAsDate = dateApplyOffset(...)\n } else input.value = v\n }\n for (const r of removes) {\n const i = this.configElements.indexOf(r)\n if (i >= 0) this.configElements.splice(i, 1)\n }\n\n return finds\n }\n\n /*\n * setLoadedConfig sets the input values for the entries in cfg, and moves\n * them to the loadedSettings box.\n */\n setLoadedConfig (cfg: Record) {\n const finds = this.setConfig(cfg)\n if (!this.sectionize || finds.length === 0) return\n this.loadedSettings.append(...finds)\n this.reorder(this.loadedSettings)\n Doc.show(this.loadedSettings, this.loadedSettingsMsg)\n if (this.defaultSettings.children.length === 0) Doc.hide(this.defaultSettings, this.defaultSettingsMsg)\n }\n\n /*\n * map reads all inputs and constructs an object from the configOpt keys and\n * values.\n */\n map (assetID: number): Record {\n const config: Record = {}\n for (const [opt, el] of this.configElements) {\n const input = Doc.safeSelector(el, 'input') as HTMLInputElement\n if (opt.regAsset !== undefined && opt.regAsset !== assetID) continue\n if (opt.isboolean && opt.key) {\n config[opt.key] = input.checked ? '1' : '0'\n } else if (opt.isdate && opt.key) {\n // Force local time interpretation by appending a time to the date\n // string, otherwise the Date constructor considers it UTC.\n const minDate = input.min ? toUnixDate(new Date(input.min + 'T00:00')) : Number.MIN_SAFE_INTEGER\n const maxDate = input.max ? toUnixDate(new Date(input.max + 'T00:00')) : Number.MAX_SAFE_INTEGER\n let date = input.value ? toUnixDate(new Date(input.value + 'T00:00')) : 0\n if (date < minDate) date = minDate\n else if (date > maxDate) date = maxDate\n config[opt.key] = '' + date\n } else if (input.value) {\n if (opt.repeatable && config[opt.key]) config[opt.key] += opt.repeatable + input.value\n else config[opt.key] = input.value\n }\n }\n return config\n }\n\n /*\n * reorder sorts the configElements in the box by the order of the\n * server-provided configOpts array.\n */\n reorder (box: HTMLElement) {\n const inputs: Record = {}\n box.querySelectorAll('input').forEach((input: ConfigOptionInput) => {\n const k = input.dataset.configKey\n if (!k) return // TS2538\n const els = []\n for (const [opt, el] of this.configElements) if (opt.key === k) els.push(el)\n inputs[k] = els\n })\n for (const opt of this.configOpts) {\n const els = inputs[opt.key] || []\n for (const el of els) box.append(el)\n }\n }\n}\n\n/*\n * ConfirmRegistrationForm should be used with the \"confirmRegistrationForm\"\n * template.\n */\nexport class ConfirmRegistrationForm {\n form: HTMLElement\n success: () => void\n page: Record\n xc: Exchange\n certFile: string\n bondAssetID: number\n pwCache: PasswordCache\n\n constructor (form: HTMLElement, success: () => void, goBack: () => void, pwCache: PasswordCache) {\n this.form = form\n this.success = success\n this.page = Doc.parseTemplate(form)\n this.certFile = ''\n this.pwCache = pwCache\n\n Doc.bind(this.page.goBack, 'click', () => goBack())\n // bondStrengthField is presently hidden since there is no scaling of user\n // limits yet, and there needs to be considerable explanation of why\n // anything other than 1 would be used. Unhide bondStrengthInput to show it\n // when we are ready. (TODO)\n Doc.bind(this.page.bondStrengthField, 'input', () => {\n const asset = app().assets[this.bondAssetID]\n if (!asset) return\n const ui = asset.unitInfo\n const bondAsset = this.xc.bondAssets[asset.symbol]\n this.page.bondAmt.textContent = Doc.formatCoinValue(this.totalBondAmount(bondAsset.amount), ui)\n })\n bind(form, this.page.submit, () => this.submitForm())\n }\n\n setExchange (xc: Exchange, certFile: string) {\n this.xc = xc\n this.certFile = certFile\n const page = this.page\n if (State.passwordIsCached() || (this.pwCache && this.pwCache.pw)) Doc.hide(page.passBox)\n else Doc.show(page.passBox)\n page.host.textContent = xc.host\n }\n\n setAsset (assetID: number) {\n const asset = app().assets[assetID]\n const ui = asset.unitInfo\n this.bondAssetID = asset.id\n const page = this.page\n const bondAsset = this.xc.bondAssets[asset.symbol]\n page.bondAmt.textContent = Doc.formatCoinValue(this.totalBondAmount(bondAsset.amount), ui)\n page.bondUnit.textContent = ui.conventional.unit.toUpperCase()\n page.logo.src = Doc.logoPath(asset.symbol)\n }\n\n totalBondAmount (singleBondAmount: number): number {\n const bondStrength = +(this.page.bondStrengthField.value ?? 1)\n return bondStrength * singleBondAmount\n }\n\n /* Form expands into its space quickly from the lower-right as it fades in. */\n async animate () {\n const form = this.form\n Doc.animate(400, prog => {\n form.style.transform = `scale(${prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n const offset = `${(1 - prog) * 500}px`\n form.style.top = offset\n form.style.left = offset\n })\n }\n\n /*\n * submitForm is called when the form is submitted.\n */\n async submitForm () {\n const page = this.page\n // if button is selected it can be clickable.\n if (!page.submit.classList.contains('selected')) {\n return\n }\n const asset = app().assets[this.bondAssetID]\n if (!asset) {\n page.regErr.innerText = intl.prep(intl.ID_SELECT_WALLET_FOR_FEE_PAYMENT)\n Doc.show(page.regErr)\n return\n }\n Doc.hide(page.regErr)\n const bondAsset = this.xc.bondAssets[asset.wallet.symbol]\n const cert = await this.certFile\n const dexAddr = this.xc.host\n const pw = page.appPass.value || (this.pwCache ? this.pwCache.pw : '')\n const postBondForm = {\n addr: dexAddr,\n cert: cert,\n pass: pw,\n bond: this.totalBondAmount(bondAsset.amount),\n asset: bondAsset.id\n }\n page.appPass.value = ''\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/postbond', postBondForm)\n loaded()\n if (!app().checkResponse(res)) {\n page.regErr.textContent = res.msg\n Doc.show(page.regErr)\n return\n }\n this.success()\n }\n}\n\n/*\n * FeeAssetSelectionForm should be used with the \"regAssetForm\" template.\n */\nexport class FeeAssetSelectionForm {\n form: HTMLElement\n success: (assetID: number) => void\n xc: Exchange\n page: Record\n assetTmpls: Record>\n\n constructor (form: HTMLElement, success: (assetID: number) => Promise) {\n this.form = form\n this.success = success\n this.page = Doc.parseTemplate(form)\n Doc.cleanTemplates(this.page.marketTmpl, this.page.assetTmpl)\n\n app().registerNoteFeeder({\n createwallet: (note: WalletCreationNote) => {\n if (note.topic === 'QueuedCreationSuccess') this.walletCreated(note.assetID)\n }\n })\n }\n\n setExchange (xc: Exchange) {\n this.xc = xc\n this.assetTmpls = {}\n const page = this.page\n Doc.empty(page.assets, page.allMarkets)\n\n const cFactor = (ui: UnitInfo) => ui.conventional.conversionFactor\n\n const marketNode = (mkt: Market, excludeIcon?: number) => {\n const n = page.marketTmpl.cloneNode(true) as HTMLElement\n const marketTmpl = Doc.parseTemplate(n)\n\n const baseAsset = xc.assets[mkt.baseid]\n const baseUnitInfo = app().unitInfo(mkt.baseid, xc)\n const quoteAsset = xc.assets[mkt.quoteid]\n const quoteUnitInfo = app().unitInfo(mkt.quoteid, xc)\n\n if (cFactor(baseUnitInfo) === 0 || cFactor(quoteUnitInfo) === 0) return null\n\n if (typeof excludeIcon !== 'undefined') {\n const excludeBase = excludeIcon === mkt.baseid\n const otherSymbol = xc.assets[excludeBase ? mkt.quoteid : mkt.baseid].symbol\n marketTmpl.logo.src = Doc.logoPath(otherSymbol)\n } else {\n const otherLogo = marketTmpl.logo.cloneNode(true) as PageElement\n marketTmpl.logo.src = Doc.logoPath(baseAsset.symbol)\n otherLogo.src = Doc.logoPath(quoteAsset.symbol)\n const parent = marketTmpl.logo.parentNode\n if (parent) parent.insertBefore(otherLogo, marketTmpl.logo.nextSibling)\n }\n\n const baseSymbol = baseAsset.symbol.toUpperCase()\n const quoteSymbol = quoteAsset.symbol.toUpperCase()\n\n marketTmpl.baseName.replaceWith(Doc.symbolize(baseSymbol))\n marketTmpl.quoteName.replaceWith(Doc.symbolize(quoteSymbol))\n\n marketTmpl.lotSize.textContent = Doc.formatCoinValue(mkt.lotsize, baseUnitInfo)\n marketTmpl.lotSizeSymbol.replaceWith(Doc.symbolize(baseSymbol))\n\n if (mkt.spot) {\n Doc.show(marketTmpl.quoteLotSize)\n const r = cFactor(quoteUnitInfo) / cFactor(baseUnitInfo)\n const quoteLot = mkt.lotsize * mkt.spot.rate / OrderUtil.RateEncodingFactor * r\n const s = Doc.formatCoinValue(quoteLot, quoteUnitInfo)\n marketTmpl.quoteLotSize.textContent = `(~${s} ${quoteSymbol})`\n }\n return n\n }\n\n for (const [symbol, bondAsset] of Object.entries(xc.bondAssets)) {\n const asset = app().assets[bondAsset.id]\n if (!asset) continue\n const unitInfo = asset.unitInfo\n const assetNode = page.assetTmpl.cloneNode(true) as HTMLElement\n Doc.bind(assetNode, 'click', () => { this.success(bondAsset.id) })\n const assetTmpl = this.assetTmpls[bondAsset.id] = Doc.parseTemplate(assetNode)\n page.assets.appendChild(assetNode)\n assetTmpl.logo.src = Doc.logoPath(symbol)\n const fee = Doc.formatCoinValue(bondAsset.amount, unitInfo)\n assetTmpl.feeAmt.textContent = String(fee)\n assetTmpl.feeSymbol.replaceWith(Doc.symbolize(asset.symbol))\n assetTmpl.confs.textContent = String(bondAsset.confs)\n setReadyMessage(assetTmpl.ready, asset)\n\n let count = 0\n for (const mkt of Object.values(xc.markets)) {\n if (mkt.baseid !== bondAsset.id && mkt.quoteid !== bondAsset.id) continue\n const node = marketNode(mkt, bondAsset.id)\n if (!node) continue\n count++\n assetTmpl.markets.appendChild(node)\n }\n if (count < 3) Doc.hide(assetTmpl.fader)\n }\n\n page.host.textContent = xc.host\n for (const mkt of Object.values(xc.markets)) {\n const node = marketNode(mkt)\n if (!node) continue\n page.allMarkets.appendChild(node)\n }\n }\n\n /*\n * walletCreated should be called when an asynchronous wallet creation\n * completes successfully.\n */\n walletCreated (assetID: number) {\n const tmpl = this.assetTmpls[assetID]\n const asset = app().assets[assetID]\n setReadyMessage(tmpl.ready, asset)\n }\n\n refresh () {\n this.setExchange(this.xc)\n }\n\n /*\n * Animation to make the elements sort of expand into their space from the\n * bottom as they fade in.\n */\n async animate () {\n const { page, form } = this\n const how = page.how\n const extraMargin = 75\n const extraTop = 50\n const fontSize = 24\n const regAssetElements = Array.from(page.assets.children) as PageElement[]\n regAssetElements.push(page.allmkts)\n form.style.opacity = '0'\n\n const aniLen = 350\n await Doc.animate(aniLen, prog => {\n for (const el of regAssetElements) {\n el.style.marginTop = `${(1 - prog) * extraMargin}px`\n el.style.transform = `scale(${prog})`\n }\n form.style.opacity = Math.pow(prog, 4).toFixed(1)\n form.style.paddingTop = `${(1 - prog) * extraTop}px`\n how.style.fontSize = `${fontSize * prog}px`\n }, 'easeOut')\n }\n}\n\n/*\n * setReadyMessage sets an asset's status message on the FeeAssetSelectionForm.\n */\nfunction setReadyMessage (el: PageElement, asset: SupportedAsset) {\n if (asset.wallet) el.textContent = intl.prep(intl.WALLET_READY)\n else if (asset.walletCreationPending) el.textContent = intl.prep(intl.WALLET_PENDING)\n else el.textContent = intl.prep(intl.SETUP_NEEDED)\n el.classList.remove('readygreen', 'setuporange')\n el.classList.add(asset.wallet ? 'readygreen' : 'setuporange')\n}\n\n/*\n * WalletWaitForm is a form used to track the wallet sync status and balance\n * in preparation for posting a bond.\n */\nexport class WalletWaitForm {\n form: HTMLElement\n success: () => void\n goBack: () => void\n page: Record\n assetID: number\n parentID?: number\n xc: Exchange\n bondAsset: BondAsset\n progressCache: ProgressPoint[]\n progressed: boolean\n funded: boolean\n // if progressed && funded, stop reporting balance or state; call success()\n bondFeeBuffer: number // in parent asset\n parentAssetSynced: boolean\n\n constructor (form: HTMLElement, success: () => void, goBack: () => void) {\n this.form = form\n this.success = success\n this.page = Doc.parseTemplate(form)\n this.assetID = -1\n this.progressCache = []\n this.progressed = false\n this.funded = false\n\n Doc.bind(this.page.goBack, 'click', () => {\n this.assetID = -1\n goBack()\n })\n\n app().registerNoteFeeder({\n walletstate: (note: WalletStateNote) => this.reportWalletState(note.wallet),\n balance: (note: BalanceNote) => this.reportBalance(note.assetID)\n })\n }\n\n /* setExchange sets the exchange for which the fee is being paid. */\n setExchange (xc: Exchange) {\n this.xc = xc\n }\n\n /* setWallet must be called before showing the WalletWaitForm. */\n setWallet (wallet: WalletState, bondFeeBuffer: number) {\n this.assetID = wallet.assetID\n this.progressCache = []\n this.progressed = false\n this.funded = false\n this.bondFeeBuffer = bondFeeBuffer // in case we're a token, parent's balance must cover\n this.parentAssetSynced = false\n const page = this.page\n const asset = app().assets[wallet.assetID]\n this.parentID = asset.token?.parentID\n const bondAsset = this.bondAsset = this.xc.bondAssets[asset.symbol]\n\n const symbolize = (el: PageElement, symbol: string) => {\n Doc.empty(el)\n el.appendChild(Doc.symbolize(symbol))\n }\n\n for (const span of Doc.applySelector(this.form, '.unit')) symbolize(span, asset.symbol)\n page.logo.src = Doc.logoPath(asset.symbol)\n page.depoAddr.textContent = wallet.address\n page.fee.textContent = Doc.formatCoinValue(bondAsset.amount, asset.unitInfo)\n\n Doc.hide(page.syncUncheck, page.syncCheck, page.balUncheck, page.balCheck, page.syncRemainBox)\n Doc.show(page.balanceBox)\n\n if (bondFeeBuffer > 0) {\n // overlap * increment + buffer\n page.totalForBond.textContent = Doc.formatCoinValue(2 * bondAsset.amount + bondFeeBuffer, asset.unitInfo)\n Doc.hide(page.sendEnough) // generic msg when no fee info available when\n Doc.hide(page.txFeeBox, page.sendEnoughForToken, page.txFeeBalanceBox) // for tokens\n Doc.hide(page.sendEnoughWithEst) // non-tokens\n\n if (asset.token) {\n Doc.show(page.txFeeBox, page.sendEnoughForToken, page.txFeeBalanceBox)\n const parentAsset = app().assets[asset.token.parentID]\n page.txFee.textContent = Doc.formatCoinValue(bondFeeBuffer, parentAsset.unitInfo)\n page.parentFees.textContent = Doc.formatCoinValue(bondFeeBuffer, parentAsset.unitInfo)\n page.tokenFees.textContent = Doc.formatCoinValue(bondAsset.amount, asset.unitInfo)\n symbolize(page.txFeeUnit, parentAsset.symbol)\n symbolize(page.parentUnit, parentAsset.symbol)\n symbolize(page.parentBalUnit, parentAsset.symbol)\n page.parentBal.textContent = parentAsset.wallet ? Doc.formatCoinValue(parentAsset.wallet.balance.available, parentAsset.unitInfo) : '0'\n } else {\n Doc.show(page.sendEnoughWithEst)\n }\n } else { // show some generic message with no amounts, this shouldn't happen... show wallet error?\n Doc.show(page.sendEnough)\n }\n\n Doc.show(wallet.synced ? page.syncCheck : wallet.syncProgress >= 1 ? page.syncSpinner : page.syncUncheck)\n Doc.show(wallet.balance.available >= 2 * bondAsset.amount + bondFeeBuffer ? page.balCheck : page.balUncheck)\n\n page.progress.textContent = (wallet.syncProgress * 100).toFixed(1)\n\n if (wallet.synced) {\n this.progressed = true\n }\n this.reportBalance(wallet.assetID)\n }\n\n /*\n * reportWalletState sets the progress and balance, ultimately calling the\n * success function if conditions are met.\n */\n reportWalletState (wallet: WalletState) {\n if (this.progressed && this.funded) return\n if (wallet.assetID === this.assetID) this.reportProgress(wallet.synced, wallet.syncProgress)\n this.reportBalance(wallet.assetID)\n }\n\n /*\n * reportBalance sets the balance display and calls success if we go over the\n * threshold.\n */\n reportBalance (assetID: number) {\n if (this.funded || this.assetID === -1) return\n if (assetID !== this.assetID && assetID !== this.parentID) return\n const page = this.page\n const asset = app().assets[this.assetID]\n\n const avail = asset.wallet.balance.available\n page.balance.textContent = Doc.formatCoinValue(avail, asset.unitInfo)\n\n if (asset.token) {\n const parentAsset = app().assets[asset.token.parentID]\n const parentAvail = parentAsset.wallet.balance.available\n page.parentBal.textContent = Doc.formatCoinValue(parentAvail, parentAsset.unitInfo)\n if (parentAvail < this.bondFeeBuffer) return\n }\n\n // NOTE: when/if we allow one-time bond post (no maintenance) from the UI we\n // may allow to proceed as long as they have enough for tx fees. For now,\n // the balance check box will remain unchecked and we will not proceed.\n if (avail < 2 * this.bondAsset.amount + this.bondFeeBuffer) return\n\n Doc.show(page.balCheck)\n Doc.hide(page.balUncheck, page.balanceBox, page.sendEnough)\n this.funded = true\n if (this.progressed) this.success()\n }\n\n /*\n * reportProgress sets the progress display and calls success if we are fully\n * synced.\n */\n reportProgress (synced: boolean, prog: number) {\n const page = this.page\n if (synced) {\n page.progress.textContent = '100'\n Doc.hide(page.syncUncheck, page.syncRemainBox, page.syncSpinner)\n Doc.show(page.syncCheck)\n this.progressed = true\n if (this.funded) this.success()\n return\n } else if (prog === 1) {\n Doc.hide(page.syncUncheck)\n Doc.show(page.syncSpinner)\n } else {\n Doc.hide(page.syncSpinner)\n Doc.show(page.syncUncheck)\n }\n page.progress.textContent = (prog * 100).toFixed(1)\n\n if (prog >= 0.999) {\n Doc.hide(page.syncRemaining)\n Doc.show(page.syncFinishingUp)\n Doc.show(page.syncRemainBox)\n // The final stage of wallet sync process can take a while (it might hang\n // at 99.9% for many minutes, indexing addresses for example), the simplest\n // way to handle it is to keep displaying \"finishing up\" message until the\n // sync is finished, since we can't reasonably show it progressing over time.\n page.syncFinishingUp.textContent = intl.prep(intl.ID_WALLET_SYNC_FINISHING_UP)\n return\n }\n // Before we get to 99.9% the remaining time estimate must be based on more\n // than one progress report. We'll cache up to the last 20 and look at the\n // difference between the first and last to make the estimate.\n const cacheSize = 20\n const cache = this.progressCache\n cache.push({\n stamp: new Date().getTime(),\n progress: prog\n })\n if (cache.length < 2) {\n // Can't meaningfully estimate remaining until we have at least 2 data points.\n return\n }\n while (cache.length > cacheSize) cache.shift()\n const [first, last] = [cache[0], cache[cache.length - 1]]\n const progDelta = last.progress - first.progress\n if (progDelta === 0) {\n // Having no progress for a while likely means we are experiencing network\n // issues, can't reasonably estimate time remaining in this case.\n return\n }\n Doc.hide(page.syncFinishingUp)\n Doc.show(page.syncRemaining)\n Doc.show(page.syncRemainBox)\n const timeDelta = last.stamp - first.stamp\n const progRate = progDelta / timeDelta\n const toGoProg = 1 - last.progress\n const toGoTime = toGoProg / progRate\n page.syncRemain.textContent = Doc.formatDuration(toGoTime)\n }\n}\n\nexport class UnlockWalletForm {\n form: HTMLElement\n success: (assetID: number) => void\n pwCache: PasswordCache | null\n page: Record\n currentAsset: SupportedAsset\n\n constructor (form: HTMLElement, success: (assetID: number) => void, pwCache?: PasswordCache) {\n this.page = Doc.idDescendants(form)\n this.form = form\n this.pwCache = pwCache || null\n this.success = success\n bind(form, this.page.submitUnlock, () => this.submit())\n }\n\n refresh (asset: SupportedAsset) {\n const page = this.page\n this.currentAsset = asset\n page.uwAssetLogo.src = Doc.logoPath(asset.symbol)\n page.uwAssetName.textContent = asset.name\n page.uwAppPass.value = ''\n page.unlockErr.textContent = ''\n Doc.hide(page.unlockErr)\n const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(page.uwAppPassBox)\n else Doc.show(page.uwAppPassBox)\n }\n\n /*\n * setError displays an error on the form.\n */\n setError (msg: string) {\n this.page.unlockErr.textContent = msg\n Doc.show(this.page.unlockErr)\n }\n\n /*\n * showErrorOnly displays only an error on the form. Hides the\n * app pass field and the submit button.\n */\n showErrorOnly (msg: string) {\n this.setError(msg)\n Doc.hide(this.page.uwAppPassBox)\n Doc.hide(this.page.submitUnlockDiv)\n }\n\n async submit () {\n const page = this.page\n const pw = page.uwAppPass.value || (this.pwCache ? this.pwCache.pw : '')\n if (!pw && !State.passwordIsCached()) {\n page.unlockErr.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.unlockErr)\n return\n }\n const assetID = this.currentAsset.id\n Doc.hide(this.page.unlockErr)\n const open = {\n assetID: assetID,\n pass: pw\n }\n page.uwAppPass.value = ''\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/openwallet', open)\n loaded()\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success(assetID)\n }\n}\n\ninterface EarlyAcceleration {\n timePast: number,\n wasAcceleration: boolean\n}\n\ninterface PreAccelerate {\n swapRate: number\n suggestedRate: number\n suggestedRange: XYRange\n earlyAcceleration?: EarlyAcceleration\n}\n\n/*\n * AccelerateOrderForm is used to submit an acceleration request for an order.\n */\nexport class AccelerateOrderForm {\n form: HTMLElement\n page: Record\n order: Order\n acceleratedRate: number\n earlyAcceleration?: EarlyAcceleration\n currencyUnit: string\n success: () => void\n\n constructor (form: HTMLElement, success: () => void) {\n this.form = form\n this.success = success\n const page = this.page = Doc.idDescendants(form)\n\n Doc.bind(page.accelerateSubmit, 'click', () => {\n this.submit()\n })\n Doc.bind(page.submitEarlyConfirm, 'click', () => {\n this.sendAccelerateRequest()\n })\n }\n\n /*\n * displayEarlyAccelerationMsg displays a message asking for confirmation\n * when the user tries to submit an acceleration transaction very soon after\n * the swap transaction was broadcast, or very soon after a previous\n * acceleration.\n */\n displayEarlyAccelerationMsg () {\n const page = this.page\n // this is checked in submit, but another check is needed for ts compiler\n if (!this.earlyAcceleration) return\n page.recentAccelerationTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n page.recentSwapTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n if (this.earlyAcceleration.wasAcceleration) {\n Doc.show(page.recentAccelerationMsg)\n Doc.hide(page.recentSwapMsg)\n page.recentAccelerationTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n } else {\n Doc.show(page.recentSwapMsg)\n Doc.hide(page.recentAccelerationMsg)\n page.recentSwapTime.textContent = `${Math.floor(this.earlyAcceleration.timePast / 60)}`\n }\n Doc.hide(page.configureAccelerationDiv, page.accelerateErr)\n Doc.show(page.earlyAccelerationDiv)\n }\n\n // sendAccelerateRequest makes an accelerate order request to the client\n // backend.\n async sendAccelerateRequest () {\n const order = this.order\n const page = this.page\n const req = {\n pw: page.acceleratePass.value,\n orderID: order.id,\n newRate: this.acceleratedRate\n }\n page.acceleratePass.value = ''\n const loaded = app().loading(page.accelerateMainDiv)\n const res = await postJSON('/api/accelerateorder', req)\n loaded()\n if (app().checkResponse(res)) {\n page.accelerateTxID.textContent = res.txID\n Doc.hide(page.accelerateMainDiv, page.preAccelerateErr, page.accelerateErr)\n Doc.show(page.accelerateMsgDiv, page.accelerateSuccess)\n this.success()\n } else {\n page.accelerateErr.textContent = intl.prep(intl.ID_ORDER_ACCELERATION_ERR_MSG, { msg: res.msg })\n Doc.hide(page.earlyAccelerationDiv)\n Doc.show(page.accelerateErr, page.configureAccelerationDiv)\n }\n }\n\n // submit is called when the submit button is clicked.\n async submit () {\n if (this.earlyAcceleration) {\n this.displayEarlyAccelerationMsg()\n } else {\n this.sendAccelerateRequest()\n }\n }\n\n // refresh should be called before the form is displayed. It makes a\n // preaccelerate request to the client backend and sets up the form\n // based on the results.\n async refresh (order: Order) {\n const page = this.page\n this.order = order\n const res = await postJSON('/api/preaccelerate', order.id)\n if (!app().checkResponse(res)) {\n page.preAccelerateErr.textContent = intl.prep(intl.ID_ORDER_ACCELERATION_ERR_MSG, { msg: res.msg })\n Doc.hide(page.accelerateMainDiv, page.accelerateSuccess)\n Doc.show(page.accelerateMsgDiv, page.preAccelerateErr)\n return\n }\n Doc.hide(page.accelerateMsgDiv, page.preAccelerateErr, page.accelerateErr, page.feeEstimateDiv, page.earlyAccelerationDiv)\n Doc.show(page.accelerateMainDiv, page.accelerateSuccess, page.configureAccelerationDiv)\n const preAccelerate: PreAccelerate = res.preAccelerate\n this.earlyAcceleration = preAccelerate.earlyAcceleration\n this.currencyUnit = preAccelerate.suggestedRange.yUnit\n page.accelerateAvgFeeRate.textContent = `${preAccelerate.swapRate} ${preAccelerate.suggestedRange.yUnit}`\n page.accelerateCurrentFeeRate.textContent = `${preAccelerate.suggestedRate} ${preAccelerate.suggestedRange.yUnit}`\n this.acceleratedRate = preAccelerate.suggestedRange.start.y\n const selected = () => { /* do nothing */ }\n const roundY = true\n const updateRate = (_: number, newY: number) => { this.acceleratedRate = newY }\n const rangeHandler = new XYRangeHandler(preAccelerate.suggestedRange,\n preAccelerate.suggestedRange.start.x, updateRate, () => this.updateAccelerationEstimate(), selected, roundY)\n Doc.empty(page.sliderContainer)\n page.sliderContainer.appendChild(rangeHandler.control)\n this.updateAccelerationEstimate()\n }\n\n // updateAccelerationEstimate makes an accelerate estimate request to the\n // client backend using the currently selected rate on the slider, and\n // displays the results.\n async updateAccelerationEstimate () {\n const page = this.page\n const order = this.order\n const req = {\n orderID: order.id,\n newRate: this.acceleratedRate\n }\n const loaded = app().loading(page.sliderContainer)\n const res = await postJSON('/api/accelerationestimate', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.accelerateErr.textContent = intl.prep(intl.ID_ORDER_ACCELERATION_FEE_ERR_MSG, { msg: res.msg })\n Doc.show(page.accelerateErr)\n return\n }\n page.feeRateEstimate.textContent = `${this.acceleratedRate} ${this.currencyUnit}`\n let assetID\n let assetSymbol\n if (order.sell) {\n assetID = order.baseID\n assetSymbol = order.baseSymbol\n } else {\n assetID = order.quoteID\n assetSymbol = order.quoteSymbol\n }\n const unitInfo = app().unitInfo(assetID)\n page.feeEstimate.textContent = `${res.fee / unitInfo.conventional.conversionFactor} ${assetSymbol}`\n Doc.show(page.feeEstimateDiv)\n }\n}\n\n/* DEXAddressForm accepts a DEX address and performs account discovery. */\nexport class DEXAddressForm {\n form: HTMLElement\n success: (xc: Exchange, cert: string) => void\n pwCache: PasswordCache | null\n page: Record\n knownExchanges: HTMLElement[]\n dexToUpdate?: string\n\n constructor (form: HTMLElement, success: (xc: Exchange, cert: string) => void, pwCache?: PasswordCache, dexToUpdate?: string) {\n this.form = form\n this.success = success\n this.pwCache = pwCache || null\n\n const page = this.page = Doc.parseTemplate(form)\n\n page.selectedCert.textContent = intl.prep(intl.ID_NONE_SELECTED)\n\n Doc.bind(page.skipRegistration, 'change', () => this.showOrHidePWBox())\n Doc.bind(page.certFile, 'change', () => this.onCertFileChange())\n Doc.bind(page.removeCert, 'click', () => this.clearCertFile())\n Doc.bind(page.addCert, 'click', () => page.certFile.click())\n Doc.bind(page.showCustom, 'click', () => {\n Doc.hide(page.showCustom)\n Doc.show(page.customBox, page.auth)\n })\n\n this.knownExchanges = Array.from(page.knownXCs.querySelectorAll('.known-exchange'))\n for (const div of this.knownExchanges) {\n Doc.bind(div, 'click', () => {\n const host = div.dataset.host\n for (const d of this.knownExchanges) d.classList.remove('selected')\n // If we don't intend to register or we have the password cached, we're\n // good to go.\n if (this.skipRegistration() || State.passwordIsCached() || (pwCache && pwCache.pw)) {\n return this.checkDEX(host)\n }\n // Highlight the entry, but the user will have to enter their password\n // and click submit.\n div.classList.add('selected')\n page.appPW.focus()\n page.addr.value = host\n })\n }\n\n bind(form, page.submit, () => this.checkDEX())\n\n if (dexToUpdate) {\n Doc.hide(page.addDexHdr, page.skipRegistrationBox)\n Doc.show(page.updateDexHdr)\n this.dexToUpdate = dexToUpdate\n }\n\n this.refresh()\n }\n\n refresh () {\n const page = this.page\n page.addr.value = ''\n page.appPW.value = ''\n this.clearCertFile()\n Doc.hide(page.err)\n if (this.knownExchanges.length === 0 || this.dexToUpdate) {\n Doc.show(page.customBox, page.auth)\n Doc.hide(page.showCustom, page.knownXCs, page.pickServerMsg, page.addCustomMsg)\n } else {\n Doc.hide(page.customBox)\n Doc.show(page.showCustom)\n }\n for (const div of this.knownExchanges) div.classList.remove('selected')\n this.showOrHidePWBox()\n }\n\n /**\n * Show or hide appPWBox depending on if password is required. Show the\n * submit button if connecting a custom server or password is required).\n */\n showOrHidePWBox () {\n const passwordCached = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n const passwordRequired = !passwordCached && !this.skipRegistration()\n const page = this.page\n if (passwordRequired) {\n Doc.show(page.appPWBox, page.auth)\n } else {\n Doc.hide(page.appPWBox)\n Doc.setVis(Doc.isDisplayed(page.customBox), page.auth)\n }\n }\n\n skipRegistration () : boolean {\n return this.page.skipRegistration.checked ?? false\n }\n\n /* Just a small size tweak and fade-in. */\n async animate () {\n const form = this.form\n Doc.animate(550, prog => {\n form.style.transform = `scale(${0.9 + 0.1 * prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n }, 'easeOut')\n }\n\n async checkDEX (addr?: string) {\n const page = this.page\n Doc.hide(page.err)\n addr = addr || page.addr.value\n if (addr === '') {\n page.err.textContent = intl.prep(intl.ID_EMPTY_DEX_ADDRESS_MSG)\n Doc.show(page.err)\n return\n }\n let cert = ''\n if (page.certFile.value) {\n const files = page.certFile.files\n if (files && files.length) {\n cert = await files[0].text()\n }\n }\n const skipRegistration = this.skipRegistration()\n let pw = ''\n if (!skipRegistration && !State.passwordIsCached()) {\n pw = page.appPW.value || (this.pwCache ? this.pwCache.pw : '')\n }\n let endpoint : string, req: any\n if (this.dexToUpdate) {\n endpoint = '/api/updatedexhost'\n req = {\n newHost: addr,\n cert: cert,\n pw: pw,\n oldHost: this.dexToUpdate\n }\n } else {\n endpoint = skipRegistration ? '/api/adddex' : '/api/discoveracct'\n req = {\n addr: addr,\n cert: cert,\n pass: pw\n }\n }\n const loaded = app().loading(this.form)\n const res = await postJSON(endpoint, req)\n loaded()\n if (!app().checkResponse(res)) {\n if (String(res.msg).includes('certificate required')) {\n Doc.show(page.needCert)\n } else {\n page.err.textContent = res.msg\n Doc.show(page.err)\n }\n return\n }\n if (!this.dexToUpdate && (skipRegistration || res.paid || Object.keys(res.xc.pendingBonds).length > 0)) {\n await app().fetchUser()\n await app().loadPage('markets')\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success(res.xc, cert)\n }\n\n /**\n * onCertFileChange when the input certFile changed, read the file\n * and setting cert name into text of selectedCert to display on the view\n */\n async onCertFileChange () {\n const page = this.page\n const files = page.certFile.files\n if (!files || !files.length) return\n page.selectedCert.textContent = files[0].name\n Doc.show(page.removeCert)\n Doc.hide(page.addCert)\n }\n\n /* clearCertFile cleanup certFile value and selectedCert text */\n clearCertFile () {\n const page = this.page\n page.certFile.value = ''\n page.selectedCert.textContent = intl.prep(intl.ID_NONE_SELECTED)\n Doc.hide(page.removeCert)\n Doc.show(page.addCert)\n }\n}\n\n/* DiscoverAccountForm performs account discovery for a pre-selected DEX. */\nexport class DiscoverAccountForm {\n form: HTMLElement\n addr: string\n success: (xc: Exchange) => void\n pwCache: PasswordCache | null\n page: Record\n\n constructor (form: HTMLElement, addr: string, success: (xc: Exchange) => void, pwCache?: PasswordCache) {\n this.form = form\n this.addr = addr\n this.success = success\n this.pwCache = pwCache || null\n\n const page = this.page = Doc.parseTemplate(form)\n page.dexHost.textContent = addr\n bind(form, page.submit, () => this.checkDEX())\n\n this.refresh()\n }\n\n refresh () {\n const page = this.page\n page.appPW.value = ''\n Doc.hide(page.err)\n const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(page.appPWBox)\n else Doc.show(page.appPWBox)\n }\n\n /* Just a small size tweak and fade-in. */\n async animate () {\n const form = this.form\n Doc.animate(550, prog => {\n form.style.transform = `scale(${0.9 + 0.1 * prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n }, 'easeOut')\n }\n\n async checkDEX () {\n const page = this.page\n Doc.hide(page.err)\n let pw = ''\n if (!State.passwordIsCached()) {\n pw = page.appPW.value || (this.pwCache ? this.pwCache.pw : '')\n }\n const req = {\n addr: this.addr,\n pass: pw\n }\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/discoveracct', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.err.textContent = res.msg\n Doc.show(page.err)\n return\n }\n if (res.paid) {\n await app().fetchUser()\n await app().loadPage('markets')\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success(res.xc)\n }\n}\n\n/* LoginForm is used to sign into the app. */\nexport class LoginForm {\n form: HTMLElement\n success: () => void\n pwCache: PasswordCache | null\n headerTxt: string\n page: Record\n\n constructor (form: HTMLElement, success: () => void, pwCache?: PasswordCache) {\n this.success = success\n this.form = form\n this.pwCache = pwCache || null\n const page = this.page = Doc.parseTemplate(form)\n this.headerTxt = page.header.textContent || ''\n\n bind(form, page.submit, () => { this.submit() })\n\n app().registerNoteFeeder({\n login: (note: CoreNote) => { this.handleLoginNote(note) }\n })\n }\n\n handleLoginNote (n: CoreNote) {\n if (n.details === '') return\n const loginMsg = Doc.idel(this.form, 'loaderMsg')\n if (loginMsg) loginMsg.textContent = n.details\n }\n\n focus () {\n this.page.pw.focus()\n }\n\n async submit () {\n const page = this.page\n Doc.hide(page.errMsg)\n const pw = page.pw.value || ''\n page.pw.value = ''\n const rememberPass = page.rememberPass.checked\n if (pw === '') {\n page.errMsg.textContent = intl.prep(intl.ID_NO_PASS_ERROR_MSG)\n Doc.show(page.errMsg)\n return\n }\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/login', { pass: pw, rememberPass })\n loaded()\n if (!app().checkResponse(res)) {\n page.errMsg.textContent = res.msg\n Doc.show(page.errMsg)\n return\n }\n if (res.notes) {\n res.notes.reverse()\n app().setNotes(res.notes)\n }\n if (this.pwCache) this.pwCache.pw = pw\n this.success()\n }\n\n /* Just a small size tweak and fade-in. */\n async animate () {\n const form = this.form\n Doc.animate(550, prog => {\n form.style.transform = `scale(${0.9 + 0.1 * prog})`\n form.style.opacity = String(Math.pow(prog, 4))\n }, 'easeOut')\n }\n}\n\nconst traitNewAddresser = 1 << 1\n\n/*\n * DepositAddress displays a deposit address, a QR code, and a button to\n * generate a new address (if supported).\n */\nexport class DepositAddress {\n form: PageElement\n page: Record\n assetID: number\n\n constructor (form: PageElement) {\n this.form = form\n const page = this.page = Doc.idDescendants(form)\n Doc.bind(page.newDepAddrBttn, 'click', async () => { this.newDepositAddress() })\n Doc.bind(page.copyAddressBtn, 'click', () => { this.copyAddress() })\n }\n\n /* Display a deposit address. */\n async setAsset (assetID: number) {\n this.assetID = assetID\n const page = this.page\n Doc.hide(page.depositErr)\n const asset = app().assets[assetID]\n page.depositLogo.src = Doc.logoPath(asset.symbol)\n const wallet = app().walletMap[assetID]\n page.depositName.textContent = asset.name\n page.depositAddress.textContent = wallet.address\n page.qrcode.src = `/generateqrcode?address=${wallet.address}`\n if ((wallet.traits & traitNewAddresser) !== 0) Doc.show(page.newDepAddrBttn)\n else Doc.hide(page.newDepAddrBttn)\n }\n\n /* Fetch a new address from the wallet. */\n async newDepositAddress () {\n const page = this.page\n Doc.hide(page.depositErr)\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/depositaddress', {\n assetID: this.assetID\n })\n loaded()\n if (!app().checkResponse(res)) {\n page.depositErr.textContent = res.msg\n Doc.show(page.depositErr)\n return\n }\n page.depositAddress.textContent = res.address\n page.qrcode.src = `/generateqrcode?address=${res.address}`\n }\n\n async copyAddress () {\n const page = this.page\n navigator.clipboard.writeText(page.depositAddress.textContent || '')\n .then(() => {\n Doc.show(page.copyAlert)\n setTimeout(() => {\n Doc.hide(page.copyAlert)\n }, 800)\n })\n .catch((reason) => {\n console.error('Unable to copy: ', reason)\n })\n }\n}\n\nconst animationLength = 300\n\n/* Swap form1 for form2 with an animation. */\nexport async function slideSwap (form1: HTMLElement, form2: HTMLElement) {\n const shift = document.body.offsetWidth / 2\n await Doc.animate(animationLength, progress => {\n form1.style.right = `${progress * shift}px`\n }, 'easeInHard')\n Doc.hide(form1)\n form1.style.right = '0'\n form2.style.right = String(-shift)\n Doc.show(form2)\n if (form2.querySelector('input')) {\n Doc.safeSelector(form2, 'input').focus()\n }\n await Doc.animate(animationLength, progress => {\n form2.style.right = `${-shift + progress * shift}px`\n }, 'easeOutHard')\n form2.style.right = '0'\n}\n\n/*\n * bind binds the click and submit events and prevents page reloading on\n * submission.\n */\nexport function bind (form: HTMLElement, submitBttn: HTMLElement, handler: (e: Event) => void) {\n const wrapper = (e: Event) => {\n if (e.preventDefault) e.preventDefault()\n handler(e)\n }\n Doc.bind(submitBttn, 'click', wrapper)\n Doc.bind(form, 'submit', wrapper)\n}\n\n// isTruthyString will be true if the provided string is recognized as a\n// value representing true.\nfunction isTruthyString (s: string) {\n return s === '1' || s.toLowerCase() === 'true'\n}\n\n// toUnixDate converts a javascript date object to a unix date, which is\n// the number of *seconds* since the start of the epoch.\nfunction toUnixDate (date: Date) {\n return Math.floor(date.getTime() / 1000)\n}\n\n// dateApplyOffset shifts a date by the timezone offset. This is used to make\n// UTC dates show the local date. This can be used to prepare a Date so\n// toISOString generates a local date string. This is also used to trick an html\n// input element to show the local date when setting the valueAsDate field. When\n// reading the date back to JS, the value field should be interpreted as local\n// using the \"T00:00\" suffix, or the Date in valueAsDate should be shifted in\n// the opposite direction.\nfunction dateApplyOffset (date: Date) {\n return new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000)\n}\n\n// dateToString converts a javascript date object to a YYYY-MM-DD format string,\n// in the local time zone.\nfunction dateToString (date: Date) {\n return dateApplyOffset(date).toISOString().split('T')[0]\n // Another common hack:\n // date.toLocaleString(\"sv-SE\", { year: \"numeric\", month: \"2-digit\", day: \"2-digit\" })\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport {\n NewWalletForm,\n DEXAddressForm,\n DiscoverAccountForm,\n LoginForm,\n ConfirmRegistrationForm,\n FeeAssetSelectionForm,\n WalletWaitForm,\n slideSwap,\n bind as bindForm\n} from './forms'\nimport * as intl from './locales'\nimport {\n app,\n PasswordCache,\n Exchange,\n PageElement\n} from './registry'\n\nexport default class RegistrationPage extends BasePage {\n body: HTMLElement\n pwCache: PasswordCache\n currentDEX: Exchange\n page: Record\n loginForm: LoginForm\n dexAddrForm: DEXAddressForm\n discoverAcctForm: DiscoverAccountForm\n newWalletForm: NewWalletForm\n regAssetForm: FeeAssetSelectionForm\n walletWaitForm: WalletWaitForm\n confirmRegisterForm: ConfirmRegistrationForm\n\n constructor (body: HTMLElement, data: any) {\n super()\n this.body = body\n this.pwCache = { pw: '' }\n const page = this.page = Doc.idDescendants(body)\n\n if (data.host && page.dexAddrForm.classList.contains('selected')) {\n page.dexAddrForm.classList.remove('selected')\n page.discoverAcctForm.classList.add('selected')\n page.discoverAcctForm.dataset.host = data.host\n }\n\n // Hide the form closers for the registration process.\n body.querySelectorAll('.form-closer').forEach(el => Doc.hide(el))\n\n // SET APP PASSWORD\n bindForm(page.appPWForm, page.appPWSubmit, () => this.setAppPass())\n Doc.bind(page.showSeedRestore, 'click', () => {\n Doc.show(page.seedRestore)\n Doc.hide(page.showSeedRestore)\n })\n\n this.loginForm = new LoginForm(page.loginForm, async () => {\n await app().fetchUser()\n if (this.discoverAcctForm) {\n this.discoverAcctForm.refresh()\n slideSwap(page.loginForm, page.discoverAcctForm)\n } else {\n this.dexAddrForm.refresh()\n slideSwap(page.loginForm, page.dexAddrForm)\n }\n }, this.pwCache)\n\n this.newWalletForm = new NewWalletForm(\n page.newWalletForm,\n assetID => this.newWalletCreated(assetID),\n this.pwCache,\n () => this.animateRegAsset(page.newWalletForm)\n )\n\n // ADD DEX\n this.dexAddrForm = new DEXAddressForm(page.dexAddrForm, async (xc, certFile) => {\n this.requestFeepayment(page.dexAddrForm, xc, certFile)\n }, this.pwCache)\n\n const addr = page.discoverAcctForm.dataset.host\n if (addr) {\n this.discoverAcctForm = new DiscoverAccountForm(page.discoverAcctForm, addr, async (xc) => {\n this.requestFeepayment(page.discoverAcctForm, xc, '')\n }, this.pwCache)\n }\n\n // SELECT REG ASSET\n this.regAssetForm = new FeeAssetSelectionForm(page.regAssetForm, async assetID => {\n this.confirmRegisterForm.setAsset(assetID)\n\n const asset = app().assets[assetID]\n const wallet = asset.wallet\n if (wallet) {\n const bondAsset = this.currentDEX.bondAssets[asset.symbol]\n if (wallet.synced && wallet.balance.available > bondAsset.amount) {\n this.animateConfirmForm(page.regAssetForm)\n return\n }\n const bondsFeeBuffer = await this.getBondsFeeBuffer(assetID, page.regAssetForm)\n this.walletWaitForm.setWallet(wallet, bondsFeeBuffer)\n slideSwap(page.regAssetForm, page.walletWait)\n return\n }\n this.newWalletForm.setAsset(assetID)\n slideSwap(page.regAssetForm, page.newWalletForm)\n })\n\n this.walletWaitForm = new WalletWaitForm(page.walletWait, () => {\n this.animateConfirmForm(page.walletWait)\n }, () => { this.animateRegAsset(page.walletWait) })\n\n // SUBMIT DEX REGISTRATION\n this.confirmRegisterForm = new ConfirmRegistrationForm(page.confirmRegForm, () => {\n this.registerDEXSuccess()\n }, () => {\n this.animateRegAsset(page.confirmRegForm)\n }, this.pwCache)\n\n const currentForm = Doc.safeSelector(page.forms, ':scope > form.selected')\n currentForm.classList.remove('selected')\n switch (currentForm) {\n case page.loginForm:\n this.loginForm.animate()\n break\n case page.dexAddrForm:\n this.dexAddrForm.animate()\n break\n case page.discoverAcctForm:\n this.discoverAcctForm.animate()\n }\n Doc.show(currentForm)\n\n if (app().authed()) this.auth()\n }\n\n unload () {\n this.pwCache.pw = ''\n }\n\n // auth should be called once user is known to be authed with the server.\n async auth () {\n await app().fetchUser()\n }\n\n async requestFeepayment (oldForm: HTMLElement, xc: Exchange, certFile: string) {\n this.currentDEX = xc\n this.confirmRegisterForm.setExchange(xc, certFile)\n this.walletWaitForm.setExchange(xc)\n this.regAssetForm.setExchange(xc)\n this.animateRegAsset(oldForm)\n }\n\n /* Swap in the asset selection form and run the animation. */\n async animateRegAsset (oldForm: HTMLElement) {\n Doc.hide(oldForm)\n this.regAssetForm.animate()\n Doc.show(this.page.regAssetForm)\n }\n\n /* Swap in the confirmation form and run the animation. */\n async animateConfirmForm (oldForm: HTMLElement) {\n this.confirmRegisterForm.animate()\n Doc.hide(oldForm)\n Doc.show(this.page.confirmRegForm)\n }\n\n // Retrieve an estimate for the tx fee needed to pay the registration fee.\n async getBondsFeeBuffer (assetID: number, form: HTMLElement) {\n const loaded = app().loading(form)\n const res = await postJSON('/api/bondsfeebuffer', { assetID })\n loaded()\n if (!app().checkResponse(res)) {\n return 0\n }\n return res.feeBuffer\n }\n\n /* Set the application password. Attached to form submission. */\n async setAppPass () {\n const page = this.page\n Doc.hide(page.appPWErrMsg)\n const pw = page.appPW.value || ''\n const pwAgain = page.appPWAgain.value\n if (pw === '') {\n page.appPWErrMsg.textContent = intl.prep(intl.ID_NO_PASS_ERROR_MSG)\n Doc.show(page.appPWErrMsg)\n return\n }\n if (pw !== pwAgain) {\n page.appPWErrMsg.textContent = intl.prep(intl.ID_PASSWORD_NOT_MATCH)\n Doc.show(page.appPWErrMsg)\n return\n }\n\n // Clear the notification cache. Useful for development purposes, since\n // the Application will only clear them on login, which would leave old\n // browser-cached notifications in place after registering even if the\n // client db is wiped.\n app().setNotes([])\n page.appPW.value = ''\n page.appPWAgain.value = ''\n const loaded = app().loading(page.appPWForm)\n const seed = page.seedInput.value\n const rememberPass = page.rememberPass.checked\n const res = await postJSON('/api/init', {\n pass: pw,\n seed,\n rememberPass\n })\n loaded()\n if (!app().checkResponse(res)) {\n page.appPWErrMsg.textContent = res.msg\n Doc.show(page.appPWErrMsg)\n return\n }\n this.pwCache.pw = pw\n this.auth()\n app().updateMenuItemsDisplay()\n this.newWalletForm.refresh()\n this.dexAddrForm.refresh()\n await slideSwap(page.appPWForm, page.dexAddrForm)\n }\n\n /* gets the contents of the cert file */\n async getCertFile () {\n let cert = ''\n if (this.dexAddrForm.page.certFile.value) {\n const files = this.dexAddrForm.page.certFile.files\n if (files && files.length) cert = await files[0].text()\n }\n return cert\n }\n\n /* Called after successful registration to a DEX. */\n async registerDEXSuccess () {\n await app().fetchUser()\n await app().loadPage('markets')\n }\n\n async newWalletCreated (assetID: number) {\n this.regAssetForm.refresh()\n const user = await app().fetchUser()\n if (!user) return\n const page = this.page\n const asset = user.assets[assetID]\n const wallet = asset.wallet\n const bondAmt = this.currentDEX.bondAssets[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > bondAmt) {\n await this.animateConfirmForm(page.newWalletForm)\n return\n }\n\n const bondsFeeBuffer = await this.getBondsFeeBuffer(assetID, page.newWalletForm)\n this.walletWaitForm.setWallet(wallet, bondsFeeBuffer)\n await slideSwap(page.newWalletForm, page.walletWait)\n }\n}\n","import { app } from './registry'\nimport Doc from './doc'\nimport BasePage from './basepage'\nimport { LoginForm } from './forms'\n\nexport default class LoginPage extends BasePage {\n form: HTMLElement\n loginForm: LoginForm\n\n constructor (body: HTMLElement) {\n super()\n this.form = Doc.idel(body, 'loginForm')\n Doc.show(this.form)\n this.loginForm = new LoginForm(this.form, () => { this.loggedIn() })\n this.loginForm.focus()\n }\n\n /* login submits the sign-in form and parses the result. */\n async loggedIn () {\n await app().fetchUser()\n await app().loadPage('markets')\n }\n}\n","import Doc, { Animation } from './doc'\nimport BasePage from './basepage'\nimport { postJSON, Errors } from './http'\nimport {\n NewWalletForm,\n WalletConfigForm,\n UnlockWalletForm,\n DepositAddress,\n bind as bindForm\n} from './forms'\nimport State from './state'\nimport * as intl from './locales'\nimport * as OrderUtil from './orderutil'\nimport {\n app,\n PageElement,\n SupportedAsset,\n WalletDefinition,\n BalanceNote,\n WalletStateNote,\n RateNote,\n Order,\n OrderFilter,\n WalletCreationNote,\n Market,\n PeerSource,\n WalletPeer\n} from './registry'\n\nconst animationLength = 300\nconst traitRescanner = 1\nconst traitLogFiler = 1 << 2\nconst traitRecoverer = 1 << 5\nconst traitWithdrawer = 1 << 6\nconst traitRestorer = 1 << 8\nconst traitTxFeeEstimator = 1 << 10\nconst traitPeerManager = 1 << 11\nconst traitsExtraOpts = traitLogFiler & traitRecoverer & traitRestorer & traitRescanner & traitPeerManager\n\ninterface ReconfigRequest {\n assetID: number\n walletType: string\n config: Record\n newWalletPW?: string\n appPW: string\n}\n\ninterface RescanRecoveryRequest {\n assetID: number\n appPW?: string\n force?: boolean\n}\n\ninterface WalletRestoration {\n target: string\n seed: string\n seedName: string\n instructions: string\n}\n\ninterface AssetButton {\n tmpl: Record\n bttn: PageElement\n}\n\nexport default class WalletsPage extends BasePage {\n body: HTMLElement\n page: Record\n assetButtons: Record\n newWalletForm: NewWalletForm\n reconfigForm: WalletConfigForm\n unlockForm: UnlockWalletForm\n depositAddrForm: DepositAddress\n keyup: (e: KeyboardEvent) => void\n changeWalletPW: boolean\n displayed: HTMLElement\n animation: Animation\n forms: PageElement[]\n forceReq: RescanRecoveryRequest\n forceUrl: string\n currentForm: PageElement\n restoreInfoCard: HTMLElement\n selectedAssetID: number\n maxSend: number\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n const page = this.page = Doc.idDescendants(body)\n\n Doc.cleanTemplates(page.restoreInfoCard, page.connectedIconTmpl, page.disconnectedIconTmpl, page.removeIconTmpl)\n this.restoreInfoCard = page.restoreInfoCard.cloneNode(true) as HTMLElement\n Doc.show(page.connectedIconTmpl, page.disconnectedIconTmpl, page.removeIconTmpl)\n\n this.forms = Doc.applySelector(page.forms, ':scope > form')\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { this.closePopups() })\n })\n Doc.bind(page.cancelForce, 'click', () => { this.closePopups() })\n\n this.selectedAssetID = -1\n Doc.cleanTemplates(\n page.iconSelectTmpl, page.balanceDetailRow, page.recentOrderTmpl\n )\n\n Doc.bind(page.createWallet, 'click', () => this.showNewWallet(this.selectedAssetID))\n Doc.bind(page.connectBttn, 'click', () => this.doConnect(this.selectedAssetID))\n Doc.bind(page.send, 'click', () => this.showSendForm(this.selectedAssetID))\n Doc.bind(page.receive, 'click', () => this.showDeposit(this.selectedAssetID))\n Doc.bind(page.unlockBttn, 'click', () => this.openWallet(this.selectedAssetID))\n Doc.bind(page.lockBttn, 'click', () => this.lock(this.selectedAssetID))\n Doc.bind(page.reconfigureBttn, 'click', () => this.showReconfig(this.selectedAssetID))\n Doc.bind(page.rescanWallet, 'click', () => this.rescanWallet(this.selectedAssetID))\n\n // Bind the new wallet form.\n this.newWalletForm = new NewWalletForm(page.newWalletForm, (assetID: number) => {\n const fmtParams = { assetName: app().assets[assetID].name }\n this.assetUpdated(assetID, page.newWalletForm, intl.prep(intl.ID_NEW_WALLET_SUCCESS, fmtParams))\n this.sortAssetButtons()\n })\n\n // Bind the wallet reconfig form.\n this.reconfigForm = new WalletConfigForm(page.reconfigInputs, false)\n\n // Bind the wallet unlock form.\n this.unlockForm = new UnlockWalletForm(page.unlockWalletForm, (assetID: number) => this.openWalletSuccess(assetID, page.unlockWalletForm))\n\n // Bind the send form.\n bindForm(page.sendForm, page.submitSendForm, async () => { this.stepSend() })\n // Send confirmation form.\n bindForm(page.vSendForm, page.vSend, async () => { this.send() })\n // Cancel send confirmation form.\n Doc.bind(page.vCancelSend, 'click', async () => { this.cancelSend() })\n // Bind the wallet reconfiguration submission.\n bindForm(page.reconfigForm, page.submitReconfig, () => this.reconfig())\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => this.closePopups())\n })\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { this.closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (Doc.isDisplayed(this.page.forms)) this.closePopups()\n }\n }\n Doc.bind(document, 'keyup', this.keyup)\n\n Doc.bind(page.downloadLogs, 'click', async () => { this.downloadLogs() })\n Doc.bind(page.exportWallet, 'click', async () => { this.displayExportWalletAuth() })\n Doc.bind(page.recoverWallet, 'click', async () => { this.showRecoverWallet() })\n bindForm(page.exportWalletAuth, page.exportWalletAuthSubmit, async () => { this.exportWalletAuthSubmit() })\n bindForm(page.recoverWalletConfirm, page.recoverWalletSubmit, () => { this.recoverWallet() })\n bindForm(page.confirmForce, page.confirmForceSubmit, async () => { this.confirmForceSubmit() })\n Doc.bind(page.disableWallet, 'click', async () => { this.showToggleWalletStatus(true) })\n Doc.bind(page.enableWallet, 'click', async () => { this.showToggleWalletStatus(false) })\n bindForm(page.toggleWalletStatusConfirm, page.toggleWalletStatusSubmit, async () => { this.toggleWalletStatus() })\n Doc.bind(page.managePeers, 'click', async () => { this.showManagePeersForm() })\n Doc.bind(page.addPeerSubmit, 'click', async () => { this.submitAddPeer() })\n\n // New deposit address button.\n this.depositAddrForm = new DepositAddress(page.deposit)\n\n // Clicking on the available amount on the Send form populates the\n // amount field.\n Doc.bind(page.walletBal, 'click', () => { this.populateMaxSend() })\n\n // Display fiat value for current send amount.\n Doc.bind(page.sendAmt, 'input', () => {\n const { unitInfo: ui } = app().assets[this.selectedAssetID]\n const amt = parseFloat(page.sendAmt.value || '0')\n const conversionFactor = ui.conventional.conversionFactor\n this.showFiatValue(this.selectedAssetID, amt * conversionFactor, page.sendValue)\n })\n\n // Clicking on maxSend on the send form should populate the amount field.\n Doc.bind(page.maxSend, 'click', () => { this.populateMaxSend() })\n\n // Validate send address on input.\n Doc.bind(page.sendAddr, 'input', async () => {\n const asset = app().assets[this.selectedAssetID]\n Doc.hide(page.validAddr)\n page.sendAddr.classList.remove('invalid')\n const addr = page.sendAddr.value || ''\n if (!asset || addr === '') return\n const valid = await this.validateSendAddress(addr, asset.id)\n if (valid) Doc.show(page.validAddr)\n else page.sendAddr.classList.add('invalid')\n })\n\n // A link on the wallet reconfiguration form to show/hide the password field.\n Doc.bind(page.showChangePW, 'click', () => {\n this.changeWalletPW = !this.changeWalletPW\n this.setPWSettingViz(this.changeWalletPW)\n })\n\n // Changing the type of wallet.\n Doc.bind(page.changeWalletTypeSelect, 'change', () => {\n this.changeWalletType()\n })\n Doc.bind(page.showChangeType, 'click', () => {\n if (Doc.isHidden(page.changeWalletType)) {\n Doc.show(page.changeWalletType, page.changeTypeHideIcon)\n Doc.hide(page.changeTypeShowIcon)\n page.changeTypeMsg.textContent = intl.prep(intl.ID_KEEP_WALLET_TYPE)\n } else this.showReconfig(this.selectedAssetID, true)\n })\n\n app().registerNoteFeeder({\n fiatrateupdate: (note: RateNote) => { this.handleRatesNote(note) },\n balance: (note: BalanceNote) => { this.handleBalanceNote(note) },\n walletstate: (note: WalletStateNote) => { this.handleWalletStateNote(note) },\n walletconfig: (note: WalletStateNote) => { this.handleWalletStateNote(note) },\n createwallet: (note: WalletCreationNote) => { this.handleCreateWalletNote(note) }\n })\n\n const firstAsset = this.sortAssetButtons()\n let selectedAsset = firstAsset.id\n const assetIDStr = State.fetchLocal(State.selectedAssetLK)\n if (assetIDStr) selectedAsset = Number(assetIDStr)\n this.setSelectedAsset(selectedAsset)\n }\n\n closePopups () {\n Doc.hide(this.page.forms)\n if (this.animation) this.animation.stop()\n }\n\n // stepSend makes a request to get an estimated fee and displays the confirm\n // send form.\n async stepSend () {\n const page = this.page\n Doc.hide(page.vSendErr, page.sendErr, page.vSendEstimates, page.txFeeNotAvailable)\n const assetID = parseInt(page.sendForm.dataset.assetID || '')\n const token = app().assets[assetID].token\n const subtract = page.subtractCheckBox.checked || false\n const conversionFactor = app().unitInfo(assetID).conventional.conversionFactor\n const value = Math.round(parseFloat(page.sendAmt.value || '') * conversionFactor)\n const addr = page.sendAddr.value || ''\n if (addr === '') return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: addr }))\n const { wallet, unitInfo: ui, symbol } = app().assets[assetID]\n\n // txfee will not be available if wallet is not a fee estimator or the\n // request failed.\n let txfee = 0\n if ((wallet.traits & traitTxFeeEstimator) !== 0) {\n const open = {\n addr: page.sendAddr.value,\n assetID: assetID,\n subtract: subtract,\n value: value\n }\n\n const loaded = app().loading(page.sendForm)\n const res = await postJSON('/api/txfee', open)\n loaded()\n if (!app().checkResponse(res)) {\n page.txFeeNotAvailable.dataset.tooltip = intl.prep(intl.ID_TXFEE_ERR_MSG, { err: res.msg })\n Doc.show(page.txFeeNotAvailable)\n // We still want to ensure user address is valid before proceeding to send\n // confirm form if there's an error while calculating the transaction fee.\n const valid = await this.validateSendAddress(addr, assetID)\n if (!valid) return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: addr || '' }))\n } else if (res.ok) {\n if (!res.validaddress) return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: page.sendAddr.value || '' }))\n txfee = res.txfee\n Doc.show(page.vSendEstimates)\n }\n } else {\n // Validate only the send address for assets that are not fee estimators.\n const valid = await this.validateSendAddress(addr, assetID)\n if (!valid) return Doc.showFormError(page.sendErr, intl.prep(intl.ID_INVALID_ADDRESS_MSG, { address: addr || '' }))\n }\n\n page.vSendSymbol.textContent = symbol.toUpperCase()\n page.vSendLogo.src = Doc.logoPath(symbol)\n\n if (token) {\n const { unitInfo: feeUI, symbol: feeSymbol } = app().assets[token.parentID]\n page.vSendFee.textContent = Doc.formatFullPrecision(txfee, feeUI) + ' ' + feeSymbol\n } else {\n page.vSendFee.textContent = Doc.formatFullPrecision(txfee, ui)\n }\n this.showFiatValue(assetID, txfee, page.vSendFeeFiat)\n page.vSendDestinationAmt.textContent = Doc.formatFullPrecision(value - txfee, ui)\n page.vTotalSend.textContent = Doc.formatFullPrecision(value, ui)\n this.showFiatValue(assetID, value, page.vTotalSendFiat)\n page.vSendAddr.textContent = page.sendAddr.value || ''\n const bal = wallet.balance.available - value\n page.balanceAfterSend.textContent = Doc.formatFullPrecision(bal, ui)\n this.showFiatValue(assetID, bal, page.balanceAfterSendFiat)\n Doc.show(page.approxSign)\n // NOTE: All tokens take this route because they cannot pay the fee.\n if (!subtract) {\n Doc.hide(page.approxSign)\n page.vSendDestinationAmt.textContent = Doc.formatFullPrecision(value, ui)\n let totalSend = value\n if (!token) totalSend += txfee\n page.vTotalSend.textContent = Doc.formatFullPrecision(totalSend, ui)\n this.showFiatValue(assetID, totalSend, page.vTotalSendFiat)\n let bal = wallet.balance.available - value\n if (!token) bal -= txfee\n // handle edge cases where bal is not enough to cover totalSend.\n // we don't want a minus display of user bal.\n if (bal <= 0) {\n page.balanceAfterSend.textContent = Doc.formatFullPrecision(0, ui)\n this.showFiatValue(assetID, 0, page.balanceAfterSendFiat)\n } else {\n page.balanceAfterSend.textContent = Doc.formatFullPrecision(bal, ui)\n this.showFiatValue(assetID, bal, page.balanceAfterSendFiat)\n }\n }\n Doc.hide(page.sendForm)\n await this.showForm(page.vSendForm)\n }\n\n // cancelSend displays the send form if user wants to make modification.\n async cancelSend () {\n const page = this.page\n Doc.hide(page.vSendForm, page.sendErr)\n await this.showForm(page.sendForm)\n }\n\n /*\n * validateSendAddress validates the provided address for an asset.\n */\n async validateSendAddress (addr: string, assetID: number): Promise {\n const resp = await postJSON('/api/validateaddress', { addr: addr, assetID: assetID })\n return app().checkResponse(resp)\n }\n\n /*\n * setPWSettingViz sets the visibility of the password field section.\n */\n setPWSettingViz (visible: boolean) {\n if (visible) {\n Doc.hide(this.page.showIcon)\n Doc.show(this.page.hideIcon, this.page.changePW)\n this.page.switchPWMsg.textContent = intl.prep(intl.ID_KEEP_WALLET_PASS)\n return\n }\n Doc.hide(this.page.hideIcon, this.page.changePW)\n Doc.show(this.page.showIcon)\n this.page.switchPWMsg.textContent = intl.prep(intl.ID_NEW_WALLET_PASS)\n }\n\n /*\n * updateWalletPeers retrieves the wallet peers and displays them in the\n * wallet peers table.\n */\n async updateWalletPeersTable () {\n const page = this.page\n\n Doc.hide(page.peerSpinner)\n\n const res = await postJSON('/api/getwalletpeers', {\n assetID: this.selectedAssetID\n })\n if (!app().checkResponse(res)) {\n page.managePeersErr.textContent = res.msg\n Doc.show(page.managePeersErr)\n return\n }\n\n while (page.peersTableBody.firstChild) {\n page.peersTableBody.removeChild(page.peersTableBody.firstChild)\n }\n\n const peers : WalletPeer[] = res.peers || []\n peers.sort((a: WalletPeer, b: WalletPeer) : number => {\n return a.source - b.source\n })\n\n const defaultText = intl.prep(intl.ID_DEFAULT)\n const addedText = intl.prep(intl.ID_ADDED)\n const discoveredText = intl.prep(intl.ID_DISCOVERED)\n\n peers.forEach((peer: WalletPeer) => {\n const row = page.peerTableRow.cloneNode(true) as PageElement\n const tmpl = Doc.parseTemplate(row)\n\n tmpl.addr.textContent = peer.addr\n\n switch (peer.source) {\n case PeerSource.WalletDefault:\n tmpl.source.textContent = defaultText\n break\n case PeerSource.UserAdded:\n tmpl.source.textContent = addedText\n break\n case PeerSource.Discovered:\n tmpl.source.textContent = discoveredText\n break\n }\n\n let connectionIcon\n if (peer.connected) {\n connectionIcon = this.page.connectedIconTmpl.cloneNode(true)\n } else {\n connectionIcon = this.page.disconnectedIconTmpl.cloneNode(true)\n }\n tmpl.connected.appendChild(connectionIcon)\n\n if (peer.source === PeerSource.UserAdded) {\n const removeIcon = this.page.removeIconTmpl.cloneNode(true)\n Doc.bind(removeIcon, 'click', async () => {\n Doc.hide(page.managePeersErr)\n const res = await postJSON('/api/removewalletpeer', {\n assetID: this.selectedAssetID,\n addr: peer.addr\n })\n if (!app().checkResponse(res)) {\n page.managePeersErr.textContent = res.msg\n Doc.show(page.managePeersErr)\n return\n }\n this.spinUntilPeersUpdate()\n })\n tmpl.remove.appendChild(removeIcon)\n }\n\n page.peersTableBody.appendChild(row)\n })\n }\n\n // showManagePeersForm displays the manage peers form.\n async showManagePeersForm () {\n const page = this.page\n await this.updateWalletPeersTable()\n Doc.hide(page.managePeersErr)\n this.showForm(page.managePeersForm)\n }\n\n // submitAddPeers sends a request for the the wallet to connect to a new\n // peer.\n async submitAddPeer () {\n const page = this.page\n Doc.hide(page.managePeersErr)\n const res = await postJSON('/api/addwalletpeer', {\n assetID: this.selectedAssetID,\n addr: page.addPeerInput.value\n })\n if (!app().checkResponse(res)) {\n page.managePeersErr.textContent = res.msg\n Doc.show(page.managePeersErr)\n return\n }\n this.spinUntilPeersUpdate()\n page.addPeerInput.value = ''\n }\n\n /*\n * spinUntilPeersUpdate will show the spinner on the manage peers fork.\n * If it is still showing after 10 seconds, the peers table will be updated\n * instead of waiting for a notification.\n */\n async spinUntilPeersUpdate () {\n const page = this.page\n Doc.show(page.peerSpinner)\n setTimeout(() => {\n if (Doc.isDisplayed(page.peerSpinner)) {\n this.updateWalletPeersTable()\n }\n }, 10000)\n }\n\n /*\n * showToggleWalletStatus displays the toggleWalletStatusConfirm form with\n * relevant help message.\n */\n showToggleWalletStatus (disable: boolean) {\n const page = this.page\n Doc.hide(page.toggleWalletStatusErr, page.walletStatusDisable, page.disableWalletMsg, page.walletStatusEnable, page.enableWalletMsg)\n if (disable) Doc.show(page.walletStatusDisable, page.disableWalletMsg)\n else Doc.show(page.walletStatusEnable, page.enableWalletMsg)\n this.showForm(page.toggleWalletStatusConfirm)\n }\n\n /*\n * toggleWalletStatus toggles a wallets status to either disabled or enabled.\n */\n async toggleWalletStatus () {\n const page = this.page\n Doc.hide(page.toggleWalletStatusErr)\n\n const asset = app().assets[this.selectedAssetID]\n const disable = !asset.wallet.disabled\n const url = '/api/togglewalletstatus'\n const req = {\n assetID: this.selectedAssetID,\n disable: disable\n }\n\n const fmtParams = { assetName: asset.name }\n const loaded = app().loading(page.toggleWalletStatusConfirm)\n const res = await postJSON(url, req)\n loaded()\n if (!app().checkResponse(res)) {\n if (res.code === Errors.activeOrdersErr) page.toggleWalletStatusErr.textContent = intl.prep(intl.ID_ACTIVE_ORDERS_ERR_MSG, fmtParams)\n else page.toggleWalletStatusErr.textContent = res.msg\n Doc.show(page.toggleWalletStatusErr)\n return\n }\n\n let successMsg = intl.prep(intl.ID_WALLET_DISABLED_MSG, fmtParams)\n if (!disable) successMsg = intl.prep(intl.ID_WALLET_ENABLED_MSG, fmtParams)\n this.assetUpdated(this.selectedAssetID, page.toggleWalletStatusConfirm, successMsg)\n }\n\n /*\n * showBox shows the box with a fade-in animation.\n */\n async showBox (box: HTMLElement, focuser?: PageElement) {\n box.style.opacity = '0'\n Doc.show(box)\n if (focuser) focuser.focus()\n await Doc.animate(animationLength, progress => {\n box.style.opacity = `${progress}`\n }, 'easeOut')\n box.style.opacity = '1'\n this.displayed = box\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: PageElement) {\n const page = this.page\n this.currentForm = form\n this.forms.forEach(form => Doc.hide(form))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n async showSuccess (msg: string) {\n const page = this.page\n page.successMessage.textContent = msg\n this.currentForm = page.checkmarkForm\n this.forms.forEach(form => Doc.hide(form))\n Doc.show(page.forms, page.checkmarkForm)\n page.checkmarkForm.style.right = '0'\n page.checkmark.style.fontSize = '0px'\n\n const [startR, startG, startB] = State.isDark() ? [223, 226, 225] : [51, 51, 51]\n const [endR, endG, endB] = [16, 163, 16]\n const [diffR, diffG, diffB] = [endR - startR, endG - startG, endB - startB]\n\n this.animation = new Animation(1200, (prog: number) => {\n page.checkmark.style.fontSize = `${prog * 80}px`\n page.checkmark.style.color = `rgb(${startR + prog * diffR}, ${startG + prog * diffG}, ${startB + prog * diffB})`\n }, 'easeOutElastic', () => {\n this.animation = new Animation(1500, () => { /* pass */ }, '', () => {\n if (this.currentForm === page.checkmarkForm) this.closePopups()\n })\n })\n }\n\n /* Show the new wallet form. */\n async showNewWallet (assetID: number) {\n const page = this.page\n const box = page.newWalletForm\n this.newWalletForm.setAsset(assetID)\n const defaultsLoaded = this.newWalletForm.loadDefaults()\n await this.showForm(box)\n await defaultsLoaded\n }\n\n // sortAssetButtons displays supported assets, sorted. Returns first asset in the\n // list.\n sortAssetButtons (): SupportedAsset {\n const page = this.page\n this.assetButtons = {}\n Doc.empty(page.assetSelect)\n const sortedAssets = [...Object.values(app().assets)]\n sortedAssets.sort((a: SupportedAsset, b: SupportedAsset) => {\n if (a.wallet && !b.wallet) return -1\n if (!a.wallet && b.wallet) return 1\n return a.symbol.localeCompare(b.symbol)\n })\n for (const a of sortedAssets) {\n const bttn = page.iconSelectTmpl.cloneNode(true) as HTMLElement\n page.assetSelect.appendChild(bttn)\n const tmpl = Doc.parseTemplate(bttn)\n this.assetButtons[a.id] = { tmpl, bttn }\n this.updateAssetButton(a.id)\n Doc.bind(bttn, 'click', () => {\n this.setSelectedAsset(a.id)\n State.storeLocal(State.selectedAssetLK, String(a.id))\n })\n }\n page.assetSelect.classList.remove('invisible')\n return sortedAssets[0]\n }\n\n updateAssetButton (assetID: number) {\n const a = app().assets[assetID]\n const { bttn, tmpl } = this.assetButtons[assetID]\n Doc.hide(tmpl.fiat, tmpl.noWallet)\n bttn.classList.add('nowallet')\n tmpl.img.src ||= Doc.logoPath(a.symbol) // don't initiate GET if already set (e.g. update on some notification)\n tmpl.name.textContent = a.name\n if (a.wallet) {\n bttn.classList.remove('nowallet')\n const { wallet: { balance: b }, unitInfo: ui } = a\n const totalBalance = b.available + b.locked + b.immature\n tmpl.balance.textContent = Doc.formatCoinValue(totalBalance, ui)\n const rate = app().fiatRatesMap[a.id]\n if (rate) {\n Doc.show(tmpl.fiat)\n tmpl.fiat.textContent = Doc.formatFiatConversion(totalBalance, rate, ui)\n }\n } else Doc.show(tmpl.noWallet)\n }\n\n setSelectedAsset (assetID: number) {\n const { assetSelect } = this.page\n for (const b of assetSelect.children) b.classList.remove('selected')\n this.assetButtons[assetID].bttn.classList.add('selected')\n this.selectedAssetID = assetID\n this.updateDisplayedAsset(assetID)\n this.showAvailableMarkets(assetID)\n this.showRecentActivity(assetID)\n }\n\n updateDisplayedAsset (assetID: number) {\n if (assetID !== this.selectedAssetID) return\n const { symbol, wallet, name } = app().assets[assetID]\n const page = this.page\n for (const el of document.querySelectorAll('[data-asset-name]')) el.textContent = name\n page.assetLogo.src = Doc.logoPath(symbol)\n Doc.hide(\n page.balanceBox, page.fiatBalanceBox, page.createWalletBox, page.walletDetails,\n page.sendReceive, page.connectBttnBox, page.statusLocked, page.statusReady,\n page.statusOff, page.unlockBttnBox, page.lockBttnBox, page.connectBttnBox,\n page.peerCountBox, page.syncProgressBox, page.statusDisabled\n )\n if (wallet) {\n this.updateDisplayedAssetBalance()\n\n const walletDef = app().walletDefinition(assetID, wallet.type)\n page.walletType.textContent = walletDef.tab\n const configurable = assetIsConfigurable(assetID)\n Doc.setVis(configurable, page.passwordWrapper)\n\n if (wallet.disabled) Doc.show(page.statusDisabled) // wallet is disabled\n else if (wallet.running) {\n Doc.show(page.sendReceive, page.peerCountBox, page.syncProgressBox)\n page.peerCount.textContent = String(wallet.peerCount)\n page.syncProgress.textContent = `${(wallet.syncProgress * 100).toFixed(1)}%`\n if (wallet.open) {\n Doc.show(page.statusReady)\n if (!app().haveActiveOrders(assetID) && wallet.encrypted) Doc.show(page.lockBttnBox)\n } else Doc.show(page.statusLocked, page.unlockBttnBox) // wallet not unlocked\n } else Doc.show(page.statusOff, page.connectBttnBox) // wallet not running\n } else Doc.show(page.createWalletBox) // no wallet\n\n page.walletDetailsBox.classList.remove('invisible')\n }\n\n updateDisplayedAssetBalance (): void {\n const page = this.page\n const { wallet, unitInfo: ui, symbol, id: assetID } = app().assets[this.selectedAssetID]\n const bal = wallet.balance\n Doc.show(page.balanceBox, page.walletDetails)\n const totalLocked = bal.locked + bal.contractlocked + bal.bondlocked\n const totalBalance = bal.available + totalLocked + bal.immature\n page.balance.textContent = Doc.formatCoinValue(totalBalance, ui)\n Doc.empty(page.balanceUnit)\n page.balanceUnit.appendChild(Doc.symbolize(symbol))\n const rate = app().fiatRatesMap[assetID]\n if (rate) {\n Doc.show(page.fiatBalanceBox)\n page.fiatBalance.textContent = Doc.formatFiatConversion(totalBalance, rate, ui)\n }\n let firstOther = false\n Doc.empty(page.balanceDetailBox)\n const addSubBalance = (category: string, subBalance: number) => {\n const row = page.balanceDetailRow.cloneNode(true) as PageElement\n if (firstOther) {\n row.classList.add('first-other')\n firstOther = false\n }\n page.balanceDetailBox.appendChild(row)\n const tmpl = Doc.parseTemplate(row)\n tmpl.category.textContent = category\n tmpl.subBalance.textContent = Doc.formatCoinValue(subBalance, ui)\n }\n addSubBalance('Available', bal.available)\n addSubBalance('Locked', totalLocked)\n addSubBalance('Immature', bal.immature)\n const sortedCats = Object.entries(bal.other || {})\n sortedCats.sort((a: [string, number], b: [string, number]): number => a[0].localeCompare(b[0]))\n firstOther = true\n if (bal.contractlocked > 0) addSubBalance('Swapping (locked)', bal.contractlocked)\n if (bal.bondlocked > 0) addSubBalance('Bonded (locked)', bal.bondlocked)\n for (const [cat, sub] of sortedCats) addSubBalance(cat, sub)\n }\n\n showAvailableMarkets (assetID: number) {\n const page = this.page\n const exchanges = app().user.exchanges\n const markets: [string, Market][] = []\n for (const xc of Object.values(exchanges)) {\n if (!xc.markets) continue\n for (const mkt of Object.values(xc.markets)) {\n if (mkt.baseid === assetID || mkt.quoteid === assetID) markets.push([xc.host, mkt])\n }\n }\n\n const spotVolume = (assetID: number, mkt: Market): number => {\n const spot = mkt.spot\n if (!spot) return 0\n const conversionFactor = app().unitInfo(assetID).conventional.conversionFactor\n const volume = assetID === mkt.baseid ? spot.vol24 : spot.vol24 * spot.rate / OrderUtil.RateEncodingFactor\n return volume / conversionFactor\n }\n\n markets.sort((a: [string, Market], b: [string, Market]): number => {\n const [hostA, mktA] = a\n const [hostB, mktB] = b\n if (!mktA.spot && !mktB.spot) return hostA.localeCompare(hostB)\n return spotVolume(assetID, mktB) - spotVolume(assetID, mktA)\n })\n Doc.empty(page.availableMarkets)\n\n for (const [host, mkt] of markets) {\n const { spot, baseid, basesymbol, quoteid, quotesymbol } = mkt\n const row = page.marketRow.cloneNode(true) as PageElement\n page.availableMarkets.appendChild(row)\n const tmpl = Doc.parseTemplate(row)\n tmpl.host.textContent = host\n tmpl.baseLogo.src = Doc.logoPath(basesymbol)\n tmpl.quoteLogo.src = Doc.logoPath(quotesymbol)\n Doc.empty(tmpl.baseSymbol, tmpl.quoteSymbol)\n tmpl.baseSymbol.appendChild(Doc.symbolize(basesymbol))\n tmpl.quoteSymbol.appendChild(Doc.symbolize(quotesymbol))\n\n if (spot) {\n const convRate = app().conventionalRate(baseid, quoteid, spot.rate, exchanges[host])\n tmpl.price.textContent = fourSigFigs(convRate)\n tmpl.priceQuoteUnit.textContent = quotesymbol.toUpperCase()\n tmpl.priceBaseUnit.textContent = basesymbol.toUpperCase()\n tmpl.volume.textContent = fourSigFigs(spotVolume(assetID, mkt))\n tmpl.volumeUnit.textContent = assetID === baseid ? basesymbol.toUpperCase() : quotesymbol.toUpperCase()\n } else Doc.hide(tmpl.priceBox, tmpl.volumeBox)\n Doc.bind(row, 'click', () => app().loadPage('markets', { host, base: baseid, quote: quoteid }))\n }\n page.marketsOverviewBox.classList.remove('invisible')\n }\n\n async showRecentActivity (assetID: number) {\n const page = this.page\n const loaded = app().loading(page.orderActivityBox)\n const filter: OrderFilter = {\n n: 20,\n assets: [assetID],\n hosts: [],\n statuses: []\n }\n const res = await postJSON('/api/orders', filter)\n loaded()\n Doc.hide(page.noActivity, page.orderActivity)\n if (!res.orders || res.orders.length === 0) {\n Doc.show(page.noActivity)\n page.orderActivityBox.classList.remove('invisible')\n return\n }\n Doc.show(page.orderActivity)\n Doc.empty(page.recentOrders)\n for (const ord of (res.orders as Order[])) {\n const row = page.recentOrderTmpl.cloneNode(true) as PageElement\n page.recentOrders.appendChild(row)\n const tmpl = Doc.parseTemplate(row)\n let from: string, to: string\n const [baseUnitInfo, quoteUnitInfo] = [app().unitInfo(ord.baseID), app().unitInfo(ord.quoteID)]\n if (ord.sell) {\n [from, to] = [ord.baseSymbol, ord.quoteSymbol]\n tmpl.fromQty.textContent = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n if (ord.type === OrderUtil.Limit) {\n tmpl.toQty.textContent = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n }\n } else {\n [from, to] = [ord.quoteSymbol, ord.baseSymbol]\n if (ord.type === OrderUtil.Market) {\n tmpl.fromQty.textContent = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n } else {\n tmpl.fromQty.textContent = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n tmpl.toQty.textContent = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n }\n }\n\n tmpl.fromLogo.src = Doc.logoPath(from)\n Doc.empty(tmpl.fromSymbol, tmpl.toSymbol)\n tmpl.fromSymbol.appendChild(Doc.symbolize(from))\n tmpl.toLogo.src = Doc.logoPath(to)\n tmpl.toSymbol.appendChild(Doc.symbolize(to))\n tmpl.status.textContent = OrderUtil.statusString(ord)\n tmpl.filled.textContent = `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`\n tmpl.age.textContent = Doc.timeSince(ord.submitTime)\n tmpl.link.href = `order/${ord.id}`\n app().bindInternalNavigation(row)\n }\n page.orderActivityBox.classList.remove('invisible')\n }\n\n async rescanWallet (assetID: number) {\n const page = this.page\n Doc.hide(page.reconfigErr)\n\n const url = '/api/rescanwallet'\n const req = { assetID: assetID }\n\n const loaded = app().loading(this.body)\n const res = await postJSON(url, req)\n loaded()\n if (res.code === Errors.activeOrdersErr) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n return\n }\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.reconfigErr, res.msg)\n return\n }\n this.assetUpdated(assetID, page.reconfigForm, intl.prep(intl.ID_RESCAN_STARTED))\n }\n\n showConfirmForce () {\n Doc.hide(this.page.confirmForceErr)\n this.showForm(this.page.confirmForce)\n }\n\n showRecoverWallet () {\n Doc.hide(this.page.recoverWalletErr)\n this.showForm(this.page.recoverWalletConfirm)\n }\n\n /* Show the open wallet form if the password is not cached, and otherwise\n * attempt to open the wallet.\n */\n async openWallet (assetID: number) {\n if (!State.passwordIsCached()) {\n this.showOpen(assetID)\n } else {\n const open = {\n assetID: assetID\n }\n const res = await postJSON('/api/openwallet', open)\n if (app().checkResponse(res)) {\n this.openWalletSuccess(assetID)\n } else {\n this.showOpen(assetID, intl.prep(intl.ID_OPEN_WALLET_ERR_MSG, { msg: res.msg }))\n }\n }\n }\n\n /* Show the form used to unlock a wallet. */\n async showOpen (assetID: number, errorMsg?: string) {\n const page = this.page\n // await this.hideBox()\n this.unlockForm.refresh(app().assets[assetID])\n if (errorMsg) this.unlockForm.showErrorOnly(errorMsg)\n this.showForm(page.unlockWalletForm)\n }\n\n /* Show the form used to change wallet configuration settings. */\n async showReconfig (assetID: number, skipAnimation?: boolean) {\n const page = this.page\n Doc.hide(page.changeWalletType, page.changeTypeHideIcon, page.reconfigErr, page.showChangeType, page.changeTypeHideIcon)\n Doc.hide(page.reconfigErr)\n Doc.hide(page.enableWallet, page.disableWallet)\n // Hide update password section by default\n this.changeWalletPW = false\n this.setPWSettingViz(this.changeWalletPW)\n const asset = app().assets[assetID]\n\n const currentDef = app().currentWalletDefinition(assetID)\n const walletDefs = asset.token ? [asset.token.definition] : asset.info ? asset.info.availablewallets : []\n\n if (walletDefs.length > 1) {\n Doc.empty(page.changeWalletTypeSelect)\n Doc.show(page.showChangeType, page.changeTypeShowIcon)\n page.changeTypeMsg.textContent = intl.prep(intl.ID_CHANGE_WALLET_TYPE)\n for (const wDef of walletDefs) {\n const option = document.createElement('option') as HTMLOptionElement\n if (wDef.type === currentDef.type) option.selected = true\n option.value = option.textContent = wDef.type\n page.changeWalletTypeSelect.appendChild(option)\n }\n } else {\n Doc.hide(page.showChangeType)\n }\n\n const wallet = app().walletMap[assetID]\n Doc.setVis(wallet.traits & traitLogFiler, page.downloadLogs)\n Doc.setVis(wallet.traits & traitRecoverer, page.recoverWallet)\n Doc.setVis(wallet.traits & traitRestorer, page.exportWallet)\n Doc.setVis(wallet.traits & traitRescanner, page.rescanWallet)\n Doc.setVis(wallet.traits & traitPeerManager, page.managePeers)\n\n Doc.setVis(wallet.traits & traitsExtraOpts, page.otherActionsLabel)\n\n if (wallet.disabled) Doc.show(page.enableWallet)\n else Doc.show(page.disableWallet)\n\n page.recfgAssetLogo.src = Doc.logoPath(asset.symbol)\n page.recfgAssetName.textContent = asset.name\n if (!skipAnimation) this.showForm(page.reconfigForm)\n const loaded = app().loading(page.reconfigForm)\n const res = await postJSON('/api/walletsettings', { assetID })\n loaded()\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.reconfigErr, res.msg)\n return\n }\n const assetHasActiveOrders = app().haveActiveOrders(assetID)\n this.reconfigForm.update(currentDef.configopts || [], assetHasActiveOrders)\n this.reconfigForm.setConfig(res.map)\n this.updateDisplayedReconfigFields(currentDef)\n }\n\n changeWalletType () {\n const page = this.page\n const walletType = page.changeWalletTypeSelect.value || ''\n const walletDef = app().walletDefinition(this.selectedAssetID, walletType)\n this.reconfigForm.update(walletDef.configopts || [], false)\n this.updateDisplayedReconfigFields(walletDef)\n }\n\n updateDisplayedReconfigFields (walletDef: WalletDefinition) {\n if (walletDef.seeded || walletDef.type === 'token') {\n Doc.hide(this.page.showChangePW, this.reconfigForm.fileSelector)\n this.changeWalletPW = false\n this.setPWSettingViz(false)\n } else Doc.show(this.page.showChangePW, this.reconfigForm.fileSelector)\n }\n\n /* Display a deposit address. */\n async showDeposit (assetID: number) {\n this.depositAddrForm.setAsset(assetID)\n this.showForm(this.page.deposit)\n }\n\n /* Show the form to either send or withdraw funds. */\n async showSendForm (assetID: number) {\n const page = this.page\n const box = page.sendForm\n const { wallet, name, unitInfo: ui, symbol, token } = app().assets[assetID]\n Doc.hide(page.toggleSubtract)\n page.subtractCheckBox.checked = false\n\n const isWithdrawer = (wallet.traits & traitWithdrawer) !== 0\n if (isWithdrawer) {\n Doc.show(page.toggleSubtract)\n }\n\n Doc.hide(page.validAddr, page.sendErr, page.maxSendDisplay)\n page.sendAddr.classList.remove('invalid')\n page.sendAddr.value = ''\n page.sendAmt.value = ''\n this.showFiatValue(assetID, 0, page.sendValue)\n page.walletBal.textContent = Doc.formatFullPrecision(wallet.balance.available, ui)\n page.sendLogo.src = Doc.logoPath(symbol)\n page.sendName.textContent = name\n // page.sendFee.textContent = wallet.feerate\n // page.sendUnit.textContent = wallet.units\n\n if (wallet.balance.available > 0 && (wallet.traits & traitTxFeeEstimator) !== 0) {\n const feeReq = {\n assetID: assetID,\n subtract: isWithdrawer,\n value: wallet.balance.available\n }\n\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/txfee', feeReq)\n loaded()\n if (app().checkResponse(res)) {\n let canSend = wallet.balance.available\n if (!token) {\n canSend -= res.txfee\n if (canSend < 0) canSend = 0\n }\n this.maxSend = canSend\n page.maxSend.textContent = Doc.formatFullPrecision(canSend, ui)\n this.showFiatValue(assetID, canSend, page.maxSendFiat)\n if (token) {\n const { unitInfo: feeUI, symbol: feeSymbol } = app().assets[token.parentID]\n page.maxSendFee.textContent = Doc.formatFullPrecision(res.txfee, feeUI) + ' ' + feeSymbol\n this.showFiatValue(token.parentID, res.txfee, page.maxSendFeeFiat)\n } else {\n page.maxSendFee.textContent = Doc.formatFullPrecision(res.txfee, ui)\n this.showFiatValue(assetID, res.txfee, page.maxSendFeeFiat)\n }\n Doc.show(page.maxSendDisplay)\n }\n }\n\n this.showFiatValue(assetID, 0, page.sendValue)\n page.walletBal.textContent = Doc.formatFullPrecision(wallet.balance.available, ui)\n page.sendLogo.src = Doc.logoPath(wallet.symbol)\n page.sendName.textContent = name\n box.dataset.assetID = String(assetID)\n this.showForm(box)\n }\n\n /* doConnect connects to a wallet via the connectwallet API route. */\n async doConnect (assetID: number) {\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/connectwallet', { assetID })\n loaded()\n if (!app().checkResponse(res)) return\n this.updateDisplayedAsset(assetID)\n }\n\n /* openWalletSuccess is the success callback for wallet unlocking. */\n async openWalletSuccess (assetID: number, form?: PageElement) {\n this.assetUpdated(assetID, form, intl.prep(intl.ID_WALLET_UNLOCKED))\n }\n\n assetUpdated (assetID: number, oldForm?: PageElement, successMsg?: string) {\n if (assetID !== this.selectedAssetID) return\n this.updateDisplayedAsset(assetID)\n if (oldForm && Object.is(this.currentForm, oldForm)) {\n if (successMsg) this.showSuccess(successMsg)\n else this.closePopups()\n }\n }\n\n /* populateMaxSend populates the amount field with the max amount the wallet\n can send. The max send amount can be the maximum amount based on our\n pre-estimation or the asset's wallet balance.\n */\n async populateMaxSend () {\n const page = this.page\n const asset = app().assets[this.selectedAssetID]\n if (!asset) return\n // Populate send amount with max send value and ensure we don't check\n // subtract checkbox for assets that don't have a withdraw method.\n if ((asset.wallet.traits & traitWithdrawer) === 0) {\n page.sendAmt.value = String(this.maxSend / asset.unitInfo.conventional.conversionFactor)\n this.showFiatValue(asset.id, this.maxSend, page.sendValue)\n page.subtractCheckBox.checked = false\n } else {\n const amt = asset.wallet.balance.available\n page.sendAmt.value = String(amt / asset.unitInfo.conventional.conversionFactor)\n this.showFiatValue(asset.id, amt, page.sendValue)\n page.subtractCheckBox.checked = true\n }\n }\n\n /* send submits the send form to the API. */\n async send (): Promise {\n const page = this.page\n const assetID = parseInt(page.sendForm.dataset.assetID ?? '')\n const subtract = page.subtractCheckBox.checked ?? false\n const conversionFactor = app().unitInfo(assetID).conventional.conversionFactor\n const pw = page.vSendPw.value || ''\n page.vSendPw.value = ''\n if (pw === '') {\n Doc.showFormError(page.vSendErr, intl.prep(intl.ID_NO_PASS_ERROR_MSG))\n return\n }\n const open = {\n assetID: assetID,\n address: page.sendAddr.value,\n subtract: subtract,\n value: Math.round(parseFloat(page.sendAmt.value ?? '') * conversionFactor),\n pw: pw\n }\n const loaded = app().loading(page.vSendForm)\n const res = await postJSON('/api/send', open)\n loaded()\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.vSendErr, res.msg)\n return\n }\n const name = app().assets[assetID].name\n this.assetUpdated(assetID, page.vSendForm, intl.prep(intl.ID_SEND_SUCCESS, { assetName: name }))\n }\n\n /* update wallet configuration */\n async reconfig (): Promise {\n const page = this.page\n const assetID = this.selectedAssetID\n Doc.hide(page.reconfigErr)\n if (!page.appPW.value && !State.passwordIsCached()) {\n Doc.showFormError(page.reconfigErr, intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG))\n return\n }\n\n let walletType = app().currentWalletDefinition(assetID).type\n if (!Doc.isHidden(page.changeWalletType)) {\n walletType = page.changeWalletTypeSelect.value || ''\n }\n\n const loaded = app().loading(page.reconfigForm)\n const req: ReconfigRequest = {\n assetID: assetID,\n config: this.reconfigForm.map(assetID),\n appPW: page.appPW.value ?? '',\n walletType: walletType\n }\n if (this.changeWalletPW) req.newWalletPW = page.newPW.value\n const res = await postJSON('/api/reconfigurewallet', req)\n page.appPW.value = ''\n page.newPW.value = ''\n loaded()\n if (!app().checkResponse(res)) {\n Doc.showFormError(page.reconfigErr, res.msg)\n return\n }\n this.assetUpdated(assetID, page.reconfigForm, intl.prep(intl.ID_RECONFIG_SUCCESS))\n }\n\n /* lock instructs the API to lock the wallet. */\n async lock (assetID: number): Promise {\n const page = this.page\n const loaded = app().loading(page.newWalletForm)\n const res = await postJSON('/api/closewallet', { assetID: assetID })\n loaded()\n if (!app().checkResponse(res)) return\n this.updateDisplayedAsset(assetID)\n }\n\n async downloadLogs (): Promise {\n const search = new URLSearchParams('')\n search.append('assetid', `${this.selectedAssetID}`)\n const url = new URL(window.location.href)\n url.search = search.toString()\n url.pathname = '/wallets/logfile'\n window.open(url.toString())\n }\n\n // displayExportWalletAuth displays a form to warn the user about the\n // dangers of exporting a wallet, and asks them to enter their password.\n async displayExportWalletAuth (): Promise {\n const page = this.page\n Doc.hide(page.exportWalletErr)\n page.exportWalletPW.value = ''\n this.showForm(page.exportWalletAuth)\n }\n\n // exportWalletAuthSubmit is called after the user enters their password to\n // authorize looking up the information to restore their wallet in an\n // external wallet.\n async exportWalletAuthSubmit (): Promise {\n const page = this.page\n const req = {\n assetID: this.selectedAssetID,\n pass: page.exportWalletPW.value\n }\n const url = '/api/restorewalletinfo'\n const loaded = app().loading(page.forms)\n const res = await postJSON(url, req)\n loaded()\n if (app().checkResponse(res)) {\n page.exportWalletPW.value = ''\n this.displayRestoreWalletInfo(res.restorationinfo)\n } else {\n Doc.showFormError(page.exportWalletErr, res.msg)\n }\n }\n\n // displayRestoreWalletInfo displays the information needed to restore a\n // wallet in external wallets.\n async displayRestoreWalletInfo (info: WalletRestoration[]): Promise {\n const page = this.page\n Doc.empty(page.restoreInfoCardsList)\n for (const wr of info) {\n const card = this.restoreInfoCard.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(card)\n tmpl.name.textContent = wr.target\n tmpl.seed.textContent = wr.seed\n tmpl.seedName.textContent = `${wr.seedName}:`\n tmpl.instructions.textContent = wr.instructions\n page.restoreInfoCardsList.appendChild(card)\n }\n this.showForm(page.restoreWalletInfo)\n }\n\n async recoverWallet (): Promise {\n const page = this.page\n Doc.hide(page.recoverWalletErr)\n const req = {\n assetID: this.selectedAssetID,\n appPW: page.recoverWalletPW.value\n }\n page.recoverWalletPW.value = ''\n const url = '/api/recoverwallet'\n const loaded = app().loading(page.forms)\n const res = await postJSON(url, req)\n loaded()\n if (res.code === Errors.activeOrdersErr) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n } else if (app().checkResponse(res)) {\n this.closePopups()\n } else {\n Doc.showFormError(page.recoverWalletErr, res.msg)\n }\n }\n\n /*\n * confirmForceSubmit resubmits either the recover or rescan requests with\n * force set to true. These two requests require force to be set to true if\n * they are called while the wallet is managing active orders.\n */\n async confirmForceSubmit (): Promise {\n const page = this.page\n this.forceReq.force = true\n const loaded = app().loading(page.forms)\n const res = await postJSON(this.forceUrl, this.forceReq)\n loaded()\n if (app().checkResponse(res)) this.closePopups()\n else {\n Doc.showFormError(page.confirmForceErr, res.msg)\n }\n }\n\n /* handleBalance handles notifications updating a wallet's balance and assets'\n value in default fiat rate.\n . */\n handleBalanceNote (note: BalanceNote): void {\n this.updateAssetButton(note.assetID)\n if (note.assetID === this.selectedAssetID) this.updateDisplayedAssetBalance()\n }\n\n /* handleRatesNote handles fiat rate notifications, updating the fiat value of\n * all supported assets.\n */\n handleRatesNote (note: RateNote): void {\n this.updateAssetButton(this.selectedAssetID)\n if (!note.fiatRates[this.selectedAssetID]) return\n this.updateDisplayedAssetBalance()\n }\n\n // showFiatValue displays the fiat equivalent for the provided amount.\n showFiatValue (assetID: number, amount: number, display: PageElement): void {\n const rate = app().fiatRatesMap[assetID]\n if (rate) {\n display.textContent = Doc.formatFiatConversion(amount, rate, app().unitInfo(assetID))\n Doc.show(display.parentElement as Element)\n } else Doc.hide(display.parentElement as Element)\n }\n\n /*\n * handleWalletStateNote is a handler for both the 'walletstate' and\n * 'walletconfig' notifications.\n */\n handleWalletStateNote (note: WalletStateNote): void {\n this.updateAssetButton(note.wallet.assetID)\n this.assetUpdated(note.wallet.assetID)\n if (note.topic === 'WalletPeersUpdate' &&\n note.wallet.assetID === this.selectedAssetID &&\n Doc.isDisplayed(this.page.managePeersForm)) {\n this.updateWalletPeersTable()\n }\n }\n\n /*\n * handleCreateWalletNote is a handler for 'createwallet' notifications.\n */\n handleCreateWalletNote (note: WalletCreationNote) {\n this.updateAssetButton(note.assetID)\n this.assetUpdated(note.assetID)\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /wallets page.\n */\n unload (): void {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n}\n\n/*\n * assetIsConfigurable indicates whether there are any user-configurable wallet\n * settings for the asset.\n */\nfunction assetIsConfigurable (assetID: number) {\n const asset = app().assets[assetID]\n if (asset.token) {\n const opts = asset.token.definition.configopts\n return opts && opts.length > 0\n }\n if (!asset.info) throw Error('this asset isn\\'t an asset, I guess')\n const defs = asset.info.availablewallets\n const zerothOpts = defs[0].configopts\n return defs.length > 1 || (zerothOpts && zerothOpts.length > 0)\n}\n\nconst FourSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n maximumSignificantDigits: 4\n})\n\nconst OneFractionalDigit = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1\n})\n\nfunction fourSigFigs (v: number) {\n if (v >= 1000 || Math.round(v) === v) return OneFractionalDigit.format(v)\n return FourSigFigs.format(v)\n}\n","import objectWithoutPropertiesLoose from \"./objectWithoutPropertiesLoose.js\";\nexport default function _objectWithoutProperties(source, excluded) {\n if (source == null) return {};\n var target = objectWithoutPropertiesLoose(source, excluded);\n var key, i;\n if (Object.getOwnPropertySymbols) {\n var sourceSymbolKeys = Object.getOwnPropertySymbols(source);\n for (i = 0; i < sourceSymbolKeys.length; i++) {\n key = sourceSymbolKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;\n target[key] = source[key];\n }\n }\n return target;\n}","export default function _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n return target;\n}","import Doc from './doc'\nimport BasePage from './basepage'\nimport State from './state'\nimport { postJSON } from './http'\nimport * as forms from './forms'\nimport * as intl from './locales'\nimport {\n app,\n Exchange,\n PageElement,\n PasswordCache\n} from './registry'\n\nconst animationLength = 300\n\nexport default class SettingsPage extends BasePage {\n body: HTMLElement\n currentDEX: Exchange\n page: Record\n forms: PageElement[]\n fiatRateSources: PageElement[]\n regAssetForm: forms.FeeAssetSelectionForm\n confirmRegisterForm: forms.ConfirmRegistrationForm\n newWalletForm: forms.NewWalletForm\n walletWaitForm: forms.WalletWaitForm\n dexAddrForm: forms.DEXAddressForm\n currentForm: PageElement\n pwCache: PasswordCache\n keyup: (e: KeyboardEvent) => void\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n const page = this.page = Doc.idDescendants(body)\n\n this.forms = Doc.applySelector(page.forms, ':scope > form')\n this.fiatRateSources = Doc.applySelector(page.fiatRateSources, 'input[type=checkbox]')\n\n Doc.bind(page.darkMode, 'click', () => {\n State.setCookie(State.darkModeCK, page.darkMode.checked || false ? '1' : '0')\n if (page.darkMode.checked) {\n document.body.classList.add('dark')\n } else {\n document.body.classList.remove('dark')\n }\n })\n\n page.showPokes.checked = State.fetchLocal(State.popupsLK) === '1'\n Doc.bind(page.showPokes, 'click', () => {\n const show = page.showPokes.checked || false\n State.storeLocal(State.popupsLK, show ? '1' : '0')\n app().showPopups = show\n })\n\n page.commitHash.textContent = app().commitHash.substring(0, 7)\n Doc.bind(page.addADex, 'click', () => {\n this.dexAddrForm.refresh()\n this.showForm(page.dexAddrForm)\n })\n\n this.fiatRateSources.forEach(src => {\n Doc.bind(src, 'change', async () => {\n const res = await postJSON('/api/toggleratesource', {\n disable: !src.checked,\n source: src.value\n })\n if (!app().checkResponse(res)) {\n src.checked = !src.checked\n }\n // Update asset rate values and disable conversion status.\n await app().fetchUser()\n })\n })\n\n // Asset selection\n this.regAssetForm = new forms.FeeAssetSelectionForm(page.regAssetForm, async (assetID: number) => {\n this.confirmRegisterForm.setAsset(assetID)\n\n const asset = app().assets[assetID]\n const wallet = asset.wallet\n if (wallet) {\n const bondAsset = this.currentDEX.bondAssets[asset.symbol]\n if (wallet.synced && wallet.balance.available > bondAsset.amount) {\n this.animateConfirmForm(page.regAssetForm)\n return\n }\n const txFee = await this.getRegistrationTxFeeEstimate(assetID, page.regAssetForm)\n this.walletWaitForm.setWallet(wallet, txFee)\n forms.slideSwap(page.regAssetForm, page.walletWait)\n return\n }\n\n this.newWalletForm.setAsset(assetID)\n this.currentForm = page.newWalletForm\n forms.slideSwap(page.regAssetForm, page.newWalletForm)\n })\n\n // Approve fee payment\n this.confirmRegisterForm = new forms.ConfirmRegistrationForm(page.confirmRegForm, () => {\n this.registerDEXSuccess()\n }, () => {\n this.animateRegAsset(page.confirmRegForm)\n }, this.pwCache)\n\n // Create a new wallet\n this.newWalletForm = new forms.NewWalletForm(\n page.newWalletForm,\n assetID => this.newWalletCreated(assetID),\n this.pwCache,\n () => this.animateRegAsset(page.newWalletForm)\n )\n\n this.walletWaitForm = new forms.WalletWaitForm(page.walletWait, () => {\n this.animateConfirmForm(page.walletWait)\n }, () => { this.animateRegAsset(page.walletWait) })\n\n // Enter an address for a new DEX\n this.dexAddrForm = new forms.DEXAddressForm(page.dexAddrForm, async (xc: Exchange, certFile: string) => {\n this.currentDEX = xc\n this.confirmRegisterForm.setExchange(xc, certFile)\n this.walletWaitForm.setExchange(xc)\n this.regAssetForm.setExchange(xc)\n this.animateRegAsset(page.dexAddrForm)\n })\n\n Doc.bind(page.importAccount, 'click', () => this.prepareAccountImport(page.authorizeAccountImportForm))\n forms.bind(page.authorizeAccountImportForm, page.authorizeImportAccountConfirm, () => this.importAccount())\n\n Doc.bind(page.changeAppPW, 'click', () => this.showForm(page.changeAppPWForm))\n forms.bind(page.changeAppPWForm, page.submitNewPW, () => this.changeAppPW())\n\n Doc.bind(page.accountFile, 'change', () => this.onAccountFileChange())\n Doc.bind(page.removeAccount, 'click', () => this.clearAccountFile())\n Doc.bind(page.addAccount, 'click', () => page.accountFile.click())\n\n Doc.bind(page.exportSeed, 'click', () => this.showForm(page.exportSeedAuth))\n forms.bind(page.exportSeedAuth, page.exportSeedSubmit, () => this.submitExportSeedReq())\n\n const closePopups = () => {\n Doc.hide(page.forms)\n page.exportSeedPW.value = ''\n page.seedDiv.textContent = ''\n }\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n closePopups()\n }\n }\n Doc.bind(document, 'keyup', this.keyup)\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { closePopups() })\n })\n }\n\n // Retrieve an estimate for the tx fee needed to pay the registration fee.\n async getRegistrationTxFeeEstimate (assetID: number, form: HTMLElement) {\n const cert = await this.getCertFile()\n const loaded = app().loading(form)\n const res = await postJSON('/api/regtxfee', {\n addr: this.currentDEX.host,\n cert: cert,\n asset: assetID\n })\n loaded()\n if (!app().checkResponse(res)) {\n return 0\n }\n return res.txfee\n }\n\n async newWalletCreated (assetID: number) {\n const user = await app().fetchUser()\n if (!user) return\n const page = this.page\n const asset = user.assets[assetID]\n const wallet = asset.wallet\n const bondAmt = this.currentDEX.bondAssets[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > bondAmt) {\n await this.animateConfirmForm(page.newWalletForm)\n return\n }\n\n const txFee = await this.getRegistrationTxFeeEstimate(assetID, page.newWalletForm)\n this.walletWaitForm.setWallet(wallet, txFee)\n this.currentForm = page.walletWait\n await forms.slideSwap(page.newWalletForm, page.walletWait)\n }\n\n async onAccountFileChange () {\n const page = this.page\n const files = page.accountFile.files\n if (!files || !files.length) return\n page.selectedAccount.textContent = files[0].name\n Doc.show(page.removeAccount)\n Doc.hide(page.addAccount)\n }\n\n /* clearAccountFile cleanup accountFile value and selectedAccount text */\n clearAccountFile () {\n const page = this.page\n page.accountFile.value = ''\n page.selectedAccount.textContent = intl.prep(intl.ID_NONE_SELECTED)\n Doc.hide(page.removeAccount)\n Doc.show(page.addAccount)\n }\n\n async prepareAccountImport (authorizeAccountImportForm: HTMLElement) {\n const page = this.page\n page.importAccountErr.textContent = ''\n this.showForm(authorizeAccountImportForm)\n }\n\n // importAccount imports the account\n async importAccount () {\n const page = this.page\n const pw = page.importAccountAppPass.value\n page.importAccountAppPass.value = ''\n let accountString = ''\n if (page.accountFile.value) {\n const files = page.accountFile.files\n if (!files || !files.length) {\n console.error('importAccount: no file specified')\n return\n }\n accountString = await files[0].text()\n }\n let account\n try {\n account = JSON.parse(accountString)\n } catch (e) {\n page.importAccountErr.textContent = e.message\n Doc.show(page.importAccountErr)\n return\n }\n if (typeof account === 'undefined') {\n page.importAccountErr.textContent = intl.prep(intl.ID_ACCT_UNDEFINED)\n Doc.show(page.importAccountErr)\n return\n }\n const { bonds = [], ...acctInf } = account\n const req = {\n pw: pw,\n account: acctInf,\n bonds: bonds\n }\n const loaded = app().loading(this.body)\n const importResponse = await postJSON('/api/importaccount', req)\n loaded()\n if (!app().checkResponse(importResponse)) {\n page.importAccountErr.textContent = importResponse.msg\n Doc.show(page.importAccountErr)\n return\n }\n await app().fetchUser()\n Doc.hide(page.forms)\n // Initial method of displaying imported account.\n window.location.reload()\n }\n\n async submitExportSeedReq () {\n const page = this.page\n const pw = page.exportSeedPW.value\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/exportseed', { pass: pw })\n loaded()\n if (!app().checkResponse(res)) {\n page.exportAccountErr.textContent = res.msg\n Doc.show(page.exportSeedE)\n return\n }\n page.exportSeedPW.value = ''\n page.seedDiv.textContent = res.seed\n this.showForm(page.authorizeSeedDisplay)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n const page = this.page\n this.currentForm = form\n this.forms.forEach(form => Doc.hide(form))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n /* gets the contents of the cert file */\n async getCertFile () {\n let cert = ''\n if (this.dexAddrForm.page.certFile.value) {\n const files = this.dexAddrForm.page.certFile.files\n if (files && files.length) cert = await files[0].text()\n }\n return cert\n }\n\n /* Called after successful registration to a DEX. */\n async registerDEXSuccess () {\n const page = this.page\n Doc.hide(page.forms)\n await app().fetchUser()\n // Initial method of displaying added dex.\n window.location.reload()\n }\n\n /* Change application password */\n async changeAppPW () {\n const page = this.page\n Doc.hide(page.changePWErrMsg)\n\n const clearValues = () => {\n page.appPW.value = ''\n page.newAppPW.value = ''\n page.confirmNewPW.value = ''\n }\n // Ensure password fields are nonempty.\n if (!page.appPW.value || !page.newAppPW.value || !page.confirmNewPW.value) {\n page.changePWErrMsg.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.changePWErrMsg)\n clearValues()\n return\n }\n // Ensure password confirmation matches.\n if (page.newAppPW.value !== page.confirmNewPW.value) {\n page.changePWErrMsg.textContent = intl.prep(intl.ID_PASSWORD_NOT_MATCH)\n Doc.show(page.changePWErrMsg)\n clearValues()\n return\n }\n const loaded = app().loading(page.changeAppPW)\n const req = {\n appPW: page.appPW.value,\n newAppPW: page.newAppPW.value\n }\n clearValues()\n const res = await postJSON('/api/changeapppass', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.changePWErrMsg.textContent = res.msg\n Doc.show(page.changePWErrMsg)\n return\n }\n Doc.hide(page.forms)\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /settings page.\n */\n unload () {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n\n /* Swap in the asset selection form and run the animation. */\n async animateRegAsset (oldForm: HTMLElement) {\n Doc.hide(oldForm)\n const form = this.page.regAssetForm\n this.currentForm = form\n this.regAssetForm.animate()\n Doc.show(form)\n }\n\n /* Swap in the confirmation form and run the animation. */\n async animateConfirmForm (oldForm: HTMLElement) {\n this.confirmRegisterForm.animate()\n const form = this.page.confirmRegForm\n this.currentForm = form\n Doc.hide(oldForm)\n Doc.show(form)\n }\n}\n","import {\n MarketOrderBook,\n MiniOrder\n} from './registry'\n\nexport default class OrderBook {\n base: number\n baseSymbol: string\n quote: number\n quoteSymbol: string\n buys: MiniOrder[]\n sells: MiniOrder[]\n\n constructor (mktBook: MarketOrderBook, baseSymbol: string, quoteSymbol: string) {\n this.base = mktBook.base\n this.baseSymbol = baseSymbol\n this.quote = mktBook.quote\n this.quoteSymbol = quoteSymbol\n // Books are sorted mid-gap first.\n this.buys = mktBook.book.buys || []\n this.sells = mktBook.book.sells || []\n }\n\n /* add adds an order to the order book. */\n add (ord: MiniOrder) {\n if (ord.qtyAtomic === 0) {\n // TODO: Somebody, for the love of god, figure out why the hell this helps\n // with the ghost orders problem. As far as I know, this order is a booked\n // order that had more than one match in an epoch and completely filled.\n // Because the first match didn't exhaust the order, there would be a\n // 'update_remaining' notification scheduled for the order. But by the\n // time OrderRouter generates the notification long after matching, the\n // order has zero qty left to fill. It's all good though, kinda, because\n // the notification is quickly followed with an 'unbook_order'\n // notification. I have tried my damnedest to catch an update_remaining\n // note without an accompanying unbook_order note, and have thus failed.\n // Yet, this fix somehow seems to work. It's infuriating, tbh.\n window.log('zeroqty', 'zero quantity order encountered', ord)\n return\n }\n const side = ord.sell ? this.sells : this.buys\n side.splice(findIdx(side, ord.rate, !ord.sell), 0, ord)\n }\n\n /* remove removes an order from the order book. */\n remove (token: string) {\n if (this.removeFromSide(this.sells, token)) return\n this.removeFromSide(this.buys, token)\n }\n\n /* removeFromSide removes an order from the list of orders. */\n removeFromSide (side: MiniOrder[], token: string) {\n const [ord, i] = this.findOrder(side, token)\n if (ord) {\n side.splice(i, 1)\n return true\n }\n return false\n }\n\n /* findOrder finds an order in a specified side */\n findOrder (side: MiniOrder[], token: string): [MiniOrder | null, number] {\n for (let i = 0; i < side.length; i++) {\n if (side[i].token === token) {\n return [side[i], i]\n }\n }\n return [null, -1]\n }\n\n /* updates the remaining quantity of an order. */\n updateRemaining (token: string, qty: number, qtyAtomic: number) {\n if (this.updateRemainingSide(this.sells, token, qty, qtyAtomic)) return\n this.updateRemainingSide(this.buys, token, qty, qtyAtomic)\n }\n\n /*\n * updateRemainingSide looks for the order in the side and updates the\n * quantity, returning true on success, false if order not found.\n */\n updateRemainingSide (side: MiniOrder[], token: string, qty: number, qtyAtomic: number) {\n const ord = this.findOrder(side, token)[0]\n if (ord) {\n ord.qty = qty\n ord.qtyAtomic = qtyAtomic\n return true\n }\n return false\n }\n\n /*\n * setEpoch sets the current epoch and clear any orders from previous epochs.\n */\n setEpoch (epochIdx: number) {\n const approve = (ord: MiniOrder) => ord.epoch === undefined || ord.epoch === 0 || ord.epoch === epochIdx\n this.sells = this.sells.filter(approve)\n this.buys = this.buys.filter(approve)\n }\n\n /* empty will return true if both the buys and sells lists are empty. */\n empty () {\n return !this.sells.length && !this.buys.length\n }\n\n /* count is the total count of both buy and sell orders. */\n count () {\n return this.sells.length + this.buys.length\n }\n\n /* bestGapOrder will return the best non-epoch order if one exists, or the\n * best epoch order if there are only epoch orders, or null if there are no\n * orders.\n */\n bestGapOrder (side: MiniOrder[]) {\n let best = null\n for (const ord of side) {\n if (!ord.epoch) return ord\n if (!best) {\n best = ord\n }\n }\n return best\n }\n\n bestGapBuy () {\n return this.bestGapOrder(this.buys)\n }\n\n bestGapSell () {\n return this.bestGapOrder(this.sells)\n }\n}\n\n/*\n * findIdx find the index at which to insert the order into the list of orders.\n */\nfunction findIdx (side: MiniOrder[], rate: number, less: boolean): number {\n for (let i = 0; i < side.length; i++) {\n if ((side[i].rate < rate) === less) return i\n }\n return side.length\n}\n","// MessageSocket is a WebSocket manager that uses the Decred DEX Message format\n// for communications.\n//\n// Message request format:\n// {\n// route: 'name',\n// id: int,\n// payload: anything or nothing\n// }\n//\n// Message response payload will be a result object with either a valid 'result'\n// field or an 'error' field\n//\n// Functions for external use:\n// registerRoute (route, handler) -- register a function to handle events\n// of the given type\n// request (route, payload) -- create a JSON message in the above format and\n// send it\n//\n// Based on messagesocket_service.js by Jonathan Chappelow @ dcrdata, which is\n// based on ws_events_dispatcher.js by Ismael Celis\nconst typeRequest = 1\n\nfunction forward (route: string, payload: any, handlers: Record void)[]>) {\n if (!route && payload.error) {\n const err = payload.error\n console.error(`websocket error (code ${err.code}): ${err.message}`)\n return\n }\n if (typeof handlers[route] === 'undefined') {\n // console.log(`unhandled message for ${route}: ${payload}`)\n return\n }\n // call each handler\n for (let i = 0; i < handlers[route].length; i++) {\n handlers[route][i](payload)\n }\n}\n\nlet id = 0\n\ntype NoteReceiver = (payload: any) => void\n\nclass MessageSocket {\n uri: string\n connection: WebSocket | null\n handlers: Record\n queue: [string, any][]\n maxQlength: number\n reloader: () => void // appears unused\n\n constructor () {\n this.handlers = {}\n this.queue = []\n this.maxQlength = 5\n }\n\n registerRoute (route: string, handler: NoteReceiver) {\n this.handlers[route] = this.handlers[route] || []\n this.handlers[route].push(handler)\n }\n\n deregisterRoute (route: string) {\n this.handlers[route] = []\n }\n\n // request sends a request-type message to the server\n request (route: string, payload: any) {\n if (!this.connection || this.connection.readyState !== window.WebSocket.OPEN) {\n while (this.queue.length > this.maxQlength - 1) this.queue.shift()\n this.queue.push([route, payload])\n return\n }\n id++\n const message = JSON.stringify({\n route: route,\n type: typeRequest,\n id: id,\n payload: payload\n })\n\n window.log('ws', 'sending', message)\n this.connection.send(message)\n }\n\n close (reason: string) {\n window.log('ws', 'close, reason:', reason, this.handlers)\n this.handlers = {}\n if (this.connection) this.connection.close()\n }\n\n connect (uri: string, reloader: () => void) {\n this.uri = uri\n this.reloader = reloader\n let retrys = 0\n const go = () => {\n window.log('ws', `connecting to ${uri}`)\n let conn: WebSocket | null = this.connection = new window.WebSocket(uri)\n if (!conn) return\n const timeout = setTimeout(() => {\n // readyState is still WebSocket.CONNECTING. Cancel and trigger onclose.\n if (conn) conn.close()\n }, 500)\n\n // unmarshal message, and forward the message to registered handlers\n conn.onmessage = (evt: MessageEvent) => {\n const message = JSON.parse(evt.data)\n forward(message.route, message.payload, this.handlers)\n }\n\n // Stub out standard functions\n conn.onclose = (evt: CloseEvent) => {\n window.log('ws', 'onclose')\n clearTimeout(timeout)\n conn = this.connection = null\n forward('close', null, this.handlers)\n retrys++\n // 1.2, 1.6, 2.0, 2.4, 3.1, 3.8, 4.8, 6.0, 7.5, 9.3, ...\n const delay = Math.min(Math.pow(1.25, retrys), 10)\n console.error(`websocket disconnected (${evt.code}), trying again in ${delay.toFixed(1)} seconds`)\n setTimeout(() => {\n go()\n }, delay * 1000)\n }\n\n conn.onopen = () => {\n window.log('ws', 'onopen')\n clearTimeout(timeout)\n if (retrys > 0) {\n retrys = 0\n reloader()\n }\n forward('open', null, this.handlers)\n const queue = this.queue\n this.queue = []\n for (const [route, message] of queue) {\n this.request(route, message)\n }\n }\n\n conn.onerror = (evt: Event) => {\n window.log('ws', 'onerror:', evt)\n forward('error', evt, this.handlers)\n }\n }\n go()\n }\n}\n\nconst ws = new MessageSocket()\nexport default ws\n","import Doc, { WalletIcons } from './doc'\nimport State from './state'\nimport BasePage from './basepage'\nimport OrderBook from './orderbook'\nimport {\n CandleChart,\n DepthChart,\n DepthLine,\n CandleReporters,\n MouseReport,\n VolumeReport,\n DepthMarker,\n Wave\n} from './charts'\nimport { postJSON } from './http'\nimport {\n NewWalletForm,\n UnlockWalletForm,\n AccelerateOrderForm,\n DepositAddress,\n bind as bindForm\n} from './forms'\nimport * as OrderUtil from './orderutil'\nimport ws from './ws'\nimport * as intl from './locales'\nimport {\n app,\n SupportedAsset,\n PageElement,\n Order,\n Market,\n OrderEstimate,\n MaxOrderEstimate,\n Exchange,\n UnitInfo,\n Asset,\n Candle,\n CandlesPayload,\n TradeForm,\n BookUpdate,\n MaxSell,\n MaxBuy,\n SwapEstimate,\n MarketOrderBook,\n APIResponse,\n PreSwap,\n PreRedeem,\n WalletStateNote,\n WalletCreationNote,\n SpotPriceNote,\n BondNote,\n OrderNote,\n EpochNote,\n BalanceNote,\n MiniOrder,\n RemainderUpdate,\n ConnEventNote,\n OrderOption,\n ConnectionStatus,\n RecentMatch,\n MatchNote\n} from './registry'\nimport { setOptionTemplates } from './opts'\n\nconst bind = Doc.bind\n\nconst bookRoute = 'book'\nconst bookOrderRoute = 'book_order'\nconst unbookOrderRoute = 'unbook_order'\nconst updateRemainingRoute = 'update_remaining'\nconst epochOrderRoute = 'epoch_order'\nconst candlesRoute = 'candles'\nconst candleUpdateRoute = 'candle_update'\nconst unmarketRoute = 'unmarket'\nconst epochMatchSummaryRoute = 'epoch_match_summary'\n\nconst animationLength = 500\n\nconst anHour = 60 * 60 * 1000 // milliseconds\n\nconst check = document.createElement('span')\ncheck.classList.add('ico-check')\n\nconst buyBtnClass = 'buygreen-bg'\nconst sellBtnClass = 'sellred-bg'\n\nconst fiveMinBinKey = '5m'\n\nconst percentFormatter = new Intl.NumberFormat(document.documentElement.lang, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 2\n})\n\nconst parentIDNone = 0xFFFFFFFF\n\ninterface MetaOrder {\n div: HTMLElement\n header: Record\n details: Record\n ord: Order\n cancelling?: boolean\n status?: number\n}\n\ninterface CancelData {\n bttn: PageElement\n order: Order\n}\n\ninterface CurrentMarket {\n dex: Exchange\n sid: string // A string market identifier used by the DEX.\n cfg: Market\n base: SupportedAsset\n quote: SupportedAsset\n baseUnitInfo: UnitInfo\n quoteUnitInfo: UnitInfo\n maxSellRequested: boolean\n maxSell: MaxOrderEstimate | null\n sellBalance: number\n buyBalance: number\n maxBuys: Record\n candleCaches: Record\n baseCfg: Asset\n quoteCfg: Asset\n rateConversionFactor: number\n}\n\ninterface LoadTracker {\n loaded: () => void\n timer: number\n}\n\ninterface OrderRow extends HTMLElement {\n manager: OrderTableRowManager\n}\n\ninterface StatsDisplay {\n row: PageElement\n tmpl: Record\n}\n\nexport default class MarketsPage extends BasePage {\n page: Record\n main: HTMLElement\n maxLoaded: (() => void) | null\n maxOrderUpdateCounter: number\n market: CurrentMarket\n currentForm: HTMLElement\n openAsset: SupportedAsset\n openFunc: () => void\n currentCreate: SupportedAsset\n maxEstimateTimer: number | null\n book: OrderBook\n cancelData: CancelData\n metaOrders: Record\n preorderCache: Record\n currentOrder: TradeForm\n depthLines: Record\n activeMarkerRate: number | null\n hovers: HTMLElement[]\n ogTitle: string\n depthChart: DepthChart\n candleChart: CandleChart\n candleDur: string\n balanceWgt: BalanceWidget\n marketList: MarketList\n quoteUnits: NodeListOf\n baseUnits: NodeListOf\n unlockForm: UnlockWalletForm\n newWalletForm: NewWalletForm\n depositAddrForm: DepositAddress\n keyup: (e: KeyboardEvent) => void\n secondTicker: number\n candlesLoading: LoadTracker | null\n accelerateOrderForm: AccelerateOrderForm\n recentMatches: RecentMatch[]\n recentMatchesSortKey: string\n recentMatchesSortDirection: 1 | -1\n stats: [StatsDisplay, StatsDisplay]\n loadingAnimations: { candles?: Wave, depth?: Wave }\n\n constructor (main: HTMLElement, data: any) {\n super()\n const page = this.page = Doc.idDescendants(main)\n this.main = main\n if (!this.main.parentElement) return // Not gonna happen, but TypeScript cares.\n // There may be multiple pending updates to the max order. This makes sure\n // that the screen is updated with the most recent one.\n this.maxOrderUpdateCounter = 0\n this.metaOrders = {}\n this.recentMatches = []\n this.preorderCache = {}\n this.depthLines = {\n hover: [],\n input: []\n }\n this.hovers = []\n // 'Recent Matches' list sort key and direction.\n this.recentMatchesSortKey = 'age'\n this.recentMatchesSortDirection = -1\n // store original title so we can re-append it when updating market value.\n this.ogTitle = document.title\n\n const depthReporters = {\n click: (x: number) => { this.reportDepthClick(x) },\n volume: (r: VolumeReport) => { this.reportDepthVolume(r) },\n mouse: (r: MouseReport) => { this.reportDepthMouse(r) },\n zoom: (z: number) => { this.reportDepthZoom(z) }\n }\n this.depthChart = new DepthChart(page.depthChart, depthReporters, State.fetchLocal(State.depthZoomLK))\n\n const candleReporters: CandleReporters = {\n mouse: c => { this.reportMouseCandle(c) }\n }\n this.candleChart = new CandleChart(page.candlesChart, candleReporters)\n\n const success = () => { /* do nothing */ }\n // Do not call cleanTemplates before creating the AccelerateOrderForm\n this.accelerateOrderForm = new AccelerateOrderForm(page.accelerateForm, success)\n\n // TODO: Store user's state and reload last known configuration.\n this.candleDur = fiveMinBinKey\n\n // Setup the register to trade button.\n // TODO: Use dexsettings page?\n const registerBttn = Doc.tmplElement(page.notRegistered, 'registerBttn')\n bind(registerBttn, 'click', () => {\n app().loadPage('register', { host: this.market.dex.host })\n })\n\n // Set up the BalanceWidget.\n {\n page.walletInfoTmpl.removeAttribute('id')\n const bWidget = page.walletInfoTmpl\n const qWidget = page.walletInfoTmpl.cloneNode(true) as PageElement\n bWidget.after(qWidget)\n const wgt = this.balanceWgt = new BalanceWidget(bWidget, qWidget)\n const baseIcons = wgt.base.stateIcons.icons\n const quoteIcons = wgt.quote.stateIcons.icons\n bind(wgt.base.tmpl.connect, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.tmpl.connect, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(wgt.base.tmpl.expired, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.tmpl.expired, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(baseIcons.sleeping, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(quoteIcons.sleeping, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(baseIcons.locked, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(quoteIcons.locked, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(baseIcons.disabled, 'click', () => { this.showToggleWalletStatus(this.market.base) })\n bind(quoteIcons.disabled, 'click', () => { this.showToggleWalletStatus(this.market.quote) })\n bind(wgt.base.tmpl.newWalletBttn, 'click', () => { this.showCreate(this.market.base) })\n bind(wgt.quote.tmpl.newWalletBttn, 'click', () => { this.showCreate(this.market.quote) })\n Doc.bind(wgt.base.tmpl.walletAddr, 'click', () => { this.showDeposit(this.market.base.id) })\n Doc.bind(wgt.quote.tmpl.walletAddr, 'click', () => { this.showDeposit(this.market.quote.id) })\n this.depositAddrForm = new DepositAddress(page.deposit)\n }\n\n // Bind toggle wallet status form.\n bindForm(page.toggleWalletStatusConfirm, page.toggleWalletStatusSubmit, async () => { this.toggleWalletStatus() })\n\n // Prepare templates for the buy and sell tables and the user's order table.\n setOptionTemplates(page)\n Doc.cleanTemplates(page.rowTemplate, page.durBttnTemplate, page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl, page.userOrderTmpl)\n\n // Store the elements that need their ticker changed when the market\n // changes.\n this.quoteUnits = main.querySelectorAll('[data-unit=quote]')\n this.baseUnits = main.querySelectorAll('[data-unit=base]')\n\n // Buttons to set order type and side.\n bind(page.buyBttn, 'click', () => {\n swapBttns(page.sellBttn, page.buyBttn)\n page.submitBttn.classList.remove(sellBtnClass)\n page.submitBttn.classList.add(buyBtnClass)\n page.maxLbl.textContent = intl.prep(intl.ID_BUY)\n this.setOrderBttnText()\n this.setOrderVisibility()\n this.drawChartLines()\n })\n bind(page.sellBttn, 'click', () => {\n swapBttns(page.buyBttn, page.sellBttn)\n page.submitBttn.classList.add(sellBtnClass)\n page.submitBttn.classList.remove(buyBtnClass)\n page.maxLbl.textContent = intl.prep(intl.ID_SELL)\n this.setOrderBttnText()\n this.setOrderVisibility()\n this.drawChartLines()\n })\n bind(page.limitBttn, 'click', () => {\n swapBttns(page.marketBttn, page.limitBttn)\n this.setOrderVisibility()\n if (!page.rateField.value) return\n this.depthLines.input = [{\n rate: parseFloat(page.rateField.value || '0'),\n color: this.isSell() ? this.depthChart.theme.sellLine : this.depthChart.theme.buyLine\n }]\n this.drawChartLines()\n })\n bind(page.marketBttn, 'click', () => {\n swapBttns(page.limitBttn, page.marketBttn)\n this.setOrderVisibility()\n this.setMarketBuyOrderEstimate()\n this.depthLines.input = []\n this.drawChartLines()\n })\n bind(page.maxOrd, 'click', () => {\n if (this.isSell()) {\n const maxSell = this.market.maxSell\n if (!maxSell) return\n page.lotField.value = String(maxSell.swap.lots)\n } else page.lotField.value = String(this.market.maxBuys[this.adjustedRate()].swap.lots)\n this.lotChanged()\n })\n\n Doc.disableMouseWheel(page.rateField, page.lotField, page.qtyField, page.mktBuyField)\n\n // Handle the full orderbook sent on the 'book' route.\n ws.registerRoute(bookRoute, (data: BookUpdate) => { this.handleBookRoute(data) })\n // Handle the new order for the order book on the 'book_order' route.\n ws.registerRoute(bookOrderRoute, (data: BookUpdate) => { this.handleBookOrderRoute(data) })\n // Remove the order sent on the 'unbook_order' route from the orderbook.\n ws.registerRoute(unbookOrderRoute, (data: BookUpdate) => { this.handleUnbookOrderRoute(data) })\n // Update the remaining quantity on a booked order.\n ws.registerRoute(updateRemainingRoute, (data: BookUpdate) => { this.handleUpdateRemainingRoute(data) })\n // Handle the new order for the order book on the 'epoch_order' route.\n ws.registerRoute(epochOrderRoute, (data: BookUpdate) => { this.handleEpochOrderRoute(data) })\n // Handle the initial candlestick data on the 'candles' route.\n ws.registerRoute(candlesRoute, (data: BookUpdate) => { this.handleCandlesRoute(data) })\n // Handle the candles update on the 'candles' route.\n ws.registerRoute(candleUpdateRoute, (data: BookUpdate) => { this.handleCandleUpdateRoute(data) })\n\n // Handle the recent matches update on the 'epoch_report' route.\n ws.registerRoute(epochMatchSummaryRoute, (data: BookUpdate) => { this.handleEpochMatchSummary(data) })\n // Bind the wallet unlock form.\n this.unlockForm = new UnlockWalletForm(page.unlockWalletForm, async () => { this.openFunc() })\n // Create a wallet\n this.newWalletForm = new NewWalletForm(page.newWalletForm, async () => { this.createWallet() })\n // Main order form.\n bindForm(page.orderForm, page.submitBttn, async () => { this.stepSubmit() })\n // Order verification form.\n bindForm(page.verifyForm, page.vSubmit, async () => { this.submitOrder() })\n // Unlock for order estimation\n Doc.bind(page.vUnlockSubmit, 'click', async () => { this.submitEstimateUnlock() })\n // Cancel order form.\n bindForm(page.cancelForm, page.cancelSubmit, async () => { this.submitCancel() })\n // Order detail view.\n Doc.bind(page.vFeeDetails, 'click', () => this.showForm(page.vDetailPane))\n Doc.bind(page.closeDetailPane, 'click', () => this.showVerifyForm())\n // // Bind active orders list's header sort events.\n page.recentMatchesTable.querySelectorAll('[data-ordercol]')\n .forEach((th: HTMLElement) => bind(\n th, 'click', () => setRecentMatchesSortCol(th.dataset.ordercol || '')\n ))\n\n const setRecentMatchesSortCol = (key: string) => {\n // First unset header's current sorted col classes.\n unsetRecentMatchesSortColClasses()\n if (this.recentMatchesSortKey === key) {\n this.recentMatchesSortDirection *= -1\n } else {\n this.recentMatchesSortKey = key\n this.recentMatchesSortDirection = 1\n }\n this.refreshRecentMatchesTable()\n setRecentMatchesSortColClasses()\n }\n\n // sortClassByDirection receives a sort direction and return a class based on it.\n const sortClassByDirection = (element: 1 | -1) => {\n if (element === 1) return 'sorted-asc'\n return 'sorted-dsc'\n }\n\n const unsetRecentMatchesSortColClasses = () => {\n page.recentMatchesTable.querySelectorAll('[data-ordercol]')\n .forEach(th => th.classList.remove('sorted-asc', 'sorted-dsc'))\n }\n\n const setRecentMatchesSortColClasses = () => {\n const key = this.recentMatchesSortKey\n const sortCls = sortClassByDirection(this.recentMatchesSortDirection)\n Doc.safeSelector(page.recentMatchesTable, `[data-ordercol=${key}]`).classList.add(sortCls)\n }\n\n // Set default's sorted col header classes.\n setRecentMatchesSortColClasses()\n\n const closePopups = () => {\n Doc.hide(page.forms)\n page.vPass.value = ''\n page.cancelPass.value = ''\n }\n\n // If the user clicks outside of a form, it should close the page overlay.\n bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (Doc.isDisplayed(page.vDetailPane) && !Doc.mouseInElement(e, page.vDetailPane)) return this.showVerifyForm()\n if (!Doc.mouseInElement(e, this.currentForm)) {\n closePopups()\n }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n closePopups()\n }\n }\n bind(document, 'keyup', this.keyup)\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { closePopups() })\n })\n\n // Event listeners for interactions with the various input fields.\n bind(page.lotField, 'change', () => { this.lotChanged() })\n bind(page.lotField, 'keyup', () => { this.lotChanged() })\n bind(page.qtyField, 'change', () => { this.quantityChanged(true) })\n bind(page.qtyField, 'keyup', () => { this.quantityChanged(false) })\n bind(page.mktBuyField, 'change', () => { this.marketBuyChanged() })\n bind(page.mktBuyField, 'keyup', () => { this.marketBuyChanged() })\n bind(page.rateField, 'change', () => { this.rateFieldChanged() })\n bind(page.rateField, 'keyup', () => { this.previewQuoteAmt(true) })\n\n // Market search input bindings.\n bind(page.marketSearchV1, 'change', () => { this.filterMarkets() })\n bind(page.marketSearchV1, 'keyup', () => { this.filterMarkets() })\n\n // Acknowledge the order disclaimer.\n const setDisclaimerAckViz = (acked: boolean) => {\n Doc.setVis(!acked, page.disclaimer, page.disclaimerAck)\n Doc.setVis(acked, page.showDisclaimer)\n }\n bind(page.disclaimerAck, 'click', () => {\n State.storeLocal(State.orderDisclaimerAckedLK, true)\n setDisclaimerAckViz(true)\n })\n bind(page.showDisclaimer, 'click', () => {\n State.storeLocal(State.orderDisclaimerAckedLK, false)\n setDisclaimerAckViz(false)\n })\n setDisclaimerAckViz(State.fetchLocal(State.orderDisclaimerAckedLK))\n\n const clearChartLines = () => {\n this.depthLines.hover = []\n this.drawChartLines()\n }\n bind(page.buyRows, 'mouseleave', clearChartLines)\n bind(page.sellRows, 'mouseleave', clearChartLines)\n bind(page.userOrders, 'mouseleave', () => {\n this.activeMarkerRate = null\n this.setDepthMarkers()\n })\n\n const stats0 = page.marketStatsV1\n const stats1 = stats0.cloneNode(true) as PageElement\n stats1.classList.add('listopen')\n Doc.hide(stats0, stats1)\n stats1.removeAttribute('id')\n app().headerSpace.appendChild(stats1)\n this.stats = [{ row: stats0, tmpl: Doc.parseTemplate(stats0) }, { row: stats1, tmpl: Doc.parseTemplate(stats1) }]\n\n const closeMarketsList = () => {\n State.storeLocal(State.leftMarketDockLK, '0')\n page.leftMarketDock.classList.remove('default')\n page.leftMarketDock.classList.add('stashed')\n for (const s of this.stats) s.row.classList.remove('listopen')\n }\n const openMarketsList = () => {\n State.storeLocal(State.leftMarketDockLK, '1')\n page.leftMarketDock.classList.remove('default', 'stashed')\n for (const s of this.stats) s.row.classList.add('listopen')\n }\n Doc.bind(page.leftHider, 'click', () => closeMarketsList())\n Doc.bind(page.marketReopener, 'click', () => openMarketsList())\n for (const s of this.stats) {\n Doc.bind(s.tmpl.marketSelect, 'click', () => {\n if (page.leftMarketDock.clientWidth === 0) openMarketsList()\n else closeMarketsList()\n })\n }\n this.marketList = new MarketList(page.marketListV1)\n // Prepare the list of markets.\n for (const row of this.marketList.markets) {\n bind(row.node, 'click', () => {\n this.startLoadingAnimations()\n this.setMarket(row.mkt.xc.host, row.mkt.baseid, row.mkt.quoteid)\n })\n }\n if (State.fetchLocal(State.leftMarketDockLK) !== '1') { // It is shown by default, hiding if necessary.\n closeMarketsList()\n }\n\n // Notification filters.\n app().registerNoteFeeder({\n order: (note: OrderNote) => { this.handleOrderNote(note) },\n match: (note: MatchNote) => { this.handleMatchNote(note) },\n epoch: (note: EpochNote) => { this.handleEpochNote(note) },\n conn: (note: ConnEventNote) => { this.handleConnNote(note) },\n balance: (note: BalanceNote) => { this.handleBalanceNote(note) },\n bondpost: (note: BondNote) => { this.handleBondUpdate(note) },\n spots: (note: SpotPriceNote) => { this.handlePriceUpdate(note) }\n })\n\n this.loadingAnimations = {}\n this.startLoadingAnimations()\n\n // Start a ticker to update time-since values.\n this.secondTicker = window.setInterval(() => {\n for (const mord of Object.values(this.metaOrders)) {\n mord.details.age.textContent = Doc.timeSince(mord.ord.submitTime)\n }\n for (const td of Doc.applySelector(page.recentMatchesLiveList, '[data-tmpl=age]')) {\n td.textContent = Doc.timeSince(parseFloat(td.dataset.sinceStamp ?? '0'))\n }\n }, 1000)\n\n this.init(data)\n }\n\n async init (data: any) {\n // Fetch the first market in the list, or the users last selected market, if\n // it exists.\n let selected\n if (data && data.host && typeof data.base !== 'undefined' && typeof data.quote !== 'undefined') {\n selected = makeMarket(data.host, parseInt(data.base), parseInt(data.quote))\n } else {\n selected = State.fetchLocal(State.lastMarketLK)\n }\n if (!selected || !this.marketList.exists(selected.host, selected.base, selected.quote)) {\n const first = this.marketList.first()\n if (first) selected = { host: first.mkt.xc.host, base: first.mkt.baseid, quote: first.mkt.quoteid }\n }\n if (selected) this.setMarket(selected.host, selected.base, selected.quote)\n\n // set the initial state for the registration status\n this.setRegistrationStatusVisibility()\n this.setBalanceVisibility()\n }\n\n startLoadingAnimations () {\n const { page, loadingAnimations: anis, depthChart, candleChart } = this\n depthChart.canvas.classList.add('invisible')\n candleChart.canvas.classList.add('invisible')\n if (anis.candles) anis.candles.stop()\n anis.candles = new Wave(page.candlesChart, { message: intl.prep(intl.ID_CANDLES_LOADING) })\n if (anis.depth) anis.depth.stop()\n anis.depth = new Wave(page.depthChart, { message: intl.prep(intl.ID_DEPTH_LOADING) })\n }\n\n /* isSell is true if the user has selected sell in the order options. */\n isSell () {\n return this.page.sellBttn.classList.contains('selected')\n }\n\n /* isLimit is true if the user has selected the \"limit order\" tab. */\n isLimit () {\n return this.page.limitBttn.classList.contains('selected')\n }\n\n /* hasPendingBonds is true if there are pending bonds */\n hasPendingBonds (): boolean {\n return Object.keys(this.market.dex.pendingBonds).length > 0\n }\n\n /* setCurrMarketPrice updates the current market price on the stats displays\n and the orderbook display. */\n setCurrMarketPrice (): void {\n const selected = this.market\n if (!selected) return\n // Get an up-to-date Market.\n const xc = app().exchanges[selected.dex.host]\n const mkt = xc.markets[selected.cfg.name]\n if (!mkt.spot) return\n\n for (const s of this.stats) {\n const bconv = xc.assets[mkt.baseid].unitInfo.conventional.conversionFactor\n s.tmpl.volume.textContent = fourSigFigs(mkt.spot.vol24 / bconv)\n setPriceAndChange(s.tmpl, xc, mkt)\n }\n\n this.page.obPrice.textContent = Doc.formatFullPrecision(mkt.spot.rate / this.market.rateConversionFactor)\n this.page.obPrice.classList.remove('sellcolor', 'buycolor')\n this.page.obPrice.classList.add(mkt.spot.change24 >= 0 ? 'buycolor' : 'sellcolor')\n Doc.setVis(mkt.spot.change24 >= 0, this.page.obUp)\n Doc.setVis(mkt.spot.change24 < 0, this.page.obDown)\n }\n\n /* setMarketDetails updates the currency names on the stats displays. */\n setMarketDetails () {\n if (!this.market) return\n for (const s of this.stats) {\n s.tmpl.baseIcon.src = Doc.logoPath(this.market.cfg.basesymbol)\n s.tmpl.quoteIcon.src = Doc.logoPath(this.market.cfg.quotesymbol)\n Doc.empty(s.tmpl.baseSymbol, s.tmpl.quoteSymbol)\n s.tmpl.baseSymbol.appendChild(Doc.symbolize(this.market.cfg.basesymbol))\n s.tmpl.quoteSymbol.appendChild(Doc.symbolize(this.market.cfg.quotesymbol))\n }\n }\n\n /* setHighLow calculates the high and low rates over the last 24 hours. */\n setHighLow () {\n const cache = this.market?.candleCaches[fiveMinBinKey]\n if (!cache) {\n for (const s of this.stats) {\n s.tmpl.high.textContent = '-'\n s.tmpl.low.textContent = '-'\n }\n return\n }\n\n // We'll eventually get this data in the spot, but for now, we must set it\n // from candles.\n let [high, low] = [0, 0]\n for (let i = cache.candles.length - 1; i >= 0; i--) {\n const c = cache.candles[i]\n if (low === 0 || (c.lowRate > 0 && c.lowRate < low)) low = c.lowRate\n if (c.highRate > high) high = c.highRate\n }\n\n const qconv = app().unitInfo(this.market.cfg.quoteid, this.market.dex).conventional.conversionFactor\n for (const s of this.stats) {\n s.tmpl.high.textContent = high > 0 ? fourSigFigs(high / qconv) : '-'\n s.tmpl.low.textContent = low > 0 ? fourSigFigs(low / qconv) : '-'\n }\n }\n\n /* assetsAreSupported is true if all the assets of the current market are\n * supported\n */\n assetsAreSupported (): {\n isSupported: boolean;\n text: string;\n } {\n const { market: { base, quote, baseCfg, quoteCfg } } = this\n if (!base || !quote) {\n const symbol = base ? quoteCfg.symbol : baseCfg.symbol\n return {\n isSupported: false,\n text: intl.prep(intl.ID_NOT_SUPPORTED, { asset: symbol.toUpperCase() })\n }\n }\n // check if versions are supported. If asset is a token, we check if its\n // parent supports the version.\n const bVers = (base.token ? app().assets[base.token.parentID].info?.versions : base.info?.versions) as number[]\n const qVers = (quote.token ? app().assets[quote.token.parentID].info?.versions : quote.info?.versions) as number[]\n // if none them are token, just check if own asset is supported.\n let text = ''\n if (!bVers.includes(baseCfg.version)) {\n text = intl.prep(intl.ID_VERSION_NOT_SUPPORTED, { asset: base.symbol.toUpperCase(), version: baseCfg.version + '' })\n } else if (!qVers.includes(quoteCfg.version)) {\n text = intl.prep(intl.ID_VERSION_NOT_SUPPORTED, { asset: quote.symbol.toUpperCase(), version: quoteCfg.version + '' })\n }\n return {\n isSupported: bVers.includes(baseCfg.version) && qVers.includes(quoteCfg.version),\n text\n }\n }\n\n /*\n * setOrderVisibility sets which form is visible based on the specified\n * options.\n */\n setOrderVisibility () {\n const page = this.page\n if (this.isLimit()) {\n Doc.show(page.priceBox, page.tifBox, page.qtyBox, page.maxBox)\n Doc.hide(page.mktBuyBox)\n this.previewQuoteAmt(true)\n } else {\n Doc.hide(page.tifBox, page.maxBox, page.priceBox)\n if (this.isSell()) {\n Doc.hide(page.mktBuyBox)\n Doc.show(page.qtyBox)\n this.previewQuoteAmt(true)\n } else {\n Doc.show(page.mktBuyBox)\n Doc.hide(page.qtyBox)\n this.previewQuoteAmt(false)\n }\n }\n }\n\n /* resolveOrderFormVisibility displays or hides the 'orderForm' based on\n * a set of conditions to be met.\n */\n resolveOrderFormVisibility () {\n const page = this.page\n // By default the order form should be hidden, and only if market is set\n // and ready for trading the form should show up.\n Doc.hide(page.orderForm, page.orderTypeBttns)\n\n if (!this.assetsAreSupported().isSupported) return // assets not supported\n\n if (this.market.dex.tier < 1) return // acct suspended or not registered\n\n const { base, quote } = this.market\n const hasWallets = base && app().assets[base.id].wallet && quote && app().assets[quote.id].wallet\n if (!hasWallets) return\n\n Doc.show(page.orderForm, page.orderTypeBttns)\n }\n\n /* setLoaderMsgVisibility displays a message in case a dex asset is not\n * supported\n */\n setLoaderMsgVisibility () {\n const { page } = this\n\n const { isSupported, text } = this.assetsAreSupported()\n if (isSupported) {\n // make sure to hide the loader msg\n Doc.hide(page.loaderMsg)\n return\n }\n page.loaderMsg.textContent = text\n Doc.show(page.loaderMsg)\n Doc.hide(page.notRegistered)\n Doc.hide(page.noWallet)\n }\n\n /* setRegistrationStatusView sets the text content and class for the\n * registration status view\n */\n setRegistrationStatusView (titleContent: string, confStatusMsg: string, titleClass: string) {\n const page = this.page\n page.regStatusTitle.textContent = titleContent\n page.regStatusConfsDisplay.textContent = confStatusMsg\n page.registrationStatus.classList.remove('completed', 'error', 'waiting')\n page.registrationStatus.classList.add(titleClass)\n }\n\n /*\n * updateRegistrationStatusView updates the view based on the current\n * registration status\n */\n updateRegistrationStatusView () {\n const { page, market: { dex } } = this\n page.regStatusDex.textContent = dex.host\n\n if (dex.tier >= 1) {\n this.setRegistrationStatusView(intl.prep(intl.ID_REGISTRATION_FEE_SUCCESS), '', 'completed')\n return\n }\n\n const confStatuses = Object.values(dex.pendingBonds).map(pending => {\n const confirmationsRequired = dex.bondAssets[pending.symbol].confs\n return `${pending.confs} / ${confirmationsRequired}`\n })\n const confStatusMsg = confStatuses.join(', ')\n this.setRegistrationStatusView(intl.prep(intl.ID_WAITING_FOR_CONFS), confStatusMsg, 'waiting')\n }\n\n /*\n * setRegistrationStatusVisibility toggles the registration status view based\n * on the dex data.\n */\n setRegistrationStatusVisibility () {\n const { page, market } = this\n if (!market || !market.dex) return\n\n // If dex is not connected to server, is not possible to know the\n // registration status.\n if (market.dex.connectionStatus !== ConnectionStatus.Connected) return\n\n this.updateRegistrationStatusView()\n\n if (market.dex.tier >= 1) {\n const toggle = () => {\n Doc.hide(page.registrationStatus, page.bondRequired)\n this.resolveOrderFormVisibility()\n }\n if (Doc.isHidden(page.orderForm)) {\n // wait a couple of seconds before showing the form so the success\n // message is shown to the user\n setTimeout(toggle, 5000)\n return\n }\n toggle()\n } else if (market.dex.viewOnly) {\n page.unregisteredDex.textContent = market.dex.host\n Doc.show(page.notRegistered)\n } else if (this.hasPendingBonds()) {\n Doc.show(page.registrationStatus)\n } else {\n page.acctTier.textContent = `${market.dex.tier}`\n page.dexSettingsLink.href = `/dexsettings/${market.dex.host}`\n Doc.show(page.bondRequired)\n }\n }\n\n setOrderBttnText () {\n if (this.isSell()) {\n this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_SELL, { asset: Doc.shortSymbol(this.market.baseCfg.symbol) })\n } else this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_BUY, { asset: Doc.shortSymbol(this.market.baseCfg.symbol) })\n }\n\n setCandleDurBttns () {\n const { page, market } = this\n Doc.empty(page.durBttnBox)\n for (const dur of market.dex.candleDurs) {\n const bttn = page.durBttnTemplate.cloneNode(true)\n bttn.textContent = dur\n Doc.bind(bttn, 'click', () => this.candleDurationSelected(dur))\n page.durBttnBox.appendChild(bttn)\n }\n }\n\n /* setMarket sets the currently displayed market. */\n async setMarket (host: string, base: number, quote: number) {\n const dex = app().user.exchanges[host]\n const page = this.page\n\n // reset form inputs\n page.lotField.value = ''\n page.qtyField.value = ''\n page.rateField.value = ''\n\n Doc.hide(page.notRegistered, page.bondRequired, page.noWallet)\n\n // If we have not yet connected, there is no dex.assets or any other\n // exchange data, so just put up a message and wait for the connection to be\n // established, at which time handleConnNote will refresh and reload.\n if (!dex || !dex.markets || dex.connectionStatus !== ConnectionStatus.Connected) {\n page.chartErrMsg.textContent = intl.prep(intl.ID_CONNECTION_FAILED)\n Doc.show(page.chartErrMsg)\n return\n }\n\n for (const s of this.stats) Doc.show(s.row)\n\n const baseCfg = dex.assets[base]\n const quoteCfg = dex.assets[quote]\n\n const [bui, qui] = [app().unitInfo(base, dex), app().unitInfo(quote, dex)]\n\n const rateConversionFactor = OrderUtil.RateEncodingFactor / bui.conventional.conversionFactor * qui.conventional.conversionFactor\n Doc.hide(page.maxOrd, page.chartErrMsg)\n if (this.maxEstimateTimer) {\n window.clearTimeout(this.maxEstimateTimer)\n this.maxEstimateTimer = null\n }\n const mktId = marketID(baseCfg.symbol, quoteCfg.symbol)\n const baseAsset = app().assets[base]\n const quoteAsset = app().assets[quote]\n this.market = {\n dex: dex,\n sid: mktId, // A string market identifier used by the DEX.\n cfg: dex.markets[mktId],\n // app().assets is a map of core.SupportedAsset type, which can be found at\n // client/core/types.go.\n base: baseAsset,\n quote: quoteAsset,\n baseUnitInfo: bui,\n quoteUnitInfo: qui,\n maxSell: null,\n maxBuys: {},\n maxSellRequested: false,\n candleCaches: {},\n baseCfg,\n quoteCfg,\n rateConversionFactor,\n sellBalance: 0,\n buyBalance: 0\n }\n\n Doc.setVis(!(baseAsset && quoteAsset) || !(baseAsset.wallet && quoteAsset.wallet), page.noWallet)\n this.setMarketDetails()\n this.setCurrMarketPrice()\n this.refreshRecentMatchesTable()\n\n // if (!dex.candleDurs || dex.candleDurs.length === 0) this.currentChart = depthChart\n\n // depth chart\n ws.request('loadmarket', makeMarket(host, base, quote))\n\n // candlesticks\n this.candleDur = fiveMinBinKey\n this.loadCandles()\n\n this.setLoaderMsgVisibility()\n this.setRegistrationStatusVisibility()\n this.resolveOrderFormVisibility()\n this.setOrderBttnText()\n this.setCandleDurBttns()\n this.previewQuoteAmt(false)\n }\n\n /*\n * reportDepthClick is a callback used by the DepthChart when the user clicks\n * on the chart area. The rate field is set to the x-value of the click.\n */\n reportDepthClick (r: number) {\n this.page.rateField.value = String(r)\n this.rateFieldChanged()\n }\n\n /*\n * reportDepthVolume accepts a volume report from the DepthChart and sets the\n * values in the chart legend.\n */\n reportDepthVolume (r: VolumeReport) {\n const page = this.page\n const { baseUnitInfo: b, quoteUnitInfo: q } = this.market\n // DepthChart reports volumes in conventional units. We'll still use\n // formatCoinValue for formatting though.\n page.sellBookedBase.textContent = Doc.formatCoinValue(r.sellBase * b.conventional.conversionFactor, b)\n page.sellBookedQuote.textContent = Doc.formatCoinValue(r.sellQuote * q.conventional.conversionFactor, q)\n page.buyBookedBase.textContent = Doc.formatCoinValue(r.buyBase * b.conventional.conversionFactor, b)\n page.buyBookedQuote.textContent = Doc.formatCoinValue(r.buyQuote * q.conventional.conversionFactor, q)\n }\n\n /*\n * reportDepthMouse accepts informations about the mouse position on the\n * chart area.\n */\n reportDepthMouse (r: MouseReport) {\n while (this.hovers.length) (this.hovers.shift() as HTMLElement).classList.remove('hover')\n const page = this.page\n if (!r) {\n Doc.hide(page.hoverData, page.depthHoverData)\n return\n }\n Doc.show(page.hoverData, page.depthHoverData)\n\n // If the user is hovered to within a small percent (based on chart width)\n // of a user order, highlight that order's row.\n for (const { div, ord } of Object.values(this.metaOrders)) {\n if (ord.status !== OrderUtil.StatusBooked) continue\n if (r.hoverMarkers.indexOf(ord.rate) > -1) {\n div.classList.add('hover')\n this.hovers.push(div)\n }\n }\n\n page.hoverPrice.textContent = Doc.formatCoinValue(r.rate)\n page.hoverVolume.textContent = Doc.formatCoinValue(r.depth)\n page.hoverVolume.style.color = r.dotColor\n }\n\n /*\n * reportDepthZoom accepts informations about the current depth chart zoom level.\n * This information is saved to disk so that the zoom level can be maintained\n * across reloads.\n */\n reportDepthZoom (zoom: number) {\n State.storeLocal(State.depthZoomLK, zoom)\n }\n\n reportMouseCandle (candle: Candle | null) {\n const page = this.page\n if (!candle) {\n Doc.hide(page.hoverData, page.candleHoverData)\n return\n }\n Doc.show(page.hoverData, page.candleHoverData)\n page.candleStart.textContent = Doc.formatCoinValue(candle.startRate / this.market.rateConversionFactor)\n page.candleEnd.textContent = Doc.formatCoinValue(candle.endRate / this.market.rateConversionFactor)\n page.candleHigh.textContent = Doc.formatCoinValue(candle.highRate / this.market.rateConversionFactor)\n page.candleLow.textContent = Doc.formatCoinValue(candle.lowRate / this.market.rateConversionFactor)\n page.candleVol.textContent = Doc.formatCoinValue(candle.matchVolume, this.market.baseUnitInfo)\n }\n\n /*\n * parseOrder pulls the order information from the form fields. Data is not\n * validated in any way.\n */\n parseOrder (): TradeForm {\n const page = this.page\n let qtyField = page.qtyField\n const limit = this.isLimit()\n const sell = this.isSell()\n const market = this.market\n let qtyConv = market.baseUnitInfo.conventional.conversionFactor\n if (!limit && !sell) {\n qtyField = page.mktBuyField\n qtyConv = market.quoteUnitInfo.conventional.conversionFactor\n }\n return {\n host: market.dex.host,\n isLimit: limit,\n sell: sell,\n base: market.base.id,\n quote: market.quote.id,\n qty: convertToAtoms(qtyField.value || '', qtyConv),\n rate: convertToAtoms(page.rateField.value || '', market.rateConversionFactor), // message-rate\n tifnow: page.tifNow.checked || false,\n options: {}\n }\n }\n\n /**\n * previewQuoteAmt shows quote amount when rate or quantity input are changed\n */\n previewQuoteAmt (show: boolean) {\n const page = this.page\n if (!this.market.base || !this.market.quote) return // Not a supported asset\n const order = this.parseOrder()\n const adjusted = this.adjustedRate()\n page.orderErr.textContent = ''\n if (adjusted) {\n if (order.sell) this.preSell()\n else this.preBuy()\n }\n this.depthLines.input = []\n if (adjusted && this.isLimit()) {\n this.depthLines.input = [{\n rate: order.rate / this.market.rateConversionFactor,\n color: order.sell ? this.depthChart.theme.sellLine : this.depthChart.theme.buyLine\n }]\n }\n this.drawChartLines()\n if (!show || !adjusted || !order.qty) {\n page.orderPreview.textContent = ''\n this.drawChartLines()\n return\n }\n const quoteAsset = app().assets[order.quote]\n const quoteQty = order.qty * order.rate / OrderUtil.RateEncodingFactor\n const total = Doc.formatCoinValue(quoteQty, this.market.quoteUnitInfo)\n\n page.orderPreview.textContent = intl.prep(intl.ID_ORDER_PREVIEW, { total, asset: quoteAsset.symbol.toUpperCase() })\n if (this.isSell()) this.preSell()\n else this.preBuy()\n }\n\n /**\n * preSell populates the max order message for the largest available sell.\n */\n preSell () {\n const mkt = this.market\n const baseWallet = app().assets[mkt.base.id].wallet\n if (baseWallet.balance.available < mkt.cfg.lotsize) {\n this.setMaxOrder(null)\n return\n }\n if (mkt.maxSell) {\n this.setMaxOrder(mkt.maxSell.swap)\n return\n }\n if (mkt.maxSellRequested) return\n mkt.maxSellRequested = true\n // We only fetch pre-sell once per balance update, so don't delay.\n this.scheduleMaxEstimate('/api/maxsell', {}, 0, (res: MaxSell) => {\n mkt.maxSellRequested = false\n mkt.maxSell = res.maxSell\n mkt.sellBalance = baseWallet.balance.available\n this.setMaxOrder(res.maxSell.swap)\n })\n }\n\n /**\n * preBuy populates the max order message for the largest available buy.\n */\n preBuy () {\n const mkt = this.market\n const rate = this.adjustedRate()\n const quoteWallet = app().assets[mkt.quote.id].wallet\n if (!quoteWallet) return\n const aLot = mkt.cfg.lotsize * (rate / OrderUtil.RateEncodingFactor)\n if (quoteWallet.balance.available < aLot) {\n this.setMaxOrder(null)\n return\n }\n if (mkt.maxBuys[rate]) {\n this.setMaxOrder(mkt.maxBuys[rate].swap)\n return\n }\n // 0 delay for first fetch after balance update or market change, otherwise\n // meter these at 1 / sec.\n const delay = Object.keys(mkt.maxBuys).length ? 350 : 0\n this.scheduleMaxEstimate('/api/maxbuy', { rate }, delay, (res: MaxBuy) => {\n mkt.maxBuys[rate] = res.maxBuy\n mkt.buyBalance = app().assets[mkt.quote.id].wallet.balance.available\n this.setMaxOrder(res.maxBuy.swap)\n })\n }\n\n /**\n * scheduleMaxEstimate shows the loading icon and schedules a call to an order\n * estimate api endpoint. If another call to scheduleMaxEstimate is made before\n * this one is fired (after delay), this call will be canceled.\n */\n scheduleMaxEstimate (path: string, args: any, delay: number, success: (res: any) => void) {\n const page = this.page\n if (!this.maxLoaded) this.maxLoaded = app().loading(page.maxOrd)\n const [bid, qid] = [this.market.base.id, this.market.quote.id]\n const [bWallet, qWallet] = [app().assets[bid].wallet, app().assets[qid].wallet]\n if (!bWallet || !bWallet.running || !qWallet || !qWallet.running) return\n if (this.maxEstimateTimer) window.clearTimeout(this.maxEstimateTimer)\n\n Doc.show(page.maxOrd, page.maxLotBox)\n Doc.hide(page.maxAboveZero)\n page.maxFromLots.textContent = intl.prep(intl.ID_CALCULATING)\n page.maxFromLotsLbl.textContent = ''\n this.maxOrderUpdateCounter++\n const counter = this.maxOrderUpdateCounter\n this.maxEstimateTimer = window.setTimeout(async () => {\n this.maxEstimateTimer = null\n if (counter !== this.maxOrderUpdateCounter) return\n const res = await postJSON(path, {\n host: this.market.dex.host,\n base: bid,\n quote: qid,\n ...args\n })\n if (counter !== this.maxOrderUpdateCounter) return\n if (!app().checkResponse(res)) {\n console.warn('max order estimate not available:', res)\n page.maxFromLots.textContent = intl.prep(intl.ID_ESTIMATE_UNAVAILABLE)\n if (this.maxLoaded) {\n this.maxLoaded()\n this.maxLoaded = null\n }\n return\n }\n success(res)\n }, delay)\n }\n\n /* setMaxOrder sets the max order text. */\n setMaxOrder (maxOrder: SwapEstimate | null) {\n const page = this.page\n if (this.maxLoaded) {\n this.maxLoaded()\n this.maxLoaded = null\n }\n Doc.show(page.maxOrd, page.maxLotBox, page.maxAboveZero)\n const sell = this.isSell()\n\n let lots = 0\n if (maxOrder) lots = maxOrder.lots\n\n page.maxFromLots.textContent = lots.toString()\n // XXX add plural into format details, so we don't need this\n page.maxFromLotsLbl.textContent = intl.prep(lots === 1 ? intl.ID_LOT : intl.ID_LOTS)\n if (!maxOrder) {\n Doc.hide(page.maxAboveZero)\n return\n }\n // Could add the estimatedFees here, but that might also be\n // confusing.\n const fromAsset = sell ? this.market.base : this.market.quote\n // DRAFT NOTE: Maybe just use base qty from lots.\n page.maxFromAmt.textContent = Doc.formatCoinValue(maxOrder.value || 0, fromAsset.unitInfo)\n page.maxFromTicker.textContent = fromAsset.symbol.toUpperCase()\n // Could subtract the maxOrder.redemptionFees here.\n // The qty conversion doesn't fit well with the new design.\n // TODO: Make this work somehow?\n // const toConversion = sell ? this.adjustedRate() / OrderUtil.RateEncodingFactor : OrderUtil.RateEncodingFactor / this.adjustedRate()\n // page.maxToAmt.textContent = Doc.formatCoinValue((maxOrder.value || 0) * toConversion, toAsset.unitInfo)\n // page.maxToTicker.textContent = toAsset.symbol.toUpperCase()\n }\n\n /*\n * validateOrder performs some basic order sanity checks, returning boolean\n * true if the order appears valid.\n */\n validateOrder (order: TradeForm) {\n const page = this.page\n if (order.isLimit && !order.rate) {\n Doc.show(page.orderErr)\n page.orderErr.textContent = intl.prep(intl.ID_NO_ZERO_RATE)\n return false\n }\n if (!order.qty) {\n Doc.show(page.orderErr)\n page.orderErr.textContent = intl.prep(intl.ID_NO_ZERO_QUANTITY)\n return false\n }\n return true\n }\n\n /* handleBook accepts the data sent in the 'book' notification. */\n handleBook (data: MarketOrderBook) {\n const { cfg, baseUnitInfo, quoteUnitInfo, baseCfg, quoteCfg } = this.market\n this.book = new OrderBook(data, baseCfg.symbol, quoteCfg.symbol)\n this.loadTable()\n for (const order of (data.book.epoch || [])) {\n if (order.rate > 0) this.book.add(order)\n this.addTableOrder(order)\n }\n if (!this.book) {\n this.depthChart.clear()\n Doc.empty(this.page.buyRows)\n Doc.empty(this.page.sellRows)\n return\n }\n Doc.show(this.page.epochLine)\n if (this.loadingAnimations.depth) this.loadingAnimations.depth.stop()\n this.depthChart.canvas.classList.remove('invisible')\n this.depthChart.set(this.book, cfg.lotsize, cfg.ratestep, baseUnitInfo, quoteUnitInfo)\n this.recentMatches = data.book.recentMatches ?? []\n this.refreshRecentMatchesTable()\n }\n\n /*\n * midGapConventional is the same as midGap, but returns the mid-gap rate as\n * the conventional ratio. This is used to convert from a conventional\n * quantity from base to quote or vice-versa.\n */\n midGapConventional () {\n const gap = this.midGap()\n if (!gap) return gap\n const { baseUnitInfo: b, quoteUnitInfo: q } = this.market\n return gap * b.conventional.conversionFactor / q.conventional.conversionFactor\n }\n\n /*\n * midGap returns the value in the middle of the best buy and best sell. If\n * either one of the buy or sell sides are empty, midGap returns the best rate\n * from the other side. If both sides are empty, midGap returns the value\n * null. The rate returned is the atomic ratio.\n */\n midGap () {\n const book = this.book\n if (!book) return\n if (book.buys && book.buys.length) {\n if (book.sells && book.sells.length) {\n return (book.buys[0].msgRate + book.sells[0].msgRate) / 2 / OrderUtil.RateEncodingFactor\n }\n return book.buys[0].msgRate / OrderUtil.RateEncodingFactor\n }\n if (book.sells && book.sells.length) {\n return book.sells[0].msgRate / OrderUtil.RateEncodingFactor\n }\n return null\n }\n\n /*\n * setMarketBuyOrderEstimate sets the \"min. buy\" display for the current\n * market.\n */\n setMarketBuyOrderEstimate () {\n const market = this.market\n const lotSize = market.cfg.lotsize\n const xc = app().user.exchanges[market.dex.host]\n const buffer = xc.markets[market.sid].buybuffer\n const gap = this.midGapConventional()\n if (gap) {\n this.page.minMktBuy.textContent = Doc.formatCoinValue(lotSize * buffer * gap, market.baseUnitInfo)\n }\n }\n\n /* refreshActiveOrders refreshes the user's active order list. */\n refreshActiveOrders () {\n const page = this.page\n const metaOrders = this.metaOrders\n const market = this.market\n for (const oid in metaOrders) delete metaOrders[oid]\n const orders = app().orders(market.dex.host, marketID(market.baseCfg.symbol, market.quoteCfg.symbol))\n // Sort orders by sort key.\n orders.sort((a: Order, b: Order) => b.submitTime - a.submitTime)\n\n Doc.empty(page.userOrders)\n Doc.setVis(orders?.length, page.userOrders)\n Doc.setVis(!orders?.length, page.userNoOrders)\n\n let unreadyOrders = false\n for (const ord of orders) {\n const div = page.userOrderTmpl.cloneNode(true) as HTMLElement\n page.userOrders.appendChild(div)\n\n const headerEl = Doc.tmplElement(div, 'header')\n const header = Doc.parseTemplate(headerEl)\n const detailsDiv = Doc.tmplElement(div, 'details')\n const details = Doc.parseTemplate(detailsDiv)\n\n const mord: MetaOrder = {\n div: div,\n header: header,\n details: details,\n ord: ord\n }\n\n // No need to track in-flight orders here. We've already added it to\n // display.\n if (ord.id) metaOrders[ord.id] = mord\n\n if (!ord.readyToTick) {\n headerEl.classList.add('unready-user-order')\n unreadyOrders = true\n }\n header.sideLight.classList.add(ord.sell ? 'sell' : 'buy')\n details.side.textContent = header.side.textContent = OrderUtil.sellString(ord)\n details.side.classList.add(ord.sell ? 'sellcolor' : 'buycolor')\n header.side.classList.add(ord.sell ? 'sellcolor' : 'buycolor')\n details.qty.textContent = header.qty.textContent = fourSigFigs(ord.qty / this.market.baseUnitInfo.conventional.conversionFactor)\n details.rate.textContent = fourSigFigs(ord.rate / this.market.rateConversionFactor)\n header.baseSymbol.textContent = ord.baseSymbol.toUpperCase()\n details.type.textContent = OrderUtil.typeString(ord)\n this.updateMetaOrder(mord)\n\n Doc.bind(div, 'mouseenter', () => {\n this.activeMarkerRate = ord.rate\n this.setDepthMarkers()\n })\n\n if (!ord.id) {\n Doc.hide(details.accelerateBttn)\n Doc.hide(details.cancelBttn)\n Doc.hide(details.link)\n } else {\n if (ord.type === OrderUtil.Limit && (ord.tif === OrderUtil.StandingTiF && ord.status < OrderUtil.StatusExecuted)) {\n Doc.show(details.cancelBttn)\n bind(details.cancelBttn, 'click', e => {\n e.stopPropagation()\n this.showCancel(div, ord.id)\n })\n }\n\n bind(details.accelerateBttn, 'click', e => {\n e.stopPropagation()\n this.showAccelerate(ord)\n })\n if (app().canAccelerateOrder(ord)) {\n Doc.show(details.accelerateBttn)\n }\n details.link.href = `order/${ord.id}`\n app().bindInternalNavigation(div)\n }\n\n Doc.bind(headerEl, 'click', () => {\n if (Doc.isDisplayed(detailsDiv)) {\n Doc.hide(detailsDiv)\n header.expander.classList.add('ico-arrowdown')\n header.expander.classList.remove('ico-arrowup')\n return\n }\n Doc.show(detailsDiv)\n header.expander.classList.remove('ico-arrowdown')\n header.expander.classList.add('ico-arrowup')\n })\n app().bindTooltips(div)\n }\n Doc.setVis(unreadyOrders, page.unreadyOrdersMsg)\n this.setDepthMarkers()\n }\n\n /*\n * updateMetaOrder sets the td contents of the user's order table row.\n */\n updateMetaOrder (mord: MetaOrder) {\n const { header, details, ord } = mord\n if (ord.status <= OrderUtil.StatusBooked) header.activeLight.classList.add('active')\n else header.activeLight.classList.remove('active')\n details.status.textContent = header.status.textContent = OrderUtil.statusString(ord)\n details.age.textContent = Doc.timeSince(ord.submitTime)\n details.filled.textContent = `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`\n details.settled.textContent = `${(OrderUtil.settled(ord) / ord.qty * 100).toFixed(1)}%`\n }\n\n /* setMarkers sets the depth chart markers for booked orders. */\n setDepthMarkers () {\n const markers: Record = {\n buys: [],\n sells: []\n }\n const rateFactor = this.market.rateConversionFactor\n for (const { ord } of Object.values(this.metaOrders)) {\n if (ord.rate && ord.status === OrderUtil.StatusBooked) {\n if (ord.sell) {\n markers.sells.push({\n rate: ord.rate / rateFactor,\n active: ord.rate === this.activeMarkerRate\n })\n } else {\n markers.buys.push({\n rate: ord.rate / rateFactor,\n active: ord.rate === this.activeMarkerRate\n })\n }\n }\n }\n this.depthChart.setMarkers(markers)\n if (this.book) this.depthChart.draw()\n }\n\n /* updateTitle update the browser title based on the midgap value and the\n * selected assets.\n */\n updateTitle () {\n // gets first price value from buy or from sell, so we can show it on\n // title.\n const midGapValue = this.midGapConventional()\n if (!midGapValue) return\n\n const { baseCfg: b, quoteCfg: q } = this.market\n const baseSymb = b.symbol.toUpperCase()\n const quoteSymb = q.symbol.toUpperCase()\n // more than 6 numbers it gets too big for the title.\n document.title = `${Doc.formatCoinValue(midGapValue)} | ${baseSymb}${quoteSymb} | ${this.ogTitle}`\n }\n\n /* handleBookRoute is the handler for the 'book' notification, which is sent\n * in response to a new market subscription. The data received will contain\n * the entire order book.\n */\n handleBookRoute (note: BookUpdate) {\n app().log('book', 'handleBookRoute:', note)\n const mktBook = note.payload\n const market = this.market\n const page = this.page\n const host = market.dex.host\n const [b, q] = [market.baseCfg, market.quoteCfg]\n if (mktBook.base !== b.id || mktBook.quote !== q.id) return\n this.refreshActiveOrders()\n this.handleBook(mktBook)\n this.updateTitle()\n this.marketList.select(host, b.id, q.id)\n\n State.storeLocal(State.lastMarketLK, {\n host: note.host,\n base: mktBook.base,\n quote: mktBook.quote\n })\n\n page.lotSize.textContent = Doc.formatCoinValue(market.cfg.lotsize, market.baseUnitInfo)\n page.rateStep.textContent = Doc.formatCoinValue(market.cfg.ratestep / market.rateConversionFactor)\n this.baseUnits.forEach(el => {\n Doc.empty(el)\n el.appendChild(Doc.symbolize(b.symbol))\n })\n this.quoteUnits.forEach(el => {\n Doc.empty(el)\n el.appendChild(Doc.symbolize(q.symbol))\n })\n this.balanceWgt.setWallets(host, b.id, q.id)\n this.setMarketBuyOrderEstimate()\n this.refreshActiveOrders()\n }\n\n /* handleBookOrderRoute is the handler for 'book_order' notifications. */\n handleBookOrderRoute (data: BookUpdate) {\n app().log('book', 'handleBookOrderRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const order = data.payload as MiniOrder\n if (order.rate > 0) this.book.add(order)\n this.addTableOrder(order)\n this.updateTitle()\n this.depthChart.draw()\n }\n\n /* handleUnbookOrderRoute is the handler for 'unbook_order' notifications. */\n handleUnbookOrderRoute (data: BookUpdate) {\n app().log('book', 'handleUnbookOrderRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const order = data.payload\n this.book.remove(order.token)\n this.removeTableOrder(order)\n this.updateTitle()\n this.depthChart.draw()\n }\n\n /*\n * handleUpdateRemainingRoute is the handler for 'update_remaining'\n * notifications.\n */\n handleUpdateRemainingRoute (data: BookUpdate) {\n app().log('book', 'handleUpdateRemainingRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const update = data.payload\n this.book.updateRemaining(update.token, update.qty, update.qtyAtomic)\n this.updateTableOrder(update)\n this.depthChart.draw()\n }\n\n /* handleEpochOrderRoute is the handler for 'epoch_order' notifications. */\n handleEpochOrderRoute (data: BookUpdate) {\n app().log('book', 'handleEpochOrderRoute:', data)\n if (data.host !== this.market.dex.host || data.marketID !== this.market.sid) return\n const order = data.payload\n if (order.msgRate > 0) this.book.add(order) // No cancels or market orders\n if (order.qtyAtomic > 0) this.addTableOrder(order) // No cancel orders\n this.depthChart.draw()\n }\n\n /* handleCandlesRoute is the handler for 'candles' notifications. */\n handleCandlesRoute (data: BookUpdate) {\n if (this.candlesLoading) {\n clearTimeout(this.candlesLoading.timer)\n this.candlesLoading.loaded()\n this.candlesLoading = null\n }\n if (data.host !== this.market.dex.host || data.marketID !== this.market.cfg.name) return\n const dur = data.payload.dur\n this.market.candleCaches[dur] = data.payload\n this.setHighLow()\n if (this.candleDur !== dur) return\n if (this.loadingAnimations.candles) this.loadingAnimations.candles.stop()\n this.candleChart.canvas.classList.remove('invisible')\n this.candleChart.setCandles(data.payload, this.market.cfg, this.market.baseUnitInfo, this.market.quoteUnitInfo)\n }\n\n handleEpochMatchSummary (data: BookUpdate) {\n this.addRecentMatches(data.payload)\n this.refreshRecentMatchesTable()\n }\n\n /* handleCandleUpdateRoute is the handler for 'candle_update' notifications. */\n handleCandleUpdateRoute (data: BookUpdate) {\n if (data.host !== this.market.dex.host) return\n const { dur, candle } = data.payload\n const cache = this.market.candleCaches[dur]\n if (!cache) return // must not have seen the 'candles' notification yet?\n const candles = cache.candles\n if (candles.length === 0) candles.push(candle)\n else {\n const last = candles[candles.length - 1]\n if (last.startStamp === candle.startStamp) candles[candles.length - 1] = candle\n else candles.push(candle)\n }\n if (this.candleDur !== dur) return\n this.candleChart.draw()\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n this.currentForm = form\n const page = this.page\n Doc.hide(...Array.from(page.forms.children))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n /* showOpen shows the form to unlock a wallet. */\n async showOpen (asset: SupportedAsset, f: () => void) {\n const page = this.page\n this.openAsset = asset\n this.openFunc = f\n this.unlockForm.refresh(app().assets[asset.id])\n this.showForm(page.unlockWalletForm)\n page.uwAppPass.focus()\n }\n\n /*\n * showToggleWalletStatus displays the toggleWalletStatusConfirm form to\n * enable a wallet.\n */\n showToggleWalletStatus (asset: SupportedAsset) {\n const page = this.page\n this.openAsset = asset\n Doc.hide(page.toggleWalletStatusErr, page.walletStatusDisable, page.disableWalletMsg)\n Doc.show(page.walletStatusEnable, page.enableWalletMsg)\n this.showForm(page.toggleWalletStatusConfirm)\n }\n\n /*\n * toggleWalletStatus toggle wallets status to enabled.\n */\n async toggleWalletStatus () {\n const page = this.page\n Doc.hide(page.toggleWalletStatusErr)\n\n const url = '/api/togglewalletstatus'\n const req = {\n assetID: this.openAsset.id,\n disable: false\n }\n\n const loaded = app().loading(page.toggleWalletStatusConfirm)\n const res = await postJSON(url, req)\n loaded()\n if (!app().checkResponse(res)) {\n page.toggleWalletStatusErr.textContent = res.msg\n Doc.show(page.toggleWalletStatusErr)\n return\n }\n\n Doc.hide(this.page.forms)\n this.balanceWgt.updateAsset(this.openAsset.id)\n }\n\n /* showVerify shows the form to accept the currently parsed order information\n * and confirm submission of the order to the dex.\n */\n showVerify () {\n this.preorderCache = {}\n const page = this.page\n const order = this.currentOrder = this.parseOrder()\n const isSell = order.sell\n const baseAsset = app().assets[order.base]\n const quoteAsset = app().assets[order.quote]\n const toAsset = isSell ? quoteAsset : baseAsset\n const fromAsset = isSell ? baseAsset : quoteAsset\n\n const setIcon = (icon: PageElement) => {\n switch (icon.dataset.icon) {\n case 'from':\n if (fromAsset.token) {\n const parentAsset = app().assets[fromAsset.token.parentID]\n icon.src = Doc.logoPath(parentAsset.symbol)\n } else {\n icon.src = Doc.logoPath(fromAsset.symbol)\n }\n break\n case 'to':\n if (toAsset.token) {\n const parentAsset = app().assets[toAsset.token.parentID]\n icon.src = Doc.logoPath(parentAsset.symbol)\n } else {\n icon.src = Doc.logoPath(toAsset.symbol)\n }\n }\n }\n\n // Set the to and from icons in the fee details pane.\n for (const icon of Doc.applySelector(page.vDetailPane, '[data-icon]')) {\n setIcon(icon)\n }\n\n // Set the to and from icons in the fee summary pane.\n for (const icon of Doc.applySelector(page.vFeeSummary, '[data-icon]')) {\n setIcon(icon)\n }\n\n Doc.hide(page.vUnlockPreorder, page.vPreorderErr)\n Doc.show(page.vPreorder)\n\n page.vBuySell.textContent = isSell ? intl.prep(intl.ID_SELLING) : intl.prep(intl.ID_BUYING)\n const buySellStr = isSell ? intl.prep(intl.ID_SELL) : intl.prep(intl.ID_BUY)\n page.vSideSubmit.textContent = buySellStr\n page.vOrderHost.textContent = order.host\n if (order.isLimit) {\n Doc.show(page.verifyLimit)\n Doc.hide(page.verifyMarket)\n const orderDesc = `Limit ${buySellStr} Order`\n page.vOrderType.textContent = order.tifnow ? orderDesc + ' (immediate)' : orderDesc\n page.vRate.textContent = Doc.formatCoinValue(order.rate / this.market.rateConversionFactor)\n page.vQty.textContent = Doc.formatCoinValue(order.qty, baseAsset.unitInfo)\n const total = order.rate / OrderUtil.RateEncodingFactor * order.qty\n page.vTotal.textContent = Doc.formatCoinValue(total, quoteAsset.unitInfo)\n // Format total fiat value.\n this.showFiatValue(quoteAsset.id, total, page.vFiatTotal)\n } else {\n Doc.hide(page.verifyLimit)\n Doc.show(page.verifyMarket)\n page.vOrderType.textContent = `Market ${buySellStr} Order`\n const ui = order.sell ? this.market.baseUnitInfo : this.market.quoteUnitInfo\n page.vmFromTotal.textContent = Doc.formatCoinValue(order.qty, ui)\n page.vmFromAsset.textContent = fromAsset.symbol.toUpperCase()\n // Format fromAsset fiat value.\n this.showFiatValue(fromAsset.id, order.qty, page.vmFromTotalFiat)\n const gap = this.midGap()\n if (gap) {\n Doc.show(page.vMarketEstimate)\n const received = order.sell ? order.qty * gap : order.qty / gap\n page.vmToTotal.textContent = Doc.formatCoinValue(received, toAsset.unitInfo)\n page.vmToAsset.textContent = toAsset.symbol.toUpperCase()\n // Format received value to fiat equivalent.\n this.showFiatValue(toAsset.id, received, page.vmTotalFiat)\n } else {\n Doc.hide(page.vMarketEstimate)\n }\n }\n // Visually differentiate between buy/sell orders.\n if (isSell) {\n page.vHeader.classList.add(sellBtnClass)\n page.vHeader.classList.remove(buyBtnClass)\n page.vSubmit.classList.add(sellBtnClass)\n page.vSubmit.classList.remove(buyBtnClass)\n } else {\n page.vHeader.classList.add(buyBtnClass)\n page.vHeader.classList.remove(sellBtnClass)\n page.vSubmit.classList.add(buyBtnClass)\n page.vSubmit.classList.remove(sellBtnClass)\n }\n this.showVerifyForm()\n page.vPass.focus()\n\n if (baseAsset.wallet.open && quoteAsset.wallet.open) this.preOrder(order)\n else {\n Doc.hide(page.vPreorder)\n if (State.passwordIsCached()) this.unlockWalletsForEstimates('')\n else Doc.show(page.vUnlockPreorder)\n }\n }\n\n // showFiatValue displays the fiat equivalent for an order quantity.\n showFiatValue (assetID: number, qty: number, display: PageElement) {\n if (display) {\n const rate = app().fiatRatesMap[assetID]\n display.textContent = Doc.formatFiatConversion(qty, rate, app().unitInfo(assetID))\n if (rate) Doc.show(display.parentElement as Element)\n else Doc.hide(display.parentElement as Element)\n }\n }\n\n /* showVerifyForm displays form to verify an order */\n async showVerifyForm () {\n const page = this.page\n Doc.hide(page.vErr)\n this.showForm(page.verifyForm)\n }\n\n /*\n * submitEstimateUnlock reads the current vUnlockPass and unlocks any locked\n * wallets.\n */\n async submitEstimateUnlock () {\n const pw = this.page.vUnlockPass.value || ''\n return await this.unlockWalletsForEstimates(pw)\n }\n\n /*\n * unlockWalletsForEstimates unlocks any locked wallets with the provided\n * password.\n */\n async unlockWalletsForEstimates (pw: string) {\n const page = this.page\n const loaded = app().loading(page.verifyForm)\n const err = await this.attemptWalletUnlock(pw)\n loaded()\n if (err) return this.setPreorderErr(err)\n Doc.show(page.vPreorder)\n Doc.hide(page.vUnlockPreorder)\n this.preOrder(this.parseOrder())\n }\n\n /*\n * attemptWalletUnlock unlocks both the base and quote wallets for the current\n * market, if locked.\n */\n async attemptWalletUnlock (pw: string) {\n const { base, quote } = this.market\n const assetIDs = []\n if (!base.wallet.open) assetIDs.push(base.id)\n if (!quote.wallet.open) assetIDs.push(quote.id)\n const req = {\n pass: pw,\n assetID: -1\n }\n for (const assetID of assetIDs) {\n req.assetID = assetID\n const res = await postJSON('/api/openwallet', req)\n if (!app().checkResponse(res)) {\n return res.msg\n }\n }\n }\n\n /* fetchPreorder fetches the pre-order estimates and options. */\n async fetchPreorder (order: TradeForm) {\n const page = this.page\n const cacheKey = JSON.stringify(order.options)\n const cached = this.preorderCache[cacheKey]\n if (cached) return cached\n\n Doc.hide(page.vPreorderErr)\n const loaded = app().loading(page.verifyForm)\n const res = await postJSON('/api/preorder', wireOrder(order))\n loaded()\n if (!app().checkResponse(res)) return { err: res.msg }\n this.preorderCache[cacheKey] = res.estimate\n return res.estimate\n }\n\n /*\n * setPreorderErr sets and displays the pre-order error message and hides the\n * pre-order details box.\n */\n setPreorderErr (msg: string) {\n const page = this.page\n Doc.hide(page.vPreorder)\n Doc.show(page.vPreorderErr)\n page.vPreorderErrTip.dataset.tooltip = msg\n }\n\n showPreOrderAdvancedOptions () {\n const page = this.page\n Doc.hide(page.showAdvancedOptions)\n Doc.show(page.hideAdvancedOptions, page.vOtherOrderOpts)\n }\n\n hidePreOrderAdvancedOptions () {\n const page = this.page\n Doc.hide(page.hideAdvancedOptions, page.vOtherOrderOpts)\n Doc.show(page.showAdvancedOptions)\n }\n\n reloadOrderOpts (order: TradeForm, swap: PreSwap, redeem: PreRedeem, changed: ()=>void) {\n const page = this.page\n Doc.empty(page.vDefaultOrderOpts, page.vOtherOrderOpts)\n const addOption = (opt: OrderOption, isSwap: boolean) => {\n const el = OrderUtil.optionElement(opt, order, changed, isSwap)\n if (opt.showByDefault) page.vDefaultOrderOpts.appendChild(el)\n else page.vOtherOrderOpts.appendChild(el)\n }\n for (const opt of swap.options || []) addOption(opt, true)\n for (const opt of redeem.options || []) addOption(opt, false)\n app().bindTooltips(page.vDefaultOrderOpts)\n app().bindTooltips(page.vOtherOrderOpts)\n }\n\n /* preOrder loads the options and fetches pre-order estimates */\n async preOrder (order: TradeForm) {\n const page = this.page\n\n // Add swap options.\n const refreshPreorder = async () => {\n const res: APIResponse = await this.fetchPreorder(order)\n if (res.err) return this.setPreorderErr(res.err)\n const est = (res as any) as OrderEstimate\n Doc.hide(page.vPreorderErr)\n Doc.show(page.vPreorder)\n const { swap, redeem } = est\n swap.options = swap.options || []\n redeem.options = redeem.options || []\n this.setFeeEstimates(swap, redeem, order)\n\n const changed = async () => {\n await refreshPreorder()\n Doc.animate(400, progress => {\n page.vFeeSummary.style.backgroundColor = `rgba(128, 128, 128, ${0.5 - 0.5 * progress})`\n })\n }\n // bind show or hide advanced pre order options.\n Doc.bind(page.showAdvancedOptions, 'click', () => { this.showPreOrderAdvancedOptions() })\n Doc.bind(page.hideAdvancedOptions, 'click', () => { this.hidePreOrderAdvancedOptions() })\n this.reloadOrderOpts(order, swap, redeem, changed)\n }\n\n refreshPreorder()\n }\n\n /* setFeeEstimates sets all of the pre-order estimate fields */\n setFeeEstimates (swap: PreSwap, redeem: PreRedeem, order: TradeForm) {\n const { page, market } = this\n if (!swap.estimate || !redeem.estimate) {\n Doc.hide(page.vPreorderEstimates)\n return // preOrder may return just options, no fee estimates\n }\n Doc.show(page.vPreorderEstimates)\n const { baseUnitInfo, quoteUnitInfo, rateConversionFactor } = market\n const fmtPct = (value: number) => {\n if (value < 0.05) return '< 0.1'\n return percentFormatter.format(value)\n }\n\n // If the asset is a token, in order to calculate the fee as a percentage\n // of the total order, we try to use the fiat rates to find out the\n // exchange rate between the token and parent assets.\n // Initially these are set to 1, which we would use if the asset is not a\n // token and no conversion is needed.\n let baseExchangeRate = 1\n let quoteExchangeRate = 1\n let baseFeeAssetUI = baseUnitInfo\n let quoteFeeAssetUI = quoteUnitInfo\n\n if (market.base.token) {\n const parent = app().assets[market.base.token.parentID]\n baseFeeAssetUI = parent.unitInfo\n const tokenFiatRate = app().fiatRatesMap[market.base.id]\n const parentFiatRate = app().fiatRatesMap[parent.id]\n if (tokenFiatRate && parentFiatRate) {\n const conventionalRate = parentFiatRate / tokenFiatRate\n baseExchangeRate = conventionalRate * baseUnitInfo.conventional.conversionFactor / parent.unitInfo.conventional.conversionFactor\n } else {\n baseExchangeRate = 0\n }\n }\n\n if (market.quote.token) {\n const parent = app().assets[market.quote.token.parentID]\n quoteFeeAssetUI = parent.unitInfo\n const tokenFiatRate = app().fiatRatesMap[market.quote.id]\n const parentFiatRate = app().fiatRatesMap[parent.id]\n if (tokenFiatRate && parentFiatRate) {\n const conventionalRate = parentFiatRate / tokenFiatRate\n quoteExchangeRate = conventionalRate * quoteUnitInfo.conventional.conversionFactor / parent.unitInfo.conventional.conversionFactor\n } else {\n quoteExchangeRate = 0\n }\n }\n\n let [toFeeAssetUI, fromFeeAssetUI] = [baseFeeAssetUI, quoteFeeAssetUI]\n let [toExchangeRate, fromExchangeRate] = [baseExchangeRate, quoteExchangeRate]\n if (this.currentOrder.sell) {\n [fromFeeAssetUI, toFeeAssetUI] = [toFeeAssetUI, fromFeeAssetUI];\n [fromExchangeRate, toExchangeRate] = [toExchangeRate, fromExchangeRate]\n }\n\n const swapped = swap.estimate.value || 0\n const swappedInParentUnits = fromExchangeRate > 0 ? swapped / fromExchangeRate : swapped\n\n // Set swap fee estimates in the details pane.\n const bestSwapPct = swap.estimate.realisticBestCase / swappedInParentUnits * 100\n page.vSwapFeesLowPct.textContent = fromExchangeRate <= 0 ? '' : `(${fmtPct(bestSwapPct)}%)`\n page.vSwapFeesLow.textContent = Doc.formatCoinValue(swap.estimate.realisticBestCase, fromFeeAssetUI)\n\n const worstSwapPct = swap.estimate.realisticWorstCase / swappedInParentUnits * 100\n page.vSwapFeesHighPct.textContent = fromExchangeRate <= 0 ? '' : `(${fmtPct(worstSwapPct)}%)`\n page.vSwapFeesHigh.textContent = Doc.formatCoinValue(swap.estimate.realisticWorstCase, fromFeeAssetUI)\n\n const swapFeesMaxPct = swap.estimate.maxFees / swappedInParentUnits * 100\n page.vSwapFeesMaxPct.textContent = fromExchangeRate <= 0 ? '' : `(${fmtPct(swapFeesMaxPct)}%)`\n page.vSwapFeesMax.textContent = Doc.formatCoinValue(swap.estimate.maxFees, fromFeeAssetUI)\n\n // Set redemption fee estimates in the details pane.\n const midGap = this.midGap()\n const estRate = midGap || order.rate / rateConversionFactor\n const received = order.sell ? swapped * estRate : swapped / estRate\n const receivedInParentUnits = toExchangeRate > 0 ? received / toExchangeRate : received\n\n const bestRedeemPct = redeem.estimate.realisticBestCase / receivedInParentUnits * 100\n page.vRedeemFeesLowPct.textContent = toExchangeRate <= 0 ? '' : `(${fmtPct(bestRedeemPct)}%)`\n page.vRedeemFeesLow.textContent = Doc.formatCoinValue(redeem.estimate.realisticBestCase, toFeeAssetUI)\n\n const worstRedeemPct = redeem.estimate.realisticWorstCase / receivedInParentUnits * 100\n page.vRedeemFeesHighPct.textContent = toExchangeRate <= 0 ? '' : `(${fmtPct(worstRedeemPct)}%)`\n page.vRedeemFeesHigh.textContent = Doc.formatCoinValue(redeem.estimate.realisticWorstCase, toFeeAssetUI)\n\n if (baseExchangeRate && quoteExchangeRate) {\n Doc.show(page.vFeeSummaryPct)\n Doc.hide(page.vFeeSummary)\n page.vFeeSummaryLow.textContent = fmtPct(bestSwapPct + bestRedeemPct)\n page.vFeeSummaryHigh.textContent = fmtPct(worstSwapPct + worstRedeemPct)\n } else {\n Doc.hide(page.vFeeSummaryPct)\n Doc.show(page.vFeeSummary)\n page.summarySwapFeesLow.textContent = page.vSwapFeesLow.textContent\n page.summarySwapFeesHigh.textContent = page.vSwapFeesHigh.textContent\n page.summaryRedeemFeesLow.textContent = page.vRedeemFeesLow.textContent\n page.summaryRedeemFeesHigh.textContent = page.vRedeemFeesHigh.textContent\n }\n }\n\n async submitCancel () {\n // this will be the page.cancelSubmit button (evt.currentTarget)\n const page = this.page\n const cancelData = this.cancelData\n const order = cancelData.order\n const req = {\n orderID: order.id,\n pw: page.cancelPass.value\n }\n page.cancelPass.value = ''\n // Toggle the loader and submit button.\n const loaded = app().loading(page.cancelSubmit)\n const res = await postJSON('/api/cancel', req)\n loaded()\n // Display error on confirmation modal.\n if (!app().checkResponse(res)) {\n page.cancelErr.textContent = res.msg\n Doc.show(page.cancelErr)\n return\n }\n // Hide confirmation modal only on success.\n Doc.hide(cancelData.bttn, page.forms)\n order.cancelling = true\n }\n\n /* showCancel shows a form to confirm submission of a cancel order. */\n showCancel (row: HTMLElement, orderID: string) {\n const ord = this.metaOrders[orderID].ord\n const page = this.page\n const remaining = ord.qty - ord.filled\n const asset = OrderUtil.isMarketBuy(ord) ? this.market.quote : this.market.base\n page.cancelRemain.textContent = Doc.formatCoinValue(remaining, asset.unitInfo)\n page.cancelUnit.textContent = asset.symbol.toUpperCase()\n Doc.hide(page.cancelErr)\n this.showForm(page.cancelForm)\n page.cancelPass.focus()\n this.cancelData = {\n bttn: Doc.tmplElement(row, 'cancelBttn'),\n order: ord\n }\n }\n\n /* showAccelerate shows the accelerate order form. */\n showAccelerate (order: Order) {\n const loaded = app().loading(this.main)\n this.accelerateOrderForm.refresh(order)\n loaded()\n this.showForm(this.page.accelerateForm)\n }\n\n /* showCreate shows the new wallet creation form. */\n showCreate (asset: SupportedAsset) {\n const page = this.page\n this.currentCreate = asset\n this.newWalletForm.setAsset(asset.id)\n this.showForm(page.newWalletForm)\n }\n\n /*\n * stepSubmit will examine the current state of wallets and step the user\n * through the process of order submission.\n * NOTE: I expect this process will be streamlined soon such that the wallets\n * will attempt to be unlocked in the order submission process, negating the\n * need to unlock ahead of time.\n */\n stepSubmit () {\n const page = this.page\n const market = this.market\n Doc.hide(page.orderErr)\n if (!this.validateOrder(this.parseOrder())) return\n const baseWallet = app().walletMap[market.base.id]\n const quoteWallet = app().walletMap[market.quote.id]\n if (!baseWallet) {\n page.orderErr.textContent = intl.prep(intl.ID_NO_ASSET_WALLET, { asset: market.base.symbol })\n Doc.show(page.orderErr)\n return\n }\n if (!quoteWallet) {\n page.orderErr.textContent = intl.prep(intl.ID_NO_ASSET_WALLET, { asset: market.quote.symbol })\n Doc.show(page.orderErr)\n return\n }\n this.showVerify()\n }\n\n /* Display a deposit address. */\n async showDeposit (assetID: number) {\n this.depositAddrForm.setAsset(assetID)\n this.showForm(this.page.deposit)\n }\n\n /*\n * handlePriceUpdate is the handler for the 'spots' notification.\n */\n handlePriceUpdate (note: SpotPriceNote) {\n if (note.host === this.market.dex.host && note.spots[this.market.cfg.name]) {\n this.setCurrMarketPrice()\n }\n this.marketList.updateSpots(note)\n }\n\n /*\n * handleBondUpdate is the handler for the 'bondpost' notification type.\n * This is used to update the registration status of the current exchange.\n */\n async handleBondUpdate (note: BondNote) {\n const dexAddr = note.dex\n if (dexAddr !== this.market.dex.host) return\n // If we just finished legacy registration, we need to update the Exchange.\n // TODO: Use tier change notification once available.\n if (note.topic === 'AccountRegistered') await app().fetchUser()\n // Update local copy of Exchange.\n this.market.dex = app().exchanges[dexAddr]\n this.setRegistrationStatusVisibility()\n }\n\n handleMatchNote (note: MatchNote) {\n const mord = this.metaOrders[note.orderID]\n if (!mord) return this.refreshActiveOrders()\n if (app().canAccelerateOrder(mord.ord)) Doc.show(mord.details.accelerateBttn)\n else Doc.hide(mord.details.accelerateBttn)\n }\n\n /*\n * handleOrderNote is the handler for the 'order'-type notification, which are\n * used to update a user's order's status.\n */\n handleOrderNote (note: OrderNote) {\n const order = note.order\n const mord = this.metaOrders[order.id]\n // - If metaOrder doesn't exist for the given order it means it was created\n // via dexcctl and the GUI isn't aware of it or it was an inflight order.\n // refreshActiveOrders must be called to grab this order.\n // - If an OrderLoaded notification is recieved, it means an order that was\n // previously not \"ready to tick\" (due to its wallets not being connected\n // and unlocked) has now become ready to tick. The active orders section\n // needs to be refreshed.\n if (!mord || note.topic === 'AsyncOrderFailure' || (note.topic === 'OrderLoaded' && order.readyToTick)) {\n return this.refreshActiveOrders()\n }\n const oldStatus = mord.status\n mord.ord = order\n if (note.topic === 'MissedCancel') Doc.show(mord.details.cancelBttn)\n if (order.filled === order.qty) Doc.hide(mord.details.cancelBttn)\n if (app().canAccelerateOrder(order)) Doc.show(mord.details.accelerateBttn)\n else Doc.hide(mord.details.accelerateBttn)\n this.updateMetaOrder(mord)\n // Only reset markers if there is a change, since the chart is redrawn.\n if ((oldStatus === OrderUtil.StatusEpoch && order.status === OrderUtil.StatusBooked) ||\n (oldStatus === OrderUtil.StatusBooked && order.status > OrderUtil.StatusBooked)) this.setDepthMarkers()\n }\n\n /*\n * handleEpochNote handles notifications signalling the start of a new epoch.\n */\n handleEpochNote (note: EpochNote) {\n app().log('book', 'handleEpochNote:', note)\n if (note.host !== this.market.dex.host || note.marketID !== this.market.sid) return\n if (this.book) {\n this.book.setEpoch(note.epoch)\n this.depthChart.draw()\n }\n\n this.clearOrderTableEpochs()\n for (const { ord, details, header } of Object.values(this.metaOrders)) {\n const alreadyMatched = note.epoch > ord.epoch\n switch (true) {\n case ord.type === OrderUtil.Limit && ord.status === OrderUtil.StatusEpoch && alreadyMatched: {\n const status = ord.tif === OrderUtil.ImmediateTiF ? intl.prep(intl.ID_EXECUTED) : intl.prep(intl.ID_BOOKED)\n details.status.textContent = header.status.textContent = status\n ord.status = ord.tif === OrderUtil.ImmediateTiF ? OrderUtil.StatusExecuted : OrderUtil.StatusBooked\n break\n }\n case ord.type === OrderUtil.Market && ord.status === OrderUtil.StatusEpoch:\n // Technically don't know if this should be 'executed' or 'settling'.\n details.status.textContent = header.status.textContent = intl.prep(intl.ID_EXECUTED)\n ord.status = OrderUtil.StatusExecuted\n break\n }\n }\n }\n\n /*\n * recentMatchesSortCompare returns sort compare function according to the active\n * sort key and direction.\n */\n recentMatchesSortCompare () {\n switch (this.recentMatchesSortKey) {\n case 'rate':\n return (a: RecentMatch, b: RecentMatch) => this.recentMatchesSortDirection * (a.rate - b.rate)\n case 'qty':\n return (a: RecentMatch, b: RecentMatch) => this.recentMatchesSortDirection * (a.qty - b.qty)\n case 'age':\n return (a: RecentMatch, b:RecentMatch) => this.recentMatchesSortDirection * (a.stamp - b.stamp)\n }\n }\n\n refreshRecentMatchesTable () {\n const page = this.page\n const recentMatches = this.recentMatches\n Doc.empty(page.recentMatchesLiveList)\n if (!recentMatches) return\n const compare = this.recentMatchesSortCompare()\n recentMatches.sort(compare)\n for (const match of recentMatches) {\n const row = page.recentMatchesTemplate.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(row)\n app().bindTooltips(row)\n tmpl.rate.textContent = Doc.formatCoinValue(match.rate / this.market.rateConversionFactor)\n tmpl.qty.textContent = Doc.formatCoinValue(match.qty, this.market.baseUnitInfo)\n tmpl.age.textContent = Doc.timeSince(match.stamp)\n tmpl.age.dataset.sinceStamp = String(match.stamp)\n row.classList.add(match.sell ? 'sellcolor' : 'buycolor')\n page.recentMatchesLiveList.append(row)\n }\n }\n\n addRecentMatches (matches: RecentMatch[]) {\n this.recentMatches = [...matches, ...this.recentMatches].slice(0, 100)\n }\n\n setBalanceVisibility () {\n const mkt = this.market\n if (!mkt || !mkt.dex) return\n this.balanceWgt.setBalanceVisibility(mkt.dex.connectionStatus === ConnectionStatus.Connected)\n }\n\n /* handleBalanceNote handles notifications updating a wallet's balance. */\n handleBalanceNote (note: BalanceNote) {\n this.setBalanceVisibility()\n this.preorderCache = {} // invalidate previous preorder results\n // if connection to dex server fails, it is not possible to retrieve\n // markets.\n const mkt = this.market\n if (!mkt || !mkt.dex || mkt.dex.connectionStatus !== ConnectionStatus.Connected) return\n // If there's a balance update, refresh the max order section.\n const avail = note.balance.available\n switch (note.assetID) {\n case mkt.baseCfg.id:\n // If we're not showing the max order panel yet, don't do anything.\n if (!mkt.maxSell) break\n if (typeof mkt.sellBalance === 'number' && mkt.sellBalance !== avail) mkt.maxSell = null\n if (this.isSell()) this.preSell()\n break\n case mkt.quoteCfg.id:\n if (!Object.keys(mkt.maxBuys).length) break\n if (typeof mkt.buyBalance === 'number' && mkt.buyBalance !== avail) mkt.maxBuys = {}\n if (!this.isSell()) this.preBuy()\n }\n }\n\n /*\n * submitOrder is attached to the affirmative button on the order validation\n * form. Clicking the button is the last step in the order submission process.\n */\n async submitOrder () {\n const page = this.page\n Doc.hide(page.orderErr, page.vErr)\n const order = this.currentOrder\n const pw = page.vPass.value\n page.vPass.value = ''\n const req = {\n order: wireOrder(order),\n pw: pw\n }\n if (!this.validateOrder(order)) return\n // Show loader and hide submit button.\n page.vSubmit.classList.add('d-hide')\n page.vLoader.classList.remove('d-hide')\n const res = await postJSON('/api/tradeasync', req)\n // Hide loader and show submit button.\n page.vSubmit.classList.remove('d-hide')\n page.vLoader.classList.add('d-hide')\n // If error, display error on confirmation modal.\n if (!app().checkResponse(res)) {\n page.vErr.textContent = res.msg\n Doc.show(page.vErr)\n return\n }\n // Hide confirmation modal only on success.\n Doc.hide(page.forms)\n this.refreshActiveOrders()\n }\n\n /*\n * createWallet is attached to successful submission of the wallet creation\n * form. createWallet is only called once the form is submitted and a success\n * response is received from the client.\n */\n async createWallet () {\n const user = await app().fetchUser()\n if (!user) return\n const asset = user.assets[this.currentCreate.id]\n Doc.hide(this.page.forms)\n this.balanceWgt.updateAsset(asset.id)\n Doc.setVis(!(this.market.base.wallet && this.market.quote.wallet), this.page.noWallet)\n this.resolveOrderFormVisibility()\n }\n\n /*\n * walletUnlocked is attached to successful submission of the wallet unlock\n * form. walletUnlocked is only called once the form is submitted and a\n * success response is received from the client.\n */\n async walletUnlocked () {\n Doc.hide(this.page.forms)\n this.balanceWgt.updateAsset(this.openAsset.id)\n }\n\n /* lotChanged is attached to the keyup and change events of the lots input. */\n lotChanged () {\n const page = this.page\n const lots = parseInt(page.lotField.value || '0')\n if (lots <= 0) {\n page.lotField.value = '0'\n page.qtyField.value = ''\n this.previewQuoteAmt(false)\n return\n }\n const lotSize = this.market.cfg.lotsize\n page.lotField.value = String(lots)\n // Conversion factor must be a multiple of 10.\n page.qtyField.value = String(lots * lotSize / this.market.baseUnitInfo.conventional.conversionFactor)\n this.previewQuoteAmt(true)\n }\n\n /*\n * quantityChanged is attached to the keyup and change events of the quantity\n * input.\n */\n quantityChanged (finalize: boolean) {\n const page = this.page\n const order = this.parseOrder()\n if (order.qty < 0) {\n page.lotField.value = '0'\n page.qtyField.value = ''\n this.previewQuoteAmt(false)\n return\n }\n const lotSize = this.market.cfg.lotsize\n const lots = Math.floor(order.qty / lotSize)\n const adjusted = lots * lotSize\n page.lotField.value = String(lots)\n if (!order.isLimit && !order.sell) return\n // Conversion factor must be a multiple of 10.\n if (finalize) page.qtyField.value = String(adjusted / this.market.baseUnitInfo.conventional.conversionFactor)\n this.previewQuoteAmt(true)\n }\n\n /*\n * marketBuyChanged is attached to the keyup and change events of the quantity\n * input for the market-buy form.\n */\n marketBuyChanged () {\n const page = this.page\n const qty = convertToAtoms(page.mktBuyField.value || '', this.market.quoteUnitInfo.conventional.conversionFactor)\n const gap = this.midGap()\n if (!gap || !qty) {\n page.mktBuyLots.textContent = '0'\n page.mktBuyScore.textContent = '0'\n return\n }\n const lotSize = this.market.cfg.lotsize\n const received = qty / gap\n page.mktBuyLots.textContent = (received / lotSize).toFixed(1)\n page.mktBuyScore.textContent = Doc.formatCoinValue(received, this.market.baseUnitInfo)\n }\n\n /*\n * rateFieldChanged is attached to the keyup and change events of the rate\n * input.\n */\n rateFieldChanged () {\n // Truncate to rate step. If it is a market buy order, do not adjust.\n const adjusted = this.adjustedRate()\n if (adjusted <= 0) {\n this.depthLines.input = []\n this.drawChartLines()\n this.page.rateField.value = '0'\n return\n }\n const order = this.parseOrder()\n const r = adjusted / this.market.rateConversionFactor\n this.page.rateField.value = String(r)\n this.depthLines.input = [{\n rate: r,\n color: order.sell ? this.depthChart.theme.sellLine : this.depthChart.theme.buyLine\n }]\n this.drawChartLines()\n this.previewQuoteAmt(true)\n }\n\n /*\n * adjustedRate is the current rate field rate, rounded down to a\n * multiple of rateStep.\n */\n adjustedRate (): number {\n const v = this.page.rateField.value\n if (!v) return NaN\n const rate = convertToAtoms(v, this.market.rateConversionFactor)\n const rateStep = this.market.cfg.ratestep\n return rate - (rate % rateStep)\n }\n\n /* loadTable reloads the table from the current order book information. */\n loadTable () {\n this.loadTableSide(true)\n this.loadTableSide(false)\n }\n\n /* binOrdersByRateAndEpoch takes a list of sorted orders and returns the\n same orders grouped into arrays. The orders are grouped by their rate\n and whether or not they are epoch queue orders. Epoch queue orders\n will come after non epoch queue orders with the same rate. */\n binOrdersByRateAndEpoch (orders: MiniOrder[]) {\n if (!orders || !orders.length) return []\n const bins = []\n let currEpochBin = []\n let currNonEpochBin = []\n let currRate = orders[0].msgRate\n if (orders[0].epoch) currEpochBin.push(orders[0])\n else currNonEpochBin.push(orders[0])\n for (let i = 1; i < orders.length; i++) {\n if (orders[i].msgRate !== currRate) {\n bins.push(currNonEpochBin)\n bins.push(currEpochBin)\n currEpochBin = []\n currNonEpochBin = []\n currRate = orders[i].msgRate\n }\n if (orders[i].epoch) currEpochBin.push(orders[i])\n else currNonEpochBin.push(orders[i])\n }\n bins.push(currNonEpochBin)\n bins.push(currEpochBin)\n return bins.filter(bin => bin.length > 0)\n }\n\n /* loadTables loads the order book side into its table. */\n loadTableSide (sell: boolean) {\n const bookSide = sell ? this.book.sells : this.book.buys\n const tbody = sell ? this.page.sellRows : this.page.buyRows\n Doc.empty(tbody)\n if (!bookSide || !bookSide.length) return\n const orderBins = this.binOrdersByRateAndEpoch(bookSide)\n orderBins.forEach(bin => { tbody.appendChild(this.orderTableRow(bin)) })\n }\n\n /* addTableOrder adds a single order to the appropriate table. */\n addTableOrder (order: MiniOrder) {\n const tbody = order.sell ? this.page.sellRows : this.page.buyRows\n let row = tbody.firstChild as OrderRow\n // Handle market order differently.\n if (order.rate === 0) {\n if (order.qtyAtomic === 0) return // a cancel order. TODO: maybe make an indicator on the target order, maybe gray out\n // This is a market order.\n if (row && row.manager.getRate() === 0) {\n row.manager.insertOrder(order)\n } else {\n row = this.orderTableRow([order])\n tbody.insertBefore(row, tbody.firstChild)\n }\n return\n }\n // Must be a limit order. Sort by rate. Skip the market order row.\n if (row && row.manager.getRate() === 0) row = row.nextSibling as OrderRow\n while (row) {\n if (row.manager.compare(order) === 0) {\n row.manager.insertOrder(order)\n return\n } else if (row.manager.compare(order) > 0) {\n const tr = this.orderTableRow([order])\n tbody.insertBefore(tr, row)\n return\n }\n row = row.nextSibling as OrderRow\n }\n const tr = this.orderTableRow([order])\n tbody.appendChild(tr)\n }\n\n /* removeTableOrder removes a single order from its table. */\n removeTableOrder (order: MiniOrder) {\n const token = order.token\n for (const tbody of [this.page.sellRows, this.page.buyRows]) {\n for (const tr of (Array.from(tbody.children) as OrderRow[])) {\n if (tr.manager.removeOrder(token)) {\n return\n }\n }\n }\n }\n\n /* updateTableOrder looks for the order in the table and updates the qty */\n updateTableOrder (u: RemainderUpdate) {\n for (const tbody of [this.page.sellRows, this.page.buyRows]) {\n for (const tr of (Array.from(tbody.children) as OrderRow[])) {\n if (tr.manager.updateOrderQty(u)) {\n return\n }\n }\n }\n }\n\n /*\n * clearOrderTableEpochs removes immediate-tif orders whose epoch has expired.\n */\n clearOrderTableEpochs () {\n this.clearOrderTableEpochSide(this.page.sellRows)\n this.clearOrderTableEpochSide(this.page.buyRows)\n }\n\n /*\n * clearOrderTableEpochs removes immediate-tif orders whose epoch has expired\n * for a single side.\n */\n clearOrderTableEpochSide (tbody: HTMLElement) {\n for (const tr of (Array.from(tbody.children)) as OrderRow[]) {\n tr.manager.removeEpochOrders()\n }\n }\n\n /*\n * orderTableRow creates a new element to insert into an order table.\n Takes a bin of orders with the same rate, and displays the total quantity.\n */\n orderTableRow (orderBin: MiniOrder[]): OrderRow {\n const tr = this.page.rowTemplate.cloneNode(true) as OrderRow\n const { baseUnitInfo, rateConversionFactor } = this.market\n const manager = new OrderTableRowManager(tr, orderBin, baseUnitInfo, rateConversionFactor)\n tr.manager = manager\n bind(tr, 'click', () => {\n this.reportDepthClick(tr.manager.getRate() / rateConversionFactor)\n })\n if (tr.manager.getRate() !== 0) {\n Doc.bind(tr, 'mouseenter', () => {\n const chart = this.depthChart\n this.depthLines.hover = [{\n rate: tr.manager.getRate() / rateConversionFactor,\n color: tr.manager.isSell() ? chart.theme.sellLine : chart.theme.buyLine\n }]\n this.drawChartLines()\n })\n }\n return tr\n }\n\n /* handleConnNote handles the 'conn' notification.\n */\n async handleConnNote (note: ConnEventNote) {\n this.marketList.setConnectionStatus(note)\n if (note.connectionStatus === ConnectionStatus.Connected) {\n // Having been disconnected from a DEX server, anything may have changed,\n // or this may be the first opportunity to get the server's config, so\n // fetch it all before reloading the markets page.\n await app().fetchUser()\n await app().loadPage('markets')\n }\n }\n\n /*\n * filterMarkets sets the display of markets in the markets list based on the\n * value of the search input.\n */\n filterMarkets () {\n const filterTxt = this.page.marketSearchV1.value?.toLowerCase()\n const filter = filterTxt ? (mkt: MarketRow) => mkt.name.includes(filterTxt) : () => true\n this.marketList.setFilter(filter)\n }\n\n /* drawChartLines draws the hover and input lines on the chart. */\n drawChartLines () {\n this.depthChart.setLines([...this.depthLines.hover, ...this.depthLines.input])\n this.depthChart.draw()\n }\n\n /* candleDurationSelected sets the candleDur and loads the candles. */\n candleDurationSelected (dur: string) {\n this.candleDur = dur\n this.loadCandles()\n }\n\n /*\n * loadCandles loads the candles for the current candleDur. If a cache is already\n * active, the cache will be used without a loadcandles request.\n */\n loadCandles () {\n for (const bttn of Doc.kids(this.page.durBttnBox)) {\n if (bttn.textContent === this.candleDur) bttn.classList.add('selected')\n else bttn.classList.remove('selected')\n }\n const { candleCaches, cfg, baseUnitInfo, quoteUnitInfo } = this.market\n const cache = candleCaches[this.candleDur]\n if (cache) {\n // this.depthChart.hide()\n // this.candleChart.show()\n this.candleChart.setCandles(cache, cfg, baseUnitInfo, quoteUnitInfo)\n return\n }\n this.requestCandles()\n }\n\n /* requestCandles sends the loadcandles request. */\n requestCandles () {\n this.candlesLoading = {\n loaded: () => { /* pass */ },\n timer: window.setTimeout(() => {\n if (this.candlesLoading) {\n this.candlesLoading = null\n console.error('candles not received')\n }\n }, 10000)\n }\n const { dex, baseCfg, quoteCfg } = this.market\n ws.request('loadcandles', { host: dex.host, base: baseCfg.id, quote: quoteCfg.id, dur: this.candleDur })\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /markets page.\n */\n unload () {\n ws.request(unmarketRoute, {})\n ws.deregisterRoute(bookRoute)\n ws.deregisterRoute(bookOrderRoute)\n ws.deregisterRoute(unbookOrderRoute)\n ws.deregisterRoute(updateRemainingRoute)\n ws.deregisterRoute(epochOrderRoute)\n ws.deregisterRoute(candlesRoute)\n ws.deregisterRoute(candleUpdateRoute)\n this.depthChart.unattach()\n this.candleChart.unattach()\n Doc.unbind(document, 'keyup', this.keyup)\n clearInterval(this.secondTicker)\n }\n}\n\n/*\n * MarketList represents the list of exchanges and markets on the left side of\n * markets view. The MarketList provides utilities for adjusting the visibility\n * and sort order of markets.\n */\nclass MarketList {\n // xcSections: ExchangeSection[]\n div: PageElement\n rowTmpl: PageElement\n markets: MarketRow[]\n selected: MarketRow\n\n constructor (div: HTMLElement) {\n this.div = div\n this.rowTmpl = Doc.idel(div, 'marketTmplV1')\n Doc.cleanTemplates(this.rowTmpl)\n this.reloadMarketsPane()\n }\n\n updateSpots (note: SpotPriceNote) {\n for (const row of this.markets) {\n if (row.mkt.xc.host !== note.host) continue\n const spot = note.spots[row.mkt.name]\n if (!spot) return\n const xc = app().exchanges[row.mkt.xc.host]\n const mkt = xc.markets[row.mkt.name]\n setPriceAndChange(row.tmpl, xc, mkt)\n }\n }\n\n reloadMarketsPane (): void {\n Doc.empty(this.div)\n this.markets = []\n\n const addMarket = (mkt: ExchangeMarket) => {\n const bui = app().unitInfo(mkt.baseid, mkt.xc)\n const qui = app().unitInfo(mkt.quoteid, mkt.xc)\n const rateConversionFactor = OrderUtil.RateEncodingFactor / bui.conventional.conversionFactor * qui.conventional.conversionFactor\n const row = new MarketRow(this.rowTmpl, mkt, rateConversionFactor)\n this.div.appendChild(row.node)\n return row\n }\n\n for (const mkt of sortedMarkets()) this.markets.push(addMarket(mkt))\n app().bindTooltips(this.div)\n }\n\n find (host: string, baseID: number, quoteID: number): MarketRow | null {\n for (const row of this.markets) {\n if (row.mkt.xc.host === host && row.mkt.baseid === baseID && row.mkt.quoteid === quoteID) return row\n }\n return null\n }\n\n /* exists will be true if the specified market exists. */\n exists (host: string, baseID: number, quoteID: number): boolean {\n return this.find(host, baseID, quoteID) !== null\n }\n\n /* first gets the first market from the first exchange, alphabetically. */\n first (): MarketRow {\n return this.markets[0]\n }\n\n /* select sets the specified market as selected. */\n select (host: string, baseID: number, quoteID: number) {\n const row = this.find(host, baseID, quoteID)\n if (!row) return console.error(`select: no market row for ${host}, ${baseID}-${quoteID}`)\n for (const mkt of this.markets) mkt.node.classList.remove('selected')\n this.selected = row\n this.selected.node.classList.add('selected')\n }\n\n /* setConnectionStatus sets the visibility of the disconnected icon based\n * on the core.ConnEventNote.\n */\n setConnectionStatus (note: ConnEventNote) {\n for (const row of this.markets) {\n if (row.mkt.xc.host !== note.host) continue\n if (note.connectionStatus === ConnectionStatus.Connected) Doc.hide(row.tmpl.disconnectedIco)\n else Doc.show(row.tmpl.disconnectedIco)\n }\n }\n\n /*\n * setFilter sets the visibility of market rows based on the provided filter.\n */\n setFilter (filter: (mkt: MarketRow) => boolean) {\n for (const row of this.markets) {\n if (filter(row)) Doc.show(row.node)\n else Doc.hide(row.node)\n }\n }\n}\n\n/*\n * MarketRow represents one row in the MarketList. A MarketRow is a subsection\n * of the ExchangeSection.\n */\nclass MarketRow {\n node: HTMLElement\n mkt: ExchangeMarket\n name: string\n baseID: number\n quoteID: number\n lotSize: number\n rateStep: number\n tmpl: Record\n rateConversionFactor: number\n\n constructor (template: HTMLElement, mkt: ExchangeMarket, rateConversionFactor: number) {\n this.mkt = mkt\n this.name = mkt.name\n this.baseID = mkt.baseid\n this.quoteID = mkt.quoteid\n this.lotSize = mkt.lotsize\n this.rateStep = mkt.ratestep\n this.rateConversionFactor = rateConversionFactor\n this.node = template.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(this.node)\n tmpl.baseIcon.src = Doc.logoPath(mkt.basesymbol)\n tmpl.quoteIcon.src = Doc.logoPath(mkt.quotesymbol)\n tmpl.baseSymbol.appendChild(Doc.symbolize(mkt.basesymbol))\n tmpl.quoteSymbol.appendChild(Doc.symbolize(mkt.quotesymbol))\n tmpl.baseName.textContent = mkt.baseName\n tmpl.host.textContent = mkt.xc.host\n tmpl.host.style.color = hostColor(mkt.xc.host)\n tmpl.host.dataset.tooltip = mkt.xc.host\n setPriceAndChange(tmpl, mkt.xc, mkt)\n if (this.mkt.xc.connectionStatus !== ConnectionStatus.Connected) Doc.show(tmpl.disconnectedIco)\n }\n}\n\ninterface BalanceWidgetElement {\n id: number\n parentID: number\n cfg: Asset | null\n node: PageElement\n tmpl: Record\n iconBox: PageElement\n stateIcons: WalletIcons\n parentBal?: PageElement\n}\n\n/*\n * BalanceWidget is a display of balance information. Because the wallet can be\n * in any number of states, and because every exchange has different funding\n * coin confirmation requirements, the BalanceWidget displays a number of state\n * indicators and buttons, as well as tabulated balance data with rows for\n * locked and immature balance.\n */\nclass BalanceWidget {\n base: BalanceWidgetElement\n quote: BalanceWidgetElement\n // parentRow: PageElement\n dex: Exchange\n\n constructor (base: HTMLElement, quote: HTMLElement) {\n Doc.hide(base, quote)\n const btmpl = Doc.parseTemplate(base)\n this.base = {\n id: 0,\n parentID: parentIDNone,\n cfg: null,\n node: base,\n tmpl: btmpl,\n iconBox: btmpl.walletState,\n stateIcons: new WalletIcons(btmpl.walletState)\n }\n btmpl.balanceRowTmpl.remove()\n\n const qtmpl = Doc.parseTemplate(quote)\n this.quote = {\n id: 0,\n parentID: parentIDNone,\n cfg: null,\n node: quote,\n tmpl: qtmpl,\n iconBox: qtmpl.walletState,\n stateIcons: new WalletIcons(qtmpl.walletState)\n }\n qtmpl.balanceRowTmpl.remove()\n\n app().registerNoteFeeder({\n balance: (note: BalanceNote) => { this.updateAsset(note.assetID) },\n walletstate: (note: WalletStateNote) => { this.updateAsset(note.wallet.assetID) },\n createwallet: (note: WalletCreationNote) => { this.updateAsset(note.assetID) }\n })\n }\n\n setBalanceVisibility (connected: boolean) {\n if (connected) Doc.show(this.base.node, this.quote.node)\n else Doc.hide(this.base.node, this.quote.node)\n }\n\n /*\n * setWallet sets the balance widget to display data for specified market.\n */\n setWallets (host: string, baseID: number, quoteID: number) {\n const parentID = (assetID: number) => {\n const asset = app().assets[assetID]\n if (asset?.token) return asset.token.parentID\n return parentIDNone\n }\n this.dex = app().user.exchanges[host]\n this.base.id = baseID\n this.base.parentID = parentID(baseID)\n this.base.cfg = this.dex.assets[baseID]\n this.quote.id = quoteID\n this.quote.parentID = parentID(quoteID)\n this.quote.cfg = this.dex.assets[quoteID]\n this.updateWallet(this.base)\n this.updateWallet(this.quote)\n }\n\n /*\n * updateWallet updates the displayed wallet information based on the\n * core.Wallet state.\n */\n updateWallet (side: BalanceWidgetElement) {\n const { cfg, tmpl, iconBox, stateIcons, id: assetID } = side\n if (!cfg) return // no wallet set yet\n const asset = app().assets[assetID]\n // Just hide everything to start.\n Doc.hide(\n tmpl.newWalletRow, tmpl.expired, tmpl.unsupported, tmpl.connect, tmpl.spinner,\n tmpl.walletState, tmpl.balanceRows, tmpl.walletAddr\n )\n tmpl.logo.src = Doc.logoPath(cfg.symbol)\n tmpl.addWalletSymbol.textContent = cfg.symbol.toUpperCase()\n Doc.empty(tmpl.symbol)\n tmpl.symbol.appendChild(Doc.symbolize(cfg.symbol))\n // Handle an unsupported asset.\n if (!asset) {\n Doc.show(tmpl.unsupported)\n return\n }\n Doc.show(iconBox)\n const wallet = asset.wallet\n stateIcons.readWallet(wallet)\n // Handle no wallet configured.\n if (!wallet) {\n if (asset.walletCreationPending) {\n Doc.show(tmpl.spinner)\n return\n }\n Doc.show(tmpl.newWalletRow)\n return\n }\n Doc.show(tmpl.walletAddr)\n // Parent asset\n const bal = wallet.balance\n // Handle not connected and no balance known for the DEX.\n if (!bal && !wallet.running && !wallet.disabled) {\n Doc.show(tmpl.connect)\n return\n }\n // If there is no balance, but the wallet is connected, show the loading\n // icon while we fetch an update.\n if (!bal) {\n app().fetchBalance(assetID)\n Doc.show(tmpl.spinner)\n return\n }\n\n // We have a wallet and a DEX-specific balance. Set all of the fields.\n Doc.show(tmpl.balanceRows)\n Doc.empty(tmpl.balanceRows)\n const addRow = (title: string, bal: number, ui: UnitInfo, icon?: PageElement) => {\n const row = tmpl.balanceRowTmpl.cloneNode(true) as PageElement\n tmpl.balanceRows.appendChild(row)\n const balTmpl = Doc.parseTemplate(row)\n balTmpl.title.textContent = title\n balTmpl.bal.textContent = Doc.formatCoinValue(bal, ui)\n if (icon) {\n balTmpl.bal.append(icon)\n side.parentBal = balTmpl.bal\n }\n }\n\n addRow(intl.prep(intl.ID_AVAILABLE), bal.available, asset.unitInfo)\n addRow(intl.prep(intl.ID_LOCKED), bal.locked + bal.contractlocked + bal.bondlocked, asset.unitInfo)\n addRow(intl.prep(intl.ID_IMMATURE), bal.immature, asset.unitInfo)\n if (asset.token) {\n const { wallet: { balance }, unitInfo, symbol } = app().assets[asset.token.parentID]\n const icon = document.createElement('img')\n icon.src = Doc.logoPath(symbol)\n icon.classList.add('micro-icon')\n addRow(intl.prep(intl.ID_FEE_BALANCE), balance.available, unitInfo, icon)\n }\n\n // If the current balance update time is older than an hour, show the\n // expiration icon. Request a balance update, if possible.\n const expired = new Date().getTime() - new Date(bal.stamp).getTime() > anHour\n if (expired && !wallet.disabled) {\n Doc.show(tmpl.expired)\n if (wallet.running) app().fetchBalance(assetID)\n } else Doc.hide(tmpl.expired)\n }\n\n /* updateParent updates the side's parent asset balance. */\n updateParent (side: BalanceWidgetElement) {\n const { wallet: { balance }, unitInfo } = app().assets[side.parentID]\n if (side.parentBal) side.parentBal.textContent = Doc.formatCoinValue(balance.available, unitInfo)\n }\n\n /*\n * updateAsset updates the info for one side of the existing market. If the\n * specified asset ID is not one of the current market's base or quote assets,\n * it is silently ignored.\n */\n updateAsset (assetID: number) {\n if (assetID === this.base.id) this.updateWallet(this.base)\n else if (assetID === this.quote.id) this.updateWallet(this.quote)\n if (assetID === this.base.parentID) this.updateParent(this.base)\n if (assetID === this.quote.parentID) this.updateParent(this.quote)\n }\n}\n\n/* makeMarket creates a market object that specifies basic market details. */\nfunction makeMarket (host: string, base?: number, quote?: number) {\n return {\n host: host,\n base: base,\n quote: quote\n }\n}\n\n/* marketID creates a DEX-compatible market name from the ticker symbols. */\nexport function marketID (b: string, q: string) { return `${b}_${q}` }\n\n/* convertToAtoms converts the float string to the basic unit of a coin. */\nfunction convertToAtoms (s: string, conversionFactor: number) {\n if (!s) return 0\n return Math.round(parseFloat(s) * conversionFactor)\n}\n\n/* swapBttns changes the 'selected' class of the buttons. */\nfunction swapBttns (before: HTMLElement, now: HTMLElement) {\n before.classList.remove('selected')\n now.classList.add('selected')\n}\n\n/*\n * wireOrder prepares a copy of the order with the options field converted to a\n * string -> string map.\n */\nfunction wireOrder (order: TradeForm) {\n const stringyOptions: Record = {}\n for (const [k, v] of Object.entries(order.options)) stringyOptions[k] = JSON.stringify(v)\n return Object.assign({}, order, { options: stringyOptions })\n}\n\n// OrderTableRowManager manages the data within a row in an order table. Each row\n// represents all the orders in the order book with the same rate, but orders that\n// are booked or still in the epoch queue are displayed in separate rows.\nclass OrderTableRowManager {\n tableRow: HTMLElement\n orderBin: MiniOrder[]\n sell: boolean\n msgRate: number\n epoch: boolean\n baseUnitInfo: UnitInfo\n rateConversionFactor: number\n\n constructor (tableRow: HTMLElement, orderBin: MiniOrder[], baseUnitInfo: UnitInfo, rateConversionFactor: number) {\n this.tableRow = tableRow\n this.orderBin = orderBin\n this.sell = orderBin[0].sell\n this.msgRate = orderBin[0].msgRate\n this.epoch = !!orderBin[0].epoch\n this.baseUnitInfo = baseUnitInfo\n this.rateConversionFactor = rateConversionFactor\n this.setRateEl()\n this.setEpochEl()\n this.updateQtyNumOrdersEl()\n }\n\n // setEpochEl displays a checkmark in the row if the orders represented by\n // this row are in the epoch queue.\n setEpochEl () {\n const epochEl = Doc.tmplElement(this.tableRow, 'epoch')\n if (this.isEpoch()) epochEl.appendChild(check.cloneNode())\n }\n\n // setRateEl popuplates the rate element in the row.\n setRateEl () {\n const rateEl = Doc.tmplElement(this.tableRow, 'rate')\n if (this.msgRate === 0) {\n rateEl.innerText = 'market'\n } else {\n const cssClass = this.isSell() ? 'sellcolor' : 'buycolor'\n rateEl.innerText = Doc.formatFullPrecision(this.msgRate / this.rateConversionFactor)\n rateEl.classList.add(cssClass)\n }\n }\n\n // updateQtyNumOrdersEl populates the quantity element in the row, and also\n // displays the number of orders if there is more than one order in the order\n // bin.\n updateQtyNumOrdersEl () {\n const qty = this.orderBin.reduce((total, curr) => total + curr.qtyAtomic, 0)\n const numOrders = this.orderBin.length\n const qtyEl = Doc.tmplElement(this.tableRow, 'qty')\n const numOrdersEl = Doc.tmplElement(this.tableRow, 'numorders')\n qtyEl.innerText = Doc.formatFullPrecision(qty, this.baseUnitInfo)\n if (numOrders > 1) {\n numOrdersEl.removeAttribute('hidden')\n numOrdersEl.innerText = String(numOrders)\n numOrdersEl.title = `quantity is comprised of ${numOrders} orders`\n } else {\n numOrdersEl.setAttribute('hidden', 'true')\n }\n }\n\n // insertOrder adds an order to the order bin and updates the row elements\n // accordingly.\n insertOrder (order: MiniOrder) {\n this.orderBin.push(order)\n this.updateQtyNumOrdersEl()\n }\n\n // updateOrderQuantity updates the quantity of the order identified by a token,\n // if it exists in the row, and updates the row elements accordingly. The function\n // returns true if the order is in the bin, and false otherwise.\n updateOrderQty (update: RemainderUpdate) {\n const { token, qty, qtyAtomic } = update\n for (let i = 0; i < this.orderBin.length; i++) {\n if (this.orderBin[i].token === token) {\n this.orderBin[i].qty = qty\n this.orderBin[i].qtyAtomic = qtyAtomic\n this.updateQtyNumOrdersEl()\n return true\n }\n }\n return false\n }\n\n // removeOrder removes the order identified by the token, if it exists in the row,\n // and updates the row elements accordingly. If the order bin is empty, the row is\n // removed from the screen. The function returns true if an order was removed, and\n // false otherwise.\n removeOrder (token: string) {\n const index = this.orderBin.findIndex(order => order.token === token)\n if (index < 0) return false\n this.orderBin.splice(index, 1)\n if (!this.orderBin.length) this.tableRow.remove()\n else this.updateQtyNumOrdersEl()\n return true\n }\n\n // removeEpochOrders removes all the orders from the row that are not in the\n // new epoch's epoch queue and updates the elements accordingly.\n removeEpochOrders (newEpoch?: number) {\n this.orderBin = this.orderBin.filter((order) => {\n return !(order.epoch && order.epoch !== newEpoch)\n })\n if (!this.orderBin.length) this.tableRow.remove()\n else this.updateQtyNumOrdersEl()\n }\n\n // getRate returns the rate of the orders in the row.\n getRate () {\n return this.msgRate\n }\n\n // isEpoch returns whether the orders in this row are in the epoch queue.\n isEpoch () {\n return this.epoch\n }\n\n // isSell returns whether the orders in this row are sell orders.\n isSell () {\n return this.sell\n }\n\n // compare takes an order and returns 0 if the order belongs in this row,\n // 1 if the order should go after this row in the table, and -1 if it should\n // be before this row in the table. Sell orders are displayed in ascending order,\n // buy orders are displayed in descending order, and epoch orders always come\n // after booked orders.\n compare (order: MiniOrder) {\n if (this.getRate() === order.msgRate && this.isEpoch() === !!order.epoch) {\n return 0\n } else if (this.getRate() !== order.msgRate) {\n return (this.getRate() > order.msgRate) === order.sell ? 1 : -1\n } else {\n return this.isEpoch() ? 1 : -1\n }\n }\n}\n\ninterface ExchangeMarket extends Market {\n xc: Exchange\n baseName: string\n bui: UnitInfo\n}\n\nfunction sortedMarkets (): ExchangeMarket[] {\n const mkts: ExchangeMarket[] = []\n const assets = app().assets\n const convertMarkets = (xc: Exchange, mkts: Market[]) => {\n return mkts.map((mkt: Market) => {\n const a = assets[mkt.baseid]\n const baseName = a ? a.name : mkt.basesymbol\n const bui = app().unitInfo(mkt.baseid, xc)\n return Object.assign({ xc, baseName, bui }, mkt)\n })\n }\n for (const xc of Object.values(app().exchanges)) mkts.push(...convertMarkets(xc, Object.values(xc.markets || {})))\n mkts.sort((a: ExchangeMarket, b: ExchangeMarket): number => {\n if (!a.spot) {\n if (b.spot) return 1 // put b first, since we have the spot\n // no spots. compare market name then host name\n if (a.name === b.name) return a.xc.host.localeCompare(b.xc.host)\n return a.name.localeCompare(b.name)\n } else if (!b.spot) return -1 // put a first, since we have the spot\n const [aLots, bLots] = [a.spot.vol24 / a.lotsize, b.spot.vol24 / b.lotsize]\n return bLots - aLots // whoever has more volume by lot count\n })\n return mkts\n}\n\nconst FourSigFigs = new Intl.NumberFormat((navigator.languages as string[]), {\n maximumSignificantDigits: 4\n})\n\nconst OneFractionalDigit = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1\n})\n\nfunction fourSigFigs (v: number) {\n if (v >= 1000 || Math.round(v) === v) return OneFractionalDigit.format(v)\n return FourSigFigs.format(v)\n}\n\nfunction setPriceAndChange (tmpl: Record, xc: Exchange, mkt: Market) {\n if (!mkt.spot) return\n tmpl.price.textContent = fourSigFigs(app().conventionalRate(mkt.baseid, mkt.quoteid, mkt.spot.rate, xc))\n const sign = mkt.spot.change24 > 0 ? '+' : ''\n tmpl.change.classList.remove('buycolor', 'sellcolor')\n tmpl.change.classList.add(mkt.spot.change24 >= 0 ? 'buycolor' : 'sellcolor')\n tmpl.change.textContent = `${sign}${(mkt.spot.change24 * 100).toFixed(1)}%`\n}\n\nconst hues = [1 / 2, 1 / 4, 3 / 4, 1 / 8, 5 / 8, 3 / 8, 7 / 8]\n\nfunction generateHue (idx: number): string {\n const h = hues[idx % hues.length]\n return `hsl(${h * 360}, 35%, 50%)`\n}\n\nfunction hostColor (host: string): string {\n const hosts = Object.keys(app().exchanges)\n hosts.sort()\n return generateHue(hosts.indexOf(host))\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport * as OrderUtil from './orderutil'\nimport * as intl from './locales'\nimport { postJSON } from './http'\nimport {\n app,\n PageElement,\n OrderFilter,\n Order\n} from './registry'\n\nconst orderBatchSize = 50\nconst animationLength = 500\n\nexport default class OrdersPage extends BasePage {\n main: HTMLElement\n offset: string\n loading: boolean\n currentForm: PageElement\n orderTmpl: PageElement\n filterState: OrderFilter\n page: Record\n\n constructor (main: HTMLElement) {\n super()\n this.main = main\n // if offset is '', there are no more orders available to auto-load for\n // never-ending scrolling.\n this.offset = ''\n this.loading = false\n const page = this.page = Doc.idDescendants(main)\n this.orderTmpl = page.rowTmpl\n this.orderTmpl.remove()\n\n // filterState will store arrays of strings. The assets and statuses\n // sub-filters will need to be converted to ints for JSON encoding.\n const filterState: OrderFilter = this.filterState = {\n hosts: [],\n assets: [],\n statuses: []\n }\n\n const search = new URLSearchParams(window.location.search)\n const readFilter = (form: HTMLElement, filterKey: string) => {\n const v = search.get(filterKey)\n if (!v || v.length === 0) return\n const subFilter = v.split(',')\n if (v) {\n (filterState as any)[filterKey] = subFilter // Kinda janky\n }\n form.querySelectorAll('input').forEach(bttn => {\n if (subFilter.indexOf(bttn.value) >= 0) bttn.checked = true\n })\n }\n readFilter(page.hostFilter, 'hosts')\n readFilter(page.assetFilter, 'assets')\n readFilter(page.statusFilter, 'statuses')\n\n const applyButtons: HTMLElement[] = []\n const monitorFilter = (form: HTMLElement, filterKey: string) => {\n const applyBttn = form.querySelector('.apply-bttn') as HTMLElement\n applyButtons.push(applyBttn)\n Doc.bind(applyBttn, 'click', () => {\n this.submitFilter()\n applyButtons.forEach(bttn => Doc.hide(bttn))\n })\n form.querySelectorAll('input').forEach(bttn => {\n Doc.bind(bttn, 'change', () => {\n const subFilter = parseSubFilter(form)\n if (compareSubFilter(subFilter, (filterState as any)[filterKey])) {\n // Same as currently loaded. Hide the apply button.\n Doc.hide(applyBttn)\n } else {\n Doc.show(applyBttn)\n }\n })\n })\n }\n\n monitorFilter(page.hostFilter, 'hosts')\n monitorFilter(page.assetFilter, 'assets')\n monitorFilter(page.statusFilter, 'statuses')\n\n Doc.bind(this.main, 'scroll', () => {\n if (this.loading) return\n const belowBottom = page.ordersTable.offsetHeight - this.main.offsetHeight - this.main.scrollTop\n if (belowBottom < 0) {\n this.nextPage()\n }\n })\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => {\n Doc.hide(page.forms)\n })\n })\n\n // If the user clicks outside of a form, it should close the page overlay.\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) {\n Doc.hide(page.forms)\n }\n })\n\n Doc.bind(page.exportOrders, 'click', () => {\n this.exportOrders()\n })\n\n page.showArchivedDateField.addEventListener('change', () => {\n if (page.showArchivedDateField.checked) Doc.show(page.archivedDateField)\n else Doc.hide(page.archivedDateField, page.deleteArchivedRecordsErr)\n })\n\n Doc.bind(page.deleteArchivedRecords, 'click', () => {\n const page = this.page\n page.showArchivedDateField.checked = false\n page.saveMatchesToFile.checked = false\n page.saveOrdersToFile.checked = false\n page.deleteArchivedRecordsErr.textContent = ''\n page.archivedRecordsLocation.textContent = ''\n page.deleteArchivedRecordsMsg.textContent = ''\n Doc.hide(page.deleteArchivedResult, page.deleteArchivedRecordsErr,\n page.deleteArchivedRecordsMsg, page.archivedRecordsLocation, page.archivedDateField)\n this.showForm(page.deleteArchivedRecordsForm)\n })\n\n Doc.bind(page.deleteArchivedRecordsSubmit, 'click', () => {\n let date = 0\n if (page.showArchivedDateField.checked) {\n date = Date.parse(page.olderThan.value || '')\n if (isNaN(date) || date <= 0) {\n Doc.showFormError(page.deleteArchivedRecordsErr, intl.prep(intl.ID_INVALID_DATE_ERR_MSG))\n return\n }\n }\n this.deleteArchivedRecords(date)\n })\n\n this.submitFilter()\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n this.currentForm = form\n const page = this.page\n Doc.hide(page.deleteArchivedRecordsForm)\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0px'\n }\n\n /* setOrders empties the order table and appends the specified orders. */\n setOrders (orders: Order[]) {\n Doc.empty(this.page.tableBody)\n this.appendOrders(orders)\n }\n\n /* appendOrders appends orders to the orders table. */\n appendOrders (orders: Order[]) {\n const tbody = this.page.tableBody\n for (const ord of orders) {\n const tr = this.orderTmpl.cloneNode(true) as HTMLElement\n const set = (tmplID: string, s: string) => { Doc.tmplElement(tr, tmplID).textContent = s }\n const mktID = `${ord.baseSymbol.toUpperCase()}-${ord.quoteSymbol.toUpperCase()}`\n set('host', `${mktID} @ ${ord.host}`)\n let from, to, fromQty\n let toQty = ''\n const [baseUnitInfo, quoteUnitInfo] = [app().unitInfo(ord.baseID), app().unitInfo(ord.quoteID)]\n if (ord.sell) {\n [from, to] = [ord.baseSymbol, ord.quoteSymbol]\n fromQty = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n if (ord.type === OrderUtil.Limit) {\n toQty = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n }\n } else {\n [from, to] = [ord.quoteSymbol, ord.baseSymbol]\n if (ord.type === OrderUtil.Market) {\n fromQty = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n } else {\n fromQty = Doc.formatCoinValue(ord.qty / OrderUtil.RateEncodingFactor * ord.rate, quoteUnitInfo)\n toQty = Doc.formatCoinValue(ord.qty, baseUnitInfo)\n }\n }\n\n set('fromQty', fromQty)\n Doc.tmplElement(tr, 'fromLogo').src = Doc.logoPath(from)\n set('fromSymbol', from)\n set('toQty', toQty)\n Doc.tmplElement(tr, 'toLogo').src = Doc.logoPath(to)\n set('toSymbol', to)\n set('type', `${OrderUtil.typeString(ord)} ${OrderUtil.sellString(ord)}`)\n set('rate', Doc.formatCoinValue(app().conventionalRate(ord.baseID, ord.quoteID, ord.rate)))\n set('status', OrderUtil.statusString(ord))\n set('filled', `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`)\n set('settled', `${(OrderUtil.settled(ord) / ord.qty * 100).toFixed(1)}%`)\n const dateTime = new Date(ord.submitTime).toLocaleString()\n set('time', `${Doc.timeSince(ord.submitTime)} ago, ${dateTime}`)\n const link = Doc.tmplElement(tr, 'link')\n link.href = `order/${ord.id}`\n app().bindInternalNavigation(tr)\n tbody.appendChild(tr)\n }\n if (orders.length === orderBatchSize) {\n this.offset = orders[orders.length - 1].id\n } else {\n this.offset = ''\n }\n }\n\n /* submitFilter submits the current filter and reloads the order table. */\n async submitFilter () {\n const page = this.page\n this.offset = ''\n const filterState = this.filterState\n filterState.hosts = parseSubFilter(page.hostFilter)\n filterState.assets = parseSubFilter(page.assetFilter).map((s: string) => parseInt(s))\n filterState.statuses = parseSubFilter(page.statusFilter).map((s: string) => parseInt(s))\n this.setOrders(await this.fetchOrders())\n }\n\n /* fetchOrders fetches orders using the current filter. */\n async fetchOrders () {\n const loaded = app().loading(this.main)\n const res = await postJSON('/api/orders', this.currentFilter())\n loaded()\n return res.orders\n }\n\n /* exportOrders downloads a csv of the user's orders based on the current filter. */\n exportOrders () {\n this.offset = ''\n const filterState = this.currentFilter()\n const url = new URL(window.location.href)\n const search = new URLSearchParams('')\n const setQuery = (k: string) => {\n const subFilter = (filterState as any)[k]\n subFilter.forEach((v: any) => {\n search.append(k, v)\n })\n }\n setQuery('hosts')\n setQuery('assets')\n setQuery('statuses')\n url.search = search.toString()\n url.pathname = '/orders/export'\n window.open(url.toString())\n }\n\n /* deleteArchivedRecords removes the user's archived orders and matches\n * created before user specified date time in millisecond. Deleted archived\n * records are saved to a CSV file if the user specify so.\n */\n async deleteArchivedRecords (olderThanMs?: number) {\n const page = this.page\n const saveMatchesToFIle = page.saveMatchesToFile.checked || false\n const saveOrdersToFile = page.saveOrdersToFile.checked || false\n const reqBody = {\n olderThanMs: olderThanMs,\n saveMatchesToFile: saveMatchesToFIle,\n saveOrdersToFile: saveOrdersToFile\n }\n const loaded = app().loading(this.main)\n const res = await postJSON('/api/deletearchivedrecords', reqBody)\n loaded()\n if (!app().checkResponse(res)) {\n return Doc.showFormError(page.deleteArchivedRecordsErr, res.msg)\n }\n\n if (res.archivedRecordsDeleted > 0) {\n page.deleteArchivedRecordsMsg.textContent = intl.prep(intl.ID_DELETE_ARCHIVED_RECORDS_RESULT, { nRecords: res.archivedRecordsDeleted })\n if (saveMatchesToFIle || saveOrdersToFile) {\n page.archivedRecordsLocation.textContent = intl.prep(intl.ID_ARCHIVED_RECORDS_PATH, { path: res.archivedRecordsPath })\n Doc.show(page.archivedRecordsLocation)\n }\n // Update the order page.\n this.submitFilter()\n } else {\n page.deleteArchivedRecordsMsg.textContent = intl.prep(intl.ID_NO_ARCHIVED_RECORDS)\n }\n Doc.show(page.deleteArchivedResult, page.deleteArchivedRecordsMsg)\n }\n\n /*\n * currentFilter converts the local filter type (which is all strings) to the\n * server's filter type.\n */\n currentFilter (): OrderFilter {\n const filterState = this.filterState\n return {\n hosts: filterState.hosts,\n assets: filterState.assets.map((s: any) => parseInt(s)),\n statuses: filterState.statuses.map((s: any) => parseInt(s)),\n n: orderBatchSize,\n offset: this.offset\n }\n }\n\n /*\n * nextPage resubmits the filter with the offset set to the last loaded order.\n */\n async nextPage () {\n if (this.offset === '' || this.loading) return\n this.loading = true\n Doc.show(this.page.orderLoader)\n const orders = await this.fetchOrders()\n this.loading = false\n Doc.hide(this.page.orderLoader)\n this.appendOrders(orders)\n }\n}\n\n/*\n * parseSubFilter parses a bool-map from the checkbox inputs in the specified\n * ancestor element.\n */\nfunction parseSubFilter (form: HTMLElement): string[] {\n const entries: string[] = []\n form.querySelectorAll('input').forEach(box => {\n if (box.checked) entries.push(box.value)\n })\n return entries\n}\n\n/* compareSubFilter compares the two filter arrays for unordered equivalence. */\nfunction compareSubFilter (filter1: any[], filter2: any[]): boolean {\n if (filter1.length !== filter2.length) return false\n for (const entry of filter1) {\n if (filter2.indexOf(entry) === -1) return false\n }\n return true\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport * as OrderUtil from './orderutil'\nimport { bind as bindForm, AccelerateOrderForm } from './forms'\nimport { postJSON } from './http'\nimport * as intl from './locales'\nimport {\n app,\n Order,\n PageElement,\n OrderNote,\n MatchNote,\n Match,\n Coin\n} from './registry'\nimport { setOptionTemplates } from './opts'\n\nconst Mainnet = 0\nconst Testnet = 1\n// const Regtest = 3\n\nconst coinIDTakerFoundMakerRedemption = 'TakerFoundMakerRedemption:'\n\nconst animationLength = 500\n\nlet net: number\n\nexport default class OrderPage extends BasePage {\n orderID: string\n order: Order\n page: Record\n currentForm: HTMLElement\n secondTicker: number\n refreshOnPopupClose: boolean\n accelerateOrderForm: AccelerateOrderForm\n stampers: PageElement[]\n\n constructor (main: HTMLElement) {\n super()\n const page = this.page = Doc.idDescendants(main)\n this.stampers = Doc.applySelector(main, '[data-stamp]')\n net = parseInt(main.dataset.net || '')\n // Find the order\n this.orderID = main.dataset.oid || ''\n\n Doc.cleanTemplates(page.matchCardTmpl)\n\n const setStamp = () => {\n for (const span of this.stampers) {\n span.textContent = Doc.timeSince(parseInt(span.dataset.stamp || ''))\n }\n }\n setStamp()\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => {\n if (this.refreshOnPopupClose) {\n window.location.replace(window.location.href)\n return\n }\n Doc.hide(page.forms)\n })\n })\n\n // Some static elements on this page contain assets that can be linked\n // to blockchain explorers (such as Etherscan) so users can easily\n // examine funding/acceleration coins data there. We'd need to set up\n // such hyperlinks here.\n main.querySelectorAll('[data-explorer-id]').forEach((link: PageElement) => {\n const assetID = parseInt(link.dataset.explorerId || '')\n setCoinHref(assetID, link)\n })\n\n if (page.cancelBttn) {\n Doc.bind(page.cancelBttn, 'click', () => {\n this.showForm(page.cancelForm)\n })\n }\n\n Doc.bind(page.accelerateBttn, 'click', () => {\n this.showAccelerateForm()\n })\n\n const success = () => {\n this.refreshOnPopupClose = true\n }\n // Do not call cleanTemplates before creating the AccelerateOrderForm\n setOptionTemplates(page)\n this.accelerateOrderForm = new AccelerateOrderForm(page.accelerateForm, success)\n Doc.cleanTemplates(page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl)\n\n // If the user clicks outside of a form, it should close the page overlay.\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) {\n if (this.refreshOnPopupClose) {\n window.location.reload()\n return\n }\n Doc.hide(page.forms)\n page.cancelPass.value = ''\n }\n })\n\n // Cancel order form\n bindForm(page.cancelForm, page.cancelSubmit, async () => { this.submitCancel() })\n\n this.secondTicker = window.setInterval(() => {\n setStamp()\n }, 10000) // update every 10 seconds\n\n app().registerNoteFeeder({\n order: (note: OrderNote) => { this.handleOrderNote(note) },\n match: (note: MatchNote) => { this.handleMatchNote(note) }\n })\n\n this.start()\n }\n\n async start () {\n let ord = app().order(this.orderID)\n // app().order can only access active orders. If the order is not active,\n // we'll need to get the data from the database.\n if (ord) this.order = ord\n else {\n ord = await this.fetchOrder()\n }\n\n // Swap out the dot-notation symbols with token-aware symbols.\n this.page.mktBaseSymbol.replaceWith(Doc.symbolize(ord.baseSymbol))\n this.page.mktQuoteSymbol.replaceWith(Doc.symbolize(ord.quoteSymbol))\n\n this.setAccelerationButtonVis()\n this.showMatchCards()\n }\n\n unload () {\n clearInterval(this.secondTicker)\n }\n\n /* fetchOrder fetches the order from the client. */\n async fetchOrder (): Promise {\n const res = await postJSON('/api/order', this.orderID)\n if (!app().checkResponse(res)) throw res.msg\n this.order = res.order\n return this.order\n }\n\n /*\n * setImmutableMatchCardElements sets the match card elements that are never\n * changed.\n */\n setImmutableMatchCardElements (matchCard: HTMLElement, match: Match) {\n const tmpl = Doc.parseTemplate(matchCard)\n\n tmpl.matchID.textContent = match.matchID\n\n const time = new Date(match.stamp)\n tmpl.matchTime.textContent = time.toLocaleTimeString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric'\n })\n\n tmpl.matchTimeAgo.dataset.stamp = match.stamp.toString()\n tmpl.matchTimeAgo.textContent = Doc.timeSince(match.stamp)\n this.stampers.push(tmpl.matchTimeAgo)\n\n const orderPortion = OrderUtil.orderPortion(this.order, match)\n const baseSymbol = Doc.bipSymbol(this.order.baseID)\n const quoteSymbol = Doc.bipSymbol(this.order.quoteID)\n const baseUnitInfo = app().unitInfo(this.order.baseID)\n const quoteUnitInfo = app().unitInfo(this.order.quoteID)\n const quoteAmount = OrderUtil.baseToQuote(match.rate, match.qty)\n\n if (match.isCancel) {\n Doc.show(tmpl.cancelInfoDiv)\n Doc.hide(tmpl.infoDiv, tmpl.status, tmpl.statusHdr)\n\n if (this.order.sell) {\n tmpl.cancelAmount.textContent = Doc.formatCoinValue(match.qty, baseUnitInfo)\n tmpl.cancelIcon.src = Doc.logoPathFromID(this.order.baseID)\n } else {\n tmpl.cancelAmount.textContent = Doc.formatCoinValue(quoteAmount, quoteUnitInfo)\n tmpl.cancelIcon.src = Doc.logoPathFromID(this.order.quoteID)\n }\n\n tmpl.cancelOrderPortion.textContent = orderPortion\n\n return\n }\n\n Doc.show(tmpl.infoDiv)\n Doc.hide(tmpl.cancelInfoDiv)\n\n tmpl.orderPortion.textContent = orderPortion\n\n if (match.side === OrderUtil.Maker) {\n tmpl.side.textContent = intl.prep(intl.ID_MAKER)\n Doc.show(\n tmpl.makerSwapYou,\n tmpl.makerRedeemYou,\n tmpl.takerSwapThem,\n tmpl.takerRedeemThem\n )\n Doc.hide(\n tmpl.takerSwapYou,\n tmpl.takerRedeemYou,\n tmpl.makerSwapThem,\n tmpl.makerRedeemThem\n )\n } else {\n tmpl.side.textContent = intl.prep(intl.ID_TAKER)\n Doc.hide(\n tmpl.makerSwapYou,\n tmpl.makerRedeemYou,\n tmpl.takerSwapThem,\n tmpl.takerRedeemThem\n )\n Doc.show(\n tmpl.takerSwapYou,\n tmpl.takerRedeemYou,\n tmpl.makerSwapThem,\n tmpl.makerRedeemThem\n )\n }\n\n if ((match.side === OrderUtil.Maker && this.order.sell) ||\n (match.side === OrderUtil.Taker && !this.order.sell)) {\n tmpl.makerSwapAsset.textContent = baseSymbol\n tmpl.takerSwapAsset.textContent = quoteSymbol\n tmpl.makerRedeemAsset.textContent = quoteSymbol\n tmpl.takerRedeemAsset.textContent = baseSymbol\n } else {\n tmpl.makerSwapAsset.textContent = quoteSymbol\n tmpl.takerSwapAsset.textContent = baseSymbol\n tmpl.makerRedeemAsset.textContent = baseSymbol\n tmpl.takerRedeemAsset.textContent = quoteSymbol\n }\n\n const rate = app().conventionalRate(this.order.baseID, this.order.quoteID, match.rate)\n tmpl.rate.textContent = `${rate} ${baseSymbol}/${quoteSymbol}`\n\n if (this.order.sell) {\n tmpl.refundAsset.textContent = baseSymbol\n tmpl.fromAmount.textContent = Doc.formatCoinValue(match.qty, baseUnitInfo)\n tmpl.toAmount.textContent = Doc.formatCoinValue(quoteAmount, quoteUnitInfo)\n tmpl.fromIcon.src = Doc.logoPathFromID(this.order.baseID)\n tmpl.toIcon.src = Doc.logoPathFromID(this.order.quoteID)\n } else {\n tmpl.refundAsset.textContent = quoteSymbol\n tmpl.fromAmount.textContent = Doc.formatCoinValue(quoteAmount, quoteUnitInfo)\n tmpl.toAmount.textContent = Doc.formatCoinValue(match.qty, baseUnitInfo)\n tmpl.fromIcon.src = Doc.logoPathFromID(this.order.quoteID)\n tmpl.toIcon.src = Doc.logoPathFromID(this.order.baseID)\n }\n }\n\n /*\n * setMutableMatchCardElements sets the match card elements which may get\n * updated on each update to the match.\n */\n setMutableMatchCardElements (matchCard: HTMLElement, m: Match) {\n if (m.isCancel) {\n return\n }\n\n const tmpl = Doc.parseTemplate(matchCard)\n tmpl.status.textContent = OrderUtil.matchStatusString(m)\n\n const setCoin = (pendingName: string, linkName: string, coin: Coin) => {\n const formatCoinID = (cid: string) => {\n if (cid.startsWith(coinIDTakerFoundMakerRedemption)) {\n const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length)\n return intl.prep(intl.ID_TAKER_FOUND_MAKER_REDEMPTION, { makerAddr: makerAddr })\n }\n return cid\n }\n const coinLink = tmpl[linkName]\n const pendingSpan = tmpl[pendingName]\n if (!coin) {\n Doc.show(tmpl[pendingName])\n Doc.hide(tmpl[linkName])\n return\n }\n coinLink.textContent = formatCoinID(coin.stringID)\n coinLink.dataset.explorerCoin = coin.stringID\n setCoinHref(coin.assetID, coinLink)\n Doc.hide(pendingSpan)\n Doc.show(coinLink)\n }\n\n setCoin('makerSwapPending', 'makerSwapCoin', makerSwapCoin(m))\n setCoin('takerSwapPending', 'takerSwapCoin', takerSwapCoin(m))\n setCoin('makerRedeemPending', 'makerRedeemCoin', makerRedeemCoin(m))\n setCoin('takerRedeemPending', 'takerRedeemCoin', takerRedeemCoin(m))\n setCoin('refundPending', 'refundCoin', m.refund)\n\n if (m.status === OrderUtil.MakerSwapCast && !m.revoked && !m.refund) {\n const c = makerSwapCoin(m)\n tmpl.makerSwapMsg.textContent = confirmationString(c)\n Doc.hide(tmpl.takerSwapMsg, tmpl.makerRedeemMsg, tmpl.takerRedeemMsg)\n Doc.show(tmpl.makerSwapMsg)\n } else if (m.status === OrderUtil.TakerSwapCast && !m.revoked && !m.refund) {\n const c = takerSwapCoin(m)\n tmpl.takerSwapMsg.textContent = confirmationString(c)\n Doc.hide(tmpl.makerSwapMsg, tmpl.makerRedeemMsg, tmpl.takerRedeemMsg)\n Doc.show(tmpl.takerSwapMsg)\n } else if (inConfirmingMakerRedeem(m) && !m.revoked && !m.refund) {\n tmpl.makerRedeemMsg.textContent = confirmationString(m.redeem)\n Doc.hide(tmpl.makerSwapMsg, tmpl.takerSwapMsg, tmpl.takerRedeemMsg)\n Doc.show(tmpl.makerRedeemMsg)\n } else if (inConfirmingTakerRedeem(m) && !m.revoked && !m.refund) {\n tmpl.takerRedeemMsg.textContent = confirmationString(m.redeem)\n Doc.hide(tmpl.makerSwapMsg, tmpl.takerSwapMsg, tmpl.makerRedeemMsg)\n Doc.show(tmpl.takerRedeemMsg)\n } else {\n Doc.hide(tmpl.makerSwapMsg, tmpl.takerSwapMsg, tmpl.makerRedeemMsg, tmpl.takerRedeemMsg)\n }\n\n Doc.setVis(!m.isCancel && (makerSwapCoin(m) || !m.revoked), tmpl.makerSwap)\n Doc.setVis(!m.isCancel && (takerSwapCoin(m) || !m.revoked), tmpl.takerSwap)\n Doc.setVis(!m.isCancel && (makerRedeemCoin(m) || !m.revoked), tmpl.makerRedeem)\n // When revoked, there is uncertainty about the taker redeem coin. The taker\n // redeem may be needed if maker redeems while taker is waiting to refund.\n Doc.setVis(!m.isCancel && (takerRedeemCoin(m) || (!m.revoked && m.active) || ((m.side === OrderUtil.Taker) && m.active && (m.counterRedeem || !m.refund))), tmpl.takerRedeem)\n // The refund placeholder should not be shown if there is a counter redeem.\n Doc.setVis(!m.isCancel && (m.refund || (m.revoked && m.active && !m.counterRedeem)), tmpl.refund)\n }\n\n /*\n * addNewMatchCard adds a new card to the list of match cards.\n */\n addNewMatchCard (match: Match) {\n const page = this.page\n const matchCard = page.matchCardTmpl.cloneNode(true) as HTMLElement\n matchCard.dataset.matchID = match.matchID\n this.setImmutableMatchCardElements(matchCard, match)\n this.setMutableMatchCardElements(matchCard, match)\n page.matchBox.appendChild(matchCard)\n }\n\n /*\n * showMatchCards creates cards for each match in the order.\n */\n showMatchCards () {\n const order = this.order\n if (!order) return\n if (!order.matches) return\n order.matches.sort((a, b) => a.stamp - b.stamp)\n order.matches.forEach((match) => this.addNewMatchCard(match))\n }\n\n /* showCancel shows a form to confirm submission of a cancel order. */\n showCancel () {\n const order = this.order\n const page = this.page\n const remaining = order.qty - order.filled\n const asset = OrderUtil.isMarketBuy(order) ? app().assets[order.quoteID] : app().assets[order.baseID]\n page.cancelRemain.textContent = Doc.formatCoinValue(remaining, asset.unitInfo)\n page.cancelUnit.textContent = asset.unitInfo.conventional.unit.toUpperCase()\n this.showForm(page.cancelForm)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n this.currentForm = form\n const page = this.page\n Doc.hide(page.cancelForm, page.accelerateForm)\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0px'\n }\n\n /* submitCancel submits a cancellation for the order. */\n async submitCancel () {\n // this will be the page.cancelSubmit button (evt.currentTarget)\n const page = this.page\n const order = this.order\n const req = {\n orderID: order.id,\n pw: page.cancelPass.value\n }\n page.cancelPass.value = ''\n const loaded = app().loading(page.cancelForm)\n const res = await postJSON('/api/cancel', req)\n loaded()\n if (!app().checkResponse(res)) return\n page.status.textContent = intl.prep(intl.ID_CANCELING)\n Doc.hide(page.forms)\n order.cancelling = true\n }\n\n /*\n * setAccelerationButtonVis shows the acceleration button if the order can\n * be accelerated.\n */\n setAccelerationButtonVis () {\n const order = this.order\n if (!order) return\n const page = this.page\n Doc.setVis(app().canAccelerateOrder(order), page.accelerateBttn, page.actionsLabel)\n }\n\n /* showAccelerateForm shows a form to accelerate an order */\n async showAccelerateForm () {\n const loaded = app().loading(this.page.accelerateBttn)\n this.accelerateOrderForm.refresh(this.order)\n loaded()\n this.showForm(this.page.accelerateForm)\n }\n\n /*\n * handleOrderNote is the handler for the 'order'-type notification, which are\n * used to update an order's status.\n */\n handleOrderNote (note: OrderNote) {\n const page = this.page\n const order = note.order\n if (order.id !== this.orderID) return\n this.order = order\n const bttn = page.cancelBttn\n if (bttn && order.status > OrderUtil.StatusBooked) Doc.hide(bttn)\n page.status.textContent = OrderUtil.statusString(order)\n for (const m of order.matches || []) this.processMatch(m)\n this.setAccelerationButtonVis()\n }\n\n /* handleMatchNote handles a 'match' notification. */\n handleMatchNote (note: MatchNote) {\n if (note.orderID !== this.orderID) return\n this.processMatch(note.match)\n this.setAccelerationButtonVis()\n }\n\n /*\n * processMatch synchronizes a match's card with a match received in a\n * 'order' or 'match' notification.\n */\n processMatch (m: Match) {\n let card: HTMLElement | null = null\n for (const div of Doc.applySelector(this.page.matchBox, '.match-card')) {\n if (div.dataset.matchID === m.matchID) {\n card = div\n break\n }\n }\n if (card) {\n this.setMutableMatchCardElements(card, m)\n } else {\n this.addNewMatchCard(m)\n }\n }\n}\n\n/*\n * confirmationString is a string describing the state of confirmations for a\n * coin\n * */\nfunction confirmationString (coin: Coin) {\n if (!coin.confs || coin.confs.required === 0) return ''\n return `${coin.confs.count} / ${coin.confs.required} ${intl.prep(intl.ID_CONFIRMATIONS)}`\n}\n\n// makerSwapCoin return's the maker's swap coin.\nfunction makerSwapCoin (m: Match) : Coin {\n return (m.side === OrderUtil.Maker) ? m.swap : m.counterSwap\n}\n\n// takerSwapCoin return's the taker's swap coin.\nfunction takerSwapCoin (m: Match) {\n return (m.side === OrderUtil.Maker) ? m.counterSwap : m.swap\n}\n\n// makerRedeemCoin return's the maker's redeem coin.\nfunction makerRedeemCoin (m: Match) {\n return (m.side === OrderUtil.Maker) ? m.redeem : m.counterRedeem\n}\n\n// takerRedeemCoin return's the taker's redeem coin.\nfunction takerRedeemCoin (m: Match) {\n return (m.side === OrderUtil.Maker) ? m.counterRedeem : m.redeem\n}\n\n/*\n* inConfirmingMakerRedeem will be true if we are the maker, and we are waiting\n* on confirmations for our own redeem.\n*/\nfunction inConfirmingMakerRedeem (m: Match) {\n return m.status < OrderUtil.MatchConfirmed && m.side === OrderUtil.Maker && m.status >= OrderUtil.MakerRedeemed\n}\n\n/*\n* inConfirmingTakerRedeem will be true if we are the taker, and we are waiting\n* on confirmations for our own redeem.\n*/\nfunction inConfirmingTakerRedeem (m: Match) {\n return m.status < OrderUtil.MatchConfirmed && m.side === OrderUtil.Taker && m.status >= OrderUtil.MatchComplete\n}\n\n/*\n * setCoinHref sets the hyperlink element's href attribute based on provided\n * assetID and data-explorer-coin value present on supplied link element.\n */\nfunction setCoinHref (assetID: number, link: PageElement) {\n const assetExplorer = CoinExplorers[assetID]\n if (!assetExplorer) return\n const formatter = assetExplorer[net]\n if (!formatter) return\n link.classList.remove('plainlink')\n link.classList.add('subtlelink')\n link.href = formatter(link.dataset.explorerCoin || '')\n}\n\nconst ethExplorers: Record string> = {\n [Mainnet]: (cid: string) => {\n if (cid.startsWith(coinIDTakerFoundMakerRedemption)) {\n const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length)\n return `https://etherscan.io/address/${makerAddr}`\n }\n if (cid.length === 42) {\n return `https://etherscan.io/address/${cid}`\n }\n return `https://etherscan.io/tx/${cid}`\n },\n [Testnet]: (cid: string) => {\n if (cid.startsWith(coinIDTakerFoundMakerRedemption)) {\n const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length)\n return `https://goerli.etherscan.io/address/${makerAddr}`\n }\n if (cid.length === 42) {\n return `https://goerli.etherscan.io/address/${cid}`\n }\n return `https://goerli.etherscan.io/tx/${cid}`\n }\n}\n\nconst CoinExplorers: Record string>> = {\n 42: { // dcr\n [Mainnet]: (cid: string) => {\n const [txid, vout] = cid.split(':')\n return `https://explorer.dcrdata.org/tx/${txid}/out/${vout}`\n },\n [Testnet]: (cid: string) => {\n const [txid, vout] = cid.split(':')\n return `https://testnet.dcrdata.org/tx/${txid}/out/${vout}`\n }\n },\n 0: { // btc\n [Mainnet]: (cid: string) => `https://mempool.space/tx/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://mempool.space/testnet/tx/${cid.split(':')[0]}`\n },\n 2: { // ltc\n [Mainnet]: (cid: string) => `https://ltc.bitaps.com/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://sochain.com/tx/LTCTEST/${cid.split(':')[0]}`\n },\n 60: ethExplorers,\n 60001: ethExplorers,\n 3: { // doge\n [Mainnet]: (cid: string) => `https://dogeblocks.com/tx/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://blockexplorer.one/dogecoin/testnet/tx/${cid.split(':')[0]}`\n },\n 145: { // bch\n [Mainnet]: (cid: string) => `https://bch.loping.net/tx/${cid.split(':')[0]}`,\n [Testnet]: (cid: string) => `https://tbch4.loping.net/tx/${cid.split(':')[0]}`\n }\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport State from './state'\nimport { postJSON } from './http'\nimport * as forms from './forms'\nimport * as intl from './locales'\n\nimport {\n app,\n PageElement,\n ConnectionStatus,\n Exchange\n} from './registry'\n\nconst animationLength = 300\n\nexport default class DexSettingsPage extends BasePage {\n body: HTMLElement\n forms: PageElement[]\n currentForm: PageElement\n page: Record\n host: string\n keyup: (e: KeyboardEvent) => void\n dexAddrForm: forms.DEXAddressForm\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n this.host = body.dataset.host ? body.dataset.host : ''\n const page = this.page = Doc.idDescendants(body)\n this.forms = Doc.applySelector(page.forms, ':scope > form')\n\n Doc.bind(page.exportDexBtn, 'click', () => this.prepareAccountExport(page.authorizeAccountExportForm))\n Doc.bind(page.disableAcctBtn, 'click', () => this.prepareAccountDisable(page.disableAccountForm))\n Doc.bind(page.updateBondOptionsBtn, 'click', () => this.prepareUpdateBondOptions())\n Doc.bind(page.updateCertBtn, 'click', () => page.certFileInput.click())\n Doc.bind(page.updateHostBtn, 'click', () => this.prepareUpdateHost())\n Doc.bind(page.certFileInput, 'change', () => this.onCertFileChange())\n\n this.dexAddrForm = new forms.DEXAddressForm(page.dexAddrForm, async (xc: Exchange) => {\n window.location.assign(`/dexsettings/${xc.host}`)\n }, undefined, this.host)\n\n forms.bind(page.updateBondOptionsForm, page.updateBondOptionsConfirm, () => this.updateBondOptions())\n forms.bind(page.authorizeAccountExportForm, page.authorizeExportAccountConfirm, () => this.exportAccount())\n forms.bind(page.disableAccountForm, page.disableAccountConfirm, () => this.disableAccount())\n\n const closePopups = () => {\n Doc.hide(page.forms)\n }\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n closePopups()\n }\n }\n Doc.bind(document, 'keyup', this.keyup)\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { closePopups() })\n })\n\n app().registerNoteFeeder({\n conn: () => { this.setConnectionStatus() }\n })\n\n this.setConnectionStatus()\n }\n\n unload () {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement) {\n const page = this.page\n this.currentForm = form\n this.forms.forEach(form => Doc.hide(form))\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n // exportAccount exports and downloads the account info.\n async exportAccount () {\n const page = this.page\n const pw = page.exportAccountAppPass.value\n const host = page.exportAccountHost.textContent\n page.exportAccountAppPass.value = ''\n const req = {\n pw,\n host\n }\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/exportaccount', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.exportAccountErr.textContent = res.msg\n Doc.show(page.exportAccountErr)\n return\n }\n res.account.bonds = res.bonds // maintain backward compat of JSON file\n const accountForExport = JSON.parse(JSON.stringify(res.account))\n const a = document.createElement('a')\n a.setAttribute('download', 'dcrAccount-' + host + '.json')\n a.setAttribute('href', 'data:text/json,' + JSON.stringify(accountForExport, null, 2))\n a.click()\n Doc.hide(page.forms)\n }\n\n // disableAccount disables the account associated with the provided host.\n async disableAccount () {\n const page = this.page\n const pw = page.disableAccountAppPW.value\n const host = page.disableAccountHost.textContent\n page.disableAccountAppPW.value = ''\n const req = {\n pw,\n host\n }\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/disableaccount', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.disableAccountErr.textContent = res.msg\n Doc.show(page.disableAccountErr)\n return\n }\n Doc.hide(page.forms)\n window.location.assign('/settings')\n }\n\n async prepareAccountExport (authorizeAccountExportForm: HTMLElement) {\n const page = this.page\n page.exportAccountHost.textContent = this.host\n page.exportAccountErr.textContent = ''\n if (State.passwordIsCached()) {\n this.exportAccount()\n } else {\n this.showForm(authorizeAccountExportForm)\n }\n }\n\n async prepareAccountDisable (disableAccountForm: HTMLElement) {\n const page = this.page\n page.disableAccountHost.textContent = this.host\n page.disableAccountErr.textContent = ''\n this.showForm(disableAccountForm)\n }\n\n // prepareUpdateBondOptions resets and prepares the Update Bond Options form.\n async prepareUpdateBondOptions () {\n const page = this.page\n const xc = app().user.exchanges[this.host]\n page.bondTargetTier.setAttribute('placeholder', xc.bondOptions.targetTier.toString())\n Doc.empty(page.bondAssetSelect)\n for (const [assetSymbol, bondAsset] of Object.entries(xc.bondAssets)) {\n const option = document.createElement('option') as HTMLOptionElement\n option.value = bondAsset.id.toString()\n option.textContent = assetSymbol.toUpperCase()\n if (bondAsset.id === xc.bondOptions.bondAsset) option.selected = true\n page.bondAssetSelect.appendChild(option)\n }\n page.bondOptionsErr.textContent = ''\n Doc.hide(page.bondOptionsErr)\n this.showForm(page.updateBondOptionsForm)\n }\n\n async prepareUpdateHost () {\n const page = this.page\n this.dexAddrForm.refresh()\n this.showForm(page.dexAddrForm)\n }\n\n async onCertFileChange () {\n const page = this.page\n Doc.hide(page.errMsg)\n const files = page.certFileInput.files\n let cert\n if (files && files.length) cert = await files[0].text()\n if (!cert) return\n const req = { host: this.host, cert: cert }\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/updatecert', req)\n loaded()\n if (!app().checkResponse(res)) {\n page.errMsg.textContent = res.msg\n Doc.show(page.errMsg)\n } else {\n Doc.show(page.updateCertMsg)\n setTimeout(() => { Doc.hide(page.updateCertMsg) }, 5000)\n }\n }\n\n setConnectionStatus () {\n const page = this.page\n const exchange = app().user.exchanges[this.host]\n const displayIcons = (connected: boolean) => {\n if (connected) {\n Doc.hide(page.disconnectedIcon)\n Doc.show(page.connectedIcon)\n } else {\n Doc.show(page.disconnectedIcon)\n Doc.hide(page.connectedIcon)\n }\n }\n if (exchange) {\n switch (exchange.connectionStatus) {\n case ConnectionStatus.Connected:\n displayIcons(true)\n page.connectionStatus.textContent = intl.prep(intl.ID_CONNECTED)\n break\n case ConnectionStatus.Disconnected:\n displayIcons(false)\n page.connectionStatus.textContent = intl.prep(intl.ID_DISCONNECTED)\n break\n case ConnectionStatus.InvalidCert:\n displayIcons(false)\n page.connectionStatus.textContent = `${intl.prep(intl.ID_DISCONNECTED)} - ${intl.prep(intl.ID_INVALID_CERTIFICATE)}`\n }\n }\n }\n\n /*\n * updateBondOptions is called when the form to update bond options is\n * submitted.\n */\n async updateBondOptions () {\n const page = this.page\n const targetTier = parseInt(page.bondTargetTier.value ?? '')\n const bondAsset = parseInt(page.bondAssetSelect.value ?? '')\n\n const bondOptions = {\n host: this.host,\n targetTier: targetTier,\n bondAsset: bondAsset\n }\n\n const loaded = app().loading(this.body)\n const res = await postJSON('/api/updatebondoptions', bondOptions)\n loaded()\n if (!app().checkResponse(res)) {\n page.bondOptionsErr.textContent = res.msg\n Doc.show(page.bondOptionsErr)\n } else {\n Doc.hide(page.bondOptionsErr)\n Doc.show(page.bondOptionsMsg)\n setTimeout(() => {\n Doc.hide(page.bondOptionsMsg)\n Doc.hide(page.forms)\n }, 5000)\n // update the in-memory values.\n const xc = app().user.exchanges[this.host]\n xc.bondOptions.bondAsset = bondAsset\n xc.bondOptions.targetTier = targetTier\n }\n }\n}\n","import {\n app,\n PageElement,\n Market,\n Exchange,\n OrderOption,\n XYRange,\n BotReport,\n BotNote,\n MaxSell,\n MaxBuy,\n MarketReport,\n SupportedAsset\n} from './registry'\nimport Doc from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport { setOptionTemplates, XYRangeOption } from './opts'\nimport State from './state'\nimport { bind as bindForm, NewWalletForm } from './forms'\nimport { RateEncodingFactor } from './orderutil'\n\nconst GapStrategyMultiplier = 'multiplier'\nconst GapStrategyAbsolute = 'absolute'\nconst GapStrategyAbsolutePlus = 'absolute-plus'\nconst GapStrategyPercent = 'percent'\nconst GapStrategyPercentPlus = 'percent-plus'\n\ninterface HostedMarket extends Market {\n host: string\n}\n\ninterface LiveProgram extends BotReport {\n tmpl: Record\n div: PageElement\n}\n\ninterface BaseOption {\n key: string\n displayname: string\n description: string\n default: number\n min: number\n max: number\n}\n\n// Oracle Weighting\nconst oracleWeightBaseOption: BaseOption = {\n key: 'oracleWeighting',\n displayname: 'Oracle Weight',\n description: 'Enable the oracle and set its weight in calculating our target price.',\n default: 0.1,\n max: 1.0,\n min: 0.0\n}\nconst oracleWeightRange: XYRange = {\n start: {\n label: '0%',\n x: 0,\n y: 0\n },\n end: {\n label: '100%',\n x: 1,\n y: 100\n },\n xUnit: '',\n yUnit: '%'\n}\nconst oracleWeightOption: OrderOption = createXYRange(oracleWeightBaseOption, oracleWeightRange)\n\nconst oracleBiasBaseOption: BaseOption = {\n key: 'oracleBias',\n displayname: 'Oracle Bias',\n description: 'Apply an adjustment to the oracles rate, up to +/-1%.',\n default: 0.0,\n max: 0.01,\n min: -0.01\n}\nconst oracleBiasRange: XYRange = {\n start: {\n label: '-1%',\n x: -0.01,\n y: -1\n },\n end: {\n label: '1%',\n x: 0.01,\n y: 1\n },\n xUnit: '',\n yUnit: '%'\n}\nconst oracleBiasOption: OrderOption = createXYRange(oracleBiasBaseOption, oracleBiasRange)\n\nconst driftToleranceBaseOption: BaseOption = {\n key: 'driftTolerance',\n displayname: 'Drift Tolerance',\n description: 'How far from the ideal price will we allow orders to drift. Typically a fraction of a percent.',\n default: 0.001,\n max: 0.01,\n min: 0\n}\nconst driftToleranceRange: XYRange = {\n start: {\n label: '0%',\n x: 0,\n y: 0\n },\n end: {\n label: '1%',\n x: 0.01,\n y: 1\n },\n xUnit: '',\n yUnit: '%'\n}\nconst driftToleranceOption: OrderOption = createXYRange(driftToleranceBaseOption, driftToleranceRange)\n\nconst gapMultiplierBaseOption: BaseOption = {\n key: 'gapMultiplier',\n displayname: 'Spread Multiplier',\n description: 'Increase the spread for reduced risk and higher potential profits, but with a lower fill rate. ' +\n 'The baseline value, 1, is the break-even value, where tx fees and profits are equivalent in an otherwise static market.',\n default: 2,\n max: 10,\n min: 1\n}\nconst gapMultiplierRange: XYRange = {\n start: {\n label: '1x',\n x: 1,\n y: 100\n },\n end: {\n label: '100x',\n x: 100,\n y: 10000\n },\n xUnit: 'X',\n yUnit: '%'\n}\nconst gapMultiplierOption: OrderOption = createXYRange(gapMultiplierBaseOption, gapMultiplierRange)\n\nconst gapPercentBaseOption: BaseOption = {\n key: 'gapPercent',\n displayname: 'Percent Spread',\n description: 'The spread is set as a percent of current spot price.',\n default: 0.005,\n max: 1,\n min: 0\n}\nconst gapPercentRange: XYRange = {\n start: {\n label: '0%',\n x: 0,\n y: 0\n },\n end: {\n label: '10%',\n x: 0.1,\n y: 10\n },\n xUnit: 'X',\n yUnit: '%'\n}\nconst gapPercentOption: OrderOption = createXYRange(gapPercentBaseOption, gapPercentRange)\n\nconst animationLength = 300\n\nexport default class MarketMakerPage extends BasePage {\n page: Record\n data: any\n createOpts: Record\n gapRanges: Record\n currentMarket: HostedMarket\n currentForm: PageElement\n keyup: (e: KeyboardEvent) => void\n programs: Record\n editProgram: BotReport | null\n gapMultiplierOpt: XYRangeOption\n gapPercentOpt: XYRangeOption\n driftToleranceOpt: XYRangeOption\n biasOpt: XYRangeOption\n weightOpt: XYRangeOption\n pwHandler: ((pw: string) => Promise) | null\n newWalletForm: NewWalletForm\n specifiedPrice: number\n currentReport: MarketReport | null\n\n constructor (main: HTMLElement, data: any) {\n super()\n const page = this.page = Doc.idDescendants(main)\n this.data = data\n this.programs = {}\n this.editProgram = null\n this.pwHandler = null\n this.createOpts = {\n [oracleBiasBaseOption.key]: oracleBiasBaseOption.default,\n [oracleWeightBaseOption.key]: oracleWeightBaseOption.default,\n [driftToleranceOption.key]: driftToleranceOption.default\n }\n this.gapRanges = {\n [gapMultiplierBaseOption.key]: gapMultiplierBaseOption.default,\n [gapPercentBaseOption.key]: gapPercentBaseOption.default\n }\n\n page.forms.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => { this.closePopups() })\n })\n\n setOptionTemplates(page)\n\n Doc.cleanTemplates(page.assetRowTmpl, page.booleanOptTmpl, page.rangeOptTmpl,\n page.orderOptTmpl, page.runningProgramTmpl, page.oracleTmpl)\n\n const selectClicked = (e: MouseEvent, isBase: boolean): void => {\n e.stopPropagation()\n const select = isBase ? page.baseSelect : page.quoteSelect\n const m = Doc.descendentMetrics(main, select)\n page.assetDropdown.style.left = `${m.bodyLeft}px`\n page.assetDropdown.style.top = `${m.bodyTop}px`\n\n const counterAsset = isBase ? this.currentMarket.quoteid : this.currentMarket.baseid\n const clickedSymbol = isBase ? this.currentMarket.basesymbol : this.currentMarket.quotesymbol\n\n // Look through markets for other base assets for the counter asset.\n const matches: Set = new Set()\n const otherAssets: Set = new Set()\n\n for (const mkt of sortedMarkets()) {\n otherAssets.add(mkt.basesymbol)\n otherAssets.add(mkt.quotesymbol)\n const [firstID, secondID] = isBase ? [mkt.quoteid, mkt.baseid] : [mkt.baseid, mkt.quoteid]\n const [firstSymbol, secondSymbol] = isBase ? [mkt.quotesymbol, mkt.basesymbol] : [mkt.basesymbol, mkt.quotesymbol]\n if (firstID === counterAsset) matches.add(secondSymbol)\n else if (secondID === counterAsset) matches.add(firstSymbol)\n }\n\n const options = Array.from(matches)\n options.sort((a: string, b: string) => a.localeCompare(b))\n for (const symbol of options) otherAssets.delete(symbol)\n const nonOptions = Array.from(otherAssets)\n nonOptions.sort((a: string, b: string) => a.localeCompare(b))\n\n Doc.empty(page.assetDropdown)\n const addOptions = (symbols: string[], avail: boolean): void => {\n for (const symbol of symbols) {\n const row = this.assetRow(symbol)\n Doc.bind(row, 'click', (e: MouseEvent) => {\n e.stopPropagation()\n if (symbol === clickedSymbol) return this.hideAssetDropdown() // no change\n this.leaveEditMode()\n if (isBase) this.setCreationBase(symbol)\n else this.setCreationQuote(symbol)\n })\n if (!avail) row.classList.add('ghost')\n page.assetDropdown.appendChild(row)\n }\n }\n addOptions(options, true)\n addOptions(nonOptions, false)\n Doc.show(page.assetDropdown)\n const clicker = (e: MouseEvent): void => {\n if (Doc.mouseInElement(e, page.assetDropdown)) return\n this.hideAssetDropdown()\n Doc.unbind(document, 'click', clicker)\n }\n Doc.bind(document, 'click', clicker)\n }\n\n Doc.bind(page.baseSelect, 'click', (e: MouseEvent) => selectClicked(e, true))\n Doc.bind(page.quoteSelect, 'click', (e: MouseEvent) => selectClicked(e, false))\n\n Doc.bind(page.marketSelect, 'change', () => {\n const [host, name] = page.marketSelect.value?.split(' ') as string[]\n this.setMarketSubchoice(host, name)\n })\n\n this.gapMultiplierOpt = new XYRangeOption(gapMultiplierOption, '', this.gapRanges, () => this.createOptsUpdated())\n this.gapPercentOpt = new XYRangeOption(gapPercentOption, '', this.gapRanges, () => this.createOptsUpdated())\n this.driftToleranceOpt = new XYRangeOption(driftToleranceOption, '', this.createOpts, () => this.createOptsUpdated())\n this.biasOpt = new XYRangeOption(oracleBiasOption, '', this.createOpts, () => this.createOptsUpdated())\n this.weightOpt = new XYRangeOption(oracleWeightOption, '', this.createOpts, () => this.createOptsUpdated())\n\n page.options.appendChild(this.gapMultiplierOpt.node)\n Doc.hide(this.gapMultiplierOpt.node) // Default is GapStrategyPercentPlus\n page.options.appendChild(this.gapPercentOpt.node)\n page.options.appendChild(this.driftToleranceOpt.node)\n page.options.appendChild(this.weightOpt.node)\n page.options.appendChild(this.biasOpt.node)\n\n Doc.bind(page.showAdvanced, 'click', () => {\n State.storeLocal(State.optionsExpansionLK, true)\n Doc.hide(page.showAdvanced)\n Doc.show(page.hideAdvanced, page.options)\n })\n\n Doc.bind(page.hideAdvanced, 'click', () => {\n State.storeLocal(State.optionsExpansionLK, false)\n Doc.hide(page.hideAdvanced, page.options)\n Doc.show(page.showAdvanced)\n })\n\n Doc.bind(page.runBttn, 'click', this.authedRoute(async (pw: string): Promise => this.createBot(pw)))\n\n Doc.bind(page.lotsInput, 'change', () => {\n page.lotsInput.value = String(Math.round(parseFloat(page.lotsInput.value ?? '0')))\n })\n\n Doc.bind(page.exitEditMode, 'click', () => this.leaveEditMode())\n\n Doc.bind(page.manualPriceBttn, 'click', () => {\n Doc.hide(page.basisPrice, page.manualPriceBttn)\n Doc.show(page.manualPriceInput)\n page.manualPriceInput.focus()\n })\n\n const setManualPrice = () => {\n const v = parseFloat(page.manualPriceInput.value ?? '0')\n page.basisPrice.textContent = Doc.formatFiveSigFigs(v)\n this.specifiedPrice = v\n this.setCurrentBasisPrice(v)\n Doc.show(page.basisPrice, page.manualPriceBttn)\n Doc.hide(page.manualPriceInput, page.noFiatBox)\n }\n\n const hideManualPrice = () => {\n page.manualPriceInput.value = ''\n Doc.show(page.basisPrice, page.manualPriceBttn)\n Doc.hide(page.manualPriceInput)\n }\n\n // Doc.bind(page.manualPriceInput, 'change', () => { setManualPrice() })\n\n Doc.bind(page.manualPriceInput, 'keyup', (e: KeyboardEvent) => {\n switch (e.key) {\n case 'Escape':\n hideManualPrice()\n break\n case 'Enter':\n case 'NumpadEnter':\n setManualPrice()\n }\n })\n\n Doc.bind(page.manualPriceInput, 'blur', () => {\n if (Doc.isHidden(page.basisPrice)) hideManualPrice()\n })\n\n Doc.bind(page.gapStrategySelect, 'change', () => this.updateGapStrategyInputs(this.currentReport))\n\n const submitPasswordForm = async () => {\n const pw = page.pwInput.value ?? ''\n if (pw === '') return\n const handler = this.pwHandler\n if (handler === null) return\n this.pwHandler = null\n await handler(pw)\n if (this.currentForm === page.pwForm) this.closePopups()\n }\n\n bindForm(page.pwForm, page.pwSubmit, () => submitPasswordForm())\n\n Doc.bind(page.pwInput, 'keyup', (e: KeyboardEvent) => {\n if (e.key !== 'Enter' && e.key !== 'NumpadEnter') return\n submitPasswordForm()\n })\n\n this.newWalletForm = new NewWalletForm(\n page.newWalletForm,\n assetID => this.newWalletCreated(assetID)\n )\n\n Doc.bind(page.createBaseWallet, 'click', () => {\n this.newWalletForm.setAsset(this.currentMarket.baseid)\n this.showForm(page.newWalletForm)\n })\n\n Doc.bind(page.createQuoteWallet, 'click', () => {\n this.newWalletForm.setAsset(this.currentMarket.quoteid)\n this.showForm(page.newWalletForm)\n })\n\n Doc.bind(page.forms, 'mousedown', (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, this.currentForm)) { this.closePopups() }\n })\n\n this.keyup = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n this.closePopups()\n }\n }\n\n const lastMkt = State.fetchLocal(State.lastMMMarketLK) as HostedMarket\n let mkt: HostedMarket | null = null\n if (lastMkt && lastMkt.host) {\n const xc = app().exchanges[lastMkt.host]\n if (xc) {\n const mktID = lastMkt.basesymbol + '_' + lastMkt.quotesymbol\n const xcMkt = xc.markets[mktID]\n if (xcMkt) mkt = Object.assign({ host: xc.host }, xcMkt)\n }\n }\n\n if (State.fetchLocal(State.optionsExpansionLK)) {\n Doc.show(page.hideAdvanced, page.options)\n Doc.hide(page.showAdvanced)\n }\n\n app().registerNoteFeeder({\n bot: (n: BotNote) => { this.handleBotNote(n) }\n })\n\n this.setMarket([mkt ?? sortedMarkets()[0]])\n this.populateRunningPrograms()\n page.createBox.classList.remove('invisible')\n page.programsBox.classList.remove('invisible')\n }\n\n unload (): void {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n\n /* showForm shows a modal form with a little animation. */\n async showForm (form: HTMLElement): Promise {\n this.currentForm = form\n const page = this.page\n Doc.hide(page.pwForm, page.newWalletForm)\n form.style.right = '10000px'\n Doc.show(page.forms, form)\n const shift = (page.forms.offsetWidth + form.offsetWidth) / 2\n await Doc.animate(animationLength, progress => {\n form.style.right = `${(1 - progress) * shift}px`\n }, 'easeOutHard')\n form.style.right = '0'\n }\n\n closePopups (): void {\n this.pwHandler = null\n this.page.pwInput.value = ''\n Doc.hide(this.page.forms)\n }\n\n hideAssetDropdown (): void {\n const page = this.page\n page.assetDropdown.scrollTop = 0\n Doc.hide(page.assetDropdown)\n }\n\n assetRow (symbol: string): PageElement {\n const row = this.page.assetRowTmpl.cloneNode(true) as PageElement\n const tmpl = Doc.parseTemplate(row)\n tmpl.logo.src = Doc.logoPath(symbol)\n Doc.empty(tmpl.symbol)\n tmpl.symbol.appendChild(Doc.symbolize(symbol))\n return row\n }\n\n setCurrentReport (r: MarketReport | null) {\n this.currentReport = r\n this.setCurrentBasisPrice(r ? r.basisPrice : 0)\n this.updateGapStrategyInputs(r)\n\n // Set max value for absolute input\n const page = this.page\n if (r && r.basisPrice > 0) {\n this.fetchMaxBuy(Math.round(r.basisPrice / this.currentMarket.atomToConv * RateEncodingFactor))\n page.basisPrice.textContent = Doc.formatFiveSigFigs(r.basisPrice)\n Doc.show(page.absMaxBox, page.basisPrice)\n Doc.hide(page.manualPriceInput, page.noFiatBox)\n this.page.gapFactorMax.textContent = Doc.formatFiveSigFigs(r.basisPrice)\n } else {\n page.lotEstQuoteLots.textContent = '[no rate]'\n page.basisPrice.textContent = 'must set manually'\n Doc.hide(page.basisPrice, page.absMaxBox)\n Doc.show(page.manualPriceInput)\n page.manualPriceInput.focus()\n const u = app().user\n if (Object.keys(u.fiatRates).length === 0) Doc.show(page.noFiatBox)\n }\n\n if (r && r.breakEvenSpread > 0) {\n Doc.show(page.breakEvenGapBox)\n page.breakEvenGap.textContent = Doc.formatFiveSigFigs(r.breakEvenSpread)\n } else Doc.hide(page.breakEvenGapBox)\n }\n\n updateGapStrategyInputVisibility () {\n const page = this.page\n const gapStrategy = page.gapStrategySelect.value\n switch (gapStrategy) {\n case GapStrategyMultiplier:\n Doc.show(this.gapMultiplierOpt.node)\n Doc.hide(page.absInputBox, page.absMaxBox, this.gapPercentOpt.node)\n break\n case GapStrategyPercent:\n case GapStrategyPercentPlus:\n Doc.show(this.gapPercentOpt.node)\n Doc.hide(page.absInputBox, page.absMaxBox, this.gapMultiplierOpt.node)\n break\n case GapStrategyAbsolute:\n case GapStrategyAbsolutePlus:\n Doc.hide(this.gapMultiplierOpt.node, this.gapPercentOpt.node)\n Doc.show(page.absInputBox, page.absMaxBox)\n }\n }\n\n updateGapStrategyInputs (r: MarketReport | null) {\n this.updateGapStrategyInputVisibility()\n switch (this.page.gapStrategySelect.value) {\n case GapStrategyMultiplier: {\n if (r && r.breakEvenSpread > 0) {\n gapMultiplierRange.start.y = r.breakEvenSpread\n gapMultiplierRange.end.y = r.breakEvenSpread * 100\n gapMultiplierRange.yUnit = this.rateUnit()\n } else {\n gapMultiplierRange.start.y = 100\n gapMultiplierRange.end.y = 10000\n gapMultiplierRange.yUnit = '%'\n }\n this.gapMultiplierOpt.handler.setConfig(gapMultiplierRange)\n break\n }\n case GapStrategyPercent:\n case GapStrategyPercentPlus: {\n if (r && r.breakEvenSpread > 0) {\n gapPercentRange.end.y = r.basisPrice\n gapPercentRange.yUnit = this.rateUnit()\n this.gapPercentOpt.handler.setConfig(gapPercentRange)\n } else {\n gapPercentRange.end.y = 10\n gapPercentRange.yUnit = '%'\n }\n }\n }\n }\n\n setCurrentBasisPrice (p: number) {\n if (p > 0) {\n const rateUnit = this.rateUnit()\n oracleBiasRange.start.y = -0.01 * p\n oracleBiasRange.end.y = 0.01 * p\n oracleBiasRange.yUnit = rateUnit\n driftToleranceRange.start.y = 0\n driftToleranceRange.end.y = p * 0.01\n driftToleranceRange.yUnit = rateUnit\n } else {\n oracleBiasRange.start.y = -1\n oracleBiasRange.end.y = 1\n oracleBiasRange.yUnit = '%'\n driftToleranceRange.start.y = 0\n driftToleranceRange.end.y = 1\n driftToleranceRange.yUnit = '%'\n }\n this.biasOpt.handler.setConfig(oracleBiasRange)\n this.driftToleranceOpt.handler.setConfig(driftToleranceRange)\n }\n\n rateUnit (): string {\n const quoteSymbol = this.currentMarket.quotesymbol.split('.')[0]\n const baseSymbol = this.currentMarket.basesymbol.split('.')[0]\n return `${quoteSymbol}/${baseSymbol}`\n }\n\n async setMarket (mkts: HostedMarket[], skipMarketUpdate?: boolean): Promise {\n const page = this.page\n\n const mkt = mkts[0]\n this.currentMarket = mkt\n this.specifiedPrice = 0\n this.setCurrentReport(null)\n page.manualPriceInput.value = ''\n\n State.storeLocal(State.lastMMMarketLK, mkt)\n\n Doc.empty(page.baseSelect, page.quoteSelect)\n page.baseSelect.appendChild(this.assetRow(mkt.basesymbol))\n page.quoteSelect.appendChild(this.assetRow(mkt.quotesymbol))\n this.hideAssetDropdown()\n\n const addMarketSelect = (mkt: HostedMarket, el: PageElement) => {\n Doc.setContent(el,\n Doc.symbolize(mkt.basesymbol),\n new Text('-') as any,\n Doc.symbolize(mkt.quotesymbol),\n new Text(' @ ') as any,\n new Text(mkt.host) as any\n )\n }\n\n Doc.hide(page.marketSelect, page.marketOneChoice)\n if (mkts.length === 1) {\n Doc.show(page.marketOneChoice)\n addMarketSelect(mkt, page.marketOneChoice)\n } else {\n Doc.show(page.marketSelect)\n Doc.empty(page.marketSelect)\n for (const mkt of mkts) {\n const opt = document.createElement('option')\n page.marketSelect.appendChild(opt)\n opt.value = `${mkt.host} ${mkt.name}`\n addMarketSelect(mkt, opt)\n }\n }\n\n if (skipMarketUpdate) return\n\n Doc.setContent(page.lotEstBaseSymbol, Doc.symbolize(mkt.basesymbol))\n Doc.setContent(page.lotEstQuoteSymbol, Doc.symbolize(mkt.quotesymbol))\n\n const setNoWallet = (a: SupportedAsset, noWalletBox: PageElement): boolean => {\n Doc.show(noWalletBox)\n const tmpl = Doc.parseTemplate(noWalletBox)\n tmpl.assetLogo.src = Doc.logoPath(a.symbol)\n tmpl.assetName.textContent = a.name\n return false\n }\n\n Doc.hide(\n page.lotEstQuoteBox, page.lotEstQuoteNoWallet, page.lotEstBaseBox, page.lotEstBaseNoWallet, page.availHeader,\n page.lotEstimateBox, page.marketInfo, page.oraclesBox, page.lotsBox, page.options, page.advancedBox\n )\n const [b, q] = [app().assets[mkt.baseid], app().assets[mkt.quoteid]]\n if (b.wallet && q.wallet) {\n Doc.show(\n page.lotEstQuoteBox, page.lotEstBaseBox, page.availHeader, page.fetchingMarkets,\n page.lotsBox, page.advancedBox\n )\n if (State.fetchLocal(State.optionsExpansionLK)) Doc.show(page.options)\n const loaded = app().loading(page.options)\n const buy = this.fetchOracleAndMaxBuy()\n const sell = this.fetchMaxSell()\n await buy\n await sell\n loaded()\n Doc.show(page.lotEstimateBox, page.marketInfo, page.oraclesBox)\n Doc.hide(page.fetchingMarkets)\n return\n }\n Doc.show(page.lotEstimateBox)\n if (q.wallet) {\n page.lotEstQuoteLots.textContent = '0'\n } else setNoWallet(q, page.lotEstQuoteNoWallet)\n\n if (b.wallet) {\n page.lotEstBaseLots.textContent = '0'\n } else setNoWallet(b, page.lotEstBaseNoWallet)\n }\n\n async fetchMaxSell (): Promise {\n const { currentMarket: mkt, page } = this\n const res = await postJSON('/api/maxsell', {\n host: mkt.host,\n base: mkt.baseid,\n quote: mkt.quoteid\n }) as MaxSell\n if (this.currentMarket !== mkt) return\n if (!app().checkResponse(res as any)) {\n page.lotEstBaseLots.textContent = '0'\n console.error(res)\n return\n }\n page.lotEstBaseLots.textContent = String(res.maxSell.swap.lots)\n }\n\n async fetchOracleAndMaxBuy (): Promise {\n const { currentMarket: mkt, page } = this\n const res = await postJSON('/api/marketreport', { host: mkt.host, baseID: mkt.baseid, quoteID: mkt.quoteid })\n if (!app().checkResponse(res)) {\n page.lotEstQuoteLots.textContent = '0'\n console.error(res)\n return\n }\n const r = res.report as MarketReport\n Doc.hide(page.manualPriceBttn)\n this.setCurrentReport(r)\n\n if (!r.oracles || r.oracles.length === 0) return\n\n Doc.empty(page.oracles)\n let weight = 0\n let weightedSum = 0\n for (const o of r.oracles ?? []) {\n const tr = page.oracleTmpl.cloneNode(true) as PageElement\n page.oracles.appendChild(tr)\n const tmpl = Doc.parseTemplate(tr)\n tmpl.logo.src = 'img/' + o.host + '.png'\n tmpl.host.textContent = ExchangeNames[o.host]\n tmpl.volume.textContent = Doc.formatThreeSigFigs(o.usdVol)\n const price = (o.bestBuy + o.bestSell) / 2\n weightedSum += o.usdVol * price\n weight += o.usdVol\n tmpl.price.textContent = Doc.formatThreeSigFigs((o.bestBuy + o.bestSell) / 2)\n }\n page.avgPrice.textContent = Doc.formatFiveSigFigs(weightedSum / weight)\n }\n\n async fetchMaxBuy (rate: number): Promise {\n const { currentMarket: mkt, page } = this\n const res = await postJSON('/api/maxbuy', {\n host: mkt.host,\n base: mkt.baseid,\n quote: mkt.quoteid,\n rate: rate ?? 0\n }) as MaxBuy\n if (this.currentMarket !== mkt) return\n page.lotEstQuoteLots.textContent = String(res.maxBuy.swap.lots)\n }\n\n setEditProgram (report: BotReport): void {\n const { createOpts, page } = this\n const pgm = report.program\n const [b, q] = [app().assets[pgm.baseID], app().assets[pgm.quoteID]]\n const mkt = app().exchanges[pgm.host].markets[`${b.symbol}_${q.symbol}`]\n this.setMarket([{\n host: pgm.host,\n ...mkt\n }], true)\n for (const p of Object.values(this.programs)) {\n if (p.programID !== report.programID) Doc.hide(p.div)\n }\n this.editProgram = report\n page.createBox.classList.add('edit')\n page.programsBox.classList.add('edit')\n page.lotsInput.value = String(pgm.lots)\n createOpts.oracleWeighting = pgm.oracleWeighting\n this.weightOpt.setValue(pgm.oracleWeighting)\n createOpts.oracleBias = pgm.oracleBias\n this.biasOpt.setValue(pgm.oracleBias)\n createOpts.driftTolerance = pgm.driftTolerance\n this.driftToleranceOpt.setValue(pgm.driftTolerance)\n page.gapStrategySelect.value = pgm.gapStrategy\n this.updateGapStrategyInputVisibility()\n this.createOptsUpdated()\n\n switch (pgm.gapStrategy) {\n case GapStrategyPercent:\n case GapStrategyPercentPlus:\n this.gapPercentOpt.setValue(pgm.gapFactor)\n break\n case GapStrategyMultiplier:\n this.gapMultiplierOpt.setValue(pgm.gapFactor)\n }\n\n Doc.bind(page.programsBox, 'click', () => this.leaveEditMode())\n }\n\n leaveEditMode (): void {\n const page = this.page\n Doc.unbind(page.programsBox, 'click', () => this.leaveEditMode())\n for (const p of Object.values(this.programs)) Doc.show(p.div)\n page.createBox.classList.remove('edit')\n page.programsBox.classList.remove('edit')\n this.editProgram = null\n this.setMarket([this.currentMarket])\n }\n\n populateRunningPrograms (): void {\n const page = this.page\n const bots = app().user.bots\n Doc.empty(page.runningPrograms)\n this.programs = {}\n for (const report of bots) page.runningPrograms.appendChild(this.programDiv(report))\n this.setProgramsHeader()\n }\n\n setProgramsHeader (): void {\n const page = this.page\n if (page.runningPrograms.children.length > 0) {\n Doc.show(page.programsHeader)\n Doc.hide(page.noProgramsMessage)\n } else {\n Doc.hide(page.programsHeader)\n Doc.show(page.noProgramsMessage)\n }\n }\n\n programDiv (report: BotReport): PageElement {\n const page = this.page\n const div = page.runningProgramTmpl.cloneNode(true) as PageElement\n const tmpl = Doc.parseTemplate(div)\n const startStop = async (endpoint: string, pw?: string): Promise => {\n Doc.hide(tmpl.startErr)\n const loaded = app().loading(div)\n const res = await postJSON(endpoint, { programID: report.programID, appPW: pw })\n loaded()\n if (!app().checkResponse(res)) {\n tmpl.startErr.textContent = res.msg\n Doc.show(tmpl.startErr)\n }\n }\n Doc.bind(tmpl.pauseBttn, 'click', () => startStop('/api/stopbot'))\n Doc.bind(tmpl.startBttn, 'click', this.authedRoute(async (pw: string) => startStop('/api/startbot', pw)))\n Doc.bind(tmpl.retireBttn, 'click', () => startStop('/api/retirebot'))\n Doc.bind(tmpl.configureBttn, 'click', (e: MouseEvent) => {\n e.stopPropagation()\n this.setEditProgram(this.programs[report.programID])\n })\n const [b, q] = [app().assets[report.program.baseID], app().assets[report.program.quoteID]]\n tmpl.base.appendChild(this.assetRow(b.symbol))\n tmpl.quote.appendChild(this.assetRow(q.symbol))\n tmpl.baseSymbol.textContent = b.symbol.toUpperCase()\n tmpl.quoteSymbol.textContent = q.symbol.toUpperCase()\n tmpl.host.textContent = report.program.host\n this.updateProgramDiv(tmpl, report)\n this.programs[report.programID] = Object.assign({ tmpl, div }, report)\n return div\n }\n\n authedRoute (handler: (pw: string) => Promise): () => void {\n return async () => {\n if (State.passwordIsCached()) return await handler('')\n this.pwHandler = handler\n await this.showForm(this.page.pwForm)\n }\n }\n\n updateProgramDiv (tmpl: Record, report: BotReport): void {\n const pgm = report.program\n Doc.hide(tmpl.programRunning, tmpl.programPaused)\n if (report.running) Doc.show(tmpl.programRunning)\n else Doc.show(tmpl.programPaused)\n tmpl.lots.textContent = String(pgm.lots)\n tmpl.boost.textContent = `${(pgm.gapFactor * 100).toFixed(1)}%`\n tmpl.driftTolerance.textContent = `${(pgm.driftTolerance * 100).toFixed(2)}%`\n tmpl.oracleWeight.textContent = `${(pgm.oracleWeighting * 100).toFixed(0)}%`\n tmpl.oracleBias.textContent = `${(pgm.oracleBias * 100).toFixed(1)}%`\n }\n\n setMarketSubchoice (host: string, name: string): void {\n if (host !== this.currentMarket.host || name !== this.currentMarket.name) this.leaveEditMode()\n this.currentMarket = Object.assign({ host }, app().exchanges[host].markets[name])\n }\n\n createOptsUpdated (): void {\n const opts = this.createOpts\n if (opts.oracleWeighting) Doc.show(this.biasOpt.node)\n else Doc.hide(this.biasOpt.node)\n }\n\n handleBotNote (n: BotNote): void {\n const page = this.page\n const r = n.report\n switch (n.topic) {\n case 'BotCreated':\n page.runningPrograms.prepend(this.programDiv(r))\n this.setProgramsHeader()\n break\n case 'BotRetired':\n this.programs[r.programID].div.remove()\n delete this.programs[r.programID]\n this.setProgramsHeader()\n break\n default: {\n const p = this.programs[r.programID]\n Object.assign(p, r)\n this.updateProgramDiv(p.tmpl, r)\n }\n }\n }\n\n setCreationBase (symbol: string) {\n const counterAsset = this.currentMarket.quotesymbol\n const markets = sortedMarkets()\n const options: HostedMarket[] = []\n // Best option: find an exact match.\n for (const mkt of markets) if (mkt.basesymbol === symbol && mkt.quotesymbol === counterAsset) options.push(mkt)\n // Next best option: same assets, reversed order.\n for (const mkt of markets) if (mkt.quotesymbol === symbol && mkt.basesymbol === counterAsset) options.push(mkt)\n // If we have exact matches, we're done.\n if (options.length > 0) return this.setMarket(options)\n // No exact matches. Must have selected a ghost-class market. Next best\n // option will be the first market where the selected asset is a base asset.\n for (const mkt of markets) if (mkt.basesymbol === symbol) return this.setMarket([mkt])\n // Last option: Market where this is the quote asset.\n for (const mkt of markets) if (mkt.quotesymbol === symbol) return this.setMarket([mkt])\n }\n\n setCreationQuote (symbol: string) {\n const counterAsset = this.currentMarket.basesymbol\n const markets = sortedMarkets()\n const options: HostedMarket[] = []\n for (const mkt of markets) if (mkt.quotesymbol === symbol && mkt.basesymbol === counterAsset) options.push(mkt)\n for (const mkt of markets) if (mkt.basesymbol === symbol && mkt.quotesymbol === counterAsset) options.push(mkt)\n if (options.length > 0) return this.setMarket(options)\n for (const mkt of markets) if (mkt.quotesymbol === symbol) return this.setMarket([mkt])\n for (const mkt of markets) if (mkt.basesymbol === symbol) return this.setMarket([mkt])\n }\n\n async createBot (appPW: string): Promise {\n const { page, currentMarket, currentReport } = this\n\n Doc.hide(page.createErr)\n const setError = (s: string) => {\n page.createErr.textContent = s\n Doc.show(page.createErr)\n }\n\n const lots = parseInt(page.lotsInput.value || '0')\n if (lots === 0) return setError('must specify > 0 lots')\n const makerProgram = Object.assign({\n host: currentMarket.host,\n baseID: currentMarket.baseid,\n quoteID: currentMarket.quoteid\n }, this.createOpts, { lots, gapStrategy: '' })\n\n const strategy = page.gapStrategySelect.value\n makerProgram.gapStrategy = strategy ?? ''\n\n const req = {\n botType: 'MakerV0',\n program: makerProgram,\n programID: 0,\n appPW: appPW,\n manualRate: 0\n }\n\n switch (strategy) {\n case GapStrategyAbsolute:\n case GapStrategyAbsolutePlus: {\n const r = parseFloat(page.absInput.value || '0')\n if (r === 0) return setError('gap must be specified for strategy = absolute')\n else if (currentReport?.basisPrice && r >= currentReport.basisPrice) return setError('gap width cannot be > current spot price')\n makerProgram.gapFactor = r\n break\n }\n case GapStrategyPercent:\n case GapStrategyPercentPlus:\n makerProgram.gapFactor = this.gapRanges.gapPercent\n break\n default:\n makerProgram.gapFactor = this.gapRanges.gapMultiplier\n }\n\n let endpoint = '/api/createbot'\n\n if (this.editProgram !== null) {\n req.programID = this.editProgram.programID\n endpoint = '/api/updatebotprogram'\n } else {\n if (!this.currentReport || this.currentReport.basisPrice === 0) {\n if (this.specifiedPrice === 0) {\n setError('price must be set manually')\n return\n }\n req.program.manualRate = this.specifiedPrice\n }\n }\n\n const loaded = app().loading(page.botCreator)\n const res = await postJSON(endpoint, req)\n loaded()\n\n if (!app().checkResponse(res)) {\n page.createErr.textContent = res.msg\n Doc.show(page.createErr)\n return\n }\n\n this.leaveEditMode()\n\n page.lotsInput.value = ''\n }\n\n newWalletCreated (assetID: number) {\n const m = this.currentMarket\n if (assetID === m.baseid) this.setCreationBase(m.basesymbol)\n else if (assetID === m.quoteid) this.setCreationQuote(m.quotesymbol)\n else return\n this.closePopups()\n }\n}\n\nfunction sortedMarkets (): HostedMarket[] {\n const mkts: HostedMarket[] = []\n const convertMarkets = (xc: Exchange): HostedMarket[] => {\n return Object.values(xc.markets).map((mkt: Market) => Object.assign({ host: xc.host }, mkt))\n }\n for (const xc of Object.values(app().user.exchanges)) mkts.push(...convertMarkets(xc))\n mkts.sort((a: Market, b: Market) => {\n if (!a.spot) {\n if (!b.spot) return a.name.localeCompare(b.name)\n return -1\n }\n if (!b.spot) return 1\n // Sort by lots.\n return b.spot.vol24 / b.lotsize - a.spot.vol24 / a.lotsize\n })\n return mkts\n}\n\nfunction createOption (opt: BaseOption): OrderOption {\n return {\n key: opt.key,\n displayname: opt.displayname,\n description: opt.description,\n default: opt.default,\n max: opt.max,\n min: opt.min,\n noecho: false,\n isboolean: false,\n isdate: false,\n disablewhenactive: false,\n isBirthdayConfig: false,\n noauth: false\n }\n}\n\nfunction createXYRange (baseOpt: BaseOption, xyRange: XYRange): OrderOption {\n const opt = createOption(baseOpt)\n opt.xyRange = xyRange\n return opt\n}\n\nconst ExchangeNames: Record = {\n 'binance.com': 'Binance',\n 'coinbase.com': 'Coinbase',\n 'bittrex.com': 'Bittrex',\n 'hitbtc.com': 'HitBTC',\n 'exmo.com': 'EXMO'\n}\n","import Doc from './doc'\nimport State from './state'\nimport RegistrationPage from './register'\nimport LoginPage from './login'\nimport WalletsPage from './wallets'\nimport SettingsPage from './settings'\nimport MarketsPage from './markets'\nimport OrdersPage from './orders'\nimport OrderPage from './order'\nimport DexSettingsPage from './dexsettings'\nimport MarketMakerPage from './mm'\nimport { RateEncodingFactor, StatusExecuted, hasActiveMatches } from './orderutil'\nimport { getJSON, postJSON, Errors } from './http'\nimport * as ntfn from './notifications'\nimport ws from './ws'\nimport * as intl from './locales'\nimport {\n User,\n SupportedAsset,\n Exchange,\n WalletState,\n BondNote,\n CoreNote,\n OrderNote,\n Market,\n Order,\n Match,\n BalanceNote,\n WalletConfigNote,\n MatchNote,\n ConnEventNote,\n SpotPriceNote,\n BotNote,\n UnitInfo,\n WalletDefinition,\n WalletBalance,\n LogMessage,\n NoteElement,\n BalanceResponse,\n APIResponse,\n RateNote,\n BotReport,\n InFlightOrder\n} from './registry'\n\nconst idel = Doc.idel // = element by id\nconst bind = Doc.bind\nconst unbind = Doc.unbind\n\nconst notificationRoute = 'notify'\nconst noteCacheSize = 100\n\ninterface Page {\n unload (): void\n}\n\ninterface PageClass {\n new (main: HTMLElement, data: any): Page;\n}\n\ninterface CoreNotePlus extends CoreNote {\n el: HTMLElement // Added in app\n}\n\n/* constructors is a map to page constructors. */\nconst constructors: Record = {\n login: LoginPage,\n register: RegistrationPage,\n markets: MarketsPage,\n wallets: WalletsPage,\n settings: SettingsPage,\n orders: OrdersPage,\n order: OrderPage,\n dexsettings: DexSettingsPage,\n mm: MarketMakerPage\n}\n\n// Application is the main javascript web application for the Decred DEX client.\nexport default class Application {\n notes: CoreNotePlus[]\n pokes: CoreNotePlus[]\n user: User\n seedGenTime: number\n commitHash: string\n showPopups: boolean\n loggers: Record\n recorders: Record\n main: HTMLElement\n header: HTMLElement\n headerSpace: HTMLElement\n assets: Record\n exchanges: Record\n walletMap: Record\n fiatRatesMap: Record\n tooltip: HTMLElement\n page: Record\n loadedPage: Page | null\n popupNotes: HTMLElement\n popupTmpl: HTMLElement\n noteReceivers: Record void>[]\n\n constructor () {\n this.notes = []\n this.pokes = []\n this.seedGenTime = 0\n this.commitHash = process.env.COMMITHASH || ''\n this.noteReceivers = []\n this.fiatRatesMap = {}\n this.showPopups = State.fetchLocal(State.popupsLK) === '1'\n\n console.log('Decred DEX Client App, Build', this.commitHash.substring(0, 7))\n\n // Loggers can be enabled by setting a truthy value to the loggerID using\n // enableLogger. Settings are stored across sessions. See docstring for the\n // log method for more info.\n this.loggers = State.fetchLocal(State.loggersLK) || {}\n window.enableLogger = (loggerID, state) => {\n if (state) this.loggers[loggerID] = true\n else delete this.loggers[loggerID]\n State.storeLocal(State.loggersLK, this.loggers)\n return `${loggerID} logger ${state ? 'enabled' : 'disabled'}`\n }\n // Enable logging from anywhere.\n window.log = (loggerID, ...a) => { this.log(loggerID, ...a) }\n\n // Recorders can record log messages, and then save them to file on request.\n const recorderKeys = State.fetchLocal(State.recordersLK) || []\n this.recorders = {}\n for (const loggerID of recorderKeys) {\n console.log('recording', loggerID)\n this.recorders[loggerID] = []\n }\n window.recordLogger = (loggerID, on) => {\n if (on) this.recorders[loggerID] = []\n else delete this.recorders[loggerID]\n State.storeLocal(State.recordersLK, Object.keys(this.recorders))\n return `${loggerID} recorder ${on ? 'enabled' : 'disabled'}`\n }\n window.dumpLogger = loggerID => {\n const record = this.recorders[loggerID]\n if (!record) return `no recorder for logger ${loggerID}`\n const a = document.createElement('a')\n a.href = `data:application/octet-stream;base64,${window.btoa(JSON.stringify(record, null, 4))}`\n a.download = `${loggerID}.json`\n document.body.appendChild(a)\n a.click()\n setTimeout(() => {\n document.body.removeChild(a)\n }, 0)\n }\n\n // use user current locale set by backend\n intl.setLocale()\n }\n\n /**\n * Start the application. This is the only thing done from the index.js entry\n * point. Read the id = main element and attach handlers.\n */\n async start () {\n // Handle back navigation from the browser.\n bind(window, 'popstate', (e: PopStateEvent) => {\n const page = e.state.page\n if (!page && page !== '') return\n this.loadPage(page, e.state.data, true)\n })\n // The main element is the interchangeable part of the page that doesn't\n // include the header. Main should define a data-handler attribute\n // associated with one of the available constructors.\n this.main = idel(document, 'main')\n const handler = this.main.dataset.handler\n // Don't fetch the user until we know what page we're on.\n await this.fetchUser()\n // The application is free to respond with a page that differs from the\n // one requested in the omnibox, e.g. routing though a login page. Set the\n // current URL state based on the actual page.\n const url = new URL(window.location.href)\n if (handlerFromPath(url.pathname) !== handler) {\n url.pathname = `/${handler}`\n url.search = ''\n window.history.replaceState({ page: handler }, '', url)\n }\n // Attach stuff.\n this.attachHeader()\n this.attachCommon(this.header)\n this.attach({})\n // Load recent notifications from Window.localStorage.\n const notes = State.fetchLocal(State.notificationsLK)\n this.setNotes(notes || [])\n // Connect the websocket and register the notification route.\n ws.connect(getSocketURI(), this.reconnected)\n ws.registerRoute(notificationRoute, (note: CoreNote) => {\n this.notify(note)\n })\n }\n\n /*\n * reconnected is called by the websocket client when a reconnection is made.\n */\n reconnected () {\n window.location.reload() // This triggers another websocket disconnect/connect (!)\n // a fetchUser() and loadPage(window.history.state.page) might work\n }\n\n /*\n * Fetch and save the user, which is the primary core state that must be\n * maintained by the Application.\n */\n async fetchUser (): Promise {\n const resp: APIResponse = await getJSON('/api/user')\n if (!this.checkResponse(resp)) return\n const user = (resp as any) as User\n this.seedGenTime = user.seedgentime\n this.user = user\n this.assets = user.assets\n this.exchanges = user.exchanges\n this.walletMap = {}\n this.fiatRatesMap = user.fiatRates\n for (const [assetID, asset] of (Object.entries(user.assets) as [any, SupportedAsset][])) {\n if (asset.wallet) {\n this.walletMap[assetID] = asset.wallet\n }\n }\n\n this.updateMenuItemsDisplay()\n return user\n }\n\n authed () {\n return this.user && this.user.authed\n }\n\n /* Load the page from the server. Insert and bind the DOM. */\n async loadPage (page: string, data?: any, skipPush?: boolean): Promise {\n // Close some menus and tooltips.\n this.tooltip.style.left = '-10000px'\n Doc.hide(this.page.noteBox, this.page.profileBox)\n // Parse the request.\n const url = new URL(`/${page}`, window.location.origin)\n const requestedHandler = handlerFromPath(page)\n // Fetch and parse the page.\n const response = await window.fetch(url.toString())\n if (!response.ok) return false\n const html = await response.text()\n const doc = Doc.noderize(html)\n const main = idel(doc, 'main')\n const delivered = main.dataset.handler\n // Append the request to the page history.\n if (!skipPush) {\n const path = delivered === requestedHandler ? url.toString() : `/${delivered}`\n window.history.pushState({ page: page, data: data }, '', path)\n }\n // Insert page and attach handlers.\n document.title = doc.title\n this.main.replaceWith(main)\n this.main = main\n this.noteReceivers = []\n Doc.empty(this.headerSpace)\n this.attach(data)\n return true\n }\n\n /* attach binds the common handlers and calls the page constructor. */\n attach (data: any) {\n const handlerID = this.main.dataset.handler\n if (!handlerID) {\n console.error('cannot attach to content with no specified handler')\n return\n }\n this.attachCommon(this.main)\n if (this.loadedPage) this.loadedPage.unload()\n const constructor = constructors[handlerID]\n if (constructor) this.loadedPage = new constructor(this.main, data)\n else this.loadedPage = null\n\n // Bind the tooltips.\n this.bindTooltips(this.main)\n }\n\n bindTooltips (ancestor: HTMLElement) {\n ancestor.querySelectorAll('[data-tooltip]').forEach((el: HTMLElement) => {\n bind(el, 'mouseenter', () => {\n this.tooltip.textContent = el.dataset.tooltip || ''\n const lyt = Doc.layoutMetrics(el)\n let left = lyt.centerX - this.tooltip.offsetWidth / 2\n if (left < 0) left = 5\n if (left + this.tooltip.offsetWidth > document.body.offsetWidth) {\n left = document.body.offsetWidth - this.tooltip.offsetWidth - 5\n }\n this.tooltip.style.left = `${left}px`\n this.tooltip.style.top = `${lyt.bodyTop - this.tooltip.offsetHeight - 5}px`\n })\n bind(el, 'mouseleave', () => {\n this.tooltip.style.left = '-10000px'\n })\n })\n }\n\n /* attachHeader attaches the header element, which unlike the main element,\n * isn't replaced during page navigation.\n */\n attachHeader () {\n this.header = idel(document.body, 'header')\n this.headerSpace = Doc.idel(this.header, 'headerSpace')\n this.popupNotes = idel(document.body, 'popupNotes')\n this.popupTmpl = Doc.tmplElement(this.popupNotes, 'note')\n if (this.popupTmpl) this.popupTmpl.remove()\n else console.error('popupTmpl element not found')\n this.tooltip = idel(document.body, 'tooltip')\n const page = this.page = Doc.idDescendants(this.header)\n page.noteTmpl.removeAttribute('id')\n page.noteTmpl.remove()\n page.pokeTmpl.removeAttribute('id')\n page.pokeTmpl.remove()\n page.loader.remove()\n Doc.show(page.loader)\n\n bind(page.noteBell, 'click', async () => {\n Doc.hide(page.pokeList)\n Doc.show(page.noteList)\n this.ackNotes()\n page.noteCat.classList.add('active')\n page.pokeCat.classList.remove('active')\n this.showDropdown(page.noteBell, page.noteBox)\n Doc.hide(page.noteIndicator)\n for (const note of this.notes) {\n if (note.acked) {\n note.el.classList.remove('firstview')\n }\n }\n this.setNoteTimes(page.noteList)\n this.setNoteTimes(page.pokeList)\n this.storeNotes()\n })\n\n bind(page.burgerIcon, 'click', () => {\n Doc.hide(page.logoutErr)\n this.showDropdown(page.burgerIcon, page.profileBox)\n })\n\n bind(page.innerNoteIcon, 'click', () => { Doc.hide(page.noteBox) })\n bind(page.innerBurgerIcon, 'click', () => { Doc.hide(page.profileBox) })\n\n bind(page.profileSignout, 'click', async () => await this.signOut())\n\n bind(page.pokeCat, 'click', () => {\n this.setNoteTimes(page.pokeList)\n page.pokeCat.classList.add('active')\n page.noteCat.classList.remove('active')\n Doc.hide(page.noteList)\n Doc.show(page.pokeList)\n this.ackNotes()\n })\n\n bind(page.noteCat, 'click', () => {\n this.setNoteTimes(page.noteList)\n page.noteCat.classList.add('active')\n page.pokeCat.classList.remove('active')\n Doc.hide(page.pokeList)\n Doc.show(page.noteList)\n this.ackNotes()\n })\n }\n\n /*\n * showDropdown sets the position and visibility of the specified dropdown\n * dialog according to the position of its icon button.\n */\n showDropdown (icon: HTMLElement, dialog: HTMLElement) {\n const ico = icon.getBoundingClientRect()\n Doc.hide(this.page.noteBox, this.page.profileBox)\n Doc.show(dialog)\n dialog.style.right = `${window.innerWidth - ico.left - ico.width + 5}px`\n dialog.style.top = `${ico.top - 4}px`\n\n const hide = (e: MouseEvent) => {\n if (!Doc.mouseInElement(e, dialog)) {\n Doc.hide(dialog)\n unbind(document, 'click', hide)\n if (dialog === this.page.noteBox && Doc.isDisplayed(this.page.noteList)) {\n this.ackNotes()\n }\n }\n }\n bind(document, 'click', hide)\n }\n\n ackNotes () {\n const acks = []\n for (const note of this.notes) {\n if (note.acked) {\n note.el.classList.remove('firstview')\n } else {\n note.acked = true\n if (note.id && note.severity > ntfn.POKE) acks.push(note.id)\n }\n }\n if (acks.length) ws.request('acknotes', acks)\n Doc.hide(this.page.noteIndicator)\n }\n\n setNoteTimes (noteList: HTMLElement) {\n for (const el of (Array.from(noteList.children) as NoteElement[])) {\n Doc.safeSelector(el, 'span.note-time').textContent = Doc.timeSince(el.note.stamp)\n }\n }\n\n /*\n * bindInternalNavigation hijacks navigation by click on any local links that\n * are descendants of ancestor.\n */\n bindInternalNavigation (ancestor: HTMLElement) {\n const pageURL = new URL(window.location.href)\n ancestor.querySelectorAll('a').forEach(a => {\n if (!a.href) return\n const url = new URL(a.href)\n if (url.origin === pageURL.origin) {\n const token = url.pathname.substring(1)\n const params: Record = {}\n if (url.search) {\n url.searchParams.forEach((v, k) => {\n params[k] = v\n })\n }\n Doc.bind(a, 'click', (e: Event) => {\n e.preventDefault()\n this.loadPage(token, params)\n })\n }\n })\n }\n\n /*\n * storeNotes stores the list of notifications in Window.localStorage. The\n * actual stored list is stripped of information not necessary for display.\n */\n storeNotes () {\n State.storeLocal(State.notificationsLK, this.notes.map(n => {\n return {\n subject: n.subject,\n details: n.details,\n severity: n.severity,\n stamp: n.stamp,\n id: n.id,\n acked: n.acked\n }\n }))\n }\n\n /*\n * updateMenuItemsDisplay should be called when the user has signed in or out,\n * and when the user registers a DEX.\n */\n updateMenuItemsDisplay () {\n const { page, user } = this\n if (!page) {\n // initial page load, header elements not yet attached but menu items\n // would already be hidden/displayed as appropriate.\n return\n }\n const authed = user && user.authed\n if (authed) page.profileBox.classList.add('authed')\n else {\n page.profileBox.classList.remove('authed')\n Doc.hide(page.noteBell, page.walletsMenuEntry, page.marketsMenuEntry)\n return\n }\n Doc.show(page.noteBell, page.walletsMenuEntry)\n if (Object.keys(user.exchanges).length > 0) {\n Doc.show(page.marketsMenuEntry)\n } else {\n Doc.hide(page.marketsMenuEntry)\n }\n }\n\n /* attachCommon scans the provided node and handles some common bindings. */\n attachCommon (node: HTMLElement) {\n this.bindInternalNavigation(node)\n }\n\n /*\n * updateBondConfs updates the information for a pending bond.\n */\n updateBondConfs (dexAddr: string, coinID: string, confs: number, assetID: number) {\n const dex = this.exchanges[dexAddr]\n const symbol = this.assets[assetID].symbol\n dex.pendingBonds[coinID] = { confs, assetID, symbol }\n }\n\n updateTier (host: string, tier: number) {\n this.exchanges[host].tier = tier\n }\n\n /*\n * handleBondNote is the handler for the 'bondpost'-type notification, which\n * is used to update the dex tier and registration status.\n */\n handleBondNote (note: BondNote) {\n switch (note.topic) {\n case 'RegUpdate':\n if (note.coinID !== null) { // should never be null for RegUpdate\n this.updateBondConfs(note.dex, note.coinID, note.confirmations, note.asset)\n }\n break\n case 'BondConfirmed':\n if (note.tier !== null) { // should never be null for BondConfirmed\n this.updateTier(note.dex, note.tier)\n }\n break\n default:\n break\n }\n }\n\n /*\n * setNotes sets the current notification cache and populates the notification\n * display.\n */\n setNotes (notes: CoreNote[]) {\n this.log('notes', 'setNotes', notes)\n this.notes = []\n Doc.empty(this.page.noteList)\n for (let i = 0; i < notes.length; i++) {\n this.prependNoteElement(notes[i], true)\n }\n this.storeNotes()\n }\n\n updateUser (note: CoreNote) {\n const { user, assets, walletMap } = this\n if (note.type === 'fiatrateupdate') {\n this.fiatRatesMap = (note as RateNote).fiatRates\n return\n }\n // Some notes can be received before we get a User during login.\n if (!user) return\n switch (note.type) {\n case 'order': {\n const orderNote = note as OrderNote\n const order = orderNote.order\n const mkt = user.exchanges[order.host].markets[order.market]\n const tempID = orderNote.tempID\n\n // Ensure market's inflight orders list is updated.\n if (note.topic === 'AsyncOrderSubmitted') {\n const inFlight = order as InFlightOrder\n inFlight.tempID = tempID\n if (!mkt.inflight) mkt.inflight = [inFlight]\n else mkt.inflight.push(inFlight)\n break\n } else if (note.topic === 'AsyncOrderFailure') {\n mkt.inflight = mkt.inflight.filter(ord => ord.tempID !== tempID)\n break\n } else {\n for (const i in mkt.inflight || []) {\n if (!(mkt.inflight[i].tempID === tempID)) continue\n mkt.inflight = mkt.inflight.filter(ord => ord.tempID !== tempID)\n break\n }\n }\n\n // Updates given order in market's orders list if it finds it.\n // Returns a bool which indicates if order was found.\n const updateOrder = (mkt: Market, ord: Order) => {\n for (const i in mkt.orders || []) {\n if (mkt.orders[i].id === ord.id) {\n mkt.orders[i] = ord\n return true\n }\n }\n return false\n }\n // If the notification order already exists we update it.\n // In case market's orders list is empty or the notification order isn't\n // part of it we add it manually as this means the order was\n // just placed.\n if (!mkt.orders) mkt.orders = [order]\n else if (!updateOrder(mkt, order)) mkt.orders.push(order)\n break\n }\n case 'balance': {\n const n: BalanceNote = note as BalanceNote\n const asset = user.assets[n.assetID]\n // Balance updates can come before the user is fetched after login.\n if (!asset) break\n const w = asset.wallet\n if (w) w.balance = n.balance\n break\n }\n case 'bondpost':\n this.handleBondNote(note as BondNote)\n break\n case 'walletstate':\n case 'walletconfig': {\n // assets can be null if failed to connect to dex server.\n if (!assets) return\n const wallet = (note as WalletConfigNote).wallet\n const asset = assets[wallet.assetID]\n asset.wallet = wallet\n walletMap[wallet.assetID] = wallet\n break\n }\n case 'match': {\n const n = note as MatchNote\n const ord = this.order(n.orderID)\n if (ord) updateMatch(ord, n.match)\n break\n }\n case 'conn': {\n const n = note as ConnEventNote\n const xc = user.exchanges[n.host]\n if (xc) xc.connectionStatus = n.connectionStatus\n break\n }\n case 'spots': {\n const n = note as SpotPriceNote\n const xc = user.exchanges[n.host]\n // Spots can come before the user is fetched after login and before/while the\n // markets page reload when it receives a dex conn note.\n if (!xc || !xc.markets) break\n for (const [mktName, spot] of Object.entries(n.spots)) xc.markets[mktName].spot = spot\n break\n }\n case 'fiatrateupdate': {\n this.fiatRatesMap = (note as RateNote).fiatRates\n break\n }\n case 'bot': {\n const n = note as BotNote\n const [r, bots] = [n.report, this.user.bots]\n const idx = bots.findIndex((report: BotReport) => report.programID === r.programID)\n switch (n.topic) {\n case 'BotRetired':\n if (idx >= 0) bots.splice(idx, 1)\n break\n default:\n if (idx >= 0) bots[idx] = n.report\n else bots.push(n.report)\n }\n }\n }\n }\n\n /*\n * notify is the top-level handler for notifications received from the client.\n * Notifications are propagated to the loadedPage.\n */\n notify (note: CoreNote) {\n // Handle type-specific updates.\n this.log('notes', 'notify', note)\n this.updateUser(note)\n // Inform the page.\n for (const feeder of this.noteReceivers) {\n const f = feeder[note.type]\n if (!f) continue\n try {\n f(note)\n } catch (error) {\n console.error('note feeder error:', error.message ? error.message : error)\n console.log(note)\n console.log(error.stack)\n }\n }\n // Discard data notifications.\n if (note.severity < ntfn.POKE) return\n // Poke notifications have their own display.\n const { popupTmpl, popupNotes, showPopups } = this\n if (showPopups) {\n const span = popupTmpl.cloneNode(true) as HTMLElement\n Doc.tmplElement(span, 'text').textContent = `${note.subject}: ${note.details}`\n const indicator = Doc.tmplElement(span, 'indicator')\n if (note.severity === ntfn.POKE) {\n Doc.hide(indicator)\n } else setSeverityClass(indicator, note.severity)\n popupNotes.appendChild(span)\n // These take up screen space. Only show max 5 at a time.\n while (popupNotes.children.length > 5) popupNotes.removeChild(popupNotes.firstChild as Node)\n setTimeout(async () => {\n await Doc.animate(500, (progress: number) => {\n span.style.opacity = String(1 - progress)\n })\n span.remove()\n }, 6000)\n }\n // Success and higher severity go to the bell dropdown.\n if (note.severity === ntfn.POKE) this.prependPokeElement(note)\n else this.prependNoteElement(note)\n }\n\n /*\n * registerNoteFeeder registers a feeder for core notifications. The feeder\n * will be de-registered when a new page is loaded.\n */\n registerNoteFeeder (receivers: Record void>) {\n this.noteReceivers.push(receivers)\n }\n\n /*\n * log prints to the console if a logger has been enabled. Loggers are created\n * implicitly by passing a loggerID to log. i.e. you don't create a logger,\n * you just log to it. Loggers are enabled by invoking a global function,\n * enableLogger(loggerID, onOffBoolean), from the browser's js console. Your\n * choices are stored across sessions. Some common and useful loggers are\n * listed below, but this list is not meant to be comprehensive.\n *\n * LoggerID Description\n * -------- -----------\n * notes Notifications of all levels.\n * book Order book feed.\n * ws.........Websocket connection status changes.\n */\n log (loggerID: string, ...msg: any) {\n if (this.loggers[loggerID]) console.log(`${nowString()}[${loggerID}]:`, ...msg)\n if (this.recorders[loggerID]) {\n this.recorders[loggerID].push({\n time: nowString(),\n msg: msg\n })\n }\n }\n\n prependPokeElement (cn: CoreNote) {\n const [el, note] = this.makePoke(cn)\n this.pokes.push(note)\n while (this.pokes.length > noteCacheSize) this.pokes.shift()\n this.prependListElement(this.page.pokeList, note, el)\n }\n\n prependNoteElement (cn: CoreNote, skipSave?: boolean) {\n const [el, note] = this.makeNote(cn)\n this.notes.push(note)\n while (this.notes.length > noteCacheSize) this.notes.shift()\n const noteList = this.page.noteList\n this.prependListElement(noteList, note, el)\n if (!skipSave) this.storeNotes()\n // Set the indicator color.\n if (this.notes.length === 0 || (Doc.isDisplayed(this.page.noteBox) && Doc.isDisplayed(noteList))) return\n let unacked = 0\n const severity = this.notes.reduce((s, note) => {\n if (!note.acked) unacked++\n if (!note.acked && note.severity > s) return note.severity\n return s\n }, ntfn.IGNORE)\n const ni = this.page.noteIndicator\n setSeverityClass(ni, severity)\n if (unacked) {\n ni.textContent = String((unacked > noteCacheSize - 1) ? `${noteCacheSize - 1}+` : unacked)\n Doc.show(ni)\n } else Doc.hide(ni)\n }\n\n prependListElement (noteList: HTMLElement, note: CoreNotePlus, el: NoteElement) {\n el.note = note\n noteList.prepend(el)\n while (noteList.children.length > noteCacheSize) noteList.removeChild(noteList.lastChild as Node)\n this.setNoteTimes(noteList)\n }\n\n /*\n * makeNote constructs a single notification element for the drop-down\n * notification list.\n */\n makeNote (note: CoreNote): [NoteElement, CoreNotePlus] {\n const el = this.page.noteTmpl.cloneNode(true) as NoteElement\n if (note.severity > ntfn.POKE) {\n const cls = note.severity === ntfn.SUCCESS ? 'good' : note.severity === ntfn.WARNING ? 'warn' : 'bad'\n Doc.safeSelector(el, 'div.note-indicator').classList.add(cls)\n }\n\n Doc.safeSelector(el, 'div.note-subject').textContent = note.subject\n Doc.safeSelector(el, 'div.note-details').textContent = note.details\n const np: CoreNotePlus = { el, ...note }\n return [el, np]\n }\n\n makePoke (note: CoreNote): [NoteElement, CoreNotePlus] {\n const el = this.page.pokeTmpl.cloneNode(true) as NoteElement\n const d = new Date(note.stamp)\n Doc.tmplElement(el, 'dateTime').textContent = `${d.toLocaleDateString()}, ${d.toLocaleTimeString()}`\n Doc.tmplElement(el, 'details').textContent = `${note.subject}: ${note.details}`\n const np: CoreNotePlus = { el, ...note }\n return [el, np]\n }\n\n /*\n * loading appends the loader to the specified element and displays the\n * loading icon. The loader will block all interaction with the specified\n * element until Application.loaded is called.\n */\n loading (el: HTMLElement): () => void {\n const loader = this.page.loader.cloneNode(true) as HTMLElement\n el.appendChild(loader)\n return () => { loader.remove() }\n }\n\n /* orders retrieves a list of orders for the specified dex and market\n * including inflight orders.\n */\n orders (host: string, mktID: string): Order[] {\n let orders: Order[] = []\n const mkt = this.user.exchanges[host].markets[mktID]\n if (mkt.orders) orders = orders.concat(mkt.orders)\n if (mkt.inflight) orders = orders.concat(mkt.inflight)\n return orders\n }\n\n /*\n * haveActiveOrders returns whether or not there are active orders involving a\n * certain asset.\n */\n haveActiveOrders (assetID: number): boolean {\n for (const xc of Object.values(this.user.exchanges)) {\n if (!xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n if (!market.orders) continue\n for (const ord of market.orders) {\n if ((ord.baseID === assetID || ord.quoteID === assetID) &&\n (ord.status < StatusExecuted || hasActiveMatches(ord))) return true\n }\n }\n }\n return false\n }\n\n /* order attempts to locate an order by order ID. */\n order (oid: string): Order | null {\n for (const xc of Object.values(this.user.exchanges)) {\n if (!xc || !xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n if (!market.orders) continue\n for (const ord of market.orders) {\n if (ord.id === oid) return ord\n }\n }\n }\n return null\n }\n\n /*\n * canAccelerateOrder returns true if the \"from\" wallet of the order\n * supports acceleration, and if the order has unconfirmed swap\n * transactions.\n */\n canAccelerateOrder (order: Order): boolean {\n const walletTraitAccelerator = 1 << 4\n let fromAssetID\n if (order.sell) fromAssetID = order.baseID\n else fromAssetID = order.quoteID\n const wallet = this.walletMap[fromAssetID]\n if (!wallet || !(wallet.traits & walletTraitAccelerator)) return false\n if (order.matches) {\n for (let i = 0; i < order.matches.length; i++) {\n const match = order.matches[i]\n if (match.swap && match.swap.confs && match.swap.confs.count === 0) {\n return true\n }\n }\n }\n return false\n }\n\n /*\n * unitInfo fetches unit info [dex.UnitInfo] for the asset. If xc\n * [core.Exchange] is provided, and this is not a SupportedAsset, the UnitInfo\n * sent from the exchange's assets map [dex.Asset] will be used.\n */\n unitInfo (assetID: number, xc?: Exchange): UnitInfo {\n const supportedAsset = this.assets[assetID]\n if (supportedAsset) return supportedAsset.unitInfo\n if (!xc || !xc.assets) {\n throw Error(intl.prep(intl.ID_UNSUPPORTED_ASSET_INFO_ERR_MSG, { assetID: `${assetID}` }))\n }\n return xc.assets[assetID].unitInfo\n }\n\n /* conventionalRate converts the encoded atomic rate to a conventional rate */\n conventionalRate (baseID: number, quoteID: number, encRate: number, xc?: Exchange): number {\n const [b, q] = [this.unitInfo(baseID, xc), this.unitInfo(quoteID, xc)]\n\n const r = b.conventional.conversionFactor / q.conventional.conversionFactor\n return encRate * r / RateEncodingFactor\n }\n\n walletDefinition (assetID: number, walletType: string): WalletDefinition {\n const asset = this.assets[assetID]\n if (asset.token) return asset.token.definition\n if (!asset.info) throw Error('where\\'s the wallet info?')\n if (walletType === '') return asset.info.availablewallets[asset.info.emptyidx]\n return asset.info.availablewallets.filter(def => def.type === walletType)[0]\n }\n\n currentWalletDefinition (assetID: number): WalletDefinition {\n const asset = this.assets[assetID]\n if (asset.token) {\n return asset.token.definition\n }\n return this.walletDefinition(assetID, this.assets[assetID].wallet.type)\n }\n\n /*\n * fetchBalance requests a balance update from the API. The API response does\n * include the balance, but we're ignoring it, since a balance update\n * notification is received via the Application anyways.\n */\n async fetchBalance (assetID: number): Promise {\n const res: BalanceResponse = await postJSON('/api/balance', { assetID: assetID })\n if (!this.checkResponse(res)) {\n throw new Error(`failed to fetch balance for asset ID ${assetID}`)\n }\n return res.balance\n }\n\n /*\n * checkResponse checks the response object as returned from the functions in\n * the http module. If the response indicates that the request failed, it\n * returns false, otherwise, true.\n */\n checkResponse (resp: APIResponse): boolean {\n return (resp.requestSuccessful && resp.ok)\n }\n\n /**\n * signOut call to /api/logout, if response with no errors occurred remove auth\n * and other privacy-critical cookies/locals and reload the page, otherwise\n * show a notification.\n */\n async signOut () {\n const res = await postJSON('/api/logout')\n if (!this.checkResponse(res)) {\n if (res.code === Errors.activeOrdersErr) {\n this.page.logoutErr.textContent = intl.prep(intl.ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG)\n } else {\n this.page.logoutErr.textContent = res.msg\n }\n Doc.show(this.page.logoutErr)\n return\n }\n State.removeCookie(State.authCK)\n State.removeCookie(State.pwKeyCK)\n State.removeLocal(State.notificationsLK)\n window.location.href = '/login'\n }\n}\n\n/* getSocketURI returns the websocket URI for the client. */\nfunction getSocketURI (): string {\n const protocol = (window.location.protocol === 'https:') ? 'wss' : 'ws'\n return `${protocol}://${window.location.host}/ws`\n}\n\n/*\n * severityClassMap maps a notification severity level to a CSS class that\n * assigns a background color.\n */\nconst severityClassMap: Record = {\n [ntfn.SUCCESS]: 'good',\n [ntfn.ERROR]: 'bad',\n [ntfn.WARNING]: 'warn'\n}\n\n/* handlerFromPath parses the handler name from the path. */\nfunction handlerFromPath (path: string): string {\n return path.replace(/^\\//, '').split('/')[0].split('?')[0].split('#')[0]\n}\n\n/* nowString creates a string formatted like HH:MM:SS.xxx */\nfunction nowString (): string {\n const stamp = new Date()\n const h = stamp.getHours().toString().padStart(2, '0')\n const m = stamp.getMinutes().toString().padStart(2, '0')\n const s = stamp.getSeconds().toString().padStart(2, '0')\n const ms = stamp.getMilliseconds().toString().padStart(3, '0')\n return `${h}:${m}:${s}.${ms}`\n}\n\nfunction setSeverityClass (el: HTMLElement, severity: number) {\n el.classList.remove('bad', 'warn', 'good')\n el.classList.add(severityClassMap[severity])\n}\n\n/* updateMatch updates the match in or adds the match to the order. */\nfunction updateMatch (order: Order, match: Match) {\n for (const i in order.matches) {\n const m = order.matches[i]\n if (m.matchID === match.matchID) {\n order.matches[i] = match\n return\n }\n }\n order.matches = order.matches || []\n order.matches.push(match)\n}\n","import { CoreNote } from './registry'\n\nexport const IGNORE = 0\nexport const DATA = 1\nexport const POKE = 2\nexport const SUCCESS = 3\nexport const WARNING = 4\nexport const ERROR = 5\n\n/*\n * make constructs a new notification. The notification structure is a mirror of\n * the structure of notifications sent from the web server.\n * NOTE: I'm hoping to make this function obsolete, since errors generated in\n * javascript should usually be displayed/cached somewhere better. For example,\n * if the error is generated during submission of a form, the error should be\n * displayed on or near the form itself, not in the notifications.\n */\nexport function make (subject: string, details: string, severity: number): CoreNote {\n return {\n subject: subject,\n details: details,\n severity: severity,\n stamp: new Date().getTime(),\n acked: false,\n type: 'internal',\n topic: 'internal',\n id: ''\n }\n}\n","import Application from './js/app'\nimport { registerApplication } from './js/registry'\nimport './css/application.scss'\n\nconst app = new Application()\nregisterApplication(app)\napp.start()\n"],"names":["_typeof","_regeneratorRuntime","module","exports","__esModule","Op","Object","prototype","hasOwn","hasOwnProperty","defineProperty","obj","key","desc","value","$Symbol","Symbol","iteratorSymbol","iterator","asyncIteratorSymbol","asyncIterator","toStringTagSymbol","toStringTag","define","enumerable","configurable","writable","err","wrap","innerFn","outerFn","self","tryLocsList","protoGenerator","Generator","generator","create","context","Context","makeInvokeMethod","tryCatch","fn","arg","type","call","ContinueSentinel","GeneratorFunction","GeneratorFunctionPrototype","IteratorPrototype","this","getProto","getPrototypeOf","NativeIteratorPrototype","values","Gp","defineIteratorMethods","forEach","method","_invoke","AsyncIterator","PromiseImpl","invoke","resolve","reject","record","result","__await","then","unwrapped","error","previousPromise","callInvokeWithMethodAndArg","state","Error","undefined","done","delegate","delegateResult","maybeInvokeDelegate","sent","_sent","dispatchException","abrupt","methodName","TypeError","info","resultName","next","nextLoc","pushTryEntry","locs","entry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","iterable","iteratorMethod","isNaN","length","i","doneResult","displayName","isGeneratorFunction","genFun","ctor","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","iter","keys","val","object","reverse","pop","skipTempReset","prev","charAt","slice","stop","rootRecord","rval","exception","handle","loc","caught","hasCatch","hasFinally","finallyEntry","complete","finish","thrown","delegateYield","runtime","regeneratorRuntime","accidentalStrictMode","globalThis","Function","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","d","a","definition","o","get","prop","_arrayLikeToArray","arr","len","arr2","Array","_unsupportedIterableToArray","minLen","toString","from","test","_slicedToArray","isArray","_i","_s","_e","_x","_r","_arr","_n","_d","asyncGeneratorStep","gen","_next","_throw","_asyncToGenerator","args","arguments","apply","_classCallCheck","instance","Constructor","_toPropertyKey","input","hint","prim","toPrimitive","res","String","_defineProperties","target","props","descriptor","_createClass","protoProps","staticProps","_defineProperty","locale","ID_NO_PASS_ERROR_MSG","ID_NO_APP_PASS_ERROR_MSG","ID_SET_BUTTON_BUY","ID_SET_BUTTON_SELL","ID_OFF","ID_READY","ID_NO_WALLET","ID_DISABLED_MSG","ID_WALLET_SYNC_PROGRESS","ID_HIDE_ADDITIONAL_SETTINGS","ID_SHOW_ADDITIONAL_SETTINGS","ID_BUY","ID_SELL","ID_NOT_SUPPORTED","ID_VERSION_NOT_SUPPORTED","ID_CONNECTION_FAILED","ID_ORDER_PREVIEW","ID_CALCULATING","ID_ESTIMATE_UNAVAILABLE","ID_NO_ZERO_RATE","ID_NO_ZERO_QUANTITY","ID_TRADE","ID_NO_ASSET_WALLET","ID_EXECUTED","ID_BOOKED","ID_CANCELING","ID_PASSWORD_NOT_MATCH","ID_ACCT_UNDEFINED","ID_KEEP_WALLET_PASS","ID_NEW_WALLET_PASS","ID_LOT","ID_LOTS","ID_UNKNOWN","ID_EPOCH","ID_ORDER_SUBMITTING","ID_SETTLING","ID_NO_MATCH","ID_CANCELED","ID_REVOKED","ID_WAITING_FOR_CONFS","ID_NONE_SELECTED","ID_REGISTRATION_FEE_SUCCESS","ID_API_ERROR","ID_ADD","ID_CREATE","ID_SETUP_WALLET","ID_WALLET_READY","ID_CHANGE_WALLET_TYPE","ID_KEEP_WALLET_TYPE","WALLET_READY","WALLET_PENDING","SETUP_NEEDED","ID_SEND_SUCCESS","ID_RECONFIG_SUCCESS","ID_RESCAN_STARTED","ID_NEW_WALLET_SUCCESS","ID_WALLET_UNLOCKED","ID_SELLING","ID_BUYING","ID_WALLET_DISABLED_MSG","ID_WALLET_ENABLED_MSG","ID_ACTIVE_ORDERS_ERR_MSG","ID_AVAILABLE","ID_LOCKED","ID_IMMATURE","ID_FEE_BALANCE","ID_CANDLES_LOADING","ID_DEPTH_LOADING","ID_INVALID_ADDRESS_MSG","ID_TXFEE_ERR_MSG","ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG","ID_INVALID_DATE_ERR_MSG","ID_NO_ARCHIVED_RECORDS","ID_DELETE_ARCHIVED_RECORDS_RESULT","ID_ARCHIVED_RECORDS_PATH","ID_DEFAULT","ID_ADDED","ID_DISCOVERED","ID_UNSUPPORTED_ASSET_INFO_ERR_MSG","ID_LIMIT_ORDER","ID_LIMIT_ORDER_IMMEDIATE_TIF","ID_MARKET_ORDER","ID_MATCH_STATUS_NEWLY_MATCHED","ID_MATCH_STATUS_MAKER_SWAP_CAST","ID_MATCH_STATUS_TAKER_SWAP_CAST","ID_MATCH_STATUS_MAKER_REDEEMED","ID_MATCH_STATUS_REDEMPTION_SENT","ID_MATCH_STATUS_REDEMPTION_CONFIRMED","ID_MATCH_STATUS_REVOKED","ID_MATCH_STATUS_REFUNDED","ID_MATCH_STATUS_REFUND_PENDING","ID_MATCH_STATUS_REDEEM_PENDING","ID_MATCH_STATUS_COMPLETE","ID_TAKER_FOUND_MAKER_REDEMPTION","ID_OPEN_WALLET_ERR_MSG","ID_ORDER_ACCELERATION_FEE_ERR_MSG","ID_ORDER_ACCELERATION_ERR_MSG","ID_CONNECTED","ID_DISCONNECTED","ID_INVALID_CERTIFICATE","ID_CONFIRMATIONS","ID_TAKER","ID_MAKER","ID_EMPTY_DEX_ADDRESS_MSG","ID_SELECT_WALLET_FOR_FEE_PAYMENT","ID_UNAVAILABLE","ID_WALLET_SYNC_FINISHING_UP","enUS","ptBR","zhCN","plPL","deDE","ar","localesMap","defaultLocale","prep","k","expression","replace","_","stringTemplateParser","window","localeDiscrepancies","ref","entries","lang","dict","s","console","log","parser","DOMParser","BipIDs","BipSymbols","intFormatter","Intl","NumberFormat","navigator","languages","threeSigFigs","minimumSignificantDigits","maximumSignificantDigits","fiveSigFigs","decimalFormatters","fullPrecisionFormatters","fullPrecisionFormatter","prec","formatter","formatters","min","max","fmt","minimumFractionDigits","maximumFractionDigits","convertToConventional","v","unitInfo","f","conventional","conversionFactor","Math","round","log10","Doc","el","id","querySelector","ev","addEventListener","removeEventListener","html","parseFromString","e","contains","rect","getBoundingClientRect","pageX","left","right","pageY","top","bottom","box","docEl","document","documentElement","scrollTop","scrollLeft","w","offsetWidth","h","offsetHeight","bodyTop","bodyLeft","width","height","centerX","centerY","parent","kid","parentMetrics","layoutMetrics","kidMetrics","els","firstChild","removeChild","ancestor","empty","kids","appendChild","classList","add","remove","vis","show","hide","duration","easingAlgo","Animation","wait","querySelectorAll","children","warn","createElement","applySelector","vAtomic","Number","isInteger","format","decimalFormatter","rate","intl","symbol","indexOf","substring","assetID","logoPath","parts","split","assetSymbol","textContent","toUpperCase","span","tmpls","tmpl","removeAttribute","dataset","t","formatDuration","Date","getTime","dur","y","mo","m","seconds","floor","count","timeMod","aYear","aMonth","aDay","anHour","aMinute","inputFields","inputField","preventDefault","msg","thread","run","easer","Easing","linear","start","end","Forever","MAX_SAFE_INTEGER","range","frameDuration","now","endAnimation","runCompletionFunction","sleep","easeIn","easeOut","easeInHard","easeOutHard","easeOutElastic","c4","PI","pow","sin","WalletIcons","stateElement","icons","sleeping","locked","unlocked","nowallet","syncing","nopeers","disabled","status","tooltip","wallet","syncIcon","running","peerCount","synced","syncProgress","toFixed","setSyncing","open","ms","setTimeout","State","cname","cvalue","setTime","expires","toUTCString","cookie","trim","cKey","filter","item","includes","darkModeCK","getCookie","pwKeyCK","localStorage","setItem","JSON","stringify","getItem","parse","removeItem","_assertThisInitialized","ReferenceError","_setPrototypeOf","p","bind","_inherits","subClass","superClass","_possibleConstructorReturn","_getPrototypeOf","setCookie","fetchLocal","popupsLK","storeLocal","leftMarketDockLK","Errors","ConnectionStatus","PeerSource","application","orderOptTmpl","booleanOptTmpl","rangeOptTmpl","BasePage","requestJSON","addr","reqBody","fetch","headers","Headers","body","response","json","requestSuccessful","text","postJSON","data","getJSON","_toConsumableArray","app","setOptionTemplates","page","Option","opt","report","node","cloneNode","parseTemplate","optName","displayname","description","chainIcon","src","on","enable","toggle","stopPropagation","disable","BooleanOption","changed","cfg","control","controls","reason","store","XYRangeOption","xyRange","setVal","x","handler","XYRangeHandler","setValue","initVal","updated","selected","roundY","Boolean","slider","rangeX","rangeY","normalizeX","setConfig","newCfg","rangeLblStart","label","rangeLblEnd","xUnit","yUnit","r","scrollingX","accept","clickOutX","xInput","xx","parseFloat","clamp","unbind","focus","clickOutY","yInput","yy","button","startX","clientWidth","startLeft","trackMouse","ee","mouseUp","skipUpdate","style","RateEncodingFactor","sellString","ord","sell","toLowerCase","toLocaleLowerCase","typeString","tif","isMarketBuy","hasActiveMatches","order","matches","active","statusString","isLive","cancelling","filled","qty","reduce","match","isCancel","settled","side","baseToQuote","base","revokedMatchStatus","matchStatus","PIPI","plusChar","fromCharCode","minusChar","darkTheme","axisLabel","gridBorder","gridLines","gapLine","zoom","zoomHover","sellLine","buyLine","sellFill","buyFill","crosshairs","legendFill","legendText","lightTheme","Chart","reporters","theme","isDark","canvas","visible","ctx","getContext","textAlign","textBaseline","mousePos","clientX","clientY","draw","ResizeObserver","resize","observe","wheelLimiter","wheel","click","setVis","visibilityState","renderScheduled","unattachers","clearRect","render","deltaY","clientHeight","plotExtents","Extents","xLblExtents","yLblExtents","plotRegion","Region","xRegion","yRegion","requestAnimationFrame","bigger","u","fontSize","font","fillStyle","labels","minX","maxX","unitLines","extents","plot","tools","applyLabelStyle","lastX","unitCenter","lbls","lbl","fillText","txt","lineWidth","strokeStyle","line","region","minY","maxY","unit","lastY","step","valFmt","yLabels","makeLabels","dataExtents","yAxisWidth","widest","plotYLabels","beginPath","dataCoords","moveTo","lineTo","stroke","DepthChart","resized","clicked","zoomed","zoomLevel","lines","markers","buys","sells","setZoomBttns","zoomInBttn","zoomOutBttn","book","wheeled","translator","unx","lotSize","rateStep","baseUnitInfo","quoteUnitInfo","qFactor","bFactor","baseUnit","quoteUnit","gap","midGap","gapWidth","minZoom","clear","halfWindow","high","low","buyMarkers","sellMarkers","sort","b","buyDepth","buyEpoch","sellDepth","sellEpoch","volumeReport","buyBase","buyQuote","sellBase","sellQuote","sum","epochSum","epoch","floatCompare","shift","buySum","last","epochBuySum","sellSum","epochSellSum","growthFactor","doYLabels","xLabels","plotXLabels","mouseData","drawFrame","formatLabelValue","topCenterX","midX","topCenterY","zoomPct","xRange","zoomText","measureText","bttnLeft","bttnTop","bttnSize","setExtents","hover","midY","drawLine","color","save","setLineDash","restore","tolerance","hoverMarkers","marker","hovered","withinTolerance","size","tip","sqrt","closePath","fill","dataX","evalSide","trigger","ptX","dotColor","bestDepth","pt","depth","drawDepth","radius","arc","dot","volume","mouse","firstPt","globalAlpha","bestGapBuy","bestGapSell","CandleChart","numToShow","ext","candleExtents","yRange","candleRegion","volumeExtents","volumeRegion","resizeTimer","clearTimeout","idx","zoomLevels","candles","candleWidth","allCandles","c","truncate","endStamp","paddedStart","paddedWidth","first","highRate","lowRate","matchVolume","highVol","market","ratestep","rFactor","rateConversionFactor","quotesymbol","screenW","spacingGuess","diff","tick","zoneOffset","getTimezoneOffset","dayStamp","lastDay","lastYear","pts","months","getMonth","getDate","getHours","getMinutes","padStart","year","getFullYear","makeCandleTimeLabels","mouseCandle","selectedStartStamp","fillRect","yExt","rangeTxt","toLocaleString","rangeWidth","xExt","rectArgs","rangeHeight","strokeRect","volDataExtents","startRate","endRate","cx","maxCandles","Wave","opts","zIndex","random","colorShift","amplitudes","ks","speeds","phases","single","angularX","angularTime","cos","ani","drawValues","cw","ch","l","message","ypad","halfH","msgRegion","hsl","bg","backgroundColor","getComputedStyle","getPropertyValue","lineCap","grad","createLinearGradient","addColorStop","prog","xMin","xMax","yMin","yMax","screenMinX","screenMaxY","screenH","xFactor","yFactor","uny","drawFunc","skipMask","clip","tx","ty","transform","tickGuess","absMax","abs","sigFigs","toPrecision","unitW","x0","y0","x1","y1","skipStroke","labelSpecs","NewWalletForm","form","success","pwCache","backFunc","pwHiders","refresh","goBack","walletTabTmpl","subform","WalletConfigForm","walletSettings","submitAdd","submit","oneBttn","registerNoteFeeder","walletstate","note","reportWalletState","createwallet","reportCreationUpdate","parentSyncer","createUpdater","passwordIsCached","pw","walletType","parentForm","createForm","pass","newWalletPass","config","map","appPass","mainForm","newWalletErr","current","asset","parentAsset","selectedDef","createWallet","checkResponse","setError","runParentSync","parentSyncPct","parentName","parentLogo","childName","childLogo","parentSyncing","syncParent","assets","topic","subject","details","parseAsset","tabs","walletTypeTabs","winfo","assetName","header","assetLogo","pinfo","walletDefs","availablewallets","wDef","tab","update","bindTooltips","walletCreationPending","token","user","parentID","walletDef","appPwCached","auth","oneBttnBox","newWalletPassBox","configOpts","configopts","isBirthdayConfig","seedGenTime","toUnixDate","containsRequired","required","noWalletPWNeeded","seeded","noauth","parentAndTokenOpts","regAsset","tokenOpts","tokenOptsCopy","dynamicOpts","defaultSettings","walletSettingsHeader","fileSelector","loadDefaults","errMsg","configpath","configID","loaded","loading","setLoadedConfig","repeatableCounter","sectionize","configElements","allSettings","tmplElement","textInputTmpl","dateInputTmpl","checkboxTmpl","repeatableTmpl","fileInput","showOther","showIcon","hideIcon","showHideMsg","otherSettings","loadedSettingsMsg","loadedSettings","defaultSettingsMsg","fileInputChanged","setOtherSettingsViz","files","configtext","append","reorder","defaultOpts","loadedOpts","insertAfter","elID","isboolean","isdate","repeatable","addOpt","configKey","safeSelector","htmlFor","prepend","logo","Image","logoPathFromID","after","noecho","autocomplete","checked","getMinMaxVal","minMax","dateToString","date","disablewhenactive","assetHasActiveOrders","activeOrders","defaultedOpts","finds","handledRepeatables","removes","vals","firstVal","newEl","parentElement","parseInt","splice","minDate","MIN_SAFE_INTEGER","maxDate","inputs","ConfirmRegistrationForm","certFile","bondStrengthField","bondAssetID","ui","bondAsset","xc","bondAssets","bondAmt","formatCoinValue","totalBondAmount","amount","submitForm","passBox","host","bondUnit","singleBondAmount","animate","opacity","offset","regErr","innerText","cert","dexAddr","postBondForm","bond","FeeAssetSelectionForm","cleanTemplates","marketTmpl","assetTmpl","walletCreated","assetTmpls","allMarkets","cFactor","marketNode","mkt","excludeIcon","baseAsset","baseid","quoteAsset","quoteid","excludeBase","otherSymbol","otherLogo","parentNode","insertBefore","nextSibling","baseSymbol","quoteSymbol","baseName","replaceWith","symbolize","quoteName","lotsize","lotSizeSymbol","spot","quoteLotSize","quoteLot","OrderUtil","assetNode","fee","feeAmt","feeSymbol","confs","setReadyMessage","ready","markets","fader","setExchange","how","regAssetElements","allmkts","marginTop","paddingTop","WalletWaitForm","progressCache","progressed","funded","balance","reportBalance","bondFeeBuffer","parentAssetSynced","depoAddr","address","syncUncheck","syncCheck","balUncheck","balCheck","syncRemainBox","balanceBox","totalForBond","sendEnough","txFeeBox","sendEnoughForToken","txFeeBalanceBox","sendEnoughWithEst","txFee","parentFees","tokenFees","txFeeUnit","parentUnit","parentBalUnit","parentBal","available","syncSpinner","progress","reportProgress","avail","parentAvail","syncRemaining","syncFinishingUp","cache","stamp","progDelta","progRate","toGoTime","syncRemain","UnlockWalletForm","idDescendants","submitUnlock","currentAsset","uwAssetLogo","uwAssetName","uwAppPass","unlockErr","uwAppPassBox","submitUnlockDiv","AccelerateOrderForm","accelerateSubmit","submitEarlyConfirm","sendAccelerateRequest","earlyAcceleration","recentAccelerationTime","timePast","recentSwapTime","wasAcceleration","recentAccelerationMsg","recentSwapMsg","configureAccelerationDiv","accelerateErr","earlyAccelerationDiv","req","acceleratePass","orderID","newRate","acceleratedRate","accelerateMainDiv","accelerateTxID","txID","preAccelerateErr","accelerateMsgDiv","accelerateSuccess","displayEarlyAccelerationMsg","feeEstimateDiv","preAccelerate","currencyUnit","suggestedRange","accelerateAvgFeeRate","swapRate","accelerateCurrentFeeRate","suggestedRate","updateRate","newY","rangeHandler","updateAccelerationEstimate","sliderContainer","feeRateEstimate","baseID","quoteID","feeEstimate","DEXAddressForm","dexToUpdate","selectedCert","skipRegistration","showOrHidePWBox","onCertFileChange","removeCert","clearCertFile","addCert","showCustom","customBox","knownExchanges","knownXCs","div","checkDEX","appPW","addDexHdr","skipRegistrationBox","updateDexHdr","pickServerMsg","addCustomMsg","passwordRequired","appPWBox","isDisplayed","endpoint","newHost","oldHost","needCert","paid","pendingBonds","fetchUser","loadPage","DiscoverAccountForm","dexHost","LoginForm","headerTxt","login","handleLoginNote","loginMsg","idel","rememberPass","notes","setNotes","DepositAddress","newDepAddrBttn","newDepositAddress","copyAddressBtn","copyAddress","depositErr","depositLogo","walletMap","depositName","depositAddress","qrcode","traits","clipboard","writeText","copyAlert","animationLength","slideSwap","form1","form2","submitBttn","wrapper","dateApplyOffset","toISOString","RegistrationPage","dexAddrForm","discoverAcctForm","bindForm","appPWForm","appPWSubmit","setAppPass","showSeedRestore","seedRestore","loginForm","newWalletForm","newWalletCreated","animateRegAsset","requestFeepayment","regAssetForm","confirmRegisterForm","setAsset","currentDEX","animateConfirmForm","getBondsFeeBuffer","bondsFeeBuffer","walletWaitForm","setWallet","walletWait","confirmRegForm","registerDEXSuccess","currentForm","forms","authed","oldForm","feeBuffer","appPWErrMsg","pwAgain","appPWAgain","seed","seedInput","updateMenuItemsDisplay","LoginPage","loggedIn","WalletsPage","restoreInfoCard","connectedIconTmpl","disconnectedIconTmpl","removeIconTmpl","closePopups","cancelForce","selectedAssetID","iconSelectTmpl","balanceDetailRow","recentOrderTmpl","showNewWallet","connectBttn","doConnect","send","showSendForm","receive","showDeposit","unlockBttn","openWallet","lockBttn","lock","reconfigureBttn","showReconfig","rescanWallet","fmtParams","assetUpdated","sortAssetButtons","reconfigForm","reconfigInputs","unlockForm","unlockWalletForm","openWalletSuccess","sendForm","submitSendForm","stepSend","vSendForm","vSend","vCancelSend","cancelSend","submitReconfig","reconfig","mouseInElement","keyup","downloadLogs","exportWallet","displayExportWalletAuth","recoverWallet","showRecoverWallet","exportWalletAuth","exportWalletAuthSubmit","recoverWalletConfirm","recoverWalletSubmit","confirmForce","confirmForceSubmit","disableWallet","showToggleWalletStatus","enableWallet","toggleWalletStatusConfirm","toggleWalletStatusSubmit","toggleWalletStatus","managePeers","showManagePeersForm","addPeerSubmit","submitAddPeer","depositAddrForm","deposit","walletBal","populateMaxSend","sendAmt","amt","showFiatValue","sendValue","maxSend","sendAddr","validAddr","validateSendAddress","showChangePW","changeWalletPW","setPWSettingViz","changeWalletTypeSelect","changeWalletType","showChangeType","isHidden","changeTypeHideIcon","changeTypeShowIcon","changeTypeMsg","fiatrateupdate","handleRatesNote","handleBalanceNote","handleWalletStateNote","walletconfig","handleCreateWalletNote","selectedAsset","assetIDStr","selectedAssetLK","setSelectedAsset","animation","vSendErr","sendErr","vSendEstimates","txFeeNotAvailable","subtract","subtractCheckBox","showFormError","txfee","ok","validaddress","vSendSymbol","vSendLogo","feeUI","vSendFee","formatFullPrecision","vSendFeeFiat","vSendDestinationAmt","vTotalSend","vTotalSendFiat","vSendAddr","bal","balanceAfterSend","balanceAfterSendFiat","approxSign","totalSend","showForm","resp","changePW","switchPWMsg","peerSpinner","managePeersErr","peersTableBody","peers","source","defaultText","addedText","discoveredText","peer","connectionIcon","row","peerTableRow","WalletDefault","UserAdded","Discovered","connected","removeIcon","spinUntilPeersUpdate","updateWalletPeersTable","managePeersForm","addPeerInput","toggleWalletStatusErr","walletStatusDisable","disableWalletMsg","walletStatusEnable","enableWalletMsg","code","activeOrdersErr","successMsg","focuser","displayed","successMessage","checkmarkForm","checkmark","startR","startG","startB","diffR","diffG","diffB","defaultsLoaded","assetButtons","assetSelect","sortedAssets","localeCompare","bttn","updateAssetButton","fiat","noWallet","img","totalBalance","immature","fiatRatesMap","formatFiatConversion","updateDisplayedAsset","showAvailableMarkets","showRecentActivity","fiatBalanceBox","createWalletBox","walletDetails","sendReceive","connectBttnBox","statusLocked","statusReady","statusOff","unlockBttnBox","lockBttnBox","peerCountBox","syncProgressBox","statusDisabled","updateDisplayedAssetBalance","walletDefinition","defs","zerothOpts","assetIsConfigurable","passwordWrapper","haveActiveOrders","encrypted","walletDetailsBox","totalLocked","contractlocked","bondlocked","balanceUnit","fiatBalance","firstOther","balanceDetailBox","addSubBalance","category","subBalance","sortedCats","other","exchanges","spotVolume","vol24","hostA","mktA","hostB","mktB","availableMarkets","basesymbol","marketRow","baseLogo","quoteLogo","convRate","conventionalRate","price","fourSigFigs","priceQuoteUnit","priceBaseUnit","volumeUnit","priceBox","volumeBox","quote","marketsOverviewBox","orderActivityBox","hosts","statuses","noActivity","orderActivity","orders","recentOrders","to","fromQty","toQty","fromLogo","fromSymbol","toSymbol","toLogo","age","timeSince","submitTime","link","href","bindInternalNavigation","reconfigErr","url","forceUrl","forceReq","showConfirmForce","confirmForceErr","recoverWalletErr","showOpen","errorMsg","showErrorOnly","skipAnimation","currentDef","currentWalletDefinition","option","traitLogFiler","otherActionsLabel","recfgAssetLogo","recfgAssetName","updateDisplayedReconfigFields","toggleSubtract","isWithdrawer","maxSendDisplay","sendLogo","sendName","feeReq","canSend","maxSendFiat","maxSendFee","maxSendFeeFiat","is","showSuccess","vSendPw","newWalletPW","newPW","search","URLSearchParams","URL","location","pathname","exportWalletErr","exportWalletPW","displayRestoreWalletInfo","restorationinfo","restoreInfoCardsList","wr","card","seedName","instructions","restoreWalletInfo","recoverWalletPW","force","fiatRates","display","FourSigFigs","OneFractionalDigit","_objectWithoutProperties","excluded","sourceKeys","getOwnPropertySymbols","sourceSymbolKeys","propertyIsEnumerable","SettingsPage","fiatRateSources","darkMode","showPokes","showPopups","commitHash","addADex","getRegistrationTxFeeEstimate","importAccount","prepareAccountImport","authorizeAccountImportForm","authorizeImportAccountConfirm","changeAppPW","changeAppPWForm","submitNewPW","accountFile","onAccountFileChange","removeAccount","clearAccountFile","addAccount","exportSeed","exportSeedAuth","exportSeedSubmit","submitExportSeedReq","exportSeedPW","seedDiv","getCertFile","selectedAccount","importAccountErr","importAccountAppPass","accountString","account","bonds","acctInf","importResponse","reload","exportAccountErr","exportSeedE","authorizeSeedDisplay","changePWErrMsg","clearValues","newAppPW","confirmNewPW","OrderBook","mktBook","qtyAtomic","less","findIdx","removeFromSide","findOrder","updateRemainingSide","epochIdx","approve","best","bestGapOrder","forward","route","payload","handlers","queue","maxQlength","connection","readyState","WebSocket","OPEN","close","uri","reloader","retrys","go","conn","timeout","onmessage","evt","onclose","delay","onopen","request","onerror","bookRoute","bookOrderRoute","unbookOrderRoute","updateRemainingRoute","epochOrderRoute","candlesRoute","candleUpdateRoute","check","buyBtnClass","sellBtnClass","fiveMinBinKey","percentFormatter","parentIDNone","MarketsPage","main","maxOrderUpdateCounter","metaOrders","recentMatches","preorderCache","depthLines","hovers","recentMatchesSortKey","recentMatchesSortDirection","ogTitle","title","depthReporters","reportDepthClick","reportDepthVolume","reportDepthMouse","z","reportDepthZoom","depthChart","depthZoomLK","candleReporters","reportMouseCandle","candleChart","candlesChart","accelerateOrderForm","accelerateForm","candleDur","registerBttn","notRegistered","dex","walletInfoTmpl","bWidget","qWidget","wgt","balanceWgt","BalanceWidget","baseIcons","stateIcons","quoteIcons","connect","walletUnlocked","expired","newWalletBttn","showCreate","walletAddr","rowTemplate","durBttnTemplate","userOrderTmpl","quoteUnits","baseUnits","buyBttn","swapBttns","sellBttn","maxLbl","setOrderBttnText","setOrderVisibility","drawChartLines","limitBttn","marketBttn","rateField","isSell","setMarketBuyOrderEstimate","maxOrd","maxSell","lotField","swap","lots","maxBuys","adjustedRate","lotChanged","disableMouseWheel","qtyField","mktBuyField","ws","handleBookRoute","handleBookOrderRoute","handleUnbookOrderRoute","handleUpdateRemainingRoute","handleEpochOrderRoute","handleCandlesRoute","handleCandleUpdateRoute","handleEpochMatchSummary","openFunc","orderForm","stepSubmit","verifyForm","vSubmit","submitOrder","vUnlockSubmit","submitEstimateUnlock","cancelForm","cancelSubmit","submitCancel","vFeeDetails","vDetailPane","closeDetailPane","showVerifyForm","recentMatchesTable","th","setRecentMatchesSortCol","ordercol","unsetRecentMatchesSortColClasses","refreshRecentMatchesTable","setRecentMatchesSortColClasses","sortCls","vPass","cancelPass","quantityChanged","marketBuyChanged","rateFieldChanged","previewQuoteAmt","marketSearchV1","filterMarkets","setDisclaimerAckViz","acked","disclaimer","disclaimerAck","showDisclaimer","orderDisclaimerAckedLK","clearChartLines","buyRows","sellRows","userOrders","activeMarkerRate","setDepthMarkers","stats0","marketStatsV1","stats1","headerSpace","stats","closeMarketsList","leftMarketDock","openMarketsList","leftHider","marketReopener","marketSelect","marketList","MarketList","marketListV1","startLoadingAnimations","setMarket","handleOrderNote","handleMatchNote","handleEpochNote","handleConnNote","bondpost","handleBondUpdate","spots","handlePriceUpdate","loadingAnimations","secondTicker","setInterval","mord","recentMatchesLiveList","td","sinceStamp","init","makeMarket","lastMarketLK","exists","setRegistrationStatusVisibility","setBalanceVisibility","anis","bconv","setPriceAndChange","obPrice","change24","obUp","obDown","baseIcon","quoteIcon","candleCaches","qconv","baseCfg","quoteCfg","isSupported","bVers","versions","qVers","version","isLimit","tifBox","qtyBox","maxBox","mktBuyBox","orderTypeBttns","assetsAreSupported","tier","loaderMsg","titleContent","confStatusMsg","titleClass","regStatusTitle","regStatusConfsDisplay","registrationStatus","regStatusDex","setRegistrationStatusView","pending","confirmationsRequired","join","connectionStatus","Connected","updateRegistrationStatusView","bondRequired","resolveOrderFormVisibility","viewOnly","unregisteredDex","hasPendingBonds","acctTier","dexSettingsLink","shortSymbol","durBttnBox","candleDurs","candleDurationSelected","chartErrMsg","qui","bui","maxEstimateTimer","mktId","marketID","sid","maxSellRequested","sellBalance","buyBalance","setMarketDetails","setCurrMarketPrice","loadCandles","setLoaderMsgVisibility","setCandleDurBttns","q","sellBookedBase","sellBookedQuote","buyBookedBase","buyBookedQuote","hoverData","depthHoverData","hoverPrice","hoverVolume","candle","candleHoverData","candleStart","candleEnd","candleHigh","candleLow","candleVol","limit","qtyConv","convertToAtoms","tifnow","tifNow","options","parseOrder","adjusted","orderErr","preSell","preBuy","orderPreview","quoteQty","total","baseWallet","setMaxOrder","scheduleMaxEstimate","quoteWallet","aLot","maxBuy","path","maxLoaded","bid","qid","bWallet","qWallet","maxLotBox","maxAboveZero","maxFromLots","maxFromLotsLbl","counter","maxOrder","fromAsset","maxFromAmt","maxFromTicker","loadTable","addTableOrder","epochLine","set","msgRate","buffer","buybuffer","midGapConventional","minMktBuy","oid","userNoOrders","unreadyOrders","headerEl","detailsDiv","readyToTick","sideLight","updateMetaOrder","cancelBttn","showCancel","accelerateBttn","showAccelerate","canAccelerateOrder","expander","unreadyOrdersMsg","activeLight","rateFactor","setMarkers","midGapValue","baseSymb","quoteSymb","refreshActiveOrders","handleBook","updateTitle","select","setWallets","removeTableOrder","updateRemaining","updateTableOrder","candlesLoading","timer","setHighLow","setCandles","addRecentMatches","startStamp","openAsset","updateAsset","currentOrder","toAsset","setIcon","icon","vFeeSummary","vUnlockPreorder","vPreorderErr","vPreorder","vBuySell","buySellStr","vSideSubmit","vOrderHost","verifyLimit","verifyMarket","orderDesc","vOrderType","vRate","vQty","vTotal","vFiatTotal","vmFromTotal","vmFromAsset","vmFromTotalFiat","vMarketEstimate","received","vmToTotal","vmToAsset","vmTotalFiat","vHeader","preOrder","unlockWalletsForEstimates","vErr","vUnlockPass","attemptWalletUnlock","setPreorderErr","assetIDs","cacheKey","cached","wireOrder","estimate","vPreorderErrTip","showAdvancedOptions","hideAdvancedOptions","vOtherOrderOpts","redeem","vDefaultOrderOpts","addOption","isSwap","change","isBaseChain","showByDefault","refreshPreorder","fetchPreorder","est","setFeeEstimates","showPreOrderAdvancedOptions","hidePreOrderAdvancedOptions","reloadOrderOpts","vPreorderEstimates","fmtPct","baseExchangeRate","quoteExchangeRate","baseFeeAssetUI","quoteFeeAssetUI","tokenFiatRate","parentFiatRate","toFeeAssetUI","fromFeeAssetUI","toExchangeRate","fromExchangeRate","swapped","swappedInParentUnits","bestSwapPct","realisticBestCase","vSwapFeesLowPct","vSwapFeesLow","worstSwapPct","realisticWorstCase","vSwapFeesHighPct","vSwapFeesHigh","swapFeesMaxPct","maxFees","vSwapFeesMaxPct","vSwapFeesMax","estRate","receivedInParentUnits","bestRedeemPct","vRedeemFeesLowPct","vRedeemFeesLow","worstRedeemPct","vRedeemFeesHighPct","vRedeemFeesHigh","vFeeSummaryPct","vFeeSummaryLow","vFeeSummaryHigh","summarySwapFeesLow","summarySwapFeesHigh","summaryRedeemFeesLow","summaryRedeemFeesHigh","cancelData","cancelErr","remaining","cancelRemain","cancelUnit","currentCreate","validateOrder","showVerify","updateSpots","oldStatus","setEpoch","clearOrderTableEpochs","alreadyMatched","compare","recentMatchesSortCompare","recentMatchesTemplate","vLoader","finalize","mktBuyLots","mktBuyScore","NaN","loadTableSide","bins","currEpochBin","currNonEpochBin","currRate","bin","bookSide","tbody","binOrdersByRateAndEpoch","orderTableRow","manager","getRate","insertOrder","tr","removeOrder","updateOrderQty","clearOrderTableEpochSide","removeEpochOrders","orderBin","OrderTableRowManager","chart","setConnectionStatus","filterTxt","setFilter","setLines","requestCandles","unattach","clearInterval","rowTmpl","reloadMarketsPane","mkts","convertMarkets","assign","aLots","sortedMarkets","MarketRow","find","disconnectedIco","template","hues","btmpl","iconBox","walletState","balanceRowTmpl","qtmpl","updateWallet","newWalletRow","unsupported","spinner","balanceRows","addWalletSymbol","readWallet","fetchBalance","addRow","balTmpl","updateParent","before","stringyOptions","tableRow","setRateEl","setEpochEl","updateQtyNumOrdersEl","epochEl","isEpoch","rateEl","cssClass","curr","numOrders","qtyEl","numOrdersEl","setAttribute","index","findIndex","newEpoch","sign","OrdersPage","orderTmpl","filterState","readFilter","filterKey","subFilter","hostFilter","assetFilter","statusFilter","applyButtons","monitorFilter","applyBttn","submitFilter","filter1","filter2","compareSubFilter","parseSubFilter","ordersTable","nextPage","exportOrders","showArchivedDateField","archivedDateField","deleteArchivedRecordsErr","deleteArchivedRecords","saveMatchesToFile","saveOrdersToFile","archivedRecordsLocation","deleteArchivedRecordsMsg","deleteArchivedResult","deleteArchivedRecordsForm","deleteArchivedRecordsSubmit","olderThan","tableBody","appendOrders","tmplID","mktID","dateTime","fetchOrders","setOrders","currentFilter","setQuery","olderThanMs","saveMatchesToFIle","archivedRecordsDeleted","nRecords","archivedRecordsPath","orderLoader","net","coinIDTakerFoundMakerRedemption","OrderPage","stampers","matchCardTmpl","setStamp","refreshOnPopupClose","setCoinHref","explorerId","showAccelerateForm","fetchOrder","mktBaseSymbol","mktQuoteSymbol","setAccelerationButtonVis","showMatchCards","matchCard","matchID","time","matchTime","toLocaleTimeString","month","day","matchTimeAgo","orderPortion","matchQty","bipSymbol","quoteAmount","cancelInfoDiv","infoDiv","statusHdr","cancelAmount","cancelIcon","cancelOrderPortion","makerSwapYou","makerRedeemYou","takerSwapThem","takerRedeemThem","takerSwapYou","takerRedeemYou","makerSwapThem","makerRedeemThem","makerSwapAsset","takerSwapAsset","makerRedeemAsset","takerRedeemAsset","refundAsset","fromAmount","toAmount","fromIcon","toIcon","revoked","counterRedeem","refund","setCoin","pendingName","linkName","coin","coinLink","pendingSpan","cid","startsWith","makerAddr","formatCoinID","stringID","explorerCoin","makerSwapCoin","takerSwapCoin","makerRedeemCoin","takerRedeemCoin","inConfirmingMakerRedeem","inConfirmingTakerRedeem","makerSwapMsg","takerSwapMsg","makerRedeemMsg","takerRedeemMsg","confirmationString","makerSwap","takerSwap","makerRedeem","takerRedeem","setImmutableMatchCardElements","setMutableMatchCardElements","matchBox","addNewMatchCard","actionsLabel","processMatch","counterSwap","assetExplorer","CoinExplorers","ethExplorers","txid","vout","DexSettingsPage","exportDexBtn","prepareAccountExport","authorizeAccountExportForm","disableAcctBtn","prepareAccountDisable","disableAccountForm","updateBondOptionsBtn","prepareUpdateBondOptions","updateCertBtn","certFileInput","updateHostBtn","prepareUpdateHost","updateBondOptionsForm","updateBondOptionsConfirm","updateBondOptions","authorizeExportAccountConfirm","exportAccount","disableAccountConfirm","disableAccount","exportAccountAppPass","exportAccountHost","accountForExport","disableAccountAppPW","disableAccountHost","disableAccountErr","bondTargetTier","bondOptions","targetTier","bondAssetSelect","bondOptionsErr","updateCertMsg","exchange","displayIcons","disconnectedIcon","connectedIcon","Disconnected","InvalidCert","bondOptionsMsg","GapStrategyMultiplier","GapStrategyAbsolute","GapStrategyAbsolutePlus","GapStrategyPercent","GapStrategyPercentPlus","oracleWeightBaseOption","default","oracleWeightOption","createXYRange","oracleBiasBaseOption","oracleBiasRange","oracleBiasOption","driftToleranceRange","driftToleranceOption","gapMultiplierBaseOption","gapMultiplierRange","gapMultiplierOption","gapPercentBaseOption","gapPercentRange","gapPercentOption","MarketMakerPage","programs","editProgram","pwHandler","createOpts","gapRanges","assetRowTmpl","runningProgramTmpl","oracleTmpl","selectClicked","isBase","baseSelect","quoteSelect","descendentMetrics","assetDropdown","counterAsset","currentMarket","clickedSymbol","Set","otherAssets","firstID","secondID","firstSymbol","secondSymbol","nonOptions","addOptions","symbols","assetRow","hideAssetDropdown","leaveEditMode","setCreationBase","setCreationQuote","clicker","setMarketSubchoice","gapMultiplierOpt","createOptsUpdated","gapPercentOpt","driftToleranceOpt","biasOpt","weightOpt","showAdvanced","optionsExpansionLK","hideAdvanced","runBttn","authedRoute","createBot","lotsInput","exitEditMode","manualPriceBttn","basisPrice","manualPriceInput","hideManualPrice","formatFiveSigFigs","specifiedPrice","setCurrentBasisPrice","noFiatBox","gapStrategySelect","updateGapStrategyInputs","currentReport","submitPasswordForm","pwInput","pwForm","pwSubmit","createBaseWallet","createQuoteWallet","lastMkt","lastMMMarketLK","xcMkt","bot","handleBotNote","populateRunningPrograms","createBox","programsBox","fetchMaxBuy","atomToConv","absMaxBox","gapFactorMax","lotEstQuoteLots","breakEvenSpread","breakEvenGapBox","breakEvenGap","absInputBox","updateGapStrategyInputVisibility","rateUnit","skipMarketUpdate","setCurrentReport","addMarketSelect","setContent","Text","marketOneChoice","lotEstBaseSymbol","lotEstQuoteSymbol","setNoWallet","noWalletBox","lotEstQuoteBox","lotEstQuoteNoWallet","lotEstBaseBox","lotEstBaseNoWallet","availHeader","lotEstimateBox","marketInfo","oraclesBox","lotsBox","advancedBox","fetchingMarkets","buy","fetchOracleAndMaxBuy","fetchMaxSell","lotEstBaseLots","oracles","weight","weightedSum","ExchangeNames","formatThreeSigFigs","usdVol","bestBuy","bestSell","avgPrice","pgm","program","programID","oracleWeighting","oracleBias","driftTolerance","gapStrategy","gapFactor","bots","runningPrograms","programDiv","setProgramsHeader","programsHeader","noProgramsMessage","startStop","startErr","pauseBttn","startBttn","retireBttn","configureBttn","setEditProgram","updateProgramDiv","programRunning","programPaused","boost","oracleWeight","createErr","makerProgram","strategy","botType","manualRate","absInput","gapPercent","gapMultiplier","botCreator","baseOpt","createOption","constructors","register","wallets","settings","dexsettings","mm","Application","pokes","process","noteReceivers","loggers","loggersLK","enableLogger","loggerID","recorderKeys","recordersLK","recorders","recordLogger","dumpLogger","btoa","download","handlerFromPath","history","replaceState","attachHeader","attachCommon","attach","notificationsLK","protocol","reconnected","notify","seedgentime","skipPush","noteBox","profileBox","origin","requestedHandler","doc","noderize","delivered","pushState","handlerID","loadedPage","unload","lyt","popupNotes","popupTmpl","noteTmpl","pokeTmpl","loader","noteBell","pokeList","noteList","ackNotes","noteCat","pokeCat","showDropdown","noteIndicator","setNoteTimes","storeNotes","burgerIcon","logoutErr","innerNoteIcon","innerBurgerIcon","profileSignout","signOut","dialog","ico","innerWidth","acks","severity","pageURL","params","searchParams","walletsMenuEntry","marketsMenuEntry","coinID","updateBondConfs","confirmations","updateTier","prependNoteElement","orderNote","tempID","inFlight","inflight","updateOrder","handleBondNote","updateMatch","mktName","updateUser","stack","indicator","setSeverityClass","prependPokeElement","receivers","nowString","cn","makePoke","prependListElement","skipSave","makeNote","unacked","ni","noteCacheSize","lastChild","cls","toLocaleDateString","concat","fromAssetID","supportedAsset","encRate","emptyidx","def","removeCookie","authCK","removeLocal","severityClassMap","getSeconds","getMilliseconds"],"sourceRoot":""} \ No newline at end of file