diff --git a/client/webserver/site/dist/entry.js b/client/webserver/site/dist/entry.js index 5e348d5963..da26425d2d 100644 --- a/client/webserver/site/dist/entry.js +++ b/client/webserver/site/dist/entry.js @@ -1,2 +1,2 @@ -(()=>{var e={757:(e,t,n)=>{e.exports=n(666)},666:e=>{var t=function(e){"use strict";var t,n=Object.prototype,r=n.hasOwnProperty,o="function"==typeof Symbol?Symbol:{},a=o.iterator||"@@iterator",i=o.asyncIterator||"@@asyncIterator",s=o.toStringTag||"@@toStringTag";function c(e,t,n){return Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{c({},"")}catch(e){c=function(e,t,n){return e[t]=n}}function u(e,t,n,r){var o=t&&t.prototype instanceof v?t:v,a=Object.create(o.prototype),i=new R(r||[]);return a._invoke=function(e,t,n){var r=h;return function(o,a){if(r===f)throw new Error("Generator is already running");if(r===p){if("throw"===o)throw a;return O()}for(n.method=o,n.arg=a;;){var i=n.delegate;if(i){var s=F(i,n);if(s){if(s===m)continue;return s}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(r===h)throw r=p,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);r=f;var c=l(e,t,n);if("normal"===c.type){if(r=n.done?p:d,c.arg===m)continue;return{value:c.arg,done:n.done}}"throw"===c.type&&(r=p,n.method="throw",n.arg=c.arg)}}}(e,n,i),a}function l(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(e){return{type:"throw",arg:e}}}e.wrap=u;var h="suspendedStart",d="suspendedYield",f="executing",p="completed",m={};function v(){}function y(){}function g(){}var w={};c(w,a,(function(){return this}));var k=Object.getPrototypeOf,b=k&&k(k(I([])));b&&b!==n&&r.call(b,a)&&(w=b);var x=g.prototype=v.prototype=Object.create(w);function C(e){["next","throw","return"].forEach((function(t){c(e,t,(function(e){return this._invoke(t,e)}))}))}function S(e,t){function n(o,a,i,s){var c=l(e[o],e,a);if("throw"!==c.type){var u=c.arg,h=u.value;return h&&"object"==typeof h&&r.call(h,"__await")?t.resolve(h.__await).then((function(e){n("next",e,i,s)}),(function(e){n("throw",e,i,s)})):t.resolve(h).then((function(e){u.value=e,i(u)}),(function(e){return n("throw",e,i,s)}))}s(c.arg)}var o;this._invoke=function(e,r){function a(){return new t((function(t,o){n(e,r,t,o)}))}return o=o?o.then(a,a):a()}}function F(e,n){var r=e.iterator[n.method];if(r===t){if(n.delegate=null,"throw"===n.method){if(e.iterator.return&&(n.method="return",n.arg=t,F(e,n),"throw"===n.method))return m;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return m}var o=l(r,e.iterator,n.arg);if("throw"===o.type)return n.method="throw",n.arg=o.arg,n.delegate=null,m;var a=o.arg;return a?a.done?(n[e.resultName]=a.value,n.next=e.nextLoc,"return"!==n.method&&(n.method="next",n.arg=t),n.delegate=null,m):a:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,m)}function E(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 A(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function R(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(E,this),this.reset(!0)}function I(e){if(e){var n=e[a];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,i=function n(){for(;++o=0;--a){var i=this.tryEntries[a],s=i.completion;if("root"===i.tryLoc)return o("end");if(i.tryLoc<=this.prev){var c=r.call(i,"catchLoc"),u=r.call(i,"finallyLoc");if(c&&u){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),A(n),m}},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 o=r.arg;A(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,r){return this.delegate={iterator:I(e),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=t),m}},e}(e.exports);try{regeneratorRuntime=t}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=t:Function("r","regeneratorRuntime = r")(t)}}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var a=t[r]={exports:{}};return e[r](a,a.exports,n),a.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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function ke(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,o=t.left+n.scrollLeft,a=e.offsetWidth,i=e.offsetHeight;return{bodyTop:r,bodyLeft:o,width:a,height:i,centerX:o+a/2,centerY:r+i/2}}},{key:"empty",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n"),e),document.createElement("div"))}},{key:"idDescendants",value:function(t){var n,r={},o=we(e.applySelector(t,"[id]"));try{for(o.s();!(n=o.n()).done;){var a=n.value;r[a.id]=a}}catch(e){o.e(e)}finally{o.f()}return r}},{key:"formatCoinValue",value:function(e,t){var n=r(Re(e,t),2),o=n[0],a=n[1];return Number.isInteger(o)?Ce.format(o):function(e){return Ae(Se,2,e)}(a).format(o)}},{key:"formatFullPrecision",value:function(e,t){var n=r(Re(e,t),2),o=n[0];return Ee(n[1]).format(o)}},{key:"formatFiatConversion",value:function(e,t,n){if(!t||0===t)return"unavailable";var o=r(Re(e,n),1)[0]*t;return Ee(2).format(o)}},{key:"logoPath",value:function(e){return-1===xe.indexOf(e)&&(e=e.substring(0,1)),"/img/coins/".concat(e,".png")}},{key:"cleanTemplates",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n0||l>0)&&l++,e>0&&(u+="".concat(e," ").concat(t," ")),l>=2},d=r(Me(c,Le),2);if(t=d[0],c=d[1],h(t,"y"))return u;var f=r(Me(c,Pe),2);if(n=f[0],c=f[1],h(n,"mo"))return u;var p=r(Me(c,We),2);if(o=p[0],c=p[1],h(o,"d"))return u;var m=r(Me(c,Be),2);if(a=m[0],c=m[1],h(a,"h"))return u;var v=r(Me(c,qe),2);if(i=v[0],c=v[1],h(i,"m"))return u;var y=r(Me(c,1e3),2);return s=y[0],c=y[1],h(s,"s"),u||"0 s"}},{key:"disableMouseWheel",value:function(){for(var e=arguments.length,t=new Array(e),n=0;ne.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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(document.cookie.split(";"));try{for(n.s();!(t=n.n()).done;){var o=r(t.value.split("="),2),a=o[0],i=o[1];if(a.trim()===e)return i}}catch(e){n.e(e)}finally{n.f()}return null}},{key:"dark",value:function(e){this.setCookie(Ue,e?"1":"0"),e?document.body.classList.add("dark"):document.body.classList.remove("dark")}},{key:"isDark",value:function(){return document.cookie.split(";").filter((function(e){return e.includes("".concat(Ue,"=1"))})).length}},{key:"passwordIsCached",value:function(){return!!this.getCookie("sessionkey")}},{key:"store",value:function(e,t){window.localStorage.setItem(e,JSON.stringify(t))}},{key:"clearAllStore",value:function(){window.localStorage.clear()}},{key:"removeAuthCK",value:function(){document.cookie="".concat("dexauth","=;expires=Thu, 01 Jan 1970 00:00:01 GMT;")}},{key:"fetch",value:function(e){var t=window.localStorage.getItem(e);return null!==t?JSON.parse(t):null}}]),e}();function _e(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Ve(e,t){return Ve=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},Ve(e,t)}function He(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&&Ve(e,t)}function Ge(e){return Ge="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},Ge(e)}function Xe(e,t){if(t&&("object"===Ge(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return _e(e)}function Qe(e){return Qe=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},Qe(e)}null===je.getCookie(Ue)&&je.setCookie(Ue,"1"),null===je.getCookie(ze)&&je.setCookie(ze,"1");var Ye,Ke,Ze=function(){function e(){i(this,e)}return c(e,[{key:"unload",value:function(){}}]),e}();function Je(e,t,n){return $e.apply(this,arguments)}function $e(){return($e=a(v().mark((function e(t,n,r){var o,a;return v().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===(o=e.sent).status){e.next=6;break}throw o;case 6:return e.next=8,o.json();case 8:return(a=e.sent).requestSuccessful=!0,e.abrupt("return",a);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 et(e,t){return tt.apply(this,arguments)}function tt(){return(tt=a(v().mark((function e(t,n){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",Je("POST",t,JSON.stringify(n)));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function nt(e){return rt.apply(this,arguments)}function rt(){return(rt=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",Je("GET",t));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ot(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 at(){return Ke}function it(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=Qe(e);if(t){var o=Qe(this).constructor;n=Reflect.construct(r,arguments,o)}else n=r.apply(this,arguments);return Xe(this,n)}}function st(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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(e.matches);try{for(n.s();!(t=n.n()).done;){var r=t.value;if(!r.revoked&&r.status<3)return!0}}catch(e){n.e(e)}finally{n.f()}return!1}function vt(e){var t=mt(e);switch(e.status){case 0:return ge(Q);case 1:return ge(Y);case 2:return e.cancelling?ge(z):t?"".concat(ge(U),"/").concat(ge(K)):ge(U);case 3:return t?ge(K):0===e.filled?ge(Z):ge(N);case 4:return t?"".concat(ge(J),"/").concat(ge(K)):ge(J);case 5:return t?"".concat(ge($),"/").concat(ge(K)):ge($)}return""}function yt(e){if(!e.matches)return 0;var t=pt(e)?function(e){return e.qty*e.rate/ht}:function(e){return e.qty};return e.matches.reduce((function(e,n){return n.isCancel?e:e+t(n)}),0)}function gt(e){if(!e.matches)return 0;var t=pt(e)?function(e){return e.qty*e.rate/ht}: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 wt(e){var t=[e.booleanOptTmpl,e.rangeOptTmpl,e.orderOptTmpl];ut=t[0],lt=t[1],ct=t[2]}var kt=function(){function e(t,n,r,o){var a=this;i(this,e),u(this,"opt",void 0),u(this,"order",void 0),u(this,"node",void 0),u(this,"tmpl",void 0),u(this,"on",void 0),this.opt=t,this.order=n;var s=this.node=ct.cloneNode(!0),c=this.tmpl=Ie.parseTemplate(s);c.optName.textContent=t.displayname,c.tooltip.dataset.tooltip=t.description;var l=r&&n.sell||!r&&!n.sell?this.baseSymbol():this.quoteSymbol();c.chainIcon.src=Ie.logoPath(l),this.on=!1,Ie.bind(s,"click",(function(){a.on||(a.on=!0,s.classList.add("selected"),o.enable())})),Ie.bind(c.toggle,"click",(function(e){a.on&&(e.stopPropagation(),a.on=!1,s.classList.remove("selected"),o.disable())}))}return c(e,[{key:"quoteSymbol",value:function(){return Ft(this.order.host,this.order.quote)}},{key:"baseSymbol",value:function(){return Ft(this.order.host,this.order.base)}}]),e}(),bt=function(e){He(n,e);var t=it(n);function n(e,r,o,a){var s;i(this,n),u(_e(s=t.call(this,e,r,a,{enable:function(){return s.enable()},disable:function(){return s.disable()}})),"control",void 0),u(_e(s),"changed",void 0),s.changed=function(){return o()};var c=e.boolean,l=s.control=ut.cloneNode(!0);return s.tmpl.controls.appendChild(l),Ie.parseTemplate(l).reason.textContent=c.reason,s.on=void 0!==r.options[e.key]?r.options[e.key]:e.default,s.on&&s.node.classList.add("selected"),s}return c(n,[{key:"store",value:function(){this.on===this.opt.default?delete this.order.options[this.opt.key]:this.order.options[this.opt.key]=this.on,this.changed()}},{key:"enable",value:function(){this.store()}},{key:"disable",value:function(){this.store()}}]),n}(kt),xt=function(e){He(n,e);var t=it(n);function n(e,r,o,a){var s;i(this,n),u(_e(s=t.call(this,e,r,a,{enable:function(){return s.enable()},disable:function(){return s.disable()}})),"handler",void 0),u(_e(s),"x",void 0),u(_e(s),"changed",void 0),s.changed=o;var c=e.xyRange,l=r.options[e.key];return s.on=void 0!==l,s.on?(s.node.classList.add("selected"),s.x=l):s.x=e.default,s.handler=new Ct(c,s.x,(function(e){s.x=e,s.order.options[s.opt.key]=e}),(function(){s.changed()}),(function(){s.node.classList.add("selected")})),s.tmpl.controls.appendChild(s.handler.control),s}return c(n,[{key:"enable",value:function(){this.order.options[this.opt.key]=this.x,this.changed()}},{key:"disable",value:function(){delete this.order.options[this.opt.key],this.changed()}}]),n}(kt),Ct=c((function e(t,n,r,o,a,s){var c=this;i(this,e),u(this,"control",void 0),u(this,"x",void 0),u(this,"updated",void 0),u(this,"changed",void 0),u(this,"selected",void 0);var l=this.control=lt.cloneNode(!0),h=Ie.parseTemplate(l);this.changed=o,this.selected=a,this.updated=r;var d=h.slider,f=h.handle,p=t.end.x-t.start.x,m=t.end.y-t.start.y,v=function(e){return(e-t.start.x)/p},y=v(n),g=this.x=n,w=y*m+t.start.y,k=new Intl.NumberFormat(navigator.languages,{minimumSignificantDigits:3,maximumSignificantDigits:3}),b=function(e){s&&(w=Math.round(w)),h.x.textContent=k.format(g),h.y.textContent=k.format(w),s&&(h.y.textContent="".concat(w)),f.style.left="calc(".concat(100*y,"% - ").concat(14*y,"px)"),c.x=g,e||c.updated(g,w)},x=function e(n){if("change"===n.type||n.target!==h.xInput){var r=h.xInput.value;if(r){var o=parseFloat(r);isNaN(o)||(g=Et(o,t.start.x,t.end.x),y=v(g),w=y*m+t.start.y,b())}Ie.hide(h.xInput),Ie.show(h.x),Ie.unbind(document,"click",e),c.changed()}};Ie.bind(h.x,"click",(function(e){Ie.hide(h.x),Ie.show(h.xInput),h.xInput.focus(),h.xInput.value=k.format(g),Ie.bind(document,"click",x),e.stopPropagation()})),Ie.bind(h.xInput,"change",x);var C=function e(n){if("change"===n.type||n.target!==h.yInput){var r=h.yInput.value;if(r){var o=parseFloat(r);isNaN(o)||(w=Et(o,t.start.y,t.end.y),y=(w-t.start.y)/m,g=t.start.x+y*p,b())}Ie.hide(h.yInput),Ie.show(h.y),Ie.unbind(document,"click",e),c.changed()}};Ie.bind(h.y,"click",(function(e){Ie.hide(h.y),Ie.show(h.yInput),h.yInput.focus(),h.yInput.value=k.format(w),Ie.bind(document,"click",C),e.stopPropagation()})),Ie.bind(h.yInput,"change",C),Ie.bind(f,"mousedown",(function(e){if(0===e.button){e.preventDefault(),c.selected();var n=e.pageX,r=d.clientWidth-f.offsetWidth,o=v(g)*r,a=function(e){e.preventDefault(),y=function(e){return Math.max(Math.min(o+(e.pageX-n),r),0)}(e)/r,g=y*p+t.start.x,w=y*m+t.start.y,b()};Ie.bind(document,"mousemove",a),Ie.bind(document,"mouseup",(function e(t){a(t),Ie.unbind(document,"mousemove",a),Ie.unbind(document,"mouseup",e),c.changed()}))}})),h.rangeLblStart.textContent=t.start.label,h.rangeLblEnd.textContent=t.end.label,h.xUnit.textContent=t.xUnit,h.yUnit.textContent=t.yUnit,b(!0)}));function St(e,t,n,r){switch(!0){case!!e.boolean:return new bt(e,t,n,r).node;case!!e.xyRange:return new xt(e,t,n,r).node;default:console.error("no option type specified",e)}return console.error("unknown option type",e),document.createElement("div")}function Ft(e,t){return at().exchanges[e].assets[t].symbol}var Et=function(e,t,n){return en?n:e};function At(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 Rt(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)?Rt(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Rt(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n.header.classList.add("bordertop"):n.header.classList.remove("bordertop"),a=r.info.availablewallets[0],Ie.empty(o),Ie.hide(o,n.newWalletErr),r.info.availablewallets.length>1){Ie.show(o),i=At(r.info.availablewallets);try{for(c=function(){var e=s.value,t=n.walletTabTmpl.cloneNode(!0);t.dataset.tooltip=e.description,t.textContent=e.tab,o.appendChild(t),Ie.bind(t,"click",(function(){var n,r=At(Ie.kids(o));try{for(r.s();!(n=r.n()).done;)n.value.classList.remove("selected")}catch(e){r.e(e)}finally{r.f()}t.classList.add("selected"),u.update(e)}))},i.s();!(s=i.n()).done;)c()}catch(e){i.e(e)}finally{i.f()}at().bindTooltips(o),o.firstChild.classList.add("selected")}return e.next=16,this.update(a);case 16:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"update",value:(r=a(v().mark((function e(t){var n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentWalletType=t.type,r=je.passwordIsCached()||this.pwCache&&this.pwCache.pw,Ie.hide(n.auth,n.oneBttnBox,n.newWalletPassBox),(o=t.configopts||[]).map((function(e){return e.isBirthdayConfig&&at().seedGenTime>0&&(e.default=jt(new Date)),e})),r&&t.seeded?Ie.show(n.oneBttnBox):t.seeded?(Ie.show(n.auth),n.newWalletPass.value="",n.submitAdd.textContent=ge(ae)):(Ie.show(n.auth),t.noauth||Ie.show(n.newWalletPassBox),n.submitAdd.textContent=ge(oe)),this.subform.update(o),this.subform.dynamicOpts.children.length?Ie.show(n.walletSettingsHeader):Ie.hide(n.walletSettingsHeader),this.refresh(),e.next=12,this.loadDefaults();case 12:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"setError",value:(n=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.page.newWalletErr.textContent=t,Ie.show(this.page.newWalletErr);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"loadDefaults",value:(t=a(v().mark((function e(){var t,n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!(t=at().walletDefinition(this.currentAsset.id,this.currentWalletType)).seeded){e.next=3;break}return e.abrupt("return");case 3:if(""!==t.configpath){e.next=5;break}return e.abrupt("return");case 5:return n=at().loading(this.form),e.next=8,et("/api/defaultwalletcfg",{assetID:this.currentAsset.id,type:this.currentWalletType});case 8:if(r=e.sent,n(),at().checkResponse(r)){e.next=13;break}return this.setError(r.msg),e.abrupt("return");case 13:this.subform.setLoadedConfig(r.config);case 14:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Ot=function(){function e(t,n){var r=this;i(this,e),u(this,"form",void 0),u(this,"configElements",void 0),u(this,"configOpts",void 0),u(this,"sectionize",void 0),u(this,"allSettings",void 0),u(this,"dynamicOpts",void 0),u(this,"textInputTmpl",void 0),u(this,"dateInputTmpl",void 0),u(this,"checkboxTmpl",void 0),u(this,"fileSelector",void 0),u(this,"fileInput",void 0),u(this,"errMsg",void 0),u(this,"showOther",void 0),u(this,"showIcon",void 0),u(this,"hideIcon",void 0),u(this,"showHideMsg",void 0),u(this,"otherSettings",void 0),u(this,"loadedSettingsMsg",void 0),u(this,"loadedSettings",void 0),u(this,"defaultSettingsMsg",void 0),u(this,"defaultSettings",void 0),this.form=t,this.configElements={},this.configOpts=[],this.sectionize=n,this.allSettings=Ie.tmplElement(t,"allSettings"),this.dynamicOpts=Ie.tmplElement(t,"dynamicOpts"),this.textInputTmpl=Ie.tmplElement(t,"textInput"),this.textInputTmpl.remove(),this.dateInputTmpl=Ie.tmplElement(t,"dateInput"),this.dateInputTmpl.remove(),this.checkboxTmpl=Ie.tmplElement(t,"checkbox"),this.checkboxTmpl.remove(),this.fileSelector=Ie.tmplElement(t,"fileSelector"),this.fileInput=Ie.tmplElement(t,"fileInput"),this.errMsg=Ie.tmplElement(t,"errMsg"),this.showOther=Ie.tmplElement(t,"showOther"),this.showIcon=Ie.tmplElement(t,"showIcon"),this.hideIcon=Ie.tmplElement(t,"hideIcon"),this.showHideMsg=Ie.tmplElement(t,"showHideMsg"),this.otherSettings=Ie.tmplElement(t,"otherSettings"),this.loadedSettingsMsg=Ie.tmplElement(t,"loadedSettingsMsg"),this.loadedSettings=Ie.tmplElement(t,"loadedSettings"),this.defaultSettingsMsg=Ie.tmplElement(t,"defaultSettingsMsg"),this.defaultSettings=Ie.tmplElement(t,"defaultSettings"),n||Ie.hide(this.showOther),Ie.bind(this.fileSelector,"click",(function(){return r.fileInput.click()})),Ie.bind(this.fileInput,"change",a(v().mark((function e(){return v().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)})))),Ie.bind(this.showOther,"click",(function(){r.setOtherSettingsViz(r.hideIcon.classList.contains("d-hide"))}))}var t;return c(e,[{key:"fileInputChanged",value:(t=a(v().mark((function e(){var t,n,r,o,a,i,s,c;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(Ie.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=at().loading(this.form),e.next=9,n[0].text();case 9:if(o=e.sent){e.next=12;break}return e.abrupt("return");case 12:return e.next=14,et("/api/parseconfig",{configtext:o});case 14:if(a=e.sent,r(),at().checkResponse(a)){e.next=20;break}return this.errMsg.textContent=a.msg,Ie.show(this.errMsg),e.abrupt("return");case 20:if(0!==Object.keys(a.map).length){e.next=22;break}return e.abrupt("return");case 22:(t=this.dynamicOpts).append.apply(t,ot(this.setConfig(a.map))),this.reorder(this.dynamicOpts),i=[this.loadedSettings.children.length,this.defaultSettings.children.length],c=i[1],0===(s=i[0])&&Ie.hide(this.loadedSettings,this.loadedSettingsMsg),0===c&&Ie.hide(this.defaultSettings,this.defaultSettingsMsg),s+c===0&&Ie.hide(this.showOther,this.otherSettings);case 28:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"update",value:function(e,t){var n=this;if(this.configElements={},this.configOpts=e,Ie.empty(this.dynamicOpts,this.defaultSettings,this.loadedSettings),0===e.length)return Ie.hide(this.form);Ie.show(this.form),this.setOtherSettingsViz(!1),Ie.hide(this.loadedSettingsMsg,this.loadedSettings,this.defaultSettingsMsg,this.defaultSettings,this.errMsg);var r,o=[],a=function(e,r){var o,a="wcfg-"+r.key;o=r.isboolean?n.checkboxTmpl.cloneNode(!0):r.isdate?n.dateInputTmpl.cloneNode(!0):n.textInputTmpl.cloneNode(!0),n.configElements[r.key]=o;var i=o.querySelector("input");i.id=a,i.configOpt=r;var s=Ie.safeSelector(o,"label");if(s.htmlFor=a,s.prepend(r.displayname),e.appendChild(o),r.noecho&&(i.type="password"),r.description&&(s.dataset.tooltip=r.description),r.isboolean)i.checked=r.default;else if(r.isdate){var c=function(e){return e?("now"===e?new Date:new Date(1e3*e)).toISOString().split("T")[0]:""};i.max=c(r.max),i.min=c(r.min),i.valueAsDate=r.default?new Date(1e3*r.default):new Date}else i.value=null!==r.default?r.default:"";i.disabled=Boolean(r.disablewhenactive&&t)},i=At(this.configOpts);try{for(i.s();!(r=i.n()).done;){var s=r.value;this.sectionize&&null!==s.default?o.push(s):a(this.dynamicOpts,s)}}catch(e){i.e(e)}finally{i.f()}if(o.length){var c,u=At(o);try{for(u.s();!(c=u.n()).done;){var l=c.value;a(this.defaultSettings,l)}}catch(e){u.e(e)}finally{u.f()}Ie.show(this.showOther,this.defaultSettingsMsg,this.defaultSettings)}else Ie.hide(this.showOther);at().bindTooltips(this.allSettings),this.dynamicOpts.children.length?Ie.show(this.dynamicOpts):Ie.hide(this.dynamicOpts)}},{key:"setOtherSettingsViz",value:function(e){if(e)return Ie.hide(this.showIcon),Ie.show(this.hideIcon,this.otherSettings),void(this.showHideMsg.textContent=ge(E));Ie.hide(this.hideIcon,this.otherSettings),Ie.show(this.showIcon),this.showHideMsg.textContent=ge(A)}},{key:"setConfig",value:function(e){var t=this,n=[];return this.allSettings.querySelectorAll("input").forEach((function(r){var o,a=r.configOpt.key,i=e[a];void 0!==i&&(n.push(t.configElements[a]),r.configOpt.isboolean?r.checked="1"===(o=i)||"true"===o.toLowerCase():r.configOpt.isdate?r.valueAsDate=new Date(1e3*parseInt(i)):r.value=i)})),n}},{key:"setLoadedConfig",value:function(e){var t,n=this.setConfig(e);this.sectionize&&0!==n.length&&((t=this.loadedSettings).append.apply(t,ot(n)),this.reorder(this.loadedSettings),Ie.show(this.loadedSettings,this.loadedSettingsMsg),0===this.defaultSettings.children.length&&Ie.hide(this.defaultSettings,this.defaultSettingsMsg))}},{key:"map",value:function(){var e={};return this.allSettings.querySelectorAll("input").forEach((function(t){if(t.configOpt.isboolean&&t.configOpt.key)e[t.configOpt.key]=t.checked?"1":"0";else if(t.configOpt.isdate&&t.configOpt.key){var n=t.min?jt(new Date(t.min)):Number.MIN_SAFE_INTEGER,r=t.max?jt(new Date(t.max)):Number.MAX_SAFE_INTEGER,o=t.value?jt(new Date(t.value)):0;or&&(o=r),e[t.configOpt.key]=""+o}else t.value&&(e[t.configOpt.key]=t.value)})),e}},{key:"reorder",value:function(e){var t=this,n={};e.querySelectorAll("input").forEach((function(e){var r=e.configOpt.key;n[r]=t.configElements[r]}));var r,o=At(this.configOpts);try{for(o.s();!(r=o.n()).done;){var a=r.value,i=n[a.key];i&&e.append(i)}}catch(e){o.e(e)}finally{o.f()}}}]),e}(),Dt=function(){function e(t,n,r,o){var a=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"page",void 0),u(this,"xc",void 0),u(this,"certFile",void 0),u(this,"feeAssetID",void 0),u(this,"pwCache",void 0),this.form=t,this.success=n,this.page=Ie.parseTemplate(t),this.certFile="",this.pwCache=o,Ie.bind(this.page.goBack,"click",(function(){return r()})),zt(t,this.page.submit,(function(){return a.submitForm()}))}var t,n;return c(e,[{key:"setExchange",value:function(e,t){this.xc=e,this.certFile=t;var n=this.page;je.passwordIsCached()||this.pwCache&&this.pwCache.pw?Ie.hide(n.passBox):Ie.show(n.passBox),n.host.textContent=e.host}},{key:"setAsset",value:function(e){var t=at().assets[e],n=t.info.unitinfo;this.feeAssetID=t.id;var r=this.page,o=this.xc.regFees[t.symbol];r.fee.textContent=Ie.formatCoinValue(o.amount,n),r.feeUnit.textContent=n.conventional.unit.toUpperCase(),r.logo.src=Ie.logoPath(t.symbol)}},{key:"animate",value:(n=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,Ie.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=a(v().mark((function e(){var t,n,r,o,a,i,s,c,u;return v().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(null!==this.feeAssetID){e.next=7;break}return t.regErr.innerText="You must select a valid wallet for the fee payment",Ie.show(t.regErr),e.abrupt("return");case 7:return n=at().user.assets[this.feeAssetID].wallet.symbol,Ie.hide(t.regErr),r=this.xc.regFees[n],e.next=12,this.certFile;case 12:return o=e.sent,a=this.xc.host,i=t.appPass.value||(this.pwCache?this.pwCache.pw:""),s={addr:a,pass:i,fee:r.amount,asset:r.id,cert:o},t.appPass.value="",c=at().loading(this.form),e.next=20,et("/api/register",s);case 20:if(u=e.sent,c(),at().checkResponse(u)){e.next=26;break}return t.regErr.textContent=u.msg,Ie.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}(),Tt=function(){function e(t,n){i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"xc",void 0),u(this,"page",void 0),this.form=t,this.success=n,this.page=Ie.parseTemplate(t),Ie.cleanTemplates(this.page.marketTmpl,this.page.assetTmpl)}var t;return c(e,[{key:"setExchange",value:function(e){var t=this;this.xc=e;var n=this.page;Ie.empty(n.assets,n.allMarkets);for(var o=function(e){return e.conventional.conversionFactor},a=function(t,r){var a=n.marketTmpl.cloneNode(!0),i=Ie.parseTemplate(a),s=e.assets[t.baseid],c=at().unitInfo(t.baseid,e),u=e.assets[t.quoteid],l=at().unitInfo(t.quoteid,e);if(0===o(c)||0===o(l))return null;if(void 0!==r){var h=r===t.baseid,d=e.assets[h?t.quoteid:t.baseid].symbol;i.logo.src=Ie.logoPath(d)}else{var f=i.logo.cloneNode(!0);i.logo.src=Ie.logoPath(s.symbol),f.src=Ie.logoPath(u.symbol);var p=i.logo.parentNode;p&&p.insertBefore(f,i.logo.nextSibling)}var m=s.symbol.toUpperCase(),v=u.symbol.toUpperCase();i.name.textContent="".concat(m,"-").concat(v);var y=Ie.formatCoinValue(t.lotsize,c);if(i.lotSize.textContent="".concat(y," ").concat(m),t.spot){Ie.show(i.quoteLotSize);var g=o(l)/o(c),w=t.lotsize*t.spot.rate/ht*g,k=Ie.formatCoinValue(w,l);i.quoteLotSize.textContent="(~".concat(k," ").concat(v,")")}return a},i=function(){var o=r(c[s],2),i=o[0],u=o[1],l=at().assets[u.id];if(!l)return"continue";var h=l.wallet,d=l.info.unitinfo,f=n.assetTmpl.cloneNode(!0);Ie.bind(f,"click",(function(){t.success(u.id)}));var p=Ie.parseTemplate(f);n.assets.appendChild(f),p.logo.src=Ie.logoPath(i);var m=Ie.formatCoinValue(u.amount,d);p.fee.textContent="".concat(m," ").concat(d.conventional.unit),p.confs.textContent=String(u.confs),p.ready.textContent=ge(h?le:he),p.ready.classList.add(h?"readygreen":"setuporange");for(var v=0,y=0,g=Object.values(e.markets);y0?(r.totalFees.textContent=Ie.formatCoinValue(a.amount+t,o.info.unitinfo),Ie.show(r.sendEnoughWithEst),Ie.hide(r.sendEnough)):(Ie.show(r.sendEnough),Ie.hide(r.sendEnoughWithEst)),Ie.show(e.synced?r.syncCheck:e.syncProgress>=1?r.syncSpinner:r.syncUncheck),Ie.show(e.balance.available>a.amount?r.balCheck:r.balUncheck),r.progress.textContent=String(Math.round(100*e.syncProgress)),e.synced&&(this.progressed=!0),this.reportBalance(e.balance,e.assetID)}},{key:"reportWalletState",value:function(e){e.assetID===this.assetID&&(this.progressed&&this.funded||(this.reportProgress(e.synced,e.syncProgress),this.reportBalance(e.balance,e.assetID)))}},{key:"reportBalance",value:function(e,t){if(!this.funded&&-1!==this.assetID&&this.assetID===t){var n=this.page,r=at().assets[this.assetID];e.available<=this.regFee.amount?n.balance.textContent=Ie.formatCoinValue(e.available,r.info.unitinfo):(Ie.show(n.balCheck),Ie.hide(n.balUncheck,n.balanceBox,n.sendEnough),this.funded=!0,this.progressed&&this.success())}}},{key:"reportProgress",value:function(e,t){var n=this.page;if(e)return n.progress.textContent="100",Ie.hide(n.syncUncheck,n.syncRemainBox,n.syncSpinner),Ie.show(n.syncCheck),this.progressed=!0,void(this.funded&&this.success());1===t?(Ie.hide(n.syncUncheck),Ie.show(n.syncSpinner)):(Ie.hide(n.syncSpinner),Ie.show(n.syncUncheck)),n.progress.textContent=String(Math.round(100*t));var r=this.progressCache;for(r.push({stamp:(new Date).getTime(),progress:t});r.length>20;)r.shift();if(1!==r.length){Ie.show(n.syncRemainBox);var o=[r[0],r[r.length-1]],a=o[0],i=o[1],s=i.progress-a.progress;if(0!==s){var c=s/(i.stamp-a.stamp),u=(1-i.progress)/c;n.syncRemain.textContent=Ie.formatDuration(u)}else n.syncRemain.textContent="> 1 day"}}}]),e}(),Pt=function(){function e(t,n,r){var o=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"pwCache",void 0),u(this,"page",void 0),u(this,"currentAsset",void 0),this.page=Ie.idDescendants(t),this.form=t,this.pwCache=r||null,this.success=n,zt(t,this.page.submitUnlock,(function(){return o.submit()}))}var t;return c(e,[{key:"refresh",value:function(e){var t=this.page;this.currentAsset=e,t.uwAssetLogo.src=Ie.logoPath(e.symbol),t.uwAssetName.textContent=e.info.name,t.uwAppPass.value="",t.unlockErr.textContent="",Ie.hide(t.unlockErr),je.passwordIsCached()||this.pwCache&&this.pwCache.pw?Ie.hide(t.uwAppPassBox):Ie.show(t.uwAppPassBox)}},{key:"setError",value:function(e){this.page.unlockErr.textContent=e,Ie.show(this.page.unlockErr)}},{key:"showErrorOnly",value:function(e){this.setError(e),Ie.hide(this.page.uwAppPassBox),Ie.hide(this.page.submitUnlockDiv)}},{key:"submit",value:(t=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.uwAppPass.value||(this.pwCache?this.pwCache.pw:""))||je.passwordIsCached()){e.next=6;break}return t.unlockErr.textContent=ge(g),Ie.show(t.unlockErr),e.abrupt("return");case 6:return Ie.hide(this.page.unlockErr),r={assetID:this.currentAsset.id,pass:n},t.uwAppPass.value="",o=at().loading(this.form),e.next=12,et("/api/openwallet",r);case 12:if(a=e.sent,o(),at().checkResponse(a)){e.next=17;break}return this.setError(a.msg),e.abrupt("return");case 17:this.pwCache&&(this.pwCache.pw=n),this.success();case 19:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Wt=function(){function e(t,n){var r=this;i(this,e),u(this,"form",void 0),u(this,"page",void 0),u(this,"order",void 0),u(this,"acceleratedRate",void 0),u(this,"earlyAcceleration",void 0),u(this,"currencyUnit",void 0),u(this,"success",void 0),this.form=t,this.success=n;var o=this.page=Ie.idDescendants(t);Ie.bind(o.accelerateSubmit,"click",(function(){r.submit()})),Ie.bind(o.submitEarlyConfirm,"click",(function(){r.sendAccelerateRequest()}))}var t,n,r,o;return c(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?(Ie.show(e.recentAccelerationMsg),Ie.hide(e.recentSwapMsg),e.recentAccelerationTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))):(Ie.show(e.recentSwapMsg),Ie.hide(e.recentAccelerationMsg),e.recentSwapTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))),Ie.hide(e.configureAccelerationDiv,e.accelerateErr),Ie.show(e.earlyAccelerationDiv))}},{key:"sendAccelerateRequest",value:(o=a(v().mark((function e(){var t,n,r,o,a;return v().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="",o=at().loading(n.accelerateMainDiv),e.next=7,et("/api/accelerateorder",r);case 7:a=e.sent,o(),at().checkResponse(a)?(n.accelerateTxID.textContent=a.txID,Ie.hide(n.accelerateMainDiv,n.preAccelerateErr,n.accelerateErr),Ie.show(n.accelerateMsgDiv,n.accelerateSuccess),this.success()):(n.accelerateErr.textContent="Error accelerating order: ".concat(a.msg),Ie.hide(n.earlyAccelerationDiv),Ie.show(n.accelerateErr,n.configureAccelerationDiv));case 10:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"submit",value:(r=a(v().mark((function e(){return v().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=a(v().mark((function e(t){var n,r,o,a,i,s,c=this;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.order=t,e.next=4,et("/api/preaccelerate",t.id);case 4:if(r=e.sent,at().checkResponse(r)){e.next=10;break}return n.preAccelerateErr.textContent="Error accelerating order: ".concat(r.msg),Ie.hide(n.accelerateMainDiv,n.accelerateSuccess),Ie.show(n.accelerateMsgDiv,n.preAccelerateErr),e.abrupt("return");case 10:Ie.hide(n.accelerateMsgDiv,n.preAccelerateErr,n.accelerateErr,n.feeEstimateDiv,n.earlyAccelerationDiv),Ie.show(n.accelerateMainDiv,n.accelerateSuccess,n.configureAccelerationDiv),o=r.preAccelerate,this.earlyAcceleration=o.earlyAcceleration,this.currencyUnit=o.suggestedRange.yUnit,n.accelerateAvgFeeRate.textContent="".concat(o.swapRate," ").concat(o.suggestedRange.yUnit),n.accelerateCurrentFeeRate.textContent="".concat(o.suggestedRate," ").concat(o.suggestedRange.yUnit),this.acceleratedRate=o.suggestedRange.start.y,a=function(){},i=function(e,t){c.acceleratedRate=t},s=new Ct(o.suggestedRange,o.suggestedRange.start.x,i,(function(){return c.updateAccelerationEstimate()}),a,!0),Ie.empty(n.sliderContainer),n.sliderContainer.appendChild(s.control),this.updateAccelerationEstimate();case 25:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"updateAccelerationEstimate",value:(t=a(v().mark((function e(){var t,n,r,o,a,i,s,c;return v().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},o=at().loading(t.sliderContainer),e.next=6,et("/api/accelerationestimate",r);case 6:if(a=e.sent,o(),at().checkResponse(a)){e.next=12;break}return t.accelerateErr.textContent="Error estimating acceleration fee: ".concat(a.msg),Ie.show(t.accelerateErr),e.abrupt("return");case 12:t.feeRateEstimate.textContent="".concat(this.acceleratedRate," ").concat(this.currencyUnit),n.sell?(i=n.baseID,s=n.baseSymbol):(i=n.quoteID,s=n.quoteSymbol),c=at().unitInfo(i),t.feeEstimate.textContent="".concat(a.fee/c.conventional.conversionFactor," ").concat(s),Ie.show(t.feeEstimateDiv);case 17:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Bt=function(){function e(t,n,r,o){var a=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"pwCache",void 0),u(this,"defaultTLSText",void 0),u(this,"page",void 0),u(this,"knownExchanges",void 0),u(this,"dexToUpdate",void 0),this.form=t,this.success=n,this.pwCache=r||null,this.defaultTLSText="none selected";var s=this.page=Ie.parseTemplate(t);s.selectedCert.textContent=this.defaultTLSText,Ie.bind(s.certFile,"change",(function(){return a.onCertFileChange()})),Ie.bind(s.removeCert,"click",(function(){return a.clearCertFile()})),Ie.bind(s.addCert,"click",(function(){return s.certFile.click()})),Ie.bind(s.showCustom,"click",(function(){Ie.hide(s.showCustom),Ie.show(s.customBox,s.auth)})),this.knownExchanges=Array.from(s.knownXCs.querySelectorAll(".known-exchange"));var c,l=At(this.knownExchanges);try{var h=function(){var e=c.value;Ie.bind(e,"click",(function(){var t,n=e.dataset.host,o=At(a.knownExchanges);try{for(o.s();!(t=o.n()).done;)t.value.classList.remove("selected")}catch(e){o.e(e)}finally{o.f()}if(je.passwordIsCached()||r&&r.pw)return a.checkDEX(n);e.classList.add("selected"),s.appPW.focus(),s.addr.value=n}))};for(l.s();!(c=l.n()).done;)h()}catch(e){l.e(e)}finally{l.f()}zt(t,s.submit,(function(){return a.checkDEX()})),o&&(Ie.hide(s.addDexHdr),Ie.show(s.updateDexHdr),this.dexToUpdate=o),this.refresh()}var t,n,r;return c(e,[{key:"refresh",value:function(){var e=this.page;e.addr.value="",e.appPW.value="",this.clearCertFile(),Ie.hide(e.err),je.passwordIsCached()||this.pwCache&&this.pwCache.pw?Ie.hide(e.appPWBox,e.auth):Ie.show(e.appPWBox,e.auth),0===this.knownExchanges.length||this.dexToUpdate?(Ie.show(e.customBox,e.auth),Ie.hide(e.showCustom,e.knownXCs,e.pickServerMsg,e.addCustomMsg)):(Ie.hide(e.customBox),Ie.show(e.showCustom));var t,n=At(this.knownExchanges);try{for(n.s();!(t=n.n()).done;)t.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}}},{key:"animate",value:(r=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,Ie.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=a(v().mark((function e(t){var n,r,o,a,i,s,c,u;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,Ie.hide(n.err),""!==(t=t||n.addr.value)){e.next=7;break}return n.err.textContent="DEX address cannot be empty",Ie.show(n.err),e.abrupt("return");case 7:if(r="",!n.certFile.value){e.next=14;break}if(!(o=n.certFile.files)||!o.length){e.next=14;break}return e.next=13,o[0].text();case 13:r=e.sent;case 14:return a="",je.passwordIsCached()||(a=n.appPW.value||(this.pwCache?this.pwCache.pw:"")),this.dexToUpdate?(i="/api/updatedexhost",s={newHost:t,cert:r,pw:a,oldHost:this.dexToUpdate}):(i="/api/discoveracct",s={addr:t,cert:r,pass:a}),c=at().loading(this.form),e.next=20,et(i,s);case 20:if(u=e.sent,c(),at().checkResponse(u,!0)){e.next=25;break}return"certificate required"===u.msg?Ie.show(n.needCert):(n.err.textContent=u.msg,Ie.show(n.err)),e.abrupt("return");case 25:if(this.dexToUpdate||!u.paid){e.next=30;break}return e.next=28,at().fetchUser();case 28:return at().loadPage("markets"),e.abrupt("return");case 30:this.pwCache&&(this.pwCache.pw=a),this.success(u.xc,r);case 32:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"onCertFileChange",value:(t=a(v().mark((function e(){var t,n;return v().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,Ie.show(t.removeCert),Ie.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=this.defaultTLSText,Ie.hide(e.removeCert),Ie.show(e.addCert)}}]),e}(),qt=function(){function e(t,n,r){var o=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"pwCache",void 0),u(this,"headerTxt",void 0),u(this,"page",void 0),this.success=n,this.form=t,this.pwCache=r||null;var a=this.page=Ie.parseTemplate(t);this.headerTxt=a.header.textContent||"",zt(t,a.submit,(function(){o.submit()}))}var t,n;return c(e,[{key:"focus",value:function(){this.page.pw.focus()}},{key:"submit",value:(n=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.errMsg),n=t.pw.value||"",t.pw.value="",r=t.rememberPass.checked,""!==n){e.next=9;break}return t.errMsg.textContent=ge(y),Ie.show(t.errMsg),e.abrupt("return");case 9:return o=at().loading(this.form),e.next=12,et("/api/login",{pass:n,rememberPass:r});case 12:if(a=e.sent,o(),at().checkResponse(a)){e.next=18;break}return t.errMsg.textContent=a.msg,Ie.show(t.errMsg),e.abrupt("return");case 18:a.notes&&a.notes.reverse(),at().setNotes(a.notes||[]),this.pwCache&&(this.pwCache.pw=n),this.success();case 22:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"animate",value:(t=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,Ie.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}(),Mt=300;function Nt(e,t){return Ut.apply(this,arguments)}function Ut(){return(Ut=a(v().mark((function e(t,n){var r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=document.body.offsetWidth/2,e.next=3,Ie.animate(Mt,(function(e){t.style.right="".concat(e*r,"px")}),"easeInHard");case 3:return Ie.hide(t),t.style.right="0",n.style.right=String(-r),Ie.show(n),n.querySelector("input")&&Ie.safeSelector(n,"input").focus(),e.next=10,Ie.animate(Mt,(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 zt(e,t,n){var r=function(e){e.preventDefault&&e.preventDefault(),n(e)};Ie.bind(t,"click",r),Ie.bind(e,"submit",r)}function jt(e){return Math.floor(e.getTime()/1e3)}var _t=function(e){He(g,e);var t,n,r,o,s,l,h,d,f,p,m=(f=g,p=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=Qe(f);if(p){var n=Qe(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return Xe(this,e)});function g(e){var t;i(this,g),u(_e(t=m.call(this)),"body",void 0),u(_e(t),"pwCache",void 0),u(_e(t),"currentDEX",void 0),u(_e(t),"page",void 0),u(_e(t),"loginForm",void 0),u(_e(t),"dexAddrForm",void 0),u(_e(t),"newWalletForm",void 0),u(_e(t),"regAssetForm",void 0),u(_e(t),"walletWaitForm",void 0),u(_e(t),"confirmRegisterForm",void 0),t.body=e,t.pwCache={pw:""};var n=t.page=Ie.idDescendants(e);e.querySelectorAll(".form-closer").forEach((function(e){return Ie.hide(e)})),zt(n.appPWForm,n.appPWSubmit,(function(){return t.setAppPass()})),Ie.bind(n.showSeedRestore,"click",(function(){Ie.show(n.seedRestore),Ie.hide(n.showSeedRestore)})),t.loginForm=new qt(n.loginForm,a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:t.dexAddrForm.refresh(),Nt(n.loginForm,n.dexAddrForm);case 4:case"end":return e.stop()}}),e)}))),t.pwCache),t.newWalletForm=new It(n.newWalletForm,(function(e){return t.newWalletCreated(e)}),t.pwCache,(function(){return t.animateRegAsset(n.newWalletForm)})),t.dexAddrForm=new Bt(n.dexAddrForm,function(){var e=a(v().mark((function e(r,o){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.currentDEX=r,t.confirmRegisterForm.setExchange(r,o),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)}}(),t.pwCache),t.regAssetForm=new Tt(n.regAssetForm,function(){var e=a(v().mark((function e(r){var o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.confirmRegisterForm.setAsset(r),o=at().assets[r],!(a=o.wallet)){e.next=14;break}if(i=t.currentDEX.regFees[o.symbol],!(a.synced&&a.balance.available>i.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 s=e.sent,t.walletWaitForm.setWallet(a,s),Nt(n.regAssetForm,n.walletWait),e.abrupt("return");case 14:t.newWalletForm.setAsset(r),t.newWalletForm.loadDefaults(),Nt(n.regAssetForm,n.newWalletForm);case 17:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),t.walletWaitForm=new Lt(n.walletWait,(function(){t.animateConfirmForm(n.walletWait)}),(function(){t.animateRegAsset(n.walletWait)})),t.confirmRegisterForm=new Dt(n.confirmRegForm,(function(){t.registerDEXSuccess()}),(function(){t.animateRegAsset(n.confirmRegForm)}),t.pwCache);var r=Ie.safeSelector(n.forms,":scope > form.selected");switch(r.classList.remove("selected"),r){case n.loginForm:t.loginForm.animate();break;case n.dexAddrForm:t.dexAddrForm.animate()}return Ie.show(r),at().user.authed&&t.auth(),t}return c(g,[{key:"unload",value:function(){this.pwCache.pw=""}},{key:"auth",value:(d=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:case"end":return e.stop()}}),e)}))),function(){return d.apply(this,arguments)})},{key:"animateRegAsset",value:(h=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:Ie.hide(t),this.regAssetForm.animate(),Ie.show(this.page.regAssetForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return h.apply(this,arguments)})},{key:"animateConfirmForm",value:(l=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),Ie.hide(t),Ie.show(this.page.confirmRegForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return l.apply(this,arguments)})},{key:"getRegistrationTxFeeEstimate",value:(s=a(v().mark((function e(t,n){var r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getCertFile();case 2:return r=e.sent,o=at().loading(n),e.next=6,et("/api/regtxfee",{addr:this.currentDEX.host,cert:r,asset:t});case 6:if(a=e.sent,o(),at().checkResponse(a,!0)){e.next=10;break}return e.abrupt("return",0);case 10:return e.abrupt("return",a.txfee);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return s.apply(this,arguments)})},{key:"setAppPass",value:(o=a(v().mark((function e(){var t,n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.appPWErrMsg),n=t.appPW.value||"",r=t.appPWAgain.value,""!==n){e.next=8;break}return t.appPWErrMsg.textContent=ge(y),Ie.show(t.appPWErrMsg),e.abrupt("return");case 8:if(n===r){e.next=12;break}return t.appPWErrMsg.textContent=ge(j),Ie.show(t.appPWErrMsg),e.abrupt("return");case 12:return at().setNotes([]),t.appPW.value="",t.appPWAgain.value="",o=at().loading(t.appPWForm),a=t.seedInput.value,i=t.rememberPass.checked,e.next=20,et("/api/init",{pass:n,seed:a,rememberPass:i});case 20:if(s=e.sent,o(),at().checkResponse(s)){e.next=26;break}return t.appPWErrMsg.textContent=s.msg,Ie.show(t.appPWErrMsg),e.abrupt("return");case 26:return this.pwCache.pw=n,this.auth(),at().updateMenuItemsDisplay(),this.newWalletForm.refresh(),this.dexAddrForm.refresh(),e.next=33,Nt(t.appPWForm,t.dexAddrForm);case 33:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"getCertFile",value:(r=a(v().mark((function e(){var t,n;return v().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=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:at().loadPage("markets");case 3:case"end":return e.stop()}}),e)}))),function(){return n.apply(this,arguments)})},{key:"newWalletCreated",value:(t=a(v().mark((function e(t){var n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.regAssetForm.refresh(),e.next=3,at().fetchUser();case 3:if(n=e.sent){e.next=6;break}return e.abrupt("return");case 6:if(r=this.page,o=n.assets[t],a=o.wallet,i=this.currentDEX.regFees[o.symbol].amount,!(a.synced&&a.balance.available>i)){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.getRegistrationTxFeeEstimate(t,r.newWalletForm);case 16:return s=e.sent,this.walletWaitForm.setWallet(a,s),e.next=20,Nt(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),g}(Ze);var Vt=function(e){He(s,e);var t,n,r,o=(n=s,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=Qe(n);if(r){var o=Qe(this).constructor;e=Reflect.construct(t,arguments,o)}else e=t.apply(this,arguments);return Xe(this,e)});function s(e){var t;return i(this,s),u(_e(t=o.call(this)),"form",void 0),u(_e(t),"loginForm",void 0),t.form=Ie.idel(e,"loginForm"),Ie.show(t.form),t.loginForm=new qt(t.form,(function(){t.loggedIn()})),t.loginForm.focus(),t}return c(s,[{key:"loggedIn",value:(t=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:at().loadPage("markets");case 3:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})}]),s}(Ze);function Ht(e,t,n){return{subject:e,details:t,severity:n,stamp:(new Date).getTime(),acked:!1,type:"internal",topic:"internal",id:""}}function Gt(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 Xt(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)?Xt(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Xt(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){Ie.bind(e,"click",(function(){t.closePopups()}))})),Ie.bind(n.cancelForce,"click",(function(){t.closePopups()})),Ie.bind(n.copyAddressBtn,"click",(function(){t.copyAddress()}));var o,s,c=function(e,t){return e.querySelector("[data-action=".concat(t,"]"))},l=t.rowInfos={},h=Gt(Ie.applySelector(n.walletTable,"tr"));try{for(h.s();!(s=h.n()).done;){var d=s.value,f=parseInt(d.dataset.assetID||"");l[f]={assetID:f,tr:d,symbol:d.dataset.symbol||"",name:d.dataset.name||"",stateIcons:new De(d),actions:{connect:c(d,"connect"),unlock:c(d,"unlock"),send:c(d,"send"),deposit:c(d,"deposit"),create:c(d,"create"),rescan:c(d,"rescan"),lock:c(d,"lock"),settings:c(d,"settings")}},o||(o=l[f])}}catch(e){h.e(e)}finally{h.f()}n.marketCard.removeAttribute("id"),n.marketCard.remove(),n.oneMarket.removeAttribute("id"),n.oneMarket.remove(),t.newWalletForm=new It(n.newWalletForm,(function(){t.createWalletSuccess()})),t.reconfigForm=new Ot(n.reconfigInputs,!1),t.unlockForm=new Pt(n.unlockWalletForm,(function(){t.openWalletSuccess()})),zt(n.sendForm,n.submitSendForm,(function(){t.send()})),zt(n.reconfigForm,n.submitReconfig,(function(){return t.reconfig()}));for(var p=function(){var e=y[m];Qt(e.tr,"click",(function(){t.showMarkets(e.assetID)}))},m=0,y=Object.values(l);m1){Ie.empty(n.changeWalletTypeSelect),Ie.show(n.showChangeType,n.changeTypeShowIcon),n.changeTypeMsg.textContent=ge(ce),a=Gt(r.info.availablewallets);try{for(a.s();!(i=a.n()).done;)s=i.value,c=document.createElement("option"),s.type===o.type&&(c.selected=!0),c.value=c.textContent=s.type,n.changeWalletTypeSelect.appendChild(c)}catch(e){a.e(e)}finally{a.f()}}else Ie.hide(n.showChangeType);return 0!=(4&(u=at().walletMap[t]).traits)?Ie.show(n.downloadLogs):Ie.hide(n.downloadLogs),0!=(32&u.traits)?Ie.show(n.recoverWallet):Ie.hide(n.recoverWallet),256&u.traits?Ie.show(n.exportWallet):Ie.hide(n.exportWallet),n.recfgAssetLogo.src=Ie.logoPath(r.symbol),n.recfgAssetName.textContent=r.info.name,e.next=17,this.hideBox();case 17:return this.animation=this.showBox(n.reconfigForm),l=at().loading(n.reconfigForm),e.next=21,et("/api/walletsettings",{assetID:t});case 21:if(h=e.sent,l(),at().checkResponse(h,!0)){e.next=27;break}return n.reconfigErr.textContent=h.msg,Ie.show(n.reconfigErr),e.abrupt("return");case 27:d=at().walletIsActive(t),this.reconfigForm.update(o.configopts||[],d),this.reconfigForm.setConfig(h.map),this.updateDisplayedReconfigFields(o);case 31:case"end":return e.stop()}}),e,this)}))),function(e){return C.apply(this,arguments)})},{key:"changeWalletType",value:function(){var e=this.page.changeWalletTypeSelect.value||"",t=at().walletDefinition(this.reconfigAsset,e);this.reconfigForm.update(t.configopts||[]),this.updateDisplayedReconfigFields(t)}},{key:"updateDisplayedReconfigFields",value:function(e){e.seeded?(Ie.hide(this.page.showChangePW),this.changeWalletPW=!1,this.setPWSettingViz(!1)):Ie.show(this.page.showChangePW)}},{key:"showDeposit",value:(x=a(v().mark((function e(t){var n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,Ie.hide(n.depositErr),r=n.deposit,o=at().assets[t],n.depositLogo.src=Ie.logoPath(o.symbol),a=at().walletMap[t],this.depositAsset=this.lastFormAsset=t,a){e.next=10;break}return at().notify(Ht("Cannot retrieve deposit address.","No wallet found for ".concat(o.info.name),5)),e.abrupt("return");case 10:return e.next=12,this.hideBox();case 12:n.depositName.textContent=o.info.name,n.depositAddress.textContent=a.address,n.qrcode.src="/generateqrcode?address=".concat(a.address),0!=(2&a.traits)?Ie.show(n.newDepAddrBttn):Ie.hide(n.newDepAddrBttn),this.animation=this.showBox(r);case 17:case"end":return e.stop()}}),e,this)}))),function(e){return x.apply(this,arguments)})},{key:"newDepositAddress",value:(b=a(v().mark((function e(){var t,n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.depositErr),n=at().loading(t.deposit),e.next=5,et("/api/depositaddress",{assetID:this.depositAsset});case 5:if(r=e.sent,n(),at().checkResponse(r,!0)){e.next=11;break}return t.depositErr.textContent=r.msg,Ie.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 b.apply(this,arguments)})},{key:"showSendForm",value:(k=a(v().mark((function e(t){var n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=n.sendForm,o=this.sendAsset=at().assets[t],this.lastFormAsset=t,(a=at().walletMap[t])||at().notify(Ht("Cannot send/withdraw.","No wallet found for ".concat(o.info.name),5)),e.next=8,this.hideBox();case 8:Ie.hide(n.senderOnlyHelpText),Ie.hide(n.toggleSubtract),n.subtractCheckBox.checked=!1,0!=(64&a.traits)?Ie.show(n.toggleSubtract):(Ie.show(n.senderOnlyHelpText),n.subtractCheckBox.checked=!1),n.sendAddr.value="",n.sendAmt.value="",n.sendPW.value="",n.sendErr.textContent="",this.showFiatValue(o.id,0,n.sendValue),n.sendAvail.textContent=Ie.formatFullPrecision(a.balance.available,o.info.unitinfo),n.sendLogo.src=Ie.logoPath(o.symbol),n.sendName.textContent=o.info.name,r.dataset.assetID=String(t),this.animation=this.showBox(r,n.walletPass);case 23:case"end":return e.stop()}}),e,this)}))),function(e){return k.apply(this,arguments)})},{key:"doConnect",value:(w=a(v().mark((function e(t){var n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=at().loading(this.body),e.next=3,et("/api/connectwallet",{assetID:t});case 3:if(r=e.sent,n(),at().checkResponse(r)){e.next=7;break}return e.abrupt("return");case 7:o=this.rowInfos[t],Ie.hide(o.actions.connect);case 9:case"end":return e.stop()}}),e,this)}))),function(e){return w.apply(this,arguments)})},{key:"createWalletSuccess",value:(y=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.rowInfos[this.walletAsset],this.showMarkets(t.assetID),e.next=4,at().fetchUser();case 4:return e.next=6,at().loadPage("wallets");case 6:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"openWalletSuccess",value:(m=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.rowInfos[this.openAsset],n=t.actions,Ie.show(n.send,n.deposit),Ie.hide(n.unlock,n.connect),at().walletMap[t.assetID].encrypted&&Ie.show(n.lock),this.showMarkets(this.openAsset);case 6:case"end":return e.stop()}}),e,this)}))),function(){return m.apply(this,arguments)})},{key:"send",value:(p=a(v().mark((function e(){var t,n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.sendErr),n=parseInt(t.sendForm.dataset.assetID||""),r=t.subtractCheckBox.checked||!1,o=at().unitInfo(n).conventional.conversionFactor,a={assetID:n,address:t.sendAddr.value,subtract:r,value:Math.round(parseFloat(t.sendAmt.value||"")*o),pw:t.sendPW.value},i=at().loading(t.sendForm),e.next=9,et("/api/send",a);case 9:if(s=e.sent,i(),at().checkResponse(s,!0)){e.next=15;break}return t.sendErr.textContent=s.msg,Ie.show(t.sendErr),e.abrupt("return");case 15:this.showMarkets(n);case 16:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"reconfig",value:(f=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.reconfigErr),t.appPW.value||je.passwordIsCached()){e.next=6;break}return t.reconfigErr.textContent=ge(g),Ie.show(t.reconfigErr),e.abrupt("return");case 6:return n=at().currentWalletDefinition(this.reconfigAsset).type,Ie.isHidden(t.changeWalletType)||(n=t.changeWalletTypeSelect.value||""),r=at().loading(t.reconfigForm),o={assetID:this.reconfigAsset,config:this.reconfigForm.map(),appPW:t.appPW.value||"",walletType:n},this.changeWalletPW&&(o.newWalletPW=t.newPW.value),e.next=13,et("/api/reconfigurewallet",o);case 13:if(a=e.sent,t.appPW.value="",t.newPW.value="",r(),at().checkResponse(a,!0)){e.next=21;break}return t.reconfigErr.textContent=a.msg,Ie.show(t.reconfigErr),e.abrupt("return");case 21:this.showMarkets(this.reconfigAsset);case 22:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"lock",value:(d=a(v().mark((function e(t,n){var r,o,a,i;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=this.page,o=at().loading(r.newWalletForm),e.next=4,et("/api/closewallet",{assetID:t});case 4:if(a=e.sent,o(),at().checkResponse(a)){e.next=8;break}return e.abrupt("return");case 8:i=n.actions,Ie.hide(i.send,i.lock,i.deposit),Ie.show(i.unlock);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return d.apply(this,arguments)})},{key:"downloadLogs",value:(h=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(t=new URLSearchParams("")).append("assetid","".concat(this.reconfigAsset)),(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 h.apply(this,arguments)})},{key:"displayExportWalletAuth",value:(l=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,Ie.hide(t.exportWalletErr),t.exportWalletPW.value="",this.showForm(t.exportWalletAuth);case 4:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"exportWalletAuthSubmit",value:(s=a(v().mark((function e(){var t,n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n={assetID:this.reconfigAsset,pass:t.exportWalletPW.value},r=at().loading(t.forms),e.next=6,et("/api/restorewalletinfo",n);case 6:o=e.sent,r(),at().checkResponse(o)?(t.exportWalletPW.value="",this.displayRestoreWalletInfo(o.restorationinfo)):(t.exportWalletErr.textContent=o.msg,Ie.show(t.exportWalletErr));case 9:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"displayRestoreWalletInfo",value:(o=a(v().mark((function e(t){var n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.page,Ie.empty(n.restoreInfoCardsList),r=Gt(t);try{for(r.s();!(o=r.n()).done;)a=o.value,i=this.restoreInfoCard.cloneNode(!0),(s=Ie.parseTemplate(i)).name.textContent=a.target,s.seed.textContent=a.seed,s.seedName.textContent="".concat(a.seedName,":"),s.instructions.textContent=a.instructions,n.restoreInfoCardsList.appendChild(i)}catch(e){r.e(e)}finally{r.f()}this.showForm(n.restoreWalletInfo);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"recoverWallet",value:(n=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.recoverWalletErr),n={assetID:this.reconfigAsset,appPW:t.recoverWalletPW.value},t.recoverWalletPW.value="",r="/api/recoverwallet",o=at().loading(t.forms),e.next=8,et(r,n);case 8:a=e.sent,o(),35===a.code?(this.forceUrl=r,this.forceReq=n,this.showConfirmForce()):at().checkResponse(a)?this.closePopups():(t.recoverWalletErr.textContent=a.msg,Ie.show(t.recoverWalletErr));case 11:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"confirmForceSubmit",value:(t=a(v().mark((function e(){var t,n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.forceReq.force=!0,n=at().loading(t.forms),e.next=5,et(this.forceUrl,this.forceReq);case 5:r=e.sent,n(),at().checkResponse(r)?this.closePopups():(t.confirmForceErr.textContent=r.msg,Ie.show(t.confirmForceErr));case 8:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleBalanceNote",value:function(e){Ie.safeSelector(this.page.walletTable,'[data-balance-target="'.concat(e.assetID,'"]')).textContent=Ie.formatFullPrecision(e.balance.available,at().unitInfo(e.assetID));var t=Ie.safeSelector(this.page.walletTable,'[data-conversion-target="'.concat(e.assetID,'"]'));t&&this.showFiatValue(e.assetID,e.balance.available,t)}},{key:"handleRatesNote",value:function(e){at().fiatRatesMap=e.fiatRates;var t,n=Gt(Object.entries(at().walletMap));try{for(n.s();!(t=n.n()).done;){var o=r(t.value,2),a=o[0],i=o[1];if(i){var s=this.page.walletTable.querySelector('[data-conversion-target="'.concat(a,'"]'));s&&this.showFiatValue(a,i.balance.available,s)}}}catch(e){n.e(e)}finally{n.f()}}},{key:"showFiatValue",value:function(e,t,n){if(n){var r=at().fiatRatesMap[e];n.textContent=Ie.formatFiatConversion(t,r,at().unitInfo(e)),r?Ie.show(n.parentElement):Ie.hide(n.parentElement)}}},{key:"handleWalletStateNote",value:function(e){this.rowInfos[e.wallet.assetID].stateIcons.readWallet(e.wallet);var t=this.page.walletTable.querySelector('[data-conversion-target="'.concat(e.wallet.assetID,'"]'));t&&this.showFiatValue(e.wallet.assetID,e.wallet.balance.available,t)}},{key:"unload",value:function(){Ie.unbind(document,"keyup",this.keyup)}}]),B}(Ze);function Kt(e){return"".concat(e.basesymbol.toUpperCase(),"-").concat(e.quotesymbol.toUpperCase())}var Zt=function(e){He(x,e);var t,n,r,o,s,l,h,d,f,p,m,y,w,k,b=(w=x,k=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=Qe(w);if(k){var n=Qe(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return Xe(this,e)});function x(e){var t;i(this,x),u(_e(t=b.call(this)),"body",void 0),u(_e(t),"currentDEX",void 0),u(_e(t),"page",void 0),u(_e(t),"forms",void 0),u(_e(t),"fiatRateSources",void 0),u(_e(t),"regAssetForm",void 0),u(_e(t),"confirmRegisterForm",void 0),u(_e(t),"newWalletForm",void 0),u(_e(t),"walletWaitForm",void 0),u(_e(t),"dexAddrForm",void 0),u(_e(t),"currentForm",void 0),u(_e(t),"pwCache",void 0),u(_e(t),"defaultTLSText",void 0),u(_e(t),"keyup",void 0),t.body=e,t.defaultTLSText="none selected";var n=t.page=Ie.idDescendants(e);t.forms=Ie.applySelector(n.forms,":scope > form"),t.fiatRateSources=Ie.applySelector(n.fiatRateSources,"input[type=checkbox]"),Ie.bind(n.darkMode,"click",(function(){je.dark(n.darkMode.checked||!1),n.darkMode.checked?document.body.classList.add("dark"):document.body.classList.remove("dark")})),Ie.bind(n.showPokes,"click",(function(){var e=n.showPokes.checked||!1;je.setCookie("popups",e?"1":"0"),at().showPopups=e})),n.commitHash.textContent=at().commitHash.substring(0,7),Ie.bind(n.addADex,"click",(function(){t.dexAddrForm.refresh(),t.showForm(n.dexAddrForm)})),t.fiatRateSources.forEach((function(e){Ie.bind(e,"change",a(v().mark((function t(){var n;return v().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,et("/api/toggleratesource",{disable:!e.checked,source:e.value});case 2:return n=t.sent,at().checkResponse(n)||(e.checked=!e.checked),t.next=6,at().fetchUser();case 6:case"end":return t.stop()}}),t)}))))})),t.regAssetForm=new Tt(n.regAssetForm,function(){var e=a(v().mark((function e(r){var o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.confirmRegisterForm.setAsset(r),o=at().assets[r],!(a=o.wallet)){e.next=14;break}if(i=t.currentDEX.regFees[o.symbol],!(a.synced&&a.balance.available>i.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 s=e.sent,t.walletWaitForm.setWallet(a,s),Nt(n.regAssetForm,n.walletWait),e.abrupt("return");case 14:t.newWalletForm.setAsset(r),t.newWalletForm.loadDefaults(),t.currentForm=n.newWalletForm,Nt(n.regAssetForm,n.newWalletForm);case 18:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),t.confirmRegisterForm=new Dt(n.confirmRegForm,(function(){t.registerDEXSuccess()}),(function(){t.animateRegAsset(n.confirmRegForm)}),t.pwCache),t.newWalletForm=new It(n.newWalletForm,(function(e){return t.newWalletCreated(e)}),t.pwCache,(function(){return t.animateRegAsset(n.newWalletForm)})),t.walletWaitForm=new Lt(n.walletWait,(function(){t.animateConfirmForm(n.walletWait)}),(function(){t.animateRegAsset(n.walletWait)})),t.dexAddrForm=new Bt(n.dexAddrForm,function(){var e=a(v().mark((function e(r,o){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.currentDEX=r,t.confirmRegisterForm.setExchange(r,o),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)}}()),Ie.bind(n.importAccount,"click",(function(){return t.prepareAccountImport(n.authorizeAccountImportForm)})),zt(n.authorizeAccountImportForm,n.authorizeImportAccountConfirm,(function(){return t.importAccount()})),Ie.bind(n.changeAppPW,"click",(function(){return t.showForm(n.changeAppPWForm)})),zt(n.changeAppPWForm,n.submitNewPW,(function(){return t.changeAppPW()})),Ie.bind(n.accountFile,"change",(function(){return t.onAccountFileChange()})),Ie.bind(n.removeAccount,"click",(function(){return t.clearAccountFile()})),Ie.bind(n.addAccount,"click",(function(){return n.accountFile.click()})),Ie.bind(n.exportSeed,"click",(function(){return t.showForm(n.exportSeedAuth)})),zt(n.exportSeedAuth,n.exportSeedSubmit,(function(){return t.submitExportSeedReq()}));var r=function(){Ie.hide(n.forms),n.exportSeedPW.value="",n.seedDiv.textContent=""};return Ie.bind(n.forms,"mousedown",(function(e){Ie.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},Ie.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){Ie.bind(e,"click",(function(){r()}))})),t}return c(x,[{key:"getRegistrationTxFeeEstimate",value:(y=a(v().mark((function e(t,n){var r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getCertFile();case 2:return r=e.sent,o=at().loading(n),e.next=6,et("/api/regtxfee",{addr:this.currentDEX.host,cert:r,asset:t});case 6:if(a=e.sent,o(),at().checkResponse(a,!0)){e.next=10;break}return e.abrupt("return",0);case 10:return e.abrupt("return",a.txfee);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return y.apply(this,arguments)})},{key:"newWalletCreated",value:(m=a(v().mark((function e(t){var n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:if(n=e.sent){e.next=5;break}return e.abrupt("return");case 5:if(r=this.page,o=n.assets[t],a=o.wallet,i=this.currentDEX.regFees[o.symbol].amount,!(a.synced&&a.balance.available>i)){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 s=e.sent,this.walletWaitForm.setWallet(a,s),this.currentForm=r.walletWait,e.next=20,Nt(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return m.apply(this,arguments)})},{key:"onAccountFileChange",value:(p=a(v().mark((function e(){var t,n;return v().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,Ie.show(t.removeAccount),Ie.hide(t.addAccount);case 7:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"clearAccountFile",value:function(){var e=this.page;e.accountFile.value="",e.selectedAccount.textContent="none selected",Ie.hide(e.removeAccount),Ie.show(e.addAccount)}},{key:"prepareAccountImport",value:(f=a(v().mark((function e(t){return v().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 f.apply(this,arguments)})},{key:"importAccount",value:(d=a(v().mark((function e(){var t,n,r,o,a,i,s,c,u;return v().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((o=t.accountFile.files)&&o.length){e.next=9;break}return console.error("importAccount: no file specified"),e.abrupt("return");case 9:return e.next=11,o[0].text();case 11:r=e.sent;case 12:e.prev=12,a=JSON.parse(r),e.next=21;break;case 16:return e.prev=16,e.t0=e.catch(12),t.importAccountErr.textContent=e.t0.message,Ie.show(t.importAccountErr),e.abrupt("return");case 21:if(void 0!==a){e.next=25;break}return t.importAccountErr.textContent=ge(_),Ie.show(t.importAccountErr),e.abrupt("return");case 25:return i={pw:n,account:a},s=at().loading(this.body),e.next=29,et("/api/importaccount",i);case 29:if(c=e.sent,s(),at().checkResponse(c)){e.next=35;break}return t.importAccountErr.textContent=c.msg,Ie.show(t.importAccountErr),e.abrupt("return");case 35:return e.next=37,et("/api/login",{pass:n});case 37:if(u=e.sent,at().checkResponse(u)){e.next=42;break}return t.importAccountErr.textContent=u.msg,Ie.show(t.importAccountErr),e.abrupt("return");case 42:return e.next=44,at().fetchUser();case 44:Ie.hide(t.forms),window.location.reload();case 46:case"end":return e.stop()}}),e,this,[[12,16]])}))),function(){return d.apply(this,arguments)})},{key:"submitExportSeedReq",value:(h=a(v().mark((function e(){var t,n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.exportSeedPW.value,r=at().loading(this.body),e.next=5,et("/api/exportseed",{pass:n});case 5:if(o=e.sent,r(),at().checkResponse(o)){e.next=11;break}return t.exportAccountErr.textContent=o.msg,Ie.show(t.exportSeedE),e.abrupt("return");case 11:t.exportSeedPW.value="",t.seedDiv.textContent=o.seed,this.showForm(t.authorizeSeedDisplay);case 14:case"end":return e.stop()}}),e,this)}))),function(){return h.apply(this,arguments)})},{key:"showForm",value:(l=a(v().mark((function e(t){var n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return Ie.hide(e)})),t.style.right="10000px",Ie.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,Ie.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 l.apply(this,arguments)})},{key:"getCertFile",value:(s=a(v().mark((function e(){var t,n;return v().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 s.apply(this,arguments)})},{key:"registerDEXSuccess",value:(o=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.forms),e.next=4,at().fetchUser();case 4:window.location.reload();case 5:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"changeAppPW",value:(r=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.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=ge(g),Ie.show(t.changePWErrMsg),n(),e.abrupt("return");case 8:if(t.newAppPW.value===t.confirmNewPW.value){e.next=13;break}return t.changePWErrMsg.textContent=ge(j),Ie.show(t.changePWErrMsg),n(),e.abrupt("return");case 13:return r=at().loading(t.changeAppPW),o={appPW:t.appPW.value,newAppPW:t.newAppPW.value},n(),e.next=18,et("/api/changeapppass",o);case 18:if(a=e.sent,r(),at().checkResponse(a,!0)){e.next=24;break}return t.changePWErrMsg.textContent=a.msg,Ie.show(t.changePWErrMsg),e.abrupt("return");case 24:Ie.hide(t.forms);case 25:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"unload",value:function(){Ie.unbind(document,"keyup",this.keyup)}},{key:"animateRegAsset",value:(n=a(v().mark((function e(t){var n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:Ie.hide(t),n=this.page.regAssetForm,this.currentForm=n,this.regAssetForm.animate(),Ie.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"animateConfirmForm",value:(t=a(v().mark((function e(t){var n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),n=this.page.confirmRegForm,this.currentForm=n,Ie.hide(t),Ie.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),x}(Ze);function Jt(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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(e);try{for(r.s();!(t=r.n()).done;){var o=t.value;if(!o.epoch)return o;n||(n=o)}}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 en(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 tn(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)?tn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function tn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=c&&s=s&&i2?2:t),this.draw(),this.reporters.zoom(this.zoomLevel))}},{key:"clicked",value:function(e){if(this.dataExtents){var t=e.clientX-this.rect.left,n=e.clientY-this.rect.y;if(this.zoomInBttn.contains(t,n))this.zoom(!0);else if(this.zoomOutBttn.contains(t,n))this.zoom(!1);else{var r=this.plotRegion.translator(this.dataExtents);this.reporters.click(r.unx(t))}}}},{key:"clear",value:function(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}},{key:"set",value:function(e,t,n,o,a){this.book=e,this.lotSize=t/o.conventional.conversionFactor;var i=[a.conventional.conversionFactor,o.conventional.conversionFactor],s=i[0],c=i[1];if(this.rateStep=n/ht*s/c,this.baseUnit=o.conventional.unit,this.quoteUnit=a.conventional.unit,!this.zoomLevel){var u=r(this.gap(),2),l=u[0],h=u[1],d=Math.max(h/l*5,.05);this.zoomLevel=Math.min(d,2)}this.draw()}},{key:"render",value:function(){var e=this;if(this.book&&this.visible){this.clear();var t=this.ctx,n=this.mousePos,o=this.book.buys,a=this.book.sells,i=r(this.gap(),2),s=i[0],c=i[1],u=this.zoomLevel*s/2,l=s+u,h=s-u,d=ot(this.markers.buys),f=ot(this.markers.sells);d.sort((function(e,t){return t.rate-e.rate})),f.sort((function(e,t){return e.rate-t.rate}));for(var p=[],m=[],v=[],y=[],g=[],w={buyBase:0,buyQuote:0,sellBase:0,sellQuote:0},k=0,b=0,x=0;x=h&&v.push([C.rate,b]),!C.epoch)for(k+=C.qty,m.push([C.rate,k]),w.buyBase+=C.qty,w.buyQuote+=C.qty*C.rate;d.length&&xn(d[0].rate,C.rate);){var S=d.shift();S&&p.push({rate:S.rate,qty:C.epoch?b:k,sell:C.sell,active:S.active})}}var F=m.length?gn(m)[1]:0;m.push([h,F]);var E=v.length?gn(v)[1]:0;v.push([h,E]),b=k=0;for(var A=0;Al||e=L},N=e.theme.sellLine;Lthis.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){var n=t.ms,r=this.mousePos,o=t.candles||[],a=Math.min(this.numToShow,o.length),i=o.slice(o.length-a);if(this.clear(),0!==a){var s,c=function(e){return Sn(e.endStamp,n)},u=function(e){return c(e)+n},l=function(e){return c(e)+.2*n},h=.6*n,d=i[0],f=i[a-1],p=[d.highRate,d.lowRate,d.matchVolume],m=p[0],v=p[1],y=p[2],g=en(i);try{for(g.s();!(s=g.n()).done;){var w=s.value;w.highRate>m&&(m=w.highRate),w.lowRatey&&(y=w.matchVolume)}}catch(e){g.e(e)}finally{g.f()}var k=this.market.ratestep,b=new pn(c(d),u(f),v,m);v===m&&(b.y.min-=k,b.y.max+=k),this.dataExtents=b;var x=this.rateConversionFactor;this.doYLabels(this.candleRegion,k,this.market.quotesymbol,(function(e){return bn(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 o=e[0],a=e[e.length-1],i=Sn(o.endStamp,t),s=Sn(a.endStamp,t)+t,c=s-i,u=Math.min(e.length,n/100),l=Sn(c/u,t);if(0===l)return console.error("zero tick",t,c,u),{lbls:[]};var h=i,d=(new Date).getTimezoneOffset(),f=function(e){return(e-=6e4*d)-e%864e5},p=f(i),m=0;f(o.endStamp)===f(a.endStamp)&&(p=0);var v,y=[];for(v=t<864e5?function(e,t){return f(t)!==p?"".concat(yn[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(yn[e.getMonth()]).concat(e.getDate()," '").concat(String(t).slice(2,4)):"".concat(yn[e.getMonth()]).concat(e.getDate())};h<=s;){var g=new Date(h);y.push({val:h,txt:v(g,h)}),p=f(h),m=g.getFullYear(),h+=l}return{lbls:y}}(i,n,this.plotRegion.width());this.plotXLabels(C,c(d),u(f),[]),this.drawFrame();var S=null;if(r&&(this.plotRegion.plot(new pn(b.x.min,b.x.max,0,1),(function(t,o){var a,s=Sn(o.unx(r.x),n),u=en(i);try{for(u.s();!(a=u.n()).done;){var l=a.value;if(c(l)===s){S=l,t.fillStyle=e.theme.gridLines,t.fillRect(o.x(c(l)),o.y(0),o.w(n),o.h(1));break}}}catch(e){u.e(e)}finally{u.f()}})),S)){var F=this.xRegion.extents.y;this.xRegion.plot(new pn(b.x.min,b.x.max,F.min,F.max),(function(t,n){if(S){e.applyLabelStyle();var r="".concat(new Date(c(S)).toLocaleString()," - ").concat(new Date(u(S)).toLocaleString()),o=t.measureText(r).width+50,a=n.x((c(S)+u(S))/2),i=a-o/2,s=e.xRegion.extents.x;is.max&&(i=s.max-o),a=i+o/2;var l=F.min+(e.xRegion.height()-16)/2;t.fillStyle=e.theme.legendFill,t.strokeStyle=e.theme.gridBorder;var h=[i-25,l-2,o+50,20];t.fillRect.apply(t,h),t.strokeRect.apply(t,h),e.applyLabelStyle(),t.fillText(r,a,e.xRegion.extents.midY,o)}}))}var E=new pn(c(d),u(f),0,y);this.volumeRegion.plot(E,(function(t,n){t.fillStyle=e.theme.gridBorder;var r,o=en(i);try{for(o.s();!(r=o.n()).done;){var a=r.value;t.fillRect(n.x(l(a)),n.y(0),n.w(h),n.h(a.matchVolume))}}catch(e){o.e(e)}finally{o.f()}})),this.candleRegion.plot(b,(function(t,n){t.lineWidth=1;var r,o=en(i);try{for(o.s();!(r=o.n()).done;){var a=r.value,s=a.startRate>a.endRate,c=[n.x(l(a)),n.y(a.startRate),n.w(h),n.h(a.endRate-a.startRate)],u=c[0],d=c[1],f=c[2],p=c[3],m=[n.y(a.highRate),n.y(a.lowRate),f/2+u],v=m[0],y=m[1],g=m[2];t.strokeStyle=s?e.theme.sellLine:e.theme.buyLine,t.fillStyle=s?e.theme.sellFill:e.theme.buyFill,t.beginPath(),t.moveTo(g,v),t.lineTo(g,y),t.stroke(),t.fillRect(u,d,f,p),t.strokeRect(u,d,f,p)}}catch(e){o.e(e)}finally{o.f()}})),this.reporters.mouse(S)}}}},{key:"setCandles",value:function(e,t,n,r){if(this.data=e,e.candles){this.market=t;var o=[r.conventional.conversionFactor,n.conventional.conversionFactor],a=o[0],i=o[1];this.rateConversionFactor=ht*i/a;var s=25;this.zoomLevels=[];for(var c=Math.max(e.candles.length,1e3);sn.x.min&&tn.y.min}},{key:"translator",value:function(e){var t=this.extents,n=e.x.min,r=e.y.min,o=e.yRange,a=e.xRange,i=t.x.min,s=t.x.max-i,c=t.y.max,u=c-t.y.min,l=s/a,h=u/o;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)*l+i})),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-i)/l+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/a*s})),h:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return-e/o*u})),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,o=this.extents;r.save(),n||(r.beginPath(),r.rect(o.x.min,o.y.min,o.xRange,o.yRange),r.clip());var a=this.translator(e),i=e.yRange,s=o.xRange/e.xRange,c=o.yRange/i,u=e.x.min,l=e.y.min,h=o.x.min+u-u*s,d=-o.y.min-(i-l)*c;a.dataCoords=function(e){r.save(),r.transform(1,0,0,-1,-u,l),r.transform(s,0,0,c,h,d),e(),r.restore()},t(this.context,a),r.restore()}}]),e}();function vn(e,t,n,r,o,a,i,s){s=s||bn;for(var c=(r-n)/(t/o),u=c+a-c%a,l=n+u-n%u,h=Math.max(Math.abs(r),Math.abs(n)),d=Math.round(Math.log10(h/u))+2,f=[],p=0;lp&&(p=v),{widest:p,lbls:f}}var yn=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function gn(e){return e[e.length-1]}function wn(e,t,n,r,o,a){e.beginPath(),e.moveTo(t,n),e.lineTo(r,o),a||e.stroke()}var kn={minimumSignificantDigits:4,maximumSignificantDigits:5};function bn(e){return e.toLocaleString("en-us",kn)}function xn(e,t){return Cn(e,t,1e-8)}function Cn(e,t,n){return Math.abs(e-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 o=0;!function a(){window.log("ws","connecting to ".concat(e));var i=n.connection=new window.WebSocket(e);if(i){var s=setTimeout((function(){i&&i.close()}),500);i.onmessage=function(e){var t=JSON.parse(e.data);En(t.route,t.payload,n.handlers)},i.onclose=function(e){window.log("ws","onclose"),clearTimeout(s),i=n.connection=null,En("close",null,n.handlers),o++;var t=Math.min(Math.pow(1.25,o),10);console.error("websocket disconnected (".concat(e.code,"), trying again in ").concat(t.toFixed(1)," seconds")),setTimeout((function(){a()}),1e3*t)},i.onopen=function(){window.log("ws","onopen"),clearTimeout(s),o>0&&(o=0,t()),En("open",null,n.handlers);var e=n.queue;n.queue=[];var a,i=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 Fn(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)?Fn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(e);try{for(i.s();!(a=i.n()).done;){var c=r(a.value,2),u=c[0],l=c[1];n.request(u,l)}}catch(e){i.e(e)}finally{i.f()}},i.onerror=function(e){window.log("ws","onerror:",e),En("error",e,n.handlers)}}}()}}]),e}());function In(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 On(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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Tn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n.7?e=.7:e<.25&&(e=.25);var t=e*(n.main.clientHeight-at().header.offsetHeight);r.marketChart.style.height="".concat(t,"px"),n.depthChart.resize(t),n.candleChart.resize(t)},C=je.fetch(zn);return C&&x(C),Ln(r.chartResizer,"mousedown",(function(e){if(0===e.button){var t;e.preventDefault();var n=function(e){e.preventDefault();var n=r.rightSide.getBoundingClientRect(),o=n.bottom-n.top;t=(e.pageY-n.top)/o,x(t)};Ln(document,"mousemove",n),Ln(document,"mouseup",(function(){t&&je.store(zn,t),Ie.unbind(document,"mousemove",n)}))}})),at().registerNoteFeeder({order:function(e){n.handleOrderNote(e)},epoch:function(e){n.handleEpochNote(e)},conn:function(e){n.handleConnNote(e)},balance:function(e){n.handleBalanceNote(e)},feepayment:function(e){n.handleFeePayment(e)},spots:function(e){n.handlePriceUpdate(e)}}),(b=t&&t.host&&void 0!==t.base&&void 0!==t.quote?Jn(t.host,parseInt(t.base),parseInt(t.quote)):je.fetch(Un))&&n.marketList.exists(b.host,b.base,b.quote)||(b=n.marketList.first()),n.setMarket(b.host,b.base,b.quote),n.secondTicker=window.setInterval((function(){for(var e=0,t=Object.values(n.metaOrders);e-1&&(i.classList.add("hover"),this.hovers.push(i))}t.hoverPrice.textContent=Ie.formatCoinValue(e.rate),t.hoverVolume.textContent=Ie.formatCoinValue(e.depth),t.hoverVolume.style.color=e.dotColor,Ie.show(t.hoverData)}else Ie.hide(t.hoverData)}},{key:"reportDepthZoom",value:function(e){je.store(jn,e)}},{key:"reportMouseCandle",value:function(e){var t=this.page;e?(t.candleStart.textContent=Ie.formatCoinValue(e.startRate/this.market.rateConversionFactor),t.candleEnd.textContent=Ie.formatCoinValue(e.endRate/this.market.rateConversionFactor),t.candleHigh.textContent=Ie.formatCoinValue(e.highRate/this.market.rateConversionFactor),t.candleLow.textContent=Ie.formatCoinValue(e.lowRate/this.market.rateConversionFactor),t.candleVol.textContent=Ie.formatCoinValue(e.matchVolume,this.market.baseUnitInfo),Ie.show(t.hoverData)):Ie.hide(t.hoverData)}},{key:"parseOrder",value:function(){var e=this.page,t=e.qtyField,n=this.isLimit(),r=this.isSell(),o=this.market;return n||r||(t=e.mktBuyField),{host:o.dex.host,isLimit:n,sell:r,base:o.base.id,quote:o.quote.id,qty:er(t.value||"",o.baseUnitInfo.conventional.conversionFactor),rate:er(e.rateField.value||"",o.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 o=at().assets[n.quote],a=n.qty*n.rate/ht,i=Ie.formatCoinValue(a,this.market.quoteUnitInfo);t.orderPreview.textContent=ge(T,{total:i,asset:o.symbol.toUpperCase()}),this.isSell()?this.preSell():this.preBuy()}}},{key:"preSell",value:function(){var e=this,t=this.market,n=at().assets[t.base.id].wallet;n.balance.available0&&this.book.add(u),this.addTableOrder(u)}}catch(e){c.e(e)}finally{c.f()}if(!this.book)return this.depthChart.clear(),Ie.empty(this.page.buyRows),void Ie.empty(this.page.sellRows);this.depthChart.set(this.book,n.lotsize,n.ratestep,r,o)}},{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/ht:e.buys[0].msgRate/ht:e.sells&&e.sells.length?e.sells[0].msgRate/ht:null}},{key:"setMarketBuyOrderEstimate",value:function(){var e=this.market,t=e.cfg.lotsize,n=at().user.exchanges[e.dex.host].markets[e.sid].buybuffer,r=this.midGapConventional();r&&(this.page.minMktBuy.textContent=Ie.formatCoinValue(t*n*r,e.baseUnitInfo))}},{key:"ordersSortCompare",value:function(){var e=this;switch(this.ordersSortKey){case"submitTime":return function(t,n){return e.ordersSortDirection*(n.submitTime-t.submitTime)};case"rate":return function(t,n){return e.ordersSortDirection*(t.rate-n.rate)};case"qty":return function(t,n){return e.ordersSortDirection*(t.qty-n.qty)};case"type":return function(t,n){return e.ordersSortDirection*ft(t).localeCompare(ft(n))};case"sell":return function(t,n){return e.ordersSortDirection*dt(t).localeCompare(dt(n))};case"status":return function(t,n){return e.ordersSortDirection*vt(t).localeCompare(vt(n))};case"settled":return function(t,n){return e.ordersSortDirection*(100*gt(t)/t.qty-100*gt(n)/n.qty)};case"filled":return function(t,n){return e.ordersSortDirection*(100*yt(t)/t.qty-100*yt(n)/n.qty)}}}},{key:"refreshActiveOrders",value:function(){var e=this,t=this.page,n=this.metaOrders,r=this.market;for(var o in n)delete n[o];var a=at().orders(r.dex.host,$n(r.baseCfg.symbol,r.quoteCfg.symbol)),i=this.ordersSortCompare();a.sort(i),Ie.empty(t.liveList);var s,c=Dn(a);try{var u=function(){var r=s.value,o=t.liveTemplate.cloneNode(!0);if(n[r.id]={row:o,order:r},Ie.bind(o,"mouseenter",(function(){e.activeMarkerRate=r.rate,e.setDepthMarkers()})),e.updateUserOrderRow(o,r),1===r.type&&1===r.tif&&r.status<3){var a=Ie.tmplElement(o,"cancelBttn");Ie.show(a),Ln(a,"click",(function(t){t.stopPropagation(),e.showCancel(o,r.id)}))}var i=Ie.tmplElement(o,"accelerateBttn");Ln(i,"click",(function(t){t.stopPropagation(),e.showAccelerate(r)})),at().canAccelerateOrder(r)&&Ie.show(i),Ie.tmplElement(o,"side").classList.add(r.sell?"sellcolor":"buycolor"),Ie.tmplElement(o,"link").href="order/".concat(r.id),at().bindInternalNavigation(o),t.liveList.appendChild(o),at().bindTooltips(o)};for(c.s();!(s=c.n()).done;)u()}catch(e){c.e(e)}finally{c.f()}this.setDepthMarkers()}},{key:"updateUserOrderRow",value:function(e,t){nr(e,"type",ft(t)),nr(e,"side",dt(t)),nr(e,"age",Ie.timeSince(t.submitTime)),nr(e,"rate",Ie.formatCoinValue(t.rate/this.market.rateConversionFactor)),nr(e,"qty",Ie.formatCoinValue(t.qty,this.market.baseUnitInfo)),nr(e,"filled","".concat((yt(t)/t.qty*100).toFixed(1),"%")),nr(e,"settled","".concat((gt(t)/t.qty*100).toFixed(1),"%")),nr(e,"status",vt(t))}},{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(at().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(at().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(at().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),this.depthChart.hide(),this.candleChart.show(),e.host===this.market.dex.host){var t=e.payload.dur;this.market.candleCaches[t]=e.payload,this.currentChart===Vn&&this.candleDur===t&&this.candleChart.setCandles(e.payload,this.market.cfg,this.market.baseUnitInfo,this.market.quoteUnitInfo)}}},{key:"handleCandleUpdateRoute",value:function(e){if(e.host===this.market.dex.host){var t=e.payload,n=t.dur,r=t.candle,o=this.market.candleCaches[n];if(o){var a=o.candles;0===a.length?a.push(r):a[a.length-1].startStamp===r.startStamp?a[a.length-1]=r:a.push(r),this.currentChart===Vn&&this.candleDur===n&&this.candleChart.draw()}}}},{key:"showForm",value:(b=a(v().mark((function e(t){var n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,Ie.hide(n.unlockWalletForm,n.verifyForm,n.newWalletForm,n.cancelForm,n.vDetailPane,n.accelerateForm),t.style.right="10000px",Ie.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,Ie.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 b.apply(this,arguments)})},{key:"showOpen",value:(g=a(v().mark((function e(t,n){var r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:r=this.page,this.openAsset=t,this.openFunc=n,this.unlockForm.refresh(at().assets[t.id]),this.showForm(r.unlockWalletForm),r.uwAppPass.focus();case 6:case"end":return e.stop()}}),e,this)}))),function(e,t){return g.apply(this,arguments)})},{key:"showVerify",value:function(){this.preorderCache={};var e,t=this.page,n=this.currentOrder=this.parseOrder(),r=n.sell,o=at().assets[n.base],a=at().assets[n.quote],i=r?a:o,s=r?o:a,c=Dn(Ie.applySelector(t.vDetailPane,"[data-icon]"));try{for(c.s();!(e=c.n()).done;){var u=e.value;switch(u.dataset.icon){case"from":u.src=Ie.logoPath(s.symbol);break;case"to":u.src=Ie.logoPath(i.symbol)}}}catch(e){c.e(e)}finally{c.f()}Ie.hide(t.vUnlockPreorder,t.vPreorderErr),Ie.show(t.vPreorder),t.vBuySell.textContent=r?"Selling":"Buying";var l=ge(r?I:R);if(t.vSideSubmit.textContent=l,t.vOrderHost.textContent=n.host,n.isLimit){Ie.show(t.verifyLimit),Ie.hide(t.verifyMarket);var h="Limit ".concat(l," Order");t.vOrderType.textContent=n.tifnow?h+" (immediate)":h,t.vRate.textContent=Ie.formatCoinValue(n.rate/this.market.rateConversionFactor),t.vQty.textContent=Ie.formatCoinValue(n.qty,o.info.unitinfo);var d=n.rate/ht*n.qty;t.vTotal.textContent=Ie.formatCoinValue(d,a.info.unitinfo),this.showFiatValue(a.id,d,t.vFiatTotal)}else{Ie.hide(t.verifyLimit),Ie.show(t.verifyMarket),t.vOrderType.textContent="Market ".concat(l," Order");var f=n.sell?this.market.baseUnitInfo:this.market.quoteUnitInfo;t.vmFromTotal.textContent=Ie.formatCoinValue(n.qty,f),t.vmFromAsset.textContent=s.symbol.toUpperCase(),this.showFiatValue(s.id,n.qty,t.vmFromTotalFiat);var p=this.midGap();if(p){Ie.show(t.vMarketEstimate);var m=n.sell?n.qty*p:n.qty/p;t.vmToTotal.textContent=Ie.formatCoinValue(m,i.info.unitinfo),t.vmToAsset.textContent=i.symbol.toUpperCase(),this.showFiatValue(i.id,m,t.vmTotalFiat)}else Ie.hide(t.vMarketEstimate)}var v="buygreen",y="sellred";r?(t.vHeader.classList.add(y),t.vHeader.classList.remove(v),t.vSubmit.classList.add(y),t.vSubmit.classList.remove(v)):(t.vHeader.classList.add(v),t.vHeader.classList.remove(y),t.vSubmit.classList.add(v),t.vSubmit.classList.remove(y)),this.showVerifyForm(),t.vPass.focus(),o.wallet.open&&a.wallet.open?this.preOrder(n):(Ie.hide(t.vPreorder),je.passwordIsCached()?this.unlockWalletsForEstimates(""):Ie.show(t.vUnlockPreorder))}},{key:"showFiatValue",value:function(e,t,n){if(n){var r=at().fiatRatesMap[e];n.textContent=Ie.formatFiatConversion(t,r,at().unitInfo(e)),r?Ie.show(n.parentElement):Ie.hide(n.parentElement)}}},{key:"showVerifyForm",value:(y=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,Ie.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:(m=a(v().mark((function e(){var t;return v().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 m.apply(this,arguments)})},{key:"unlockWalletsForEstimates",value:(p=a(v().mark((function e(t){var n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=at().loading(n.verifyForm),e.next=4,this.attemptWalletUnlock(t);case 4:if(o=e.sent,r(),!o){e.next=8;break}return e.abrupt("return",this.setPreorderErr(o));case 8:Ie.show(n.vPreorder),Ie.hide(n.vUnlockPreorder),this.preOrder(this.parseOrder());case 11:case"end":return e.stop()}}),e,this)}))),function(e){return p.apply(this,arguments)})},{key:"attemptWalletUnlock",value:(f=a(v().mark((function e(t){var n,r,o,a,i,s,c,u,l;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.market,r=n.base,o=n.quote,a=[],r.wallet.open||a.push(r.id),o.wallet.open||a.push(o.id),i={pass:t,assetID:-1},s=0,c=a;case 6:if(!(s2)&&this.setDepthMarkers()}},{key:"handleEpochNote",value:function(e){if(at().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);to.epoch,i=Ie.tmplElement(r.row,"status");switch(!0){case 1===o.type&&1===o.status&&a:i.textContent=0===o.tif?ge(N):ge(U),o.status=0===o.tif?3:2;break;case 2===o.type&&1===o.status:i.textContent=ge(N),o.status=3}}}}},{key:"setBalanceVisibility",value:function(){this.market.dex.connectionStatus===Ye.Connected?Ie.show(this.page.balanceTable):Ie.hide(this.page.balanceTable)}},{key:"handleBalanceNote",value:function(e){if(this.setBalanceVisibility(),this.market.dex.connectionStatus===Ye.Connected){var t=this.market,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:(s=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.orderErr,t.vErr),n=this.currentOrder,r=t.vPass.value,t.vPass.value="",o={order:rr(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,et("/api/trade",o);case 12:if(a=e.sent,t.vSubmit.classList.remove("d-hide"),t.vLoader.classList.add("d-hide"),at().checkResponse(a,!0)){e.next=19;break}return t.vErr.textContent=a.msg,Ie.show(t.vErr),e.abrupt("return");case 19:Ie.hide(t.forms),this.refreshActiveOrders(),this.depthChart.draw();case 22:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"createWallet",value:(o=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:if(t=e.sent){e.next=5;break}return e.abrupt("return");case 5:n=t.assets[this.currentCreate.id],Ie.hide(this.page.forms),this.balanceWgt.updateAsset(n.id),this.resolveOrderFormVisibility();case 9:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"walletUnlocked",value:(n=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:Ie.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,o=Math.floor(n.qty/r),a=o*r;t.lotField.value=String(o),(n.isLimit||n.sell)&&(e&&(t.qtyField.value=String(a/this.market.baseUnitInfo.conventional.conversionFactor)),this.previewQuoteAmt(!0))}},{key:"marketBuyChanged",value:function(){var e=this.page,t=er(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,o=t/n;e.mktBuyLots.textContent=(o/r).toFixed(1),e.mktBuyScore.textContent=Ie.formatCoinValue(o,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=er(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=[],o=e[0].msgRate;e[0].epoch?n.push(e[0]):r.push(e[0]);for(var a=1;a0}))}},{key:"loadTableSide",value:function(e){var t=this,n=e?this.book.sells:this.book.buys,r=e?this.page.sellRows:this.page.buyRows;Ie.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 o=this.orderTableRow([e]);t.appendChild(o)}else 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];n0?"+":"";t.pctChange.textContent="".concat(a).concat(o,"%"),t.pctChange.classList.remove("upgreen","downred","grey"),t.pctChange.classList.add(0===r?"grey":r>0?"upgreen":"downred");var i=at().assets[n.baseid];i&&(Ie.show(t.bottomRow),t.assetName.textContent=i.info.name,t.price.textContent=Ie.formatCoinValue(e.rate/this.rateConversionFactor))}}}]),e}(),Zn=function(){function e(t){var n=this;i(this,e),u(this,"base",void 0),u(this,"quote",void 0),u(this,"dex",void 0);var r=Ie.idDescendants(t);this.base={id:0,cfg:null,logo:r.baseImg,avail:r.baseAvail,newWalletRow:r.baseNewWalletRow,newWalletBttn:r.baseNewButton,locked:r.baseLocked,immature:r.baseImmature,unsupported:r.baseUnsupported,expired:r.baseExpired,connect:r.baseConnect,spinner:r.baseSpinner,iconBox:r.baseWalletState,stateIcons:new De(r.baseWalletState)},this.quote={id:0,cfg:null,logo:r.quoteImg,avail:r.quoteAvail,newWalletRow:r.quoteNewWalletRow,newWalletBttn:r.quoteNewButton,locked:r.quoteLocked,immature:r.quoteImmature,unsupported:r.quoteUnsupported,expired:r.quoteExpired,connect:r.quoteConnect,spinner:r.quoteSpinner,iconBox:r.quoteWalletState,stateIcons:new De(r.quoteWalletState)},at().registerNoteFeeder({balance:function(e){n.updateAsset(e.assetID)},walletstate:function(e){n.updateAsset(e.wallet.assetID)}})}return c(e,[{key:"setWallets",value:function(e,t,n){this.dex=at().user.exchanges[e],this.base.id=t,this.base.cfg=this.dex.assets[t],this.quote.id=n,this.quote.cfg=this.dex.assets[n],this.updateWallet(this.base),this.updateWallet(this.quote)}},{key:"updateWallet",value:function(e){if(e.cfg){var t=at().assets[e.id];if(Ie.hide(e.newWalletRow,e.avail,e.immature,e.locked,e.expired,e.unsupported,e.connect,e.spinner,e.iconBox),e.logo.src=Ie.logoPath(e.cfg.symbol),t){Ie.show(e.iconBox);var n=t.wallet;if(e.stateIcons.readWallet(n),n){var r=n.balance;if(r||n.running){if(!r)return at().fetchBalance(e.id),void Ie.show(e.spinner);Ie.show(e.avail,e.immature,e.locked),e.avail.textContent=Ie.formatCoinValue(r.available,t.info.unitinfo),e.locked.textContent=Ie.formatCoinValue(r.locked+r.contractlocked,t.info.unitinfo),e.immature.textContent=Ie.formatCoinValue(r.immature,t.info.unitinfo),(new Date).getTime()-new Date(r.stamp).getTime()>36e5?(Ie.show(e.expired),n.running&&at().fetchBalance(e.id)):Ie.hide(e.expired)}else Ie.show(e.connect)}else Ie.show(e.newWalletRow)}else Ie.show(e.unsupported)}}},{key:"updateAsset",value:function(e){e===this.base.id?this.updateWallet(this.base):e===this.quote.id&&this.updateWallet(this.quote)}}]),e}();function Jn(e,t,n){return{host:e,base:t,quote:n}}function $n(e,t){return"".concat(e,"_").concat(t)}function er(e,t){return e?Math.round(parseFloat(e)*t):0}function tr(e,t){e.classList.remove("selected"),t.classList.add("selected")}function nr(e,t,n){Ie.tmplElement(e,t).textContent=n}function rr(e){for(var t={},n=0,o=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,o=0;oe.msgRate===e.sell?1:-1:this.isEpoch()?1:-1}}]),e}();function ar(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 ir(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)?ir(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function ir(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0&&(e.checked=!0)}))}};a(n.hostFilter,"hosts"),a(n.assetFilter,"assets"),a(n.statusFilter,"statuses");var s=[],c=function(e,n){var o=e.querySelector(".apply-bttn");s.push(o),Ie.bind(o,"click",(function(){t.submitFilter(),s.forEach((function(e){return Ie.hide(e)}))})),e.querySelectorAll("input").forEach((function(t){Ie.bind(t,"change",(function(){!function(e,t){if(e.length!==t.length)return!1;var n,r=ar(e);try{for(r.s();!(n=r.n()).done;){var o=n.value;if(-1===t.indexOf(o))return!1}}catch(e){r.e(e)}finally{r.f()}return!0}(pr(e),r[n])?Ie.show(o):Ie.hide(o)}))}))};return c(n.hostFilter,"hosts"),c(n.assetFilter,"assets"),c(n.statusFilter,"statuses"),Ie.bind(t.main,"scroll",(function(){t.loading||n.ordersTable.offsetHeight-t.main.offsetHeight-t.main.scrollTop<0&&t.nextPage()})),Ie.bind(n.exportOrders,"click",(function(){t.exportOrders()})),t.submitFilter(),t}return c(h,[{key:"setOrders",value:function(e){Ie.empty(this.page.tableBody),this.appendOrders(e)}},{key:"appendOrders",value:function(e){var t,n=this,r=this.page.tableBody,o=ar(e);try{var a=function(){var e=t.value,o=n.orderTmpl.cloneNode(!0),a=function(e,t){Ie.tmplElement(o,e).textContent=t},i="".concat(e.baseSymbol.toUpperCase(),"-").concat(e.quoteSymbol.toUpperCase());a("host","".concat(i," @ ").concat(e.host));var s=void 0,c=void 0,u=void 0,l="",h=[at().unitInfo(e.baseID),at().unitInfo(e.quoteID)],d=h[0],f=h[1];if(e.sell){var p=[e.baseSymbol,e.quoteSymbol];s=p[0],c=p[1],u=Ie.formatCoinValue(e.qty,d),1===e.type&&(l=Ie.formatCoinValue(e.qty/ht*e.rate,f))}else{var m=[e.quoteSymbol,e.baseSymbol];s=m[0],c=m[1],2===e.type?u=Ie.formatCoinValue(e.qty,d):(u=Ie.formatCoinValue(e.qty/ht*e.rate,f),l=Ie.formatCoinValue(e.qty,d))}a("fromQty",u),Ie.tmplElement(o,"fromLogo").src=Ie.logoPath(s),a("fromSymbol",s),a("toQty",l),Ie.tmplElement(o,"toLogo").src=Ie.logoPath(c),a("toSymbol",c),a("type","".concat(ft(e)," ").concat(dt(e))),a("rate",Ie.formatCoinValue(at().conventionalRate(e.baseID,e.quoteID,e.rate))),a("status",vt(e)),a("filled","".concat((yt(e)/e.qty*100).toFixed(1),"%")),a("settled","".concat((gt(e)/e.qty*100).toFixed(1),"%"));var v=new Date(e.submitTime).toLocaleString();a("time","".concat(Ie.timeSince(e.submitTime)," ago, ").concat(v)),Ie.tmplElement(o,"link").href="order/".concat(e.id),at().bindInternalNavigation(o),r.appendChild(o)};for(o.s();!(t=o.n()).done;)a()}catch(e){o.e(e)}finally{o.f()}50===e.length?this.offset=e[e.length-1].id:this.offset=""}},{key:"submitFilter",value:(r=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.offset="",(n=this.filterState).hosts=pr(t.hostFilter),n.assets=pr(t.assetFilter).map((function(e){return parseInt(e)})),n.statuses=pr(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 r.apply(this,arguments)})},{key:"fetchOrders",value:(n=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=at().loading(this.main),e.next=3,et("/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 n.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:"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=a(v().mark((function e(){var t;return v().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,Ie.show(this.page.orderLoader),e.next=6,this.fetchOrders();case 6:t=e.sent,this.loading=!1,Ie.hide(this.page.orderLoader),this.appendOrders(t);case 10:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),h}(Ze);function pr(e){var t=[];return e.querySelectorAll("input").forEach((function(e){e.checked&&t.push(e.value)})),t}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 vr(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)?vr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function vr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&Ie.hide(r),t.status.textContent=vt(n);var o,a=mr(n.matches||[]);try{for(a.s();!(o=a.n()).done;){var i=o.value;this.processMatch(i)}}catch(e){a.e(e)}finally{a.f()}this.showAccelerationButton()}}},{key:"handleMatchNote",value:function(e){e.orderID===this.orderID&&this.processMatch(e.match)}},{key:"processMatch",value:function(e){var t,n=null,r=mr(Ie.applySelector(this.page.matchBox,".match-card"));try{for(r.s();!(t=r.n()).done;){var o=t.value;if(o.dataset.matchID===e.matchID){n=o;break}}}catch(e){r.e(e)}finally{r.f()}if(n){var a=function(e,t,r){if(n&&r){Ie.show(Ie.tmplElement(n,e));var o=Ie.tmplElement(n,t);o.textContent=r.stringID,o.dataset.explorerCoin=r.stringID,kr(o)}};a("swap","swapCoin",e.swap),a("counterSwap","counterSwapCoin",e.counterSwap),a("redeem","redeemCoin",e.redeem),a("counterRedeem","counterRedeemCoin",e.counterRedeem),a("refund","refundCoin",e.refund);var i=Ie.tmplElement(n,"swapMsg"),s=Ie.tmplElement(n,"counterSwapMsg");!function(e){return 1===e.side&&1===e.status||0===e.side&&2===e.status}(e)?function(e){return 0===e.side&&1===e.status||1===e.side&&2===e.status}(e)?(i.textContent=wr(e.swap),Ie.hide(Ie.tmplElement(n,"counterSwapMsg")),Ie.show(i)):Ie.hide(i,s):(s.textContent=wr(e.counterSwap),Ie.hide(Ie.tmplElement(n,"swapMsg")),Ie.show(s)),Ie.tmplElement(n,"status").textContent=function(e){if(e.revoked)return e.active?"Revoked - Refund PENDING":e.refund?"Revoked - Refunded":e.redeem?"Revoked - Redeemed":"Revoked - Complete";switch(e.status){case 0:return"(0 / 4) Newly Matched";case 1:return"(1 / 4) First Swap Sent";case 2:return"(2 / 4) Second Swap Sent";case 3:return 0===e.side?"Match Complete":"(3 / 4) Maker Redeemed";case 4:return"Match Complete"}return"Unknown Match Status"}(e)}}}]),d}(Ze);function wr(e){return e.confs?"".concat(e.confs.count," / ").concat(e.confs.required," confirmations"):""}function kr(e){var t=br[parseInt(e.dataset.explorerId||"")];if(t){var n=t[yr];n&&(e.classList.remove("plainlink"),e.classList.add("subtlelink"),e.href=n(e.dataset.explorerCoin||""))}}var br={42:(sr={},u(sr,0,(function(e){var t=r(e.split(":"),2),n=t[0],o=t[1];return"https://explorer.dcrdata.org/tx/".concat(n,"/out/").concat(o)})),u(sr,1,(function(e){var t=r(e.split(":"),2),n=t[0],o=t[1];return"https://testnet.dcrdata.org/tx/".concat(n,"/out/").concat(o)})),sr),0:(cr={},u(cr,0,(function(e){return"https://mempool.space/tx/".concat(e.split(":")[0])})),u(cr,1,(function(e){return"https://mempool.space/testnet/tx/".concat(e.split(":")[0])})),cr),2:(ur={},u(ur,0,(function(e){return"https://ltc.bitaps.com/".concat(e.split(":")[0])})),u(ur,1,(function(e){return"https://sochain.com/tx/LTCTEST/".concat(e.split(":")[0])})),ur),60:(lr={},u(lr,0,(function(e){return 42===e.length?"https://etherscan.io/address/".concat(e):"https://etherscan.io/tx/".concat(e)})),u(lr,1,(function(e){return 42===e.length?"https://goerli.etherscan.io/address/".concat(e):"https://goerli.etherscan.io/tx/".concat(e)})),lr),3:(hr={},u(hr,0,(function(e){return"https://dogeblocks.com/tx/".concat(e.split(":")[0])})),u(hr,1,(function(e){return"https://blockexplorer.one/dogecoin/testnet/tx/".concat(e.split(":")[0])})),hr),145:(dr={},u(dr,0,(function(e){return"https://bch.loping.net/tx/".concat(e.split(":")[0])})),u(dr,1,(function(e){return"https://tbch4.loping.net/tx/".concat(e.split(":")[0])})),dr)};var xr,Cr=function(e){He(m,e);var t,n,r,o,s,l,h,d,f,p=(d=m,f=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=Qe(d);if(f){var n=Qe(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return Xe(this,e)});function m(e){var t;i(this,m),u(_e(t=p.call(this)),"body",void 0),u(_e(t),"forms",void 0),u(_e(t),"currentForm",void 0),u(_e(t),"page",void 0),u(_e(t),"host",void 0),u(_e(t),"keyup",void 0),u(_e(t),"dexAddrForm",void 0),t.body=e,t.host=e.dataset.host?e.dataset.host:"";var n=t.page=Ie.idDescendants(e);t.forms=Ie.applySelector(n.forms,":scope > form"),Ie.bind(n.exportDexBtn,"click",(function(){return t.prepareAccountExport(n.authorizeAccountExportForm)})),Ie.bind(n.disableAcctBtn,"click",(function(){return t.prepareAccountDisable(n.disableAccountForm)})),Ie.bind(n.updateCertBtn,"click",(function(){return n.certFileInput.click()})),Ie.bind(n.updateHostBtn,"click",(function(){return t.prepareUpdateHost()})),Ie.bind(n.certFileInput,"change",(function(){return t.onCertFileChange()})),t.dexAddrForm=new Bt(n.dexAddrForm,function(){var e=a(v().mark((function e(t){return v().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),zt(n.authorizeAccountExportForm,n.authorizeExportAccountConfirm,(function(){return t.exportAccount()})),zt(n.disableAccountForm,n.disableAccountConfirm,(function(){return t.disableAccount()}));var r=function(){Ie.hide(n.forms)};return Ie.bind(n.forms,"mousedown",(function(e){Ie.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},Ie.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){Ie.bind(e,"click",(function(){r()}))})),at().registerNoteFeeder({conn:function(){t.setConnectionStatus()}}),t.setConnectionStatus(),t}return c(m,[{key:"showForm",value:(h=a(v().mark((function e(t){var n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return Ie.hide(e)})),t.style.right="10000px",Ie.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,Ie.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 h.apply(this,arguments)})},{key:"exportAccount",value:(l=a(v().mark((function e(){var t,n,r,o,a,i,s,c;return v().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="",o={pw:n,host:r},a=at().loading(this.body),e.next=8,et("/api/exportaccount",o);case 8:if(i=e.sent,a(),at().checkResponse(i)){e.next=14;break}return t.exportAccountErr.textContent=i.msg,Ie.show(t.exportAccountErr),e.abrupt("return");case 14:s=JSON.parse(JSON.stringify(i.account)),(c=document.createElement("a")).setAttribute("download","dcrAccount-"+r+".json"),c.setAttribute("href","data:text/json,"+JSON.stringify(s,null,2)),c.click(),Ie.hide(t.forms);case 20:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"disableAccount",value:(s=a(v().mark((function e(){var t,n,r,o,a,i;return v().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="",o={pw:n,host:r},a=at().loading(this.body),e.next=8,et("/api/disableaccount",o);case 8:if(i=e.sent,a(),at().checkResponse(i,!0)){e.next=14;break}return t.disableAccountErr.textContent=i.msg,Ie.show(t.disableAccountErr),e.abrupt("return");case 14:Ie.hide(t.forms),window.location.assign("/settings");case 16:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"prepareAccountExport",value:(o=a(v().mark((function e(t){var n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).exportAccountHost.textContent=this.host,n.exportAccountErr.textContent="",je.passwordIsCached()?this.exportAccount():this.showForm(t);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"prepareAccountDisable",value:(r=a(v().mark((function e(t){var n;return v().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 r.apply(this,arguments)})},{key:"prepareUpdateHost",value:(n=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,this.dexAddrForm.refresh(),this.showForm(t.dexAddrForm);case 3:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"onCertFileChange",value:(t=a(v().mark((function e(){var t,n,r,o,a,i;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.errMsg),!(n=t.certFileInput.files)||!n.length){e.next=7;break}return e.next=6,n[0].text();case 6:r=e.sent;case 7:if(r){e.next=9;break}return e.abrupt("return");case 9:return o={host:this.host,cert:r},a=at().loading(this.body),e.next=13,et("/api/updatecert",o);case 13:i=e.sent,a(),at().checkResponse(i,!0)?(Ie.show(t.updateCertMsg),setTimeout((function(){Ie.hide(t.updateCertMsg)}),5e3)):(t.errMsg.textContent=i.msg,Ie.show(t.errMsg));case 16:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"setConnectionStatus",value:function(){var e=this.page,t=at().user.exchanges[this.host],n=function(t){t?(Ie.hide(e.disconnectedIcon),Ie.show(e.connectedIcon)):(Ie.show(e.disconnectedIcon),Ie.hide(e.connectedIcon))};if(t)switch(t.connectionStatus){case Ye.Connected:n(!0),e.connectionStatus.textContent="Connected";break;case Ye.Disconnected:n(!1),e.connectionStatus.textContent="Disconnected";break;case Ye.InvalidCert:n(!1),e.connectionStatus.textContent="Disconnected - Invalid Certificate"}}}]),m}(Ze);function Sr(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 Fr(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Ar(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n-1:0),o=1;o-1,this.checkResponse(t,n)){e.next=6;break}return e.abrupt("return");case 6:o=t,this.seedGenTime=o.seedgentime,this.user=o,this.assets=o.assets,this.exchanges=o.exchanges,this.walletMap={},this.fiatRatesMap=o.fiatRates,a=Er(Object.entries(o.assets));try{for(a.s();!(i=a.n()).done;)s=r(i.value,2),c=s[0],(u=s[1]).wallet&&(this.walletMap[c]=u.wallet)}catch(e){a.e(e)}finally{a.f()}return this.updateMenuItemsDisplay(),e.abrupt("return",o);case 17:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"loadPage",value:(o=a(v().mark((function e(t,n,r){var o,a,i,s,c,u,l,h;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.tooltip.style.left="-10000px",Ie.hide(this.page.noteBox,this.page.profileBox),o=new URL("/".concat(t),window.location.origin),a=qr(t),e.next=6,window.fetch(o.toString());case 6:if((i=e.sent).ok){e.next=9;break}return e.abrupt("return",!1);case 9:return e.next=11,i.text();case 11:return s=e.sent,c=Ie.noderize(s),u=Rr(c,"main"),l=u.dataset.handler,r||(h=l===a?o.toString():"/".concat(l),window.history.pushState({page:t,data:n},"",h)),document.title=c.title,this.main.replaceWith(u),this.main=u,this.noteReceivers=[],this.attach(n),e.abrupt("return",!0);case 22:case"end":return e.stop()}}),e,this)}))),function(e,t,n){return o.apply(this,arguments)})},{key:"attach",value:function(e){var t=this.main.dataset.handler;if(t){this.attachCommon(this.main),this.loadedPage&&this.loadedPage.unload();var n=Lr[t];this.loadedPage=n?new n(this.main,e):null,this.bindTooltips(this.main)}else console.error("cannot attach to content with no specified handler")}},{key:"bindTooltips",value:function(e){var t=this;e.querySelectorAll("[data-tooltip]").forEach((function(e){Ir(e,"mouseenter",(function(){t.tooltip.textContent=e.dataset.tooltip||"";var n=Ie.layoutMetrics(e),r=n.centerX-t.tooltip.offsetWidth/2;r<0&&(r=5),r+t.tooltip.offsetWidth>document.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")})),Ir(e,"mouseleave",(function(){t.tooltip.style.left="-10000px"}))}))}},{key:"attachHeader",value:function(){var e=this;this.header=Rr(document.body,"header"),this.popupNotes=Rr(document.body,"popupNotes"),this.popupTmpl=Ie.tmplElement(this.popupNotes,"note"),this.popupTmpl?this.popupTmpl.remove():console.error("popupTmpl element not found"),this.tooltip=Rr(document.body,"tooltip");var t=this.page=Ie.idDescendants(this.header);t.noteTmpl.removeAttribute("id"),t.noteTmpl.remove(),t.pokeTmpl.removeAttribute("id"),t.pokeTmpl.remove(),t.loader.remove(),Ie.show(t.loader),Ir(t.noteMenuEntry,"click",a(v().mark((function n(){var r,o,a;return v().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:Ie.hide(t.pokeList),Ie.show(t.noteList),e.ackNotes(),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),e.showDropdown(t.noteMenuEntry,t.noteBox),Ie.hide(t.noteIndicator),r=Er(e.notes);try{for(r.s();!(o=r.n()).done;)(a=o.value).acked&&a.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)})))),Ir(t.profileMenuEntry,"click",(function(){e.showDropdown(t.profileMenuEntry,t.profileBox)})),Ir(t.innerNoteIcon,"click",(function(){Ie.hide(t.noteBox)})),Ir(t.innerProfileIcon,"click",(function(){Ie.hide(t.profileBox)})),Ir(t.profileSignout,"click",a(v().mark((function t(){return v().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)})))),Ir(t.pokeCat,"click",(function(){e.setNoteTimes(t.pokeList),t.pokeCat.classList.add("active"),t.noteCat.classList.remove("active"),Ie.hide(t.noteList),Ie.show(t.pokeList),e.ackNotes()})),Ir(t.noteCat,"click",(function(){e.setNoteTimes(t.noteList),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),Ie.hide(t.pokeList),Ie.show(t.noteList),e.ackNotes()}))}},{key:"showDropdown",value:function(e,t){var n=this,r=e.getBoundingClientRect();Ie.hide(this.page.noteBox,this.page.profileBox),Ie.show(t),t.style.right="".concat(window.innerWidth-r.left-r.width+11,"px"),t.style.top="".concat(r.top-9,"px"),Ir(document,"click",(function e(r){Ie.mouseInElement(r,t)||(Ie.hide(t),Or(document,"click",e),t===n.page.noteBox&&Ie.isDisplayed(n.page.noteList)&&n.ackNotes())}))}},{key:"ackNotes",value:function(){var e,t=[],n=Er(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&&Rn.request("acknotes",t),Ie.hide(this.page.noteIndicator)}},{key:"setNoteTimes",value:function(e){var t,n=Er(Array.from(e.children));try{for(n.s();!(t=n.n()).done;){var r=t.value;Ie.safeSelector(r,"span.note-time").textContent=Ie.timeSince(r.note.stamp)}}catch(e){n.e(e)}finally{n.f()}}},{key:"bindInternalNavigation",value:function(e){var t=this,n=new URL(window.location.href);e.querySelectorAll("a").forEach((function(e){if(e.href){var r=new URL(e.href);if(r.origin===n.origin){var o=r.pathname.substring(1),a={};r.search&&r.searchParams.forEach((function(e,t){a[t]=e})),Ie.bind(e,"click",(function(e){e.preventDefault(),t.loadPage(o,a)}))}}}))}},{key:"storeNotes",value:function(){je.store("notifications",this.notes.map((function(e){return{subject:e.subject,details:e.details,severity:e.severity,stamp:e.stamp,id:e.id,acked:e.acked}})))}},{key:"updateMenuItemsDisplay",value:function(){var e=this.page;e&&(this.user.authed?(Ie.show(e.noteMenuEntry,e.walletsMenuEntry,e.profileMenuEntry),Object.keys(this.user.exchanges).length>0?Ie.show(e.marketsMenuEntry):Ie.hide(e.marketsMenuEntry)):Ie.hide(e.noteMenuEntry,e.walletsMenuEntry,e.marketsMenuEntry,e.profileMenuEntry))}},{key:"attachCommon",value:function(e){this.bindInternalNavigation(e)}},{key:"updateExchangeRegistration",value:function(e,t,n){var r=this.exchanges[e],o=this.assets[n].symbol;r.pendingFee={confs:t,assetID:n,symbol:o}}},{key:"setDEXPaid",value:function(e){this.exchanges[e].pendingFee=null}},{key:"handleFeePaymentNote",value:function(e){switch(e.topic){case"RegUpdate":this.updateExchangeRegistration(e.dex,e.confirmations,e.asset);break;case"AccountRegistered":this.setDEXPaid(e.dex)}}},{key:"setNotes",value:function(e){this.log("notes","setNotes",e),this.notes=[],Ie.empty(this.page.noteList);for(var t=0;t5;)R.removeChild(R.firstChild);setTimeout(a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Ie.animate(500,(function(e){E.style.opacity=String(1-e)}));case 2:E.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),o=1;o100;)this.pokes.shift();this.prependListElement(this.page.pokeList,o,n)}},{key:"prependNoteElement",value:function(e,t){var n=r(this.makeNote(e),2),o=n[0],a=n[1];for(this.notes.push(a);this.notes.length>100;)this.notes.shift();var i=this.page.noteList;if(this.prependListElement(i,a,o),t||this.storeNotes(),!(0===this.notes.length||Ie.isDisplayed(this.page.noteBox)&&Ie.isDisplayed(i))){var s=0,c=this.notes.reduce((function(e,t){return t.acked||s++,!t.acked&&t.severity>e?t.severity:e}),0),u=this.page.noteIndicator;Nr(u,c),s?(u.textContent=String(s>99?"".concat(99,"+"):s),Ie.show(u)):Ie.hide(u)}}},{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";Ie.safeSelector(t,"div.note-indicator").classList.add(n)}return Ie.safeSelector(t,"div.note-subject").textContent=e.subject,Ie.safeSelector(t,"div.note-details").textContent=e.details,[t,Fr({el:t},e)]}},{key:"makePoke",value:function(e){var t=this.page.pokeTmpl.cloneNode(!0),n=new Date(e.stamp);return Ie.tmplElement(t,"dateTime").textContent="".concat(n.toLocaleDateString(),", ").concat(n.toLocaleTimeString()),Ie.tmplElement(t,"details").textContent="".concat(e.subject,": ").concat(e.details),[t,Fr({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=this.user.exchanges[e].markets[t].orders;return n||(n=[],this.user.exchanges[e].markets[t].orders=n),n}},{key:"haveAssetOrders",value:function(e){for(var t=0,n=Object.values(this.user.exchanges);t{var e={757:(e,t,n)=>{e.exports=n(666)},666:e=>{var t=function(e){"use strict";var t,n=Object.prototype,r=n.hasOwnProperty,o="function"==typeof Symbol?Symbol:{},a=o.iterator||"@@iterator",i=o.asyncIterator||"@@asyncIterator",s=o.toStringTag||"@@toStringTag";function c(e,t,n){return Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{c({},"")}catch(e){c=function(e,t,n){return e[t]=n}}function u(e,t,n,r){var o=t&&t.prototype instanceof v?t:v,a=Object.create(o.prototype),i=new R(r||[]);return a._invoke=function(e,t,n){var r=h;return function(o,a){if(r===f)throw new Error("Generator is already running");if(r===p){if("throw"===o)throw a;return O()}for(n.method=o,n.arg=a;;){var i=n.delegate;if(i){var s=F(i,n);if(s){if(s===m)continue;return s}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(r===h)throw r=p,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);r=f;var c=l(e,t,n);if("normal"===c.type){if(r=n.done?p:d,c.arg===m)continue;return{value:c.arg,done:n.done}}"throw"===c.type&&(r=p,n.method="throw",n.arg=c.arg)}}}(e,n,i),a}function l(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(e){return{type:"throw",arg:e}}}e.wrap=u;var h="suspendedStart",d="suspendedYield",f="executing",p="completed",m={};function v(){}function y(){}function g(){}var w={};c(w,a,(function(){return this}));var k=Object.getPrototypeOf,b=k&&k(k(I([])));b&&b!==n&&r.call(b,a)&&(w=b);var x=g.prototype=v.prototype=Object.create(w);function C(e){["next","throw","return"].forEach((function(t){c(e,t,(function(e){return this._invoke(t,e)}))}))}function S(e,t){function n(o,a,i,s){var c=l(e[o],e,a);if("throw"!==c.type){var u=c.arg,h=u.value;return h&&"object"==typeof h&&r.call(h,"__await")?t.resolve(h.__await).then((function(e){n("next",e,i,s)}),(function(e){n("throw",e,i,s)})):t.resolve(h).then((function(e){u.value=e,i(u)}),(function(e){return n("throw",e,i,s)}))}s(c.arg)}var o;this._invoke=function(e,r){function a(){return new t((function(t,o){n(e,r,t,o)}))}return o=o?o.then(a,a):a()}}function F(e,n){var r=e.iterator[n.method];if(r===t){if(n.delegate=null,"throw"===n.method){if(e.iterator.return&&(n.method="return",n.arg=t,F(e,n),"throw"===n.method))return m;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return m}var o=l(r,e.iterator,n.arg);if("throw"===o.type)return n.method="throw",n.arg=o.arg,n.delegate=null,m;var a=o.arg;return a?a.done?(n[e.resultName]=a.value,n.next=e.nextLoc,"return"!==n.method&&(n.method="next",n.arg=t),n.delegate=null,m):a:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,m)}function E(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 A(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function R(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(E,this),this.reset(!0)}function I(e){if(e){var n=e[a];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,i=function n(){for(;++o=0;--a){var i=this.tryEntries[a],s=i.completion;if("root"===i.tryLoc)return o("end");if(i.tryLoc<=this.prev){var c=r.call(i,"catchLoc"),u=r.call(i,"finallyLoc");if(c&&u){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),A(n),m}},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 o=r.arg;A(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,r){return this.delegate={iterator:I(e),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=t),m}},e}(e.exports);try{regeneratorRuntime=t}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=t:Function("r","regeneratorRuntime = r")(t)}}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var a=t[r]={exports:{}};return e[r](a,a.exports,n),a.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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function ke(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,o=t.left+n.scrollLeft,a=e.offsetWidth,i=e.offsetHeight;return{bodyTop:r,bodyLeft:o,width:a,height:i,centerX:o+a/2,centerY:r+i/2}}},{key:"empty",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n"),e),document.createElement("div"))}},{key:"idDescendants",value:function(t){var n,r={},o=we(e.applySelector(t,"[id]"));try{for(o.s();!(n=o.n()).done;){var a=n.value;r[a.id]=a}}catch(e){o.e(e)}finally{o.f()}return r}},{key:"formatCoinValue",value:function(e,t){var n=r(Re(e,t),2),o=n[0],a=n[1];return Number.isInteger(o)?Ce.format(o):function(e){return Ae(Se,2,e)}(a).format(o)}},{key:"formatFullPrecision",value:function(e,t){var n=r(Re(e,t),2),o=n[0];return Ee(n[1]).format(o)}},{key:"formatFiatConversion",value:function(e,t,n){if(!t||0===t)return"unavailable";var o=r(Re(e,n),1)[0]*t;return Ee(2).format(o)}},{key:"logoPath",value:function(e){return-1===xe.indexOf(e)&&(e=e.substring(0,1)),"/img/coins/".concat(e,".png")}},{key:"cleanTemplates",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n0||l>0)&&l++,e>0&&(u+="".concat(e," ").concat(t," ")),l>=2},d=r(Me(c,Le),2);if(t=d[0],c=d[1],h(t,"y"))return u;var f=r(Me(c,Pe),2);if(n=f[0],c=f[1],h(n,"mo"))return u;var p=r(Me(c,We),2);if(o=p[0],c=p[1],h(o,"d"))return u;var m=r(Me(c,Be),2);if(a=m[0],c=m[1],h(a,"h"))return u;var v=r(Me(c,qe),2);if(i=v[0],c=v[1],h(i,"m"))return u;var y=r(Me(c,1e3),2);return s=y[0],c=y[1],h(s,"s"),u||"0 s"}},{key:"disableMouseWheel",value:function(){for(var e=arguments.length,t=new Array(e),n=0;ne.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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(document.cookie.split(";"));try{for(n.s();!(t=n.n()).done;){var o=r(t.value.split("="),2),a=o[0],i=o[1];if(a.trim()===e)return i}}catch(e){n.e(e)}finally{n.f()}return null}},{key:"dark",value:function(e){this.setCookie(Ue,e?"1":"0"),e?document.body.classList.add("dark"):document.body.classList.remove("dark")}},{key:"isDark",value:function(){return document.cookie.split(";").filter((function(e){return e.includes("".concat(Ue,"=1"))})).length}},{key:"passwordIsCached",value:function(){return!!this.getCookie("sessionkey")}},{key:"store",value:function(e,t){window.localStorage.setItem(e,JSON.stringify(t))}},{key:"clearAllStore",value:function(){window.localStorage.clear()}},{key:"removeAuthCK",value:function(){document.cookie="".concat("dexauth","=;expires=Thu, 01 Jan 1970 00:00:01 GMT;")}},{key:"fetch",value:function(e){var t=window.localStorage.getItem(e);return null!==t?JSON.parse(t):null}}]),e}();function _e(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Ve(e,t){return Ve=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},Ve(e,t)}function He(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&&Ve(e,t)}function Ge(e){return Ge="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},Ge(e)}function Xe(e,t){if(t&&("object"===Ge(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return _e(e)}function Qe(e){return Qe=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},Qe(e)}null===je.getCookie(Ue)&&je.setCookie(Ue,"1"),null===je.getCookie(ze)&&je.setCookie(ze,"1");var Ye,Ke,Je=function(){function e(){i(this,e)}return c(e,[{key:"unload",value:function(){}}]),e}();function Ze(e,t,n){return $e.apply(this,arguments)}function $e(){return($e=a(v().mark((function e(t,n,r){var o,a;return v().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===(o=e.sent).status){e.next=6;break}throw o;case 6:return e.next=8,o.json();case 8:return(a=e.sent).requestSuccessful=!0,e.abrupt("return",a);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 et(e,t){return tt.apply(this,arguments)}function tt(){return(tt=a(v().mark((function e(t,n){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",Ze("POST",t,JSON.stringify(n)));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function nt(e){return rt.apply(this,arguments)}function rt(){return(rt=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",Ze("GET",t));case 1:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ot(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 at(){return Ke}function it(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=Qe(e);if(t){var o=Qe(this).constructor;n=Reflect.construct(r,arguments,o)}else n=r.apply(this,arguments);return Xe(this,n)}}function st(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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(e.matches);try{for(n.s();!(t=n.n()).done;){var r=t.value;if(!r.revoked&&r.status<3)return!0}}catch(e){n.e(e)}finally{n.f()}return!1}function vt(e){var t=mt(e);switch(e.status){case 0:return ge(Q);case 1:return ge(Y);case 2:return e.cancelling?ge(z):t?"".concat(ge(U),"/").concat(ge(K)):ge(U);case 3:return t?ge(K):0===e.filled?ge(J):ge(N);case 4:return t?"".concat(ge(Z),"/").concat(ge(K)):ge(Z);case 5:return t?"".concat(ge($),"/").concat(ge(K)):ge($)}return""}function yt(e){if(!e.matches)return 0;var t=pt(e)?function(e){return e.qty*e.rate/ht}:function(e){return e.qty};return e.matches.reduce((function(e,n){return n.isCancel?e:e+t(n)}),0)}function gt(e){if(!e.matches)return 0;var t=pt(e)?function(e){return e.qty*e.rate/ht}: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 wt(e){var t=[e.booleanOptTmpl,e.rangeOptTmpl,e.orderOptTmpl];ut=t[0],lt=t[1],ct=t[2]}var kt=function(){function e(t,n,r,o){var a=this;i(this,e),u(this,"opt",void 0),u(this,"order",void 0),u(this,"node",void 0),u(this,"tmpl",void 0),u(this,"on",void 0),this.opt=t,this.order=n;var s=this.node=ct.cloneNode(!0),c=this.tmpl=Ie.parseTemplate(s);c.optName.textContent=t.displayname,c.tooltip.dataset.tooltip=t.description;var l=r&&n.sell||!r&&!n.sell?this.baseSymbol():this.quoteSymbol();c.chainIcon.src=Ie.logoPath(l),this.on=!1,Ie.bind(s,"click",(function(){a.on||(a.on=!0,s.classList.add("selected"),o.enable())})),Ie.bind(c.toggle,"click",(function(e){a.on&&(e.stopPropagation(),a.on=!1,s.classList.remove("selected"),o.disable())}))}return c(e,[{key:"quoteSymbol",value:function(){return Ft(this.order.host,this.order.quote)}},{key:"baseSymbol",value:function(){return Ft(this.order.host,this.order.base)}}]),e}(),bt=function(e){He(n,e);var t=it(n);function n(e,r,o,a){var s;i(this,n),u(_e(s=t.call(this,e,r,a,{enable:function(){return s.enable()},disable:function(){return s.disable()}})),"control",void 0),u(_e(s),"changed",void 0),s.changed=function(){return o()};var c=e.boolean,l=s.control=ut.cloneNode(!0);return s.tmpl.controls.appendChild(l),Ie.parseTemplate(l).reason.textContent=c.reason,s.on=void 0!==r.options[e.key]?r.options[e.key]:e.default,s.on&&s.node.classList.add("selected"),s}return c(n,[{key:"store",value:function(){this.on===this.opt.default?delete this.order.options[this.opt.key]:this.order.options[this.opt.key]=this.on,this.changed()}},{key:"enable",value:function(){this.store()}},{key:"disable",value:function(){this.store()}}]),n}(kt),xt=function(e){He(n,e);var t=it(n);function n(e,r,o,a){var s;i(this,n),u(_e(s=t.call(this,e,r,a,{enable:function(){return s.enable()},disable:function(){return s.disable()}})),"handler",void 0),u(_e(s),"x",void 0),u(_e(s),"changed",void 0),s.changed=o;var c=e.xyRange,l=r.options[e.key];return s.on=void 0!==l,s.on?(s.node.classList.add("selected"),s.x=l):s.x=e.default,s.handler=new Ct(c,s.x,(function(e){s.x=e,s.order.options[s.opt.key]=e}),(function(){s.changed()}),(function(){s.node.classList.add("selected")})),s.tmpl.controls.appendChild(s.handler.control),s}return c(n,[{key:"enable",value:function(){this.order.options[this.opt.key]=this.x,this.changed()}},{key:"disable",value:function(){delete this.order.options[this.opt.key],this.changed()}}]),n}(kt),Ct=c((function e(t,n,r,o,a,s){var c=this;i(this,e),u(this,"control",void 0),u(this,"x",void 0),u(this,"updated",void 0),u(this,"changed",void 0),u(this,"selected",void 0);var l=this.control=lt.cloneNode(!0),h=Ie.parseTemplate(l);this.changed=o,this.selected=a,this.updated=r;var d=h.slider,f=h.handle,p=t.end.x-t.start.x,m=t.end.y-t.start.y,v=function(e){return(e-t.start.x)/p},y=v(n),g=this.x=n,w=y*m+t.start.y,k=new Intl.NumberFormat(navigator.languages,{minimumSignificantDigits:3,maximumSignificantDigits:3}),b=function(e){s&&(w=Math.round(w)),h.x.textContent=k.format(g),h.y.textContent=k.format(w),s&&(h.y.textContent="".concat(w)),f.style.left="calc(".concat(100*y,"% - ").concat(14*y,"px)"),c.x=g,e||c.updated(g,w)},x=function e(n){if("change"===n.type||n.target!==h.xInput){var r=h.xInput.value;if(r){var o=parseFloat(r);isNaN(o)||(g=Et(o,t.start.x,t.end.x),y=v(g),w=y*m+t.start.y,b())}Ie.hide(h.xInput),Ie.show(h.x),Ie.unbind(document,"click",e),c.changed()}};Ie.bind(h.x,"click",(function(e){Ie.hide(h.x),Ie.show(h.xInput),h.xInput.focus(),h.xInput.value=k.format(g),Ie.bind(document,"click",x),e.stopPropagation()})),Ie.bind(h.xInput,"change",x);var C=function e(n){if("change"===n.type||n.target!==h.yInput){var r=h.yInput.value;if(r){var o=parseFloat(r);isNaN(o)||(w=Et(o,t.start.y,t.end.y),y=(w-t.start.y)/m,g=t.start.x+y*p,b())}Ie.hide(h.yInput),Ie.show(h.y),Ie.unbind(document,"click",e),c.changed()}};Ie.bind(h.y,"click",(function(e){Ie.hide(h.y),Ie.show(h.yInput),h.yInput.focus(),h.yInput.value=k.format(w),Ie.bind(document,"click",C),e.stopPropagation()})),Ie.bind(h.yInput,"change",C),Ie.bind(f,"mousedown",(function(e){if(0===e.button){e.preventDefault(),c.selected();var n=e.pageX,r=d.clientWidth-f.offsetWidth,o=v(g)*r,a=function(e){e.preventDefault(),y=function(e){return Math.max(Math.min(o+(e.pageX-n),r),0)}(e)/r,g=y*p+t.start.x,w=y*m+t.start.y,b()};Ie.bind(document,"mousemove",a),Ie.bind(document,"mouseup",(function e(t){a(t),Ie.unbind(document,"mousemove",a),Ie.unbind(document,"mouseup",e),c.changed()}))}})),h.rangeLblStart.textContent=t.start.label,h.rangeLblEnd.textContent=t.end.label,h.xUnit.textContent=t.xUnit,h.yUnit.textContent=t.yUnit,b(!0)}));function St(e,t,n,r){switch(!0){case!!e.boolean:return new bt(e,t,n,r).node;case!!e.xyRange:return new xt(e,t,n,r).node;default:console.error("no option type specified",e)}return console.error("unknown option type",e),document.createElement("div")}function Ft(e,t){return at().exchanges[e].assets[t].symbol}var Et=function(e,t,n){return en?n:e};function At(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 Rt(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)?Rt(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Rt(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n.header.classList.add("bordertop"):n.header.classList.remove("bordertop"),a=r.info.availablewallets[0],Ie.empty(o),Ie.hide(o,n.newWalletErr),r.info.availablewallets.length>1){Ie.show(o),i=At(r.info.availablewallets);try{for(c=function(){var e=s.value,t=n.walletTabTmpl.cloneNode(!0);t.dataset.tooltip=e.description,t.textContent=e.tab,o.appendChild(t),Ie.bind(t,"click",(function(){var n,r=At(Ie.kids(o));try{for(r.s();!(n=r.n()).done;)n.value.classList.remove("selected")}catch(e){r.e(e)}finally{r.f()}t.classList.add("selected"),u.update(e)}))},i.s();!(s=i.n()).done;)c()}catch(e){i.e(e)}finally{i.f()}at().bindTooltips(o),o.firstChild.classList.add("selected")}return e.next=16,this.update(a);case 16:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"update",value:(r=a(v().mark((function e(t){var n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentWalletType=t.type,r=je.passwordIsCached()||this.pwCache&&this.pwCache.pw,Ie.hide(n.auth,n.oneBttnBox,n.newWalletPassBox),(o=t.configopts||[]).map((function(e){return e.isBirthdayConfig&&at().seedGenTime>0&&(e.default=jt(new Date)),e})),r&&t.seeded?Ie.show(n.oneBttnBox):t.seeded?(Ie.show(n.auth),n.newWalletPass.value="",n.submitAdd.textContent=ge(ae)):(Ie.show(n.auth),t.noauth||Ie.show(n.newWalletPassBox),n.submitAdd.textContent=ge(oe)),this.subform.update(o),this.subform.dynamicOpts.children.length?Ie.show(n.walletSettingsHeader):Ie.hide(n.walletSettingsHeader),this.refresh(),e.next=12,this.loadDefaults();case 12:case"end":return e.stop()}}),e,this)}))),function(e){return r.apply(this,arguments)})},{key:"setError",value:(n=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.page.newWalletErr.textContent=t,Ie.show(this.page.newWalletErr);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"loadDefaults",value:(t=a(v().mark((function e(){var t,n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!(t=at().walletDefinition(this.currentAsset.id,this.currentWalletType)).seeded){e.next=3;break}return e.abrupt("return");case 3:if(""!==t.configpath){e.next=5;break}return e.abrupt("return");case 5:return n=at().loading(this.form),e.next=8,et("/api/defaultwalletcfg",{assetID:this.currentAsset.id,type:this.currentWalletType});case 8:if(r=e.sent,n(),at().checkResponse(r)){e.next=13;break}return this.setError(r.msg),e.abrupt("return");case 13:this.subform.setLoadedConfig(r.config);case 14:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Ot=function(){function e(t,n){var r=this;i(this,e),u(this,"form",void 0),u(this,"configElements",void 0),u(this,"configOpts",void 0),u(this,"sectionize",void 0),u(this,"allSettings",void 0),u(this,"dynamicOpts",void 0),u(this,"textInputTmpl",void 0),u(this,"dateInputTmpl",void 0),u(this,"checkboxTmpl",void 0),u(this,"fileSelector",void 0),u(this,"fileInput",void 0),u(this,"errMsg",void 0),u(this,"showOther",void 0),u(this,"showIcon",void 0),u(this,"hideIcon",void 0),u(this,"showHideMsg",void 0),u(this,"otherSettings",void 0),u(this,"loadedSettingsMsg",void 0),u(this,"loadedSettings",void 0),u(this,"defaultSettingsMsg",void 0),u(this,"defaultSettings",void 0),this.form=t,this.configElements={},this.configOpts=[],this.sectionize=n,this.allSettings=Ie.tmplElement(t,"allSettings"),this.dynamicOpts=Ie.tmplElement(t,"dynamicOpts"),this.textInputTmpl=Ie.tmplElement(t,"textInput"),this.textInputTmpl.remove(),this.dateInputTmpl=Ie.tmplElement(t,"dateInput"),this.dateInputTmpl.remove(),this.checkboxTmpl=Ie.tmplElement(t,"checkbox"),this.checkboxTmpl.remove(),this.fileSelector=Ie.tmplElement(t,"fileSelector"),this.fileInput=Ie.tmplElement(t,"fileInput"),this.errMsg=Ie.tmplElement(t,"errMsg"),this.showOther=Ie.tmplElement(t,"showOther"),this.showIcon=Ie.tmplElement(t,"showIcon"),this.hideIcon=Ie.tmplElement(t,"hideIcon"),this.showHideMsg=Ie.tmplElement(t,"showHideMsg"),this.otherSettings=Ie.tmplElement(t,"otherSettings"),this.loadedSettingsMsg=Ie.tmplElement(t,"loadedSettingsMsg"),this.loadedSettings=Ie.tmplElement(t,"loadedSettings"),this.defaultSettingsMsg=Ie.tmplElement(t,"defaultSettingsMsg"),this.defaultSettings=Ie.tmplElement(t,"defaultSettings"),n||Ie.hide(this.showOther),Ie.bind(this.fileSelector,"click",(function(){return r.fileInput.click()})),Ie.bind(this.fileInput,"change",a(v().mark((function e(){return v().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)})))),Ie.bind(this.showOther,"click",(function(){r.setOtherSettingsViz(r.hideIcon.classList.contains("d-hide"))}))}var t;return c(e,[{key:"fileInputChanged",value:(t=a(v().mark((function e(){var t,n,r,o,a,i,s,c;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(Ie.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=at().loading(this.form),e.next=9,n[0].text();case 9:if(o=e.sent){e.next=12;break}return e.abrupt("return");case 12:return e.next=14,et("/api/parseconfig",{configtext:o});case 14:if(a=e.sent,r(),at().checkResponse(a)){e.next=20;break}return this.errMsg.textContent=a.msg,Ie.show(this.errMsg),e.abrupt("return");case 20:if(0!==Object.keys(a.map).length){e.next=22;break}return e.abrupt("return");case 22:(t=this.dynamicOpts).append.apply(t,ot(this.setConfig(a.map))),this.reorder(this.dynamicOpts),i=[this.loadedSettings.children.length,this.defaultSettings.children.length],c=i[1],0===(s=i[0])&&Ie.hide(this.loadedSettings,this.loadedSettingsMsg),0===c&&Ie.hide(this.defaultSettings,this.defaultSettingsMsg),s+c===0&&Ie.hide(this.showOther,this.otherSettings);case 28:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"update",value:function(e,t){var n=this;if(this.configElements={},this.configOpts=e,Ie.empty(this.dynamicOpts,this.defaultSettings,this.loadedSettings),0===e.length)return Ie.hide(this.form);Ie.show(this.form),this.setOtherSettingsViz(!1),Ie.hide(this.loadedSettingsMsg,this.loadedSettings,this.defaultSettingsMsg,this.defaultSettings,this.errMsg);var r,o=[],a=function(e,r){var o,a="wcfg-"+r.key;o=r.isboolean?n.checkboxTmpl.cloneNode(!0):r.isdate?n.dateInputTmpl.cloneNode(!0):n.textInputTmpl.cloneNode(!0),n.configElements[r.key]=o;var i=o.querySelector("input");i.id=a,i.configOpt=r;var s=Ie.safeSelector(o,"label");if(s.htmlFor=a,s.prepend(r.displayname),e.appendChild(o),r.noecho&&(i.type="password"),r.description&&(s.dataset.tooltip=r.description),r.isboolean)i.checked=r.default;else if(r.isdate){var c=function(e){return e?("now"===e?new Date:new Date(1e3*e)).toISOString().split("T")[0]:""};i.max=c(r.max),i.min=c(r.min),i.valueAsDate=r.default?new Date(1e3*r.default):new Date}else i.value=null!==r.default?r.default:"";i.disabled=Boolean(r.disablewhenactive&&t)},i=At(this.configOpts);try{for(i.s();!(r=i.n()).done;){var s=r.value;this.sectionize&&null!==s.default?o.push(s):a(this.dynamicOpts,s)}}catch(e){i.e(e)}finally{i.f()}if(o.length){var c,u=At(o);try{for(u.s();!(c=u.n()).done;){var l=c.value;a(this.defaultSettings,l)}}catch(e){u.e(e)}finally{u.f()}Ie.show(this.showOther,this.defaultSettingsMsg,this.defaultSettings)}else Ie.hide(this.showOther);at().bindTooltips(this.allSettings),this.dynamicOpts.children.length?Ie.show(this.dynamicOpts):Ie.hide(this.dynamicOpts)}},{key:"setOtherSettingsViz",value:function(e){if(e)return Ie.hide(this.showIcon),Ie.show(this.hideIcon,this.otherSettings),void(this.showHideMsg.textContent=ge(E));Ie.hide(this.hideIcon,this.otherSettings),Ie.show(this.showIcon),this.showHideMsg.textContent=ge(A)}},{key:"setConfig",value:function(e){var t=this,n=[];return this.allSettings.querySelectorAll("input").forEach((function(r){var o,a=r.configOpt.key,i=e[a];void 0!==i&&(n.push(t.configElements[a]),r.configOpt.isboolean?r.checked="1"===(o=i)||"true"===o.toLowerCase():r.configOpt.isdate?r.valueAsDate=new Date(1e3*parseInt(i)):r.value=i)})),n}},{key:"setLoadedConfig",value:function(e){var t,n=this.setConfig(e);this.sectionize&&0!==n.length&&((t=this.loadedSettings).append.apply(t,ot(n)),this.reorder(this.loadedSettings),Ie.show(this.loadedSettings,this.loadedSettingsMsg),0===this.defaultSettings.children.length&&Ie.hide(this.defaultSettings,this.defaultSettingsMsg))}},{key:"map",value:function(){var e={};return this.allSettings.querySelectorAll("input").forEach((function(t){if(t.configOpt.isboolean&&t.configOpt.key)e[t.configOpt.key]=t.checked?"1":"0";else if(t.configOpt.isdate&&t.configOpt.key){var n=t.min?jt(new Date(t.min)):Number.MIN_SAFE_INTEGER,r=t.max?jt(new Date(t.max)):Number.MAX_SAFE_INTEGER,o=t.value?jt(new Date(t.value)):0;or&&(o=r),e[t.configOpt.key]=""+o}else t.value&&(e[t.configOpt.key]=t.value)})),e}},{key:"reorder",value:function(e){var t=this,n={};e.querySelectorAll("input").forEach((function(e){var r=e.configOpt.key;n[r]=t.configElements[r]}));var r,o=At(this.configOpts);try{for(o.s();!(r=o.n()).done;){var a=r.value,i=n[a.key];i&&e.append(i)}}catch(e){o.e(e)}finally{o.f()}}}]),e}(),Dt=function(){function e(t,n,r,o){var a=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"page",void 0),u(this,"xc",void 0),u(this,"certFile",void 0),u(this,"feeAssetID",void 0),u(this,"pwCache",void 0),this.form=t,this.success=n,this.page=Ie.parseTemplate(t),this.certFile="",this.pwCache=o,Ie.bind(this.page.goBack,"click",(function(){return r()})),zt(t,this.page.submit,(function(){return a.submitForm()}))}var t,n;return c(e,[{key:"setExchange",value:function(e,t){this.xc=e,this.certFile=t;var n=this.page;je.passwordIsCached()||this.pwCache&&this.pwCache.pw?Ie.hide(n.passBox):Ie.show(n.passBox),n.host.textContent=e.host}},{key:"setAsset",value:function(e){var t=at().assets[e],n=t.info.unitinfo;this.feeAssetID=t.id;var r=this.page,o=this.xc.regFees[t.symbol];r.fee.textContent=Ie.formatCoinValue(o.amount,n),r.feeUnit.textContent=n.conventional.unit.toUpperCase(),r.logo.src=Ie.logoPath(t.symbol)}},{key:"animate",value:(n=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,Ie.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=a(v().mark((function e(){var t,n,r,o,a,i,s,c,u;return v().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(null!==this.feeAssetID){e.next=7;break}return t.regErr.innerText="You must select a valid wallet for the fee payment",Ie.show(t.regErr),e.abrupt("return");case 7:return n=at().user.assets[this.feeAssetID].wallet.symbol,Ie.hide(t.regErr),r=this.xc.regFees[n],e.next=12,this.certFile;case 12:return o=e.sent,a=this.xc.host,i=t.appPass.value||(this.pwCache?this.pwCache.pw:""),s={addr:a,pass:i,fee:r.amount,asset:r.id,cert:o},t.appPass.value="",c=at().loading(this.form),e.next=20,et("/api/register",s);case 20:if(u=e.sent,c(),at().checkResponse(u)){e.next=26;break}return t.regErr.textContent=u.msg,Ie.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}(),Tt=function(){function e(t,n){i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"xc",void 0),u(this,"page",void 0),this.form=t,this.success=n,this.page=Ie.parseTemplate(t),Ie.cleanTemplates(this.page.marketTmpl,this.page.assetTmpl)}var t;return c(e,[{key:"setExchange",value:function(e){var t=this;this.xc=e;var n=this.page;Ie.empty(n.assets,n.allMarkets);for(var o=function(e){return e.conventional.conversionFactor},a=function(t,r){var a=n.marketTmpl.cloneNode(!0),i=Ie.parseTemplate(a),s=e.assets[t.baseid],c=at().unitInfo(t.baseid,e),u=e.assets[t.quoteid],l=at().unitInfo(t.quoteid,e);if(0===o(c)||0===o(l))return null;if(void 0!==r){var h=r===t.baseid,d=e.assets[h?t.quoteid:t.baseid].symbol;i.logo.src=Ie.logoPath(d)}else{var f=i.logo.cloneNode(!0);i.logo.src=Ie.logoPath(s.symbol),f.src=Ie.logoPath(u.symbol);var p=i.logo.parentNode;p&&p.insertBefore(f,i.logo.nextSibling)}var m=s.symbol.toUpperCase(),v=u.symbol.toUpperCase();i.name.textContent="".concat(m,"-").concat(v);var y=Ie.formatCoinValue(t.lotsize,c);if(i.lotSize.textContent="".concat(y," ").concat(m),t.spot){Ie.show(i.quoteLotSize);var g=o(l)/o(c),w=t.lotsize*t.spot.rate/ht*g,k=Ie.formatCoinValue(w,l);i.quoteLotSize.textContent="(~".concat(k," ").concat(v,")")}return a},i=function(){var o=r(c[s],2),i=o[0],u=o[1],l=at().assets[u.id];if(!l)return"continue";var h=l.wallet,d=l.info.unitinfo,f=n.assetTmpl.cloneNode(!0);Ie.bind(f,"click",(function(){t.success(u.id)}));var p=Ie.parseTemplate(f);n.assets.appendChild(f),p.logo.src=Ie.logoPath(i);var m=Ie.formatCoinValue(u.amount,d);p.fee.textContent="".concat(m," ").concat(d.conventional.unit),p.confs.textContent=String(u.confs),p.ready.textContent=ge(h?le:he),p.ready.classList.add(h?"readygreen":"setuporange");for(var v=0,y=0,g=Object.values(e.markets);y0?(r.totalFees.textContent=Ie.formatCoinValue(a.amount+t,o.info.unitinfo),Ie.show(r.sendEnoughWithEst),Ie.hide(r.sendEnough)):(Ie.show(r.sendEnough),Ie.hide(r.sendEnoughWithEst)),Ie.show(e.synced?r.syncCheck:e.syncProgress>=1?r.syncSpinner:r.syncUncheck),Ie.show(e.balance.available>a.amount?r.balCheck:r.balUncheck),r.progress.textContent=String(Math.round(100*e.syncProgress)),e.synced&&(this.progressed=!0),this.reportBalance(e.balance,e.assetID)}},{key:"reportWalletState",value:function(e){e.assetID===this.assetID&&(this.progressed&&this.funded||(this.reportProgress(e.synced,e.syncProgress),this.reportBalance(e.balance,e.assetID)))}},{key:"reportBalance",value:function(e,t){if(!this.funded&&-1!==this.assetID&&this.assetID===t){var n=this.page,r=at().assets[this.assetID];e.available<=this.regFee.amount?n.balance.textContent=Ie.formatCoinValue(e.available,r.info.unitinfo):(Ie.show(n.balCheck),Ie.hide(n.balUncheck,n.balanceBox,n.sendEnough),this.funded=!0,this.progressed&&this.success())}}},{key:"reportProgress",value:function(e,t){var n=this.page;if(e)return n.progress.textContent="100",Ie.hide(n.syncUncheck,n.syncRemainBox,n.syncSpinner),Ie.show(n.syncCheck),this.progressed=!0,void(this.funded&&this.success());1===t?(Ie.hide(n.syncUncheck),Ie.show(n.syncSpinner)):(Ie.hide(n.syncSpinner),Ie.show(n.syncUncheck)),n.progress.textContent=String(Math.round(100*t));var r=this.progressCache;for(r.push({stamp:(new Date).getTime(),progress:t});r.length>20;)r.shift();if(1!==r.length){Ie.show(n.syncRemainBox);var o=[r[0],r[r.length-1]],a=o[0],i=o[1],s=i.progress-a.progress;if(0!==s){var c=s/(i.stamp-a.stamp),u=(1-i.progress)/c;n.syncRemain.textContent=Ie.formatDuration(u)}else n.syncRemain.textContent="> 1 day"}}}]),e}(),Pt=function(){function e(t,n,r){var o=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"pwCache",void 0),u(this,"page",void 0),u(this,"currentAsset",void 0),this.page=Ie.idDescendants(t),this.form=t,this.pwCache=r||null,this.success=n,zt(t,this.page.submitUnlock,(function(){return o.submit()}))}var t;return c(e,[{key:"refresh",value:function(e){var t=this.page;this.currentAsset=e,t.uwAssetLogo.src=Ie.logoPath(e.symbol),t.uwAssetName.textContent=e.info.name,t.uwAppPass.value="",t.unlockErr.textContent="",Ie.hide(t.unlockErr),je.passwordIsCached()||this.pwCache&&this.pwCache.pw?Ie.hide(t.uwAppPassBox):Ie.show(t.uwAppPassBox)}},{key:"setError",value:function(e){this.page.unlockErr.textContent=e,Ie.show(this.page.unlockErr)}},{key:"showErrorOnly",value:function(e){this.setError(e),Ie.hide(this.page.uwAppPassBox),Ie.hide(this.page.submitUnlockDiv)}},{key:"submit",value:(t=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,(n=t.uwAppPass.value||(this.pwCache?this.pwCache.pw:""))||je.passwordIsCached()){e.next=6;break}return t.unlockErr.textContent=ge(g),Ie.show(t.unlockErr),e.abrupt("return");case 6:return Ie.hide(this.page.unlockErr),r={assetID:this.currentAsset.id,pass:n},t.uwAppPass.value="",o=at().loading(this.form),e.next=12,et("/api/openwallet",r);case 12:if(a=e.sent,o(),at().checkResponse(a)){e.next=17;break}return this.setError(a.msg),e.abrupt("return");case 17:this.pwCache&&(this.pwCache.pw=n),this.success();case 19:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Wt=function(){function e(t,n){var r=this;i(this,e),u(this,"form",void 0),u(this,"page",void 0),u(this,"order",void 0),u(this,"acceleratedRate",void 0),u(this,"earlyAcceleration",void 0),u(this,"currencyUnit",void 0),u(this,"success",void 0),this.form=t,this.success=n;var o=this.page=Ie.idDescendants(t);Ie.bind(o.accelerateSubmit,"click",(function(){r.submit()})),Ie.bind(o.submitEarlyConfirm,"click",(function(){r.sendAccelerateRequest()}))}var t,n,r,o;return c(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?(Ie.show(e.recentAccelerationMsg),Ie.hide(e.recentSwapMsg),e.recentAccelerationTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))):(Ie.show(e.recentSwapMsg),Ie.hide(e.recentAccelerationMsg),e.recentSwapTime.textContent="".concat(Math.floor(this.earlyAcceleration.timePast/60))),Ie.hide(e.configureAccelerationDiv,e.accelerateErr),Ie.show(e.earlyAccelerationDiv))}},{key:"sendAccelerateRequest",value:(o=a(v().mark((function e(){var t,n,r,o,a;return v().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="",o=at().loading(n.accelerateMainDiv),e.next=7,et("/api/accelerateorder",r);case 7:a=e.sent,o(),at().checkResponse(a)?(n.accelerateTxID.textContent=a.txID,Ie.hide(n.accelerateMainDiv,n.preAccelerateErr,n.accelerateErr),Ie.show(n.accelerateMsgDiv,n.accelerateSuccess),this.success()):(n.accelerateErr.textContent="Error accelerating order: ".concat(a.msg),Ie.hide(n.earlyAccelerationDiv),Ie.show(n.accelerateErr,n.configureAccelerationDiv));case 10:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"submit",value:(r=a(v().mark((function e(){return v().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=a(v().mark((function e(t){var n,r,o,a,i,s,c=this;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.order=t,e.next=4,et("/api/preaccelerate",t.id);case 4:if(r=e.sent,at().checkResponse(r)){e.next=10;break}return n.preAccelerateErr.textContent="Error accelerating order: ".concat(r.msg),Ie.hide(n.accelerateMainDiv,n.accelerateSuccess),Ie.show(n.accelerateMsgDiv,n.preAccelerateErr),e.abrupt("return");case 10:Ie.hide(n.accelerateMsgDiv,n.preAccelerateErr,n.accelerateErr,n.feeEstimateDiv,n.earlyAccelerationDiv),Ie.show(n.accelerateMainDiv,n.accelerateSuccess,n.configureAccelerationDiv),o=r.preAccelerate,this.earlyAcceleration=o.earlyAcceleration,this.currencyUnit=o.suggestedRange.yUnit,n.accelerateAvgFeeRate.textContent="".concat(o.swapRate," ").concat(o.suggestedRange.yUnit),n.accelerateCurrentFeeRate.textContent="".concat(o.suggestedRate," ").concat(o.suggestedRange.yUnit),this.acceleratedRate=o.suggestedRange.start.y,a=function(){},i=function(e,t){c.acceleratedRate=t},s=new Ct(o.suggestedRange,o.suggestedRange.start.x,i,(function(){return c.updateAccelerationEstimate()}),a,!0),Ie.empty(n.sliderContainer),n.sliderContainer.appendChild(s.control),this.updateAccelerationEstimate();case 25:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"updateAccelerationEstimate",value:(t=a(v().mark((function e(){var t,n,r,o,a,i,s,c;return v().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},o=at().loading(t.sliderContainer),e.next=6,et("/api/accelerationestimate",r);case 6:if(a=e.sent,o(),at().checkResponse(a)){e.next=12;break}return t.accelerateErr.textContent="Error estimating acceleration fee: ".concat(a.msg),Ie.show(t.accelerateErr),e.abrupt("return");case 12:t.feeRateEstimate.textContent="".concat(this.acceleratedRate," ").concat(this.currencyUnit),n.sell?(i=n.baseID,s=n.baseSymbol):(i=n.quoteID,s=n.quoteSymbol),c=at().unitInfo(i),t.feeEstimate.textContent="".concat(a.fee/c.conventional.conversionFactor," ").concat(s),Ie.show(t.feeEstimateDiv);case 17:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),e}(),Bt=function(){function e(t,n,r,o){var a=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"pwCache",void 0),u(this,"defaultTLSText",void 0),u(this,"page",void 0),u(this,"knownExchanges",void 0),u(this,"dexToUpdate",void 0),this.form=t,this.success=n,this.pwCache=r||null,this.defaultTLSText="none selected";var s=this.page=Ie.parseTemplate(t);s.selectedCert.textContent=this.defaultTLSText,Ie.bind(s.certFile,"change",(function(){return a.onCertFileChange()})),Ie.bind(s.removeCert,"click",(function(){return a.clearCertFile()})),Ie.bind(s.addCert,"click",(function(){return s.certFile.click()})),Ie.bind(s.showCustom,"click",(function(){Ie.hide(s.showCustom),Ie.show(s.customBox,s.auth)})),this.knownExchanges=Array.from(s.knownXCs.querySelectorAll(".known-exchange"));var c,l=At(this.knownExchanges);try{var h=function(){var e=c.value;Ie.bind(e,"click",(function(){var t,n=e.dataset.host,o=At(a.knownExchanges);try{for(o.s();!(t=o.n()).done;)t.value.classList.remove("selected")}catch(e){o.e(e)}finally{o.f()}if(je.passwordIsCached()||r&&r.pw)return a.checkDEX(n);e.classList.add("selected"),s.appPW.focus(),s.addr.value=n}))};for(l.s();!(c=l.n()).done;)h()}catch(e){l.e(e)}finally{l.f()}zt(t,s.submit,(function(){return a.checkDEX()})),o&&(Ie.hide(s.addDexHdr),Ie.show(s.updateDexHdr),this.dexToUpdate=o),this.refresh()}var t,n,r;return c(e,[{key:"refresh",value:function(){var e=this.page;e.addr.value="",e.appPW.value="",this.clearCertFile(),Ie.hide(e.err),je.passwordIsCached()||this.pwCache&&this.pwCache.pw?Ie.hide(e.appPWBox,e.auth):Ie.show(e.appPWBox,e.auth),0===this.knownExchanges.length||this.dexToUpdate?(Ie.show(e.customBox,e.auth),Ie.hide(e.showCustom,e.knownXCs,e.pickServerMsg,e.addCustomMsg)):(Ie.hide(e.customBox),Ie.show(e.showCustom));var t,n=At(this.knownExchanges);try{for(n.s();!(t=n.n()).done;)t.value.classList.remove("selected")}catch(e){n.e(e)}finally{n.f()}}},{key:"animate",value:(r=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,Ie.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=a(v().mark((function e(t){var n,r,o,a,i,s,c,u;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,Ie.hide(n.err),""!==(t=t||n.addr.value)){e.next=7;break}return n.err.textContent="DEX address cannot be empty",Ie.show(n.err),e.abrupt("return");case 7:if(r="",!n.certFile.value){e.next=14;break}if(!(o=n.certFile.files)||!o.length){e.next=14;break}return e.next=13,o[0].text();case 13:r=e.sent;case 14:return a="",je.passwordIsCached()||(a=n.appPW.value||(this.pwCache?this.pwCache.pw:"")),this.dexToUpdate?(i="/api/updatedexhost",s={newHost:t,cert:r,pw:a,oldHost:this.dexToUpdate}):(i="/api/discoveracct",s={addr:t,cert:r,pass:a}),c=at().loading(this.form),e.next=20,et(i,s);case 20:if(u=e.sent,c(),at().checkResponse(u,!0)){e.next=25;break}return"certificate required"===u.msg?Ie.show(n.needCert):(n.err.textContent=u.msg,Ie.show(n.err)),e.abrupt("return");case 25:if(this.dexToUpdate||!u.paid){e.next=30;break}return e.next=28,at().fetchUser();case 28:return at().loadPage("markets"),e.abrupt("return");case 30:this.pwCache&&(this.pwCache.pw=a),this.success(u.xc,r);case 32:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"onCertFileChange",value:(t=a(v().mark((function e(){var t,n;return v().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,Ie.show(t.removeCert),Ie.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=this.defaultTLSText,Ie.hide(e.removeCert),Ie.show(e.addCert)}}]),e}(),qt=function(){function e(t,n,r){var o=this;i(this,e),u(this,"form",void 0),u(this,"success",void 0),u(this,"pwCache",void 0),u(this,"headerTxt",void 0),u(this,"page",void 0),this.success=n,this.form=t,this.pwCache=r||null;var a=this.page=Ie.parseTemplate(t);this.headerTxt=a.header.textContent||"",zt(t,a.submit,(function(){o.submit()}))}var t,n;return c(e,[{key:"focus",value:function(){this.page.pw.focus()}},{key:"submit",value:(n=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.errMsg),n=t.pw.value||"",t.pw.value="",r=t.rememberPass.checked,""!==n){e.next=9;break}return t.errMsg.textContent=ge(y),Ie.show(t.errMsg),e.abrupt("return");case 9:return o=at().loading(this.form),e.next=12,et("/api/login",{pass:n,rememberPass:r});case 12:if(a=e.sent,o(),at().checkResponse(a)){e.next=18;break}return t.errMsg.textContent=a.msg,Ie.show(t.errMsg),e.abrupt("return");case 18:a.notes&&a.notes.reverse(),at().setNotes(a.notes||[]),this.pwCache&&(this.pwCache.pw=n),this.success();case 22:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"animate",value:(t=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.form,Ie.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}(),Mt=300;function Nt(e,t){return Ut.apply(this,arguments)}function Ut(){return(Ut=a(v().mark((function e(t,n){var r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=document.body.offsetWidth/2,e.next=3,Ie.animate(Mt,(function(e){t.style.right="".concat(e*r,"px")}),"easeInHard");case 3:return Ie.hide(t),t.style.right="0",n.style.right=String(-r),Ie.show(n),n.querySelector("input")&&Ie.safeSelector(n,"input").focus(),e.next=10,Ie.animate(Mt,(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 zt(e,t,n){var r=function(e){e.preventDefault&&e.preventDefault(),n(e)};Ie.bind(t,"click",r),Ie.bind(e,"submit",r)}function jt(e){return Math.floor(e.getTime()/1e3)}var _t=function(e){He(g,e);var t,n,r,o,s,l,h,d,f,p,m=(f=g,p=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=Qe(f);if(p){var n=Qe(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return Xe(this,e)});function g(e){var t;i(this,g),u(_e(t=m.call(this)),"body",void 0),u(_e(t),"pwCache",void 0),u(_e(t),"currentDEX",void 0),u(_e(t),"page",void 0),u(_e(t),"loginForm",void 0),u(_e(t),"dexAddrForm",void 0),u(_e(t),"newWalletForm",void 0),u(_e(t),"regAssetForm",void 0),u(_e(t),"walletWaitForm",void 0),u(_e(t),"confirmRegisterForm",void 0),t.body=e,t.pwCache={pw:""};var n=t.page=Ie.idDescendants(e);e.querySelectorAll(".form-closer").forEach((function(e){return Ie.hide(e)})),zt(n.appPWForm,n.appPWSubmit,(function(){return t.setAppPass()})),Ie.bind(n.showSeedRestore,"click",(function(){Ie.show(n.seedRestore),Ie.hide(n.showSeedRestore)})),t.loginForm=new qt(n.loginForm,a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:t.dexAddrForm.refresh(),Nt(n.loginForm,n.dexAddrForm);case 4:case"end":return e.stop()}}),e)}))),t.pwCache),t.newWalletForm=new It(n.newWalletForm,(function(e){return t.newWalletCreated(e)}),t.pwCache,(function(){return t.animateRegAsset(n.newWalletForm)})),t.dexAddrForm=new Bt(n.dexAddrForm,function(){var e=a(v().mark((function e(r,o){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.currentDEX=r,t.confirmRegisterForm.setExchange(r,o),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)}}(),t.pwCache),t.regAssetForm=new Tt(n.regAssetForm,function(){var e=a(v().mark((function e(r){var o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.confirmRegisterForm.setAsset(r),o=at().assets[r],!(a=o.wallet)){e.next=14;break}if(i=t.currentDEX.regFees[o.symbol],!(a.synced&&a.balance.available>i.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 s=e.sent,t.walletWaitForm.setWallet(a,s),Nt(n.regAssetForm,n.walletWait),e.abrupt("return");case 14:t.newWalletForm.setAsset(r),t.newWalletForm.loadDefaults(),Nt(n.regAssetForm,n.newWalletForm);case 17:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),t.walletWaitForm=new Lt(n.walletWait,(function(){t.animateConfirmForm(n.walletWait)}),(function(){t.animateRegAsset(n.walletWait)})),t.confirmRegisterForm=new Dt(n.confirmRegForm,(function(){t.registerDEXSuccess()}),(function(){t.animateRegAsset(n.confirmRegForm)}),t.pwCache);var r=Ie.safeSelector(n.forms,":scope > form.selected");switch(r.classList.remove("selected"),r){case n.loginForm:t.loginForm.animate();break;case n.dexAddrForm:t.dexAddrForm.animate()}return Ie.show(r),at().user.authed&&t.auth(),t}return c(g,[{key:"unload",value:function(){this.pwCache.pw=""}},{key:"auth",value:(d=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:case"end":return e.stop()}}),e)}))),function(){return d.apply(this,arguments)})},{key:"animateRegAsset",value:(h=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:Ie.hide(t),this.regAssetForm.animate(),Ie.show(this.page.regAssetForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return h.apply(this,arguments)})},{key:"animateConfirmForm",value:(l=a(v().mark((function e(t){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),Ie.hide(t),Ie.show(this.page.confirmRegForm);case 3:case"end":return e.stop()}}),e,this)}))),function(e){return l.apply(this,arguments)})},{key:"getRegistrationTxFeeEstimate",value:(s=a(v().mark((function e(t,n){var r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getCertFile();case 2:return r=e.sent,o=at().loading(n),e.next=6,et("/api/regtxfee",{addr:this.currentDEX.host,cert:r,asset:t});case 6:if(a=e.sent,o(),at().checkResponse(a,!0)){e.next=10;break}return e.abrupt("return",0);case 10:return e.abrupt("return",a.txfee);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return s.apply(this,arguments)})},{key:"setAppPass",value:(o=a(v().mark((function e(){var t,n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.appPWErrMsg),n=t.appPW.value||"",r=t.appPWAgain.value,""!==n){e.next=8;break}return t.appPWErrMsg.textContent=ge(y),Ie.show(t.appPWErrMsg),e.abrupt("return");case 8:if(n===r){e.next=12;break}return t.appPWErrMsg.textContent=ge(j),Ie.show(t.appPWErrMsg),e.abrupt("return");case 12:return at().setNotes([]),t.appPW.value="",t.appPWAgain.value="",o=at().loading(t.appPWForm),a=t.seedInput.value,i=t.rememberPass.checked,e.next=20,et("/api/init",{pass:n,seed:a,rememberPass:i});case 20:if(s=e.sent,o(),at().checkResponse(s)){e.next=26;break}return t.appPWErrMsg.textContent=s.msg,Ie.show(t.appPWErrMsg),e.abrupt("return");case 26:return this.pwCache.pw=n,this.auth(),at().updateMenuItemsDisplay(),this.newWalletForm.refresh(),this.dexAddrForm.refresh(),e.next=33,Nt(t.appPWForm,t.dexAddrForm);case 33:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"getCertFile",value:(r=a(v().mark((function e(){var t,n;return v().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=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:at().loadPage("markets");case 3:case"end":return e.stop()}}),e)}))),function(){return n.apply(this,arguments)})},{key:"newWalletCreated",value:(t=a(v().mark((function e(t){var n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.regAssetForm.refresh(),e.next=3,at().fetchUser();case 3:if(n=e.sent){e.next=6;break}return e.abrupt("return");case 6:if(r=this.page,o=n.assets[t],a=o.wallet,i=this.currentDEX.regFees[o.symbol].amount,!(a.synced&&a.balance.available>i)){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.getRegistrationTxFeeEstimate(t,r.newWalletForm);case 16:return s=e.sent,this.walletWaitForm.setWallet(a,s),e.next=20,Nt(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),g}(Je);var Vt=function(e){He(s,e);var t,n,r,o=(n=s,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=Qe(n);if(r){var o=Qe(this).constructor;e=Reflect.construct(t,arguments,o)}else e=t.apply(this,arguments);return Xe(this,e)});function s(e){var t;return i(this,s),u(_e(t=o.call(this)),"form",void 0),u(_e(t),"loginForm",void 0),t.form=Ie.idel(e,"loginForm"),Ie.show(t.form),t.loginForm=new qt(t.form,(function(){t.loggedIn()})),t.loginForm.focus(),t}return c(s,[{key:"loggedIn",value:(t=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:at().loadPage("markets");case 3:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})}]),s}(Je);function Ht(e,t,n){return{subject:e,details:t,severity:n,stamp:(new Date).getTime(),acked:!1,type:"internal",topic:"internal",id:""}}function Gt(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 Xt(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)?Xt(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Xt(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){Ie.bind(e,"click",(function(){t.closePopups()}))})),Ie.bind(n.cancelForce,"click",(function(){t.closePopups()})),Ie.bind(n.copyAddressBtn,"click",(function(){t.copyAddress()}));var o,s,c=function(e,t){return e.querySelector("[data-action=".concat(t,"]"))},l=t.rowInfos={},h=Gt(Ie.applySelector(n.walletTable,"tr"));try{for(h.s();!(s=h.n()).done;){var d=s.value,f=parseInt(d.dataset.assetID||"");l[f]={assetID:f,tr:d,symbol:d.dataset.symbol||"",name:d.dataset.name||"",stateIcons:new De(d),actions:{connect:c(d,"connect"),unlock:c(d,"unlock"),send:c(d,"send"),deposit:c(d,"deposit"),create:c(d,"create"),rescan:c(d,"rescan"),lock:c(d,"lock"),settings:c(d,"settings")}},o||(o=l[f])}}catch(e){h.e(e)}finally{h.f()}n.marketCard.removeAttribute("id"),n.marketCard.remove(),n.oneMarket.removeAttribute("id"),n.oneMarket.remove(),t.newWalletForm=new It(n.newWalletForm,(function(){t.createWalletSuccess()})),t.reconfigForm=new Ot(n.reconfigInputs,!1),t.unlockForm=new Pt(n.unlockWalletForm,(function(){t.openWalletSuccess()})),zt(n.sendForm,n.submitSendForm,(function(){t.send()})),zt(n.reconfigForm,n.submitReconfig,(function(){return t.reconfig()}));for(var p=function(){var e=y[m];Qt(e.tr,"click",(function(){t.showMarkets(e.assetID)}))},m=0,y=Object.values(l);m1){Ie.empty(n.changeWalletTypeSelect),Ie.show(n.showChangeType,n.changeTypeShowIcon),n.changeTypeMsg.textContent=ge(ce),a=Gt(r.info.availablewallets);try{for(a.s();!(i=a.n()).done;)s=i.value,c=document.createElement("option"),s.type===o.type&&(c.selected=!0),c.value=c.textContent=s.type,n.changeWalletTypeSelect.appendChild(c)}catch(e){a.e(e)}finally{a.f()}}else Ie.hide(n.showChangeType);return 0!=(4&(u=at().walletMap[t]).traits)?Ie.show(n.downloadLogs):Ie.hide(n.downloadLogs),0!=(32&u.traits)?Ie.show(n.recoverWallet):Ie.hide(n.recoverWallet),256&u.traits?Ie.show(n.exportWallet):Ie.hide(n.exportWallet),n.recfgAssetLogo.src=Ie.logoPath(r.symbol),n.recfgAssetName.textContent=r.info.name,e.next=17,this.hideBox();case 17:return this.animation=this.showBox(n.reconfigForm),l=at().loading(n.reconfigForm),e.next=21,et("/api/walletsettings",{assetID:t});case 21:if(h=e.sent,l(),at().checkResponse(h,!0)){e.next=27;break}return n.reconfigErr.textContent=h.msg,Ie.show(n.reconfigErr),e.abrupt("return");case 27:d=at().walletIsActive(t),this.reconfigForm.update(o.configopts||[],d),this.reconfigForm.setConfig(h.map),this.updateDisplayedReconfigFields(o);case 31:case"end":return e.stop()}}),e,this)}))),function(e){return C.apply(this,arguments)})},{key:"changeWalletType",value:function(){var e=this.page.changeWalletTypeSelect.value||"",t=at().walletDefinition(this.reconfigAsset,e);this.reconfigForm.update(t.configopts||[]),this.updateDisplayedReconfigFields(t)}},{key:"updateDisplayedReconfigFields",value:function(e){e.seeded?(Ie.hide(this.page.showChangePW),this.changeWalletPW=!1,this.setPWSettingViz(!1)):Ie.show(this.page.showChangePW)}},{key:"showDeposit",value:(x=a(v().mark((function e(t){var n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.page,Ie.hide(n.depositErr),r=n.deposit,o=at().assets[t],n.depositLogo.src=Ie.logoPath(o.symbol),a=at().walletMap[t],this.depositAsset=this.lastFormAsset=t,a){e.next=10;break}return at().notify(Ht("Cannot retrieve deposit address.","No wallet found for ".concat(o.info.name),5)),e.abrupt("return");case 10:return e.next=12,this.hideBox();case 12:n.depositName.textContent=o.info.name,n.depositAddress.textContent=a.address,n.qrcode.src="/generateqrcode?address=".concat(a.address),0!=(2&a.traits)?Ie.show(n.newDepAddrBttn):Ie.hide(n.newDepAddrBttn),this.animation=this.showBox(r);case 17:case"end":return e.stop()}}),e,this)}))),function(e){return x.apply(this,arguments)})},{key:"newDepositAddress",value:(b=a(v().mark((function e(){var t,n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.depositErr),n=at().loading(t.deposit),e.next=5,et("/api/depositaddress",{assetID:this.depositAsset});case 5:if(r=e.sent,n(),at().checkResponse(r,!0)){e.next=11;break}return t.depositErr.textContent=r.msg,Ie.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 b.apply(this,arguments)})},{key:"showSendForm",value:(k=a(v().mark((function e(t){var n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=n.sendForm,o=this.sendAsset=at().assets[t],this.lastFormAsset=t,(a=at().walletMap[t])||at().notify(Ht("Cannot send/withdraw.","No wallet found for ".concat(o.info.name),5)),e.next=8,this.hideBox();case 8:Ie.hide(n.senderOnlyHelpText),Ie.hide(n.toggleSubtract),n.subtractCheckBox.checked=!1,0!=(64&a.traits)?Ie.show(n.toggleSubtract):(Ie.show(n.senderOnlyHelpText),n.subtractCheckBox.checked=!1),n.sendAddr.value="",n.sendAmt.value="",n.sendPW.value="",n.sendErr.textContent="",this.showFiatValue(o.id,0,n.sendValue),n.sendAvail.textContent=Ie.formatFullPrecision(a.balance.available,o.info.unitinfo),n.sendLogo.src=Ie.logoPath(o.symbol),n.sendName.textContent=o.info.name,r.dataset.assetID=String(t),this.animation=this.showBox(r,n.walletPass);case 23:case"end":return e.stop()}}),e,this)}))),function(e){return k.apply(this,arguments)})},{key:"doConnect",value:(w=a(v().mark((function e(t){var n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=at().loading(this.body),e.next=3,et("/api/connectwallet",{assetID:t});case 3:if(r=e.sent,n(),at().checkResponse(r)){e.next=7;break}return e.abrupt("return");case 7:o=this.rowInfos[t],Ie.hide(o.actions.connect);case 9:case"end":return e.stop()}}),e,this)}))),function(e){return w.apply(this,arguments)})},{key:"createWalletSuccess",value:(y=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.rowInfos[this.walletAsset],this.showMarkets(t.assetID),e.next=4,at().fetchUser();case 4:return e.next=6,at().loadPage("wallets");case 6:case"end":return e.stop()}}),e,this)}))),function(){return y.apply(this,arguments)})},{key:"openWalletSuccess",value:(m=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.rowInfos[this.openAsset],n=t.actions,Ie.show(n.send,n.deposit),Ie.hide(n.unlock,n.connect),at().walletMap[t.assetID].encrypted&&Ie.show(n.lock),this.showMarkets(this.openAsset);case 6:case"end":return e.stop()}}),e,this)}))),function(){return m.apply(this,arguments)})},{key:"send",value:(p=a(v().mark((function e(){var t,n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.sendErr),n=parseInt(t.sendForm.dataset.assetID||""),r=t.subtractCheckBox.checked||!1,o=at().unitInfo(n).conventional.conversionFactor,a={assetID:n,address:t.sendAddr.value,subtract:r,value:Math.round(parseFloat(t.sendAmt.value||"")*o),pw:t.sendPW.value},i=at().loading(t.sendForm),e.next=9,et("/api/send",a);case 9:if(s=e.sent,i(),at().checkResponse(s,!0)){e.next=15;break}return t.sendErr.textContent=s.msg,Ie.show(t.sendErr),e.abrupt("return");case 15:this.showMarkets(n);case 16:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"reconfig",value:(f=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.reconfigErr),t.appPW.value||je.passwordIsCached()){e.next=6;break}return t.reconfigErr.textContent=ge(g),Ie.show(t.reconfigErr),e.abrupt("return");case 6:return n=at().currentWalletDefinition(this.reconfigAsset).type,Ie.isHidden(t.changeWalletType)||(n=t.changeWalletTypeSelect.value||""),r=at().loading(t.reconfigForm),o={assetID:this.reconfigAsset,config:this.reconfigForm.map(),appPW:t.appPW.value||"",walletType:n},this.changeWalletPW&&(o.newWalletPW=t.newPW.value),e.next=13,et("/api/reconfigurewallet",o);case 13:if(a=e.sent,t.appPW.value="",t.newPW.value="",r(),at().checkResponse(a,!0)){e.next=21;break}return t.reconfigErr.textContent=a.msg,Ie.show(t.reconfigErr),e.abrupt("return");case 21:this.showMarkets(this.reconfigAsset);case 22:case"end":return e.stop()}}),e,this)}))),function(){return f.apply(this,arguments)})},{key:"lock",value:(d=a(v().mark((function e(t,n){var r,o,a,i;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=this.page,o=at().loading(r.newWalletForm),e.next=4,et("/api/closewallet",{assetID:t});case 4:if(a=e.sent,o(),at().checkResponse(a)){e.next=8;break}return e.abrupt("return");case 8:i=n.actions,Ie.hide(i.send,i.lock,i.deposit),Ie.show(i.unlock);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return d.apply(this,arguments)})},{key:"downloadLogs",value:(h=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(t=new URLSearchParams("")).append("assetid","".concat(this.reconfigAsset)),(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 h.apply(this,arguments)})},{key:"displayExportWalletAuth",value:(l=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,Ie.hide(t.exportWalletErr),t.exportWalletPW.value="",this.showForm(t.exportWalletAuth);case 4:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"exportWalletAuthSubmit",value:(s=a(v().mark((function e(){var t,n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n={assetID:this.reconfigAsset,pass:t.exportWalletPW.value},r=at().loading(t.forms),e.next=6,et("/api/restorewalletinfo",n);case 6:o=e.sent,r(),at().checkResponse(o)?(t.exportWalletPW.value="",this.displayRestoreWalletInfo(o.restorationinfo)):(t.exportWalletErr.textContent=o.msg,Ie.show(t.exportWalletErr));case 9:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"displayRestoreWalletInfo",value:(o=a(v().mark((function e(t){var n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.page,Ie.empty(n.restoreInfoCardsList),r=Gt(t);try{for(r.s();!(o=r.n()).done;)a=o.value,i=this.restoreInfoCard.cloneNode(!0),(s=Ie.parseTemplate(i)).name.textContent=a.target,s.seed.textContent=a.seed,s.seedName.textContent="".concat(a.seedName,":"),s.instructions.textContent=a.instructions,n.restoreInfoCardsList.appendChild(i)}catch(e){r.e(e)}finally{r.f()}this.showForm(n.restoreWalletInfo);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"recoverWallet",value:(n=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.recoverWalletErr),n={assetID:this.reconfigAsset,appPW:t.recoverWalletPW.value},t.recoverWalletPW.value="",r="/api/recoverwallet",o=at().loading(t.forms),e.next=8,et(r,n);case 8:a=e.sent,o(),35===a.code?(this.forceUrl=r,this.forceReq=n,this.showConfirmForce()):at().checkResponse(a)?this.closePopups():(t.recoverWalletErr.textContent=a.msg,Ie.show(t.recoverWalletErr));case 11:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"confirmForceSubmit",value:(t=a(v().mark((function e(){var t,n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.forceReq.force=!0,n=at().loading(t.forms),e.next=5,et(this.forceUrl,this.forceReq);case 5:r=e.sent,n(),at().checkResponse(r)?this.closePopups():(t.confirmForceErr.textContent=r.msg,Ie.show(t.confirmForceErr));case 8:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"handleBalanceNote",value:function(e){Ie.safeSelector(this.page.walletTable,'[data-balance-target="'.concat(e.assetID,'"]')).textContent=Ie.formatFullPrecision(e.balance.available,at().unitInfo(e.assetID));var t=Ie.safeSelector(this.page.walletTable,'[data-conversion-target="'.concat(e.assetID,'"]'));t&&this.showFiatValue(e.assetID,e.balance.available,t)}},{key:"handleRatesNote",value:function(e){at().fiatRatesMap=e.fiatRates;var t,n=Gt(Object.entries(at().walletMap));try{for(n.s();!(t=n.n()).done;){var o=r(t.value,2),a=o[0],i=o[1];if(i){var s=this.page.walletTable.querySelector('[data-conversion-target="'.concat(a,'"]'));s&&this.showFiatValue(a,i.balance.available,s)}}}catch(e){n.e(e)}finally{n.f()}}},{key:"showFiatValue",value:function(e,t,n){if(n){var r=at().fiatRatesMap[e];n.textContent=Ie.formatFiatConversion(t,r,at().unitInfo(e)),r?Ie.show(n.parentElement):Ie.hide(n.parentElement)}}},{key:"handleWalletStateNote",value:function(e){this.rowInfos[e.wallet.assetID].stateIcons.readWallet(e.wallet);var t=this.page.walletTable.querySelector('[data-conversion-target="'.concat(e.wallet.assetID,'"]'));t&&this.showFiatValue(e.wallet.assetID,e.wallet.balance.available,t)}},{key:"unload",value:function(){Ie.unbind(document,"keyup",this.keyup)}}]),B}(Je);function Kt(e){return"".concat(e.basesymbol.toUpperCase(),"-").concat(e.quotesymbol.toUpperCase())}var Jt=function(e){He(x,e);var t,n,r,o,s,l,h,d,f,p,m,y,w,k,b=(w=x,k=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=Qe(w);if(k){var n=Qe(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return Xe(this,e)});function x(e){var t;i(this,x),u(_e(t=b.call(this)),"body",void 0),u(_e(t),"currentDEX",void 0),u(_e(t),"page",void 0),u(_e(t),"forms",void 0),u(_e(t),"fiatRateSources",void 0),u(_e(t),"regAssetForm",void 0),u(_e(t),"confirmRegisterForm",void 0),u(_e(t),"newWalletForm",void 0),u(_e(t),"walletWaitForm",void 0),u(_e(t),"dexAddrForm",void 0),u(_e(t),"currentForm",void 0),u(_e(t),"pwCache",void 0),u(_e(t),"defaultTLSText",void 0),u(_e(t),"keyup",void 0),t.body=e,t.defaultTLSText="none selected";var n=t.page=Ie.idDescendants(e);t.forms=Ie.applySelector(n.forms,":scope > form"),t.fiatRateSources=Ie.applySelector(n.fiatRateSources,"input[type=checkbox]"),Ie.bind(n.darkMode,"click",(function(){je.dark(n.darkMode.checked||!1),n.darkMode.checked?document.body.classList.add("dark"):document.body.classList.remove("dark")})),Ie.bind(n.showPokes,"click",(function(){var e=n.showPokes.checked||!1;je.setCookie("popups",e?"1":"0"),at().showPopups=e})),n.commitHash.textContent=at().commitHash.substring(0,7),Ie.bind(n.addADex,"click",(function(){t.dexAddrForm.refresh(),t.showForm(n.dexAddrForm)})),t.fiatRateSources.forEach((function(e){Ie.bind(e,"change",a(v().mark((function t(){var n;return v().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,et("/api/toggleratesource",{disable:!e.checked,source:e.value});case 2:return n=t.sent,at().checkResponse(n)||(e.checked=!e.checked),t.next=6,at().fetchUser();case 6:case"end":return t.stop()}}),t)}))))})),t.regAssetForm=new Tt(n.regAssetForm,function(){var e=a(v().mark((function e(r){var o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.confirmRegisterForm.setAsset(r),o=at().assets[r],!(a=o.wallet)){e.next=14;break}if(i=t.currentDEX.regFees[o.symbol],!(a.synced&&a.balance.available>i.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 s=e.sent,t.walletWaitForm.setWallet(a,s),Nt(n.regAssetForm,n.walletWait),e.abrupt("return");case 14:t.newWalletForm.setAsset(r),t.newWalletForm.loadDefaults(),t.currentForm=n.newWalletForm,Nt(n.regAssetForm,n.newWalletForm);case 18:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()),t.confirmRegisterForm=new Dt(n.confirmRegForm,(function(){t.registerDEXSuccess()}),(function(){t.animateRegAsset(n.confirmRegForm)}),t.pwCache),t.newWalletForm=new It(n.newWalletForm,(function(e){return t.newWalletCreated(e)}),t.pwCache,(function(){return t.animateRegAsset(n.newWalletForm)})),t.walletWaitForm=new Lt(n.walletWait,(function(){t.animateConfirmForm(n.walletWait)}),(function(){t.animateRegAsset(n.walletWait)})),t.dexAddrForm=new Bt(n.dexAddrForm,function(){var e=a(v().mark((function e(r,o){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t.currentDEX=r,t.confirmRegisterForm.setExchange(r,o),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)}}()),Ie.bind(n.importAccount,"click",(function(){return t.prepareAccountImport(n.authorizeAccountImportForm)})),zt(n.authorizeAccountImportForm,n.authorizeImportAccountConfirm,(function(){return t.importAccount()})),Ie.bind(n.changeAppPW,"click",(function(){return t.showForm(n.changeAppPWForm)})),zt(n.changeAppPWForm,n.submitNewPW,(function(){return t.changeAppPW()})),Ie.bind(n.accountFile,"change",(function(){return t.onAccountFileChange()})),Ie.bind(n.removeAccount,"click",(function(){return t.clearAccountFile()})),Ie.bind(n.addAccount,"click",(function(){return n.accountFile.click()})),Ie.bind(n.exportSeed,"click",(function(){return t.showForm(n.exportSeedAuth)})),zt(n.exportSeedAuth,n.exportSeedSubmit,(function(){return t.submitExportSeedReq()}));var r=function(){Ie.hide(n.forms),n.exportSeedPW.value="",n.seedDiv.textContent=""};return Ie.bind(n.forms,"mousedown",(function(e){Ie.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},Ie.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){Ie.bind(e,"click",(function(){r()}))})),t}return c(x,[{key:"getRegistrationTxFeeEstimate",value:(y=a(v().mark((function e(t,n){var r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getCertFile();case 2:return r=e.sent,o=at().loading(n),e.next=6,et("/api/regtxfee",{addr:this.currentDEX.host,cert:r,asset:t});case 6:if(a=e.sent,o(),at().checkResponse(a,!0)){e.next=10;break}return e.abrupt("return",0);case 10:return e.abrupt("return",a.txfee);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return y.apply(this,arguments)})},{key:"newWalletCreated",value:(m=a(v().mark((function e(t){var n,r,o,a,i,s;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:if(n=e.sent){e.next=5;break}return e.abrupt("return");case 5:if(r=this.page,o=n.assets[t],a=o.wallet,i=this.currentDEX.regFees[o.symbol].amount,!(a.synced&&a.balance.available>i)){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 s=e.sent,this.walletWaitForm.setWallet(a,s),this.currentForm=r.walletWait,e.next=20,Nt(r.newWalletForm,r.walletWait);case 20:case"end":return e.stop()}}),e,this)}))),function(e){return m.apply(this,arguments)})},{key:"onAccountFileChange",value:(p=a(v().mark((function e(){var t,n;return v().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,Ie.show(t.removeAccount),Ie.hide(t.addAccount);case 7:case"end":return e.stop()}}),e,this)}))),function(){return p.apply(this,arguments)})},{key:"clearAccountFile",value:function(){var e=this.page;e.accountFile.value="",e.selectedAccount.textContent="none selected",Ie.hide(e.removeAccount),Ie.show(e.addAccount)}},{key:"prepareAccountImport",value:(f=a(v().mark((function e(t){return v().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 f.apply(this,arguments)})},{key:"importAccount",value:(d=a(v().mark((function e(){var t,n,r,o,a,i,s,c,u;return v().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((o=t.accountFile.files)&&o.length){e.next=9;break}return console.error("importAccount: no file specified"),e.abrupt("return");case 9:return e.next=11,o[0].text();case 11:r=e.sent;case 12:e.prev=12,a=JSON.parse(r),e.next=21;break;case 16:return e.prev=16,e.t0=e.catch(12),t.importAccountErr.textContent=e.t0.message,Ie.show(t.importAccountErr),e.abrupt("return");case 21:if(void 0!==a){e.next=25;break}return t.importAccountErr.textContent=ge(_),Ie.show(t.importAccountErr),e.abrupt("return");case 25:return i={pw:n,account:a},s=at().loading(this.body),e.next=29,et("/api/importaccount",i);case 29:if(c=e.sent,s(),at().checkResponse(c)){e.next=35;break}return t.importAccountErr.textContent=c.msg,Ie.show(t.importAccountErr),e.abrupt("return");case 35:return e.next=37,et("/api/login",{pass:n});case 37:if(u=e.sent,at().checkResponse(u)){e.next=42;break}return t.importAccountErr.textContent=u.msg,Ie.show(t.importAccountErr),e.abrupt("return");case 42:return e.next=44,at().fetchUser();case 44:Ie.hide(t.forms),window.location.reload();case 46:case"end":return e.stop()}}),e,this,[[12,16]])}))),function(){return d.apply(this,arguments)})},{key:"submitExportSeedReq",value:(h=a(v().mark((function e(){var t,n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,n=t.exportSeedPW.value,r=at().loading(this.body),e.next=5,et("/api/exportseed",{pass:n});case 5:if(o=e.sent,r(),at().checkResponse(o)){e.next=11;break}return t.exportAccountErr.textContent=o.msg,Ie.show(t.exportSeedE),e.abrupt("return");case 11:t.exportSeedPW.value="",t.seedDiv.textContent=o.seed,this.showForm(t.authorizeSeedDisplay);case 14:case"end":return e.stop()}}),e,this)}))),function(){return h.apply(this,arguments)})},{key:"showForm",value:(l=a(v().mark((function e(t){var n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return Ie.hide(e)})),t.style.right="10000px",Ie.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,Ie.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 l.apply(this,arguments)})},{key:"getCertFile",value:(s=a(v().mark((function e(){var t,n;return v().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 s.apply(this,arguments)})},{key:"registerDEXSuccess",value:(o=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,Ie.hide(t.forms),e.next=4,at().fetchUser();case 4:window.location.reload();case 5:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"changeAppPW",value:(r=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.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=ge(g),Ie.show(t.changePWErrMsg),n(),e.abrupt("return");case 8:if(t.newAppPW.value===t.confirmNewPW.value){e.next=13;break}return t.changePWErrMsg.textContent=ge(j),Ie.show(t.changePWErrMsg),n(),e.abrupt("return");case 13:return r=at().loading(t.changeAppPW),o={appPW:t.appPW.value,newAppPW:t.newAppPW.value},n(),e.next=18,et("/api/changeapppass",o);case 18:if(a=e.sent,r(),at().checkResponse(a,!0)){e.next=24;break}return t.changePWErrMsg.textContent=a.msg,Ie.show(t.changePWErrMsg),e.abrupt("return");case 24:Ie.hide(t.forms);case 25:case"end":return e.stop()}}),e,this)}))),function(){return r.apply(this,arguments)})},{key:"unload",value:function(){Ie.unbind(document,"keyup",this.keyup)}},{key:"animateRegAsset",value:(n=a(v().mark((function e(t){var n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:Ie.hide(t),n=this.page.regAssetForm,this.currentForm=n,this.regAssetForm.animate(),Ie.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return n.apply(this,arguments)})},{key:"animateConfirmForm",value:(t=a(v().mark((function e(t){var n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.confirmRegisterForm.animate(),n=this.page.confirmRegForm,this.currentForm=n,Ie.hide(t),Ie.show(n);case 5:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})}]),x}(Je);function Zt(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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(e);try{for(r.s();!(t=r.n()).done;){var o=t.value;if(!o.epoch)return o;n||(n=o)}}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 en(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 tn(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)?tn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function tn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=c&&s=s&&i2?2:t),this.draw(),this.reporters.zoom(this.zoomLevel))}},{key:"clicked",value:function(e){if(this.dataExtents){var t=e.clientX-this.rect.left,n=e.clientY-this.rect.y;if(this.zoomInBttn.contains(t,n))this.zoom(!0);else if(this.zoomOutBttn.contains(t,n))this.zoom(!1);else{var r=this.plotRegion.translator(this.dataExtents);this.reporters.click(r.unx(t))}}}},{key:"clear",value:function(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}},{key:"set",value:function(e,t,n,o,a){this.book=e,this.lotSize=t/o.conventional.conversionFactor;var i=[a.conventional.conversionFactor,o.conventional.conversionFactor],s=i[0],c=i[1];if(this.rateStep=n/ht*s/c,this.baseUnit=o.conventional.unit,this.quoteUnit=a.conventional.unit,!this.zoomLevel){var u=r(this.gap(),2),l=u[0],h=u[1],d=Math.max(h/l*5,.05);this.zoomLevel=Math.min(d,2)}this.draw()}},{key:"render",value:function(){var e=this;if(this.book&&this.visible){this.clear();var t=this.ctx,n=this.mousePos,o=this.book.buys,a=this.book.sells,i=r(this.gap(),2),s=i[0],c=i[1],u=this.zoomLevel*s/2,l=s+u,h=s-u,d=ot(this.markers.buys),f=ot(this.markers.sells);d.sort((function(e,t){return t.rate-e.rate})),f.sort((function(e,t){return e.rate-t.rate}));for(var p=[],m=[],v=[],y=[],g=[],w={buyBase:0,buyQuote:0,sellBase:0,sellQuote:0},k=0,b=0,x=0;x=h&&v.push([C.rate,b]),!C.epoch)for(k+=C.qty,m.push([C.rate,k]),w.buyBase+=C.qty,w.buyQuote+=C.qty*C.rate;d.length&&xn(d[0].rate,C.rate);){var S=d.shift();S&&p.push({rate:S.rate,qty:C.epoch?b:k,sell:C.sell,active:S.active})}}var F=m.length?gn(m)[1]:0;m.push([h,F]);var E=v.length?gn(v)[1]:0;v.push([h,E]),b=k=0;for(var A=0;Al||e=L},N=e.theme.sellLine;Lthis.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){var n=t.ms,r=this.mousePos,o=t.candles||[],a=Math.min(this.numToShow,o.length),i=o.slice(o.length-a);if(this.clear(),0!==a){var s,c=function(e){return Sn(e.endStamp,n)},u=function(e){return c(e)+n},l=function(e){return c(e)+.2*n},h=.6*n,d=i[0],f=i[a-1],p=[d.highRate,d.lowRate,d.matchVolume],m=p[0],v=p[1],y=p[2],g=en(i);try{for(g.s();!(s=g.n()).done;){var w=s.value;w.highRate>m&&(m=w.highRate),w.lowRatey&&(y=w.matchVolume)}}catch(e){g.e(e)}finally{g.f()}var k=this.market.ratestep,b=new pn(c(d),u(f),v,m);v===m&&(b.y.min-=k,b.y.max+=k),this.dataExtents=b;var x=this.rateConversionFactor;this.doYLabels(this.candleRegion,k,this.market.quotesymbol,(function(e){return bn(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 o=e[0],a=e[e.length-1],i=Sn(o.endStamp,t),s=Sn(a.endStamp,t)+t,c=s-i,u=Math.min(e.length,n/100),l=Sn(c/u,t);if(0===l)return console.error("zero tick",t,c,u),{lbls:[]};var h=i,d=(new Date).getTimezoneOffset(),f=function(e){return(e-=6e4*d)-e%864e5},p=f(i),m=0;f(o.endStamp)===f(a.endStamp)&&(p=0);var v,y=[];for(v=t<864e5?function(e,t){return f(t)!==p?"".concat(yn[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(yn[e.getMonth()]).concat(e.getDate()," '").concat(String(t).slice(2,4)):"".concat(yn[e.getMonth()]).concat(e.getDate())};h<=s;){var g=new Date(h);y.push({val:h,txt:v(g,h)}),p=f(h),m=g.getFullYear(),h+=l}return{lbls:y}}(i,n,this.plotRegion.width());this.plotXLabels(C,c(d),u(f),[]),this.drawFrame();var S=null;if(r&&(this.plotRegion.plot(new pn(b.x.min,b.x.max,0,1),(function(t,o){var a,s=Sn(o.unx(r.x),n),u=en(i);try{for(u.s();!(a=u.n()).done;){var l=a.value;if(c(l)===s){S=l,t.fillStyle=e.theme.gridLines,t.fillRect(o.x(c(l)),o.y(0),o.w(n),o.h(1));break}}}catch(e){u.e(e)}finally{u.f()}})),S)){var F=this.xRegion.extents.y;this.xRegion.plot(new pn(b.x.min,b.x.max,F.min,F.max),(function(t,n){if(S){e.applyLabelStyle();var r="".concat(new Date(c(S)).toLocaleString()," - ").concat(new Date(u(S)).toLocaleString()),o=t.measureText(r).width+50,a=n.x((c(S)+u(S))/2),i=a-o/2,s=e.xRegion.extents.x;is.max&&(i=s.max-o),a=i+o/2;var l=F.min+(e.xRegion.height()-16)/2;t.fillStyle=e.theme.legendFill,t.strokeStyle=e.theme.gridBorder;var h=[i-25,l-2,o+50,20];t.fillRect.apply(t,h),t.strokeRect.apply(t,h),e.applyLabelStyle(),t.fillText(r,a,e.xRegion.extents.midY,o)}}))}var E=new pn(c(d),u(f),0,y);this.volumeRegion.plot(E,(function(t,n){t.fillStyle=e.theme.gridBorder;var r,o=en(i);try{for(o.s();!(r=o.n()).done;){var a=r.value;t.fillRect(n.x(l(a)),n.y(0),n.w(h),n.h(a.matchVolume))}}catch(e){o.e(e)}finally{o.f()}})),this.candleRegion.plot(b,(function(t,n){t.lineWidth=1;var r,o=en(i);try{for(o.s();!(r=o.n()).done;){var a=r.value,s=a.startRate>a.endRate,c=[n.x(l(a)),n.y(a.startRate),n.w(h),n.h(a.endRate-a.startRate)],u=c[0],d=c[1],f=c[2],p=c[3],m=[n.y(a.highRate),n.y(a.lowRate),f/2+u],v=m[0],y=m[1],g=m[2];t.strokeStyle=s?e.theme.sellLine:e.theme.buyLine,t.fillStyle=s?e.theme.sellFill:e.theme.buyFill,t.beginPath(),t.moveTo(g,v),t.lineTo(g,y),t.stroke(),t.fillRect(u,d,f,p),t.strokeRect(u,d,f,p)}}catch(e){o.e(e)}finally{o.f()}})),this.reporters.mouse(S)}}}},{key:"setCandles",value:function(e,t,n,r){if(this.data=e,e.candles){this.market=t;var o=[r.conventional.conversionFactor,n.conventional.conversionFactor],a=o[0],i=o[1];this.rateConversionFactor=ht*i/a;var s=25;this.zoomLevels=[];for(var c=Math.max(e.candles.length,1e3);sn.x.min&&tn.y.min}},{key:"translator",value:function(e){var t=this.extents,n=e.x.min,r=e.y.min,o=e.yRange,a=e.xRange,i=t.x.min,s=t.x.max-i,c=t.y.max,u=c-t.y.min,l=s/a,h=u/o;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)*l+i})),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-i)/l+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/a*s})),h:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return-e/o*u})),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,o=this.extents;r.save(),n||(r.beginPath(),r.rect(o.x.min,o.y.min,o.xRange,o.yRange),r.clip());var a=this.translator(e),i=e.yRange,s=o.xRange/e.xRange,c=o.yRange/i,u=e.x.min,l=e.y.min,h=o.x.min+u-u*s,d=-o.y.min-(i-l)*c;a.dataCoords=function(e){r.save(),r.transform(1,0,0,-1,-u,l),r.transform(s,0,0,c,h,d),e(),r.restore()},t(this.context,a),r.restore()}}]),e}();function vn(e,t,n,r,o,a,i,s){s=s||bn;var c=t/o,u=r-n;if(c<1||u<=0)return{lbls:[]};for(var l=u/c,h=l+a-l%a,d=n+h-n%h,f=Math.max(Math.abs(r),Math.abs(n)),p=Math.round(Math.log10(f/h))+2,m=[],v=0;dv&&(v=g),{widest:v,lbls:m}}var yn=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function gn(e){return e[e.length-1]}function wn(e,t,n,r,o,a){e.beginPath(),e.moveTo(t,n),e.lineTo(r,o),a||e.stroke()}var kn={minimumSignificantDigits:4,maximumSignificantDigits:5};function bn(e){return e.toLocaleString("en-us",kn)}function xn(e,t){return Cn(e,t,1e-8)}function Cn(e,t,n){return Math.abs(e-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 o=0;!function a(){window.log("ws","connecting to ".concat(e));var i=n.connection=new window.WebSocket(e);if(i){var s=setTimeout((function(){i&&i.close()}),500);i.onmessage=function(e){var t=JSON.parse(e.data);En(t.route,t.payload,n.handlers)},i.onclose=function(e){window.log("ws","onclose"),clearTimeout(s),i=n.connection=null,En("close",null,n.handlers),o++;var t=Math.min(Math.pow(1.25,o),10);console.error("websocket disconnected (".concat(e.code,"), trying again in ").concat(t.toFixed(1)," seconds")),setTimeout((function(){a()}),1e3*t)},i.onopen=function(){window.log("ws","onopen"),clearTimeout(s),o>0&&(o=0,t()),En("open",null,n.handlers);var e=n.queue;n.queue=[];var a,i=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 Fn(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)?Fn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}(e);try{for(i.s();!(a=i.n()).done;){var c=r(a.value,2),u=c[0],l=c[1];n.request(u,l)}}catch(e){i.e(e)}finally{i.f()}},i.onerror=function(e){window.log("ws","onerror:",e),En("error",e,n.handlers)}}}()}}]),e}());function In(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 On(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:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){s=!0,a=e})),f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Tn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n.7?e=.7:e<.25&&(e=.25);var t=e*(n.main.clientHeight-at().header.offsetHeight);r.marketChart.style.height="".concat(t,"px"),n.depthChart.resize(t),n.candleChart.resize(t)},C=je.fetch(jn);return C&&x(C),Ln(r.chartResizer,"mousedown",(function(e){if(0===e.button){var t;e.preventDefault();var n=function(e){e.preventDefault();var n=r.rightSide.getBoundingClientRect(),o=n.bottom-n.top;t=(e.pageY-n.top)/o,x(t)};Ln(document,"mousemove",n),Ln(document,"mouseup",(function(){t&&je.store(jn,t),Ie.unbind(document,"mousemove",n)}))}})),at().registerNoteFeeder({order:function(e){n.handleOrderNote(e)},epoch:function(e){n.handleEpochNote(e)},conn:function(e){n.handleConnNote(e)},balance:function(e){n.handleBalanceNote(e)},feepayment:function(e){n.handleFeePayment(e)},spots:function(e){n.handlePriceUpdate(e)}}),(b=t&&t.host&&void 0!==t.base&&void 0!==t.quote?$n(t.host,parseInt(t.base),parseInt(t.quote)):je.fetch(zn))&&n.marketList.exists(b.host,b.base,b.quote)||(b=n.marketList.first()),n.setMarket(b.host,b.base,b.quote),n.secondTicker=window.setInterval((function(){for(var e=0,t=Object.values(n.metaOrders);e-1&&(i.classList.add("hover"),this.hovers.push(i))}t.hoverPrice.textContent=Ie.formatCoinValue(e.rate),t.hoverVolume.textContent=Ie.formatCoinValue(e.depth),t.hoverVolume.style.color=e.dotColor,Ie.show(t.hoverData)}else Ie.hide(t.hoverData)}},{key:"reportDepthZoom",value:function(e){je.store(_n,e)}},{key:"reportMouseCandle",value:function(e){var t=this.page;e?(t.candleStart.textContent=Ie.formatCoinValue(e.startRate/this.market.rateConversionFactor),t.candleEnd.textContent=Ie.formatCoinValue(e.endRate/this.market.rateConversionFactor),t.candleHigh.textContent=Ie.formatCoinValue(e.highRate/this.market.rateConversionFactor),t.candleLow.textContent=Ie.formatCoinValue(e.lowRate/this.market.rateConversionFactor),t.candleVol.textContent=Ie.formatCoinValue(e.matchVolume,this.market.baseUnitInfo),Ie.show(t.hoverData)):Ie.hide(t.hoverData)}},{key:"parseOrder",value:function(){var e=this.page,t=e.qtyField,n=this.isLimit(),r=this.isSell(),o=this.market;return n||r||(t=e.mktBuyField),{host:o.dex.host,isLimit:n,sell:r,base:o.base.id,quote:o.quote.id,qty:tr(t.value||"",o.baseUnitInfo.conventional.conversionFactor),rate:tr(e.rateField.value||"",o.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 o=at().assets[n.quote],a=n.qty*n.rate/ht,i=Ie.formatCoinValue(a,this.market.quoteUnitInfo);t.orderPreview.textContent=ge(T,{total:i,asset:o.symbol.toUpperCase()}),this.isSell()?this.preSell():this.preBuy()}}},{key:"preSell",value:function(){var e=this,t=this.market,n=at().assets[t.base.id].wallet;n.balance.available0&&this.book.add(u),this.addTableOrder(u)}}catch(e){c.e(e)}finally{c.f()}if(!this.book)return this.depthChart.clear(),Ie.empty(this.page.buyRows),void Ie.empty(this.page.sellRows);this.depthChart.set(this.book,n.lotsize,n.ratestep,r,o)}},{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/ht:e.buys[0].msgRate/ht:e.sells&&e.sells.length?e.sells[0].msgRate/ht:null}},{key:"setMarketBuyOrderEstimate",value:function(){var e=this.market,t=e.cfg.lotsize,n=at().user.exchanges[e.dex.host].markets[e.sid].buybuffer,r=this.midGapConventional();r&&(this.page.minMktBuy.textContent=Ie.formatCoinValue(t*n*r,e.baseUnitInfo))}},{key:"ordersSortCompare",value:function(){var e=this;switch(this.ordersSortKey){case"submitTime":return function(t,n){return e.ordersSortDirection*(n.submitTime-t.submitTime)};case"rate":return function(t,n){return e.ordersSortDirection*(t.rate-n.rate)};case"qty":return function(t,n){return e.ordersSortDirection*(t.qty-n.qty)};case"type":return function(t,n){return e.ordersSortDirection*ft(t).localeCompare(ft(n))};case"sell":return function(t,n){return e.ordersSortDirection*dt(t).localeCompare(dt(n))};case"status":return function(t,n){return e.ordersSortDirection*vt(t).localeCompare(vt(n))};case"settled":return function(t,n){return e.ordersSortDirection*(100*gt(t)/t.qty-100*gt(n)/n.qty)};case"filled":return function(t,n){return e.ordersSortDirection*(100*yt(t)/t.qty-100*yt(n)/n.qty)}}}},{key:"refreshActiveOrders",value:function(){var e=this,t=this.page,n=this.metaOrders,r=this.market;for(var o in n)delete n[o];var a=at().orders(r.dex.host,er(r.baseCfg.symbol,r.quoteCfg.symbol)),i=this.ordersSortCompare();a.sort(i),Ie.empty(t.liveList);var s,c=Dn(a);try{var u=function(){var r=s.value,o=t.liveTemplate.cloneNode(!0);if(n[r.id]={row:o,order:r},Ie.bind(o,"mouseenter",(function(){e.activeMarkerRate=r.rate,e.setDepthMarkers()})),e.updateUserOrderRow(o,r),1===r.type&&1===r.tif&&r.status<3){var a=Ie.tmplElement(o,"cancelBttn");Ie.show(a),Ln(a,"click",(function(t){t.stopPropagation(),e.showCancel(o,r.id)}))}var i=Ie.tmplElement(o,"accelerateBttn");Ln(i,"click",(function(t){t.stopPropagation(),e.showAccelerate(r)})),at().canAccelerateOrder(r)&&Ie.show(i),Ie.tmplElement(o,"side").classList.add(r.sell?"sellcolor":"buycolor"),Ie.tmplElement(o,"link").href="order/".concat(r.id),at().bindInternalNavigation(o),t.liveList.appendChild(o),at().bindTooltips(o)};for(c.s();!(s=c.n()).done;)u()}catch(e){c.e(e)}finally{c.f()}this.setDepthMarkers()}},{key:"updateUserOrderRow",value:function(e,t){rr(e,"type",ft(t)),rr(e,"side",dt(t)),rr(e,"age",Ie.timeSince(t.submitTime)),rr(e,"rate",Ie.formatCoinValue(t.rate/this.market.rateConversionFactor)),rr(e,"qty",Ie.formatCoinValue(t.qty,this.market.baseUnitInfo)),rr(e,"filled","".concat((yt(t)/t.qty*100).toFixed(1),"%")),rr(e,"settled","".concat((gt(t)/t.qty*100).toFixed(1),"%")),rr(e,"status",vt(t))}},{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(at().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(at().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(at().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),this.depthChart.hide(),this.candleChart.show(),e.host===this.market.dex.host){var t=e.payload.dur;this.market.candleCaches[t]=e.payload,this.currentChart===Hn&&this.candleDur===t&&this.candleChart.setCandles(e.payload,this.market.cfg,this.market.baseUnitInfo,this.market.quoteUnitInfo)}}},{key:"handleCandleUpdateRoute",value:function(e){if(e.host===this.market.dex.host){var t=e.payload,n=t.dur,r=t.candle,o=this.market.candleCaches[n];if(o){var a=o.candles;0===a.length?a.push(r):a[a.length-1].startStamp===r.startStamp?a[a.length-1]=r:a.push(r),this.currentChart===Hn&&this.candleDur===n&&this.candleChart.draw()}}}},{key:"showForm",value:(b=a(v().mark((function e(t){var n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.currentForm=t,n=this.page,Ie.hide(n.unlockWalletForm,n.verifyForm,n.newWalletForm,n.cancelForm,n.vDetailPane,n.accelerateForm),t.style.right="10000px",Ie.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,Ie.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 b.apply(this,arguments)})},{key:"showOpen",value:(g=a(v().mark((function e(t,n){var r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:r=this.page,this.openAsset=t,this.openFunc=n,this.unlockForm.refresh(at().assets[t.id]),this.showForm(r.unlockWalletForm),r.uwAppPass.focus();case 6:case"end":return e.stop()}}),e,this)}))),function(e,t){return g.apply(this,arguments)})},{key:"showVerify",value:function(){this.preorderCache={};var e,t=this.page,n=this.currentOrder=this.parseOrder(),r=n.sell,o=at().assets[n.base],a=at().assets[n.quote],i=r?a:o,s=r?o:a,c=Dn(Ie.applySelector(t.vDetailPane,"[data-icon]"));try{for(c.s();!(e=c.n()).done;){var u=e.value;switch(u.dataset.icon){case"from":u.src=Ie.logoPath(s.symbol);break;case"to":u.src=Ie.logoPath(i.symbol)}}}catch(e){c.e(e)}finally{c.f()}Ie.hide(t.vUnlockPreorder,t.vPreorderErr),Ie.show(t.vPreorder),t.vBuySell.textContent=r?"Selling":"Buying";var l=ge(r?I:R);if(t.vSideSubmit.textContent=l,t.vOrderHost.textContent=n.host,n.isLimit){Ie.show(t.verifyLimit),Ie.hide(t.verifyMarket);var h="Limit ".concat(l," Order");t.vOrderType.textContent=n.tifnow?h+" (immediate)":h,t.vRate.textContent=Ie.formatCoinValue(n.rate/this.market.rateConversionFactor),t.vQty.textContent=Ie.formatCoinValue(n.qty,o.info.unitinfo);var d=n.rate/ht*n.qty;t.vTotal.textContent=Ie.formatCoinValue(d,a.info.unitinfo),this.showFiatValue(a.id,d,t.vFiatTotal)}else{Ie.hide(t.verifyLimit),Ie.show(t.verifyMarket),t.vOrderType.textContent="Market ".concat(l," Order");var f=n.sell?this.market.baseUnitInfo:this.market.quoteUnitInfo;t.vmFromTotal.textContent=Ie.formatCoinValue(n.qty,f),t.vmFromAsset.textContent=s.symbol.toUpperCase(),this.showFiatValue(s.id,n.qty,t.vmFromTotalFiat);var p=this.midGap();if(p){Ie.show(t.vMarketEstimate);var m=n.sell?n.qty*p:n.qty/p;t.vmToTotal.textContent=Ie.formatCoinValue(m,i.info.unitinfo),t.vmToAsset.textContent=i.symbol.toUpperCase(),this.showFiatValue(i.id,m,t.vmTotalFiat)}else Ie.hide(t.vMarketEstimate)}var v="buygreen",y="sellred";r?(t.vHeader.classList.add(y),t.vHeader.classList.remove(v),t.vSubmit.classList.add(y),t.vSubmit.classList.remove(v)):(t.vHeader.classList.add(v),t.vHeader.classList.remove(y),t.vSubmit.classList.add(v),t.vSubmit.classList.remove(y)),this.showVerifyForm(),t.vPass.focus(),o.wallet.open&&a.wallet.open?this.preOrder(n):(Ie.hide(t.vPreorder),je.passwordIsCached()?this.unlockWalletsForEstimates(""):Ie.show(t.vUnlockPreorder))}},{key:"showFiatValue",value:function(e,t,n){if(n){var r=at().fiatRatesMap[e];n.textContent=Ie.formatFiatConversion(t,r,at().unitInfo(e)),r?Ie.show(n.parentElement):Ie.hide(n.parentElement)}}},{key:"showVerifyForm",value:(y=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,Ie.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:(m=a(v().mark((function e(){var t;return v().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 m.apply(this,arguments)})},{key:"unlockWalletsForEstimates",value:(p=a(v().mark((function e(t){var n,r,o;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,r=at().loading(n.verifyForm),e.next=4,this.attemptWalletUnlock(t);case 4:if(o=e.sent,r(),!o){e.next=8;break}return e.abrupt("return",this.setPreorderErr(o));case 8:Ie.show(n.vPreorder),Ie.hide(n.vUnlockPreorder),this.preOrder(this.parseOrder());case 11:case"end":return e.stop()}}),e,this)}))),function(e){return p.apply(this,arguments)})},{key:"attemptWalletUnlock",value:(f=a(v().mark((function e(t){var n,r,o,a,i,s,c,u,l;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=this.market,r=n.base,o=n.quote,a=[],r.wallet.open||a.push(r.id),o.wallet.open||a.push(o.id),i={pass:t,assetID:-1},s=0,c=a;case 6:if(!(s2)&&this.setDepthMarkers()}},{key:"handleEpochNote",value:function(e){if(at().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);to.epoch,i=Ie.tmplElement(r.row,"status");switch(!0){case 1===o.type&&1===o.status&&a:i.textContent=0===o.tif?ge(N):ge(U),o.status=0===o.tif?3:2;break;case 2===o.type&&1===o.status:i.textContent=ge(N),o.status=3}}}}},{key:"setBalanceVisibility",value:function(){this.market.dex.connectionStatus===Ye.Connected?Ie.show(this.page.balanceTable):Ie.hide(this.page.balanceTable)}},{key:"handleBalanceNote",value:function(e){if(this.setBalanceVisibility(),this.market.dex.connectionStatus===Ye.Connected){var t=this.market,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:(s=a(v().mark((function e(){var t,n,r,o,a;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.orderErr,t.vErr),n=this.currentOrder,r=t.vPass.value,t.vPass.value="",o={order:or(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,et("/api/trade",o);case 12:if(a=e.sent,t.vSubmit.classList.remove("d-hide"),t.vLoader.classList.add("d-hide"),at().checkResponse(a,!0)){e.next=19;break}return t.vErr.textContent=a.msg,Ie.show(t.vErr),e.abrupt("return");case 19:Ie.hide(t.forms),this.refreshActiveOrders(),this.depthChart.draw();case 22:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"createWallet",value:(o=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,at().fetchUser();case 2:if(t=e.sent){e.next=5;break}return e.abrupt("return");case 5:n=t.assets[this.currentCreate.id],Ie.hide(this.page.forms),this.balanceWgt.updateAsset(n.id),this.resolveOrderFormVisibility();case 9:case"end":return e.stop()}}),e,this)}))),function(){return o.apply(this,arguments)})},{key:"walletUnlocked",value:(n=a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:Ie.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,o=Math.floor(n.qty/r),a=o*r;t.lotField.value=String(o),(n.isLimit||n.sell)&&(e&&(t.qtyField.value=String(a/this.market.baseUnitInfo.conventional.conversionFactor)),this.previewQuoteAmt(!0))}},{key:"marketBuyChanged",value:function(){var e=this.page,t=tr(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,o=t/n;e.mktBuyLots.textContent=(o/r).toFixed(1),e.mktBuyScore.textContent=Ie.formatCoinValue(o,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=tr(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=[],o=e[0].msgRate;e[0].epoch?n.push(e[0]):r.push(e[0]);for(var a=1;a0}))}},{key:"loadTableSide",value:function(e){var t=this,n=e?this.book.sells:this.book.buys,r=e?this.page.sellRows:this.page.buyRows;Ie.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 o=this.orderTableRow([e]);t.appendChild(o)}else 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];n0?"+":"";t.pctChange.textContent="".concat(a).concat(o,"%"),t.pctChange.classList.remove("upgreen","downred","grey"),t.pctChange.classList.add(0===r?"grey":r>0?"upgreen":"downred");var i=at().assets[n.baseid];i&&(Ie.show(t.bottomRow),t.assetName.textContent=i.info.name,t.price.textContent=Ie.formatCoinValue(e.rate/this.rateConversionFactor))}}}]),e}(),Zn=function(){function e(t){var n=this;i(this,e),u(this,"base",void 0),u(this,"quote",void 0),u(this,"dex",void 0);var r=Ie.idDescendants(t);this.base={id:0,cfg:null,logo:r.baseImg,avail:r.baseAvail,newWalletRow:r.baseNewWalletRow,newWalletBttn:r.baseNewButton,locked:r.baseLocked,immature:r.baseImmature,unsupported:r.baseUnsupported,expired:r.baseExpired,connect:r.baseConnect,spinner:r.baseSpinner,iconBox:r.baseWalletState,stateIcons:new De(r.baseWalletState)},this.quote={id:0,cfg:null,logo:r.quoteImg,avail:r.quoteAvail,newWalletRow:r.quoteNewWalletRow,newWalletBttn:r.quoteNewButton,locked:r.quoteLocked,immature:r.quoteImmature,unsupported:r.quoteUnsupported,expired:r.quoteExpired,connect:r.quoteConnect,spinner:r.quoteSpinner,iconBox:r.quoteWalletState,stateIcons:new De(r.quoteWalletState)},at().registerNoteFeeder({balance:function(e){n.updateAsset(e.assetID)},walletstate:function(e){n.updateAsset(e.wallet.assetID)}})}return c(e,[{key:"setWallets",value:function(e,t,n){this.dex=at().user.exchanges[e],this.base.id=t,this.base.cfg=this.dex.assets[t],this.quote.id=n,this.quote.cfg=this.dex.assets[n],this.updateWallet(this.base),this.updateWallet(this.quote)}},{key:"updateWallet",value:function(e){if(e.cfg){var t=at().assets[e.id];if(Ie.hide(e.newWalletRow,e.avail,e.immature,e.locked,e.expired,e.unsupported,e.connect,e.spinner,e.iconBox),e.logo.src=Ie.logoPath(e.cfg.symbol),t){Ie.show(e.iconBox);var n=t.wallet;if(e.stateIcons.readWallet(n),n){var r=n.balance;if(r||n.running){if(!r)return at().fetchBalance(e.id),void Ie.show(e.spinner);Ie.show(e.avail,e.immature,e.locked),e.avail.textContent=Ie.formatCoinValue(r.available,t.info.unitinfo),e.locked.textContent=Ie.formatCoinValue(r.locked+r.contractlocked,t.info.unitinfo),e.immature.textContent=Ie.formatCoinValue(r.immature,t.info.unitinfo),(new Date).getTime()-new Date(r.stamp).getTime()>36e5?(Ie.show(e.expired),n.running&&at().fetchBalance(e.id)):Ie.hide(e.expired)}else Ie.show(e.connect)}else Ie.show(e.newWalletRow)}else Ie.show(e.unsupported)}}},{key:"updateAsset",value:function(e){e===this.base.id?this.updateWallet(this.base):e===this.quote.id&&this.updateWallet(this.quote)}}]),e}();function $n(e,t,n){return{host:e,base:t,quote:n}}function er(e,t){return"".concat(e,"_").concat(t)}function tr(e,t){return e?Math.round(parseFloat(e)*t):0}function nr(e,t){e.classList.remove("selected"),t.classList.add("selected")}function rr(e,t,n){Ie.tmplElement(e,t).textContent=n}function or(e){for(var t={},n=0,o=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,o=0;oe.msgRate===e.sell?1:-1:this.isEpoch()?1:-1}}]),e}();function ir(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 sr(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)?sr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function sr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0&&(e.checked=!0)}))}};a(n.hostFilter,"hosts"),a(n.assetFilter,"assets"),a(n.statusFilter,"statuses");var s=[],c=function(e,n){var o=e.querySelector(".apply-bttn");s.push(o),Ie.bind(o,"click",(function(){t.submitFilter(),s.forEach((function(e){return Ie.hide(e)}))})),e.querySelectorAll("input").forEach((function(t){Ie.bind(t,"change",(function(){!function(e,t){if(e.length!==t.length)return!1;var n,r=ir(e);try{for(r.s();!(n=r.n()).done;){var o=n.value;if(-1===t.indexOf(o))return!1}}catch(e){r.e(e)}finally{r.f()}return!0}(mr(e),r[n])?Ie.show(o):Ie.hide(o)}))}))};return c(n.hostFilter,"hosts"),c(n.assetFilter,"assets"),c(n.statusFilter,"statuses"),Ie.bind(t.main,"scroll",(function(){t.loading||n.ordersTable.offsetHeight-t.main.offsetHeight-t.main.scrollTop<0&&t.nextPage()})),Ie.bind(n.exportOrders,"click",(function(){t.exportOrders()})),t.submitFilter(),t}return c(h,[{key:"setOrders",value:function(e){Ie.empty(this.page.tableBody),this.appendOrders(e)}},{key:"appendOrders",value:function(e){var t,n=this,r=this.page.tableBody,o=ir(e);try{var a=function(){var e=t.value,o=n.orderTmpl.cloneNode(!0),a=function(e,t){Ie.tmplElement(o,e).textContent=t},i="".concat(e.baseSymbol.toUpperCase(),"-").concat(e.quoteSymbol.toUpperCase());a("host","".concat(i," @ ").concat(e.host));var s=void 0,c=void 0,u=void 0,l="",h=[at().unitInfo(e.baseID),at().unitInfo(e.quoteID)],d=h[0],f=h[1];if(e.sell){var p=[e.baseSymbol,e.quoteSymbol];s=p[0],c=p[1],u=Ie.formatCoinValue(e.qty,d),1===e.type&&(l=Ie.formatCoinValue(e.qty/ht*e.rate,f))}else{var m=[e.quoteSymbol,e.baseSymbol];s=m[0],c=m[1],2===e.type?u=Ie.formatCoinValue(e.qty,d):(u=Ie.formatCoinValue(e.qty/ht*e.rate,f),l=Ie.formatCoinValue(e.qty,d))}a("fromQty",u),Ie.tmplElement(o,"fromLogo").src=Ie.logoPath(s),a("fromSymbol",s),a("toQty",l),Ie.tmplElement(o,"toLogo").src=Ie.logoPath(c),a("toSymbol",c),a("type","".concat(ft(e)," ").concat(dt(e))),a("rate",Ie.formatCoinValue(at().conventionalRate(e.baseID,e.quoteID,e.rate))),a("status",vt(e)),a("filled","".concat((yt(e)/e.qty*100).toFixed(1),"%")),a("settled","".concat((gt(e)/e.qty*100).toFixed(1),"%"));var v=new Date(e.submitTime).toLocaleString();a("time","".concat(Ie.timeSince(e.submitTime)," ago, ").concat(v)),Ie.tmplElement(o,"link").href="order/".concat(e.id),at().bindInternalNavigation(o),r.appendChild(o)};for(o.s();!(t=o.n()).done;)a()}catch(e){o.e(e)}finally{o.f()}50===e.length?this.offset=e[e.length-1].id:this.offset=""}},{key:"submitFilter",value:(r=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.page,this.offset="",(n=this.filterState).hosts=mr(t.hostFilter),n.assets=mr(t.assetFilter).map((function(e){return parseInt(e)})),n.statuses=mr(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 r.apply(this,arguments)})},{key:"fetchOrders",value:(n=a(v().mark((function e(){var t,n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=at().loading(this.main),e.next=3,et("/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 n.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:"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=a(v().mark((function e(){var t;return v().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,Ie.show(this.page.orderLoader),e.next=6,this.fetchOrders();case 6:t=e.sent,this.loading=!1,Ie.hide(this.page.orderLoader),this.appendOrders(t);case 10:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})}]),h}(Je);function mr(e){var t=[];return e.querySelectorAll("input").forEach((function(e){e.checked&&t.push(e.value)})),t}function vr(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 yr(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)?yr(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function yr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&Ie.hide(r),t.status.textContent=vt(n);var o,a=vr(n.matches||[]);try{for(a.s();!(o=a.n()).done;){var i=o.value;this.processMatch(i)}}catch(e){a.e(e)}finally{a.f()}this.showAccelerationButton()}}},{key:"handleMatchNote",value:function(e){e.orderID===this.orderID&&this.processMatch(e.match)}},{key:"processMatch",value:function(e){var t,n=null,r=vr(Ie.applySelector(this.page.matchBox,".match-card"));try{for(r.s();!(t=r.n()).done;){var o=t.value;if(o.dataset.matchID===e.matchID){n=o;break}}}catch(e){r.e(e)}finally{r.f()}if(n){var a=function(e,t,r){if(n&&r){Ie.show(Ie.tmplElement(n,e));var o=Ie.tmplElement(n,t);o.textContent=r.stringID,o.dataset.explorerCoin=r.stringID,br(o)}};a("swap","swapCoin",e.swap),a("counterSwap","counterSwapCoin",e.counterSwap),a("redeem","redeemCoin",e.redeem),a("counterRedeem","counterRedeemCoin",e.counterRedeem),a("refund","refundCoin",e.refund);var i=Ie.tmplElement(n,"swapMsg"),s=Ie.tmplElement(n,"counterSwapMsg");!function(e){return 1===e.side&&1===e.status||0===e.side&&2===e.status}(e)?function(e){return 0===e.side&&1===e.status||1===e.side&&2===e.status}(e)?(i.textContent=kr(e.swap),Ie.hide(Ie.tmplElement(n,"counterSwapMsg")),Ie.show(i)):Ie.hide(i,s):(s.textContent=kr(e.counterSwap),Ie.hide(Ie.tmplElement(n,"swapMsg")),Ie.show(s)),Ie.tmplElement(n,"status").textContent=function(e){if(e.revoked)return e.active?"Revoked - Refund PENDING":e.refund?"Revoked - Refunded":e.redeem?"Revoked - Redeemed":"Revoked - Complete";switch(e.status){case 0:return"(0 / 4) Newly Matched";case 1:return"(1 / 4) First Swap Sent";case 2:return"(2 / 4) Second Swap Sent";case 3:return 0===e.side?"Match Complete":"(3 / 4) Maker Redeemed";case 4:return"Match Complete"}return"Unknown Match Status"}(e)}}}]),d}(Je);function kr(e){return e.confs?"".concat(e.confs.count," / ").concat(e.confs.required," confirmations"):""}function br(e){var t=xr[parseInt(e.dataset.explorerId||"")];if(t){var n=t[gr];n&&(e.classList.remove("plainlink"),e.classList.add("subtlelink"),e.href=n(e.dataset.explorerCoin||""))}}var xr={42:(cr={},u(cr,0,(function(e){var t=r(e.split(":"),2),n=t[0],o=t[1];return"https://explorer.dcrdata.org/tx/".concat(n,"/out/").concat(o)})),u(cr,1,(function(e){var t=r(e.split(":"),2),n=t[0],o=t[1];return"https://testnet.dcrdata.org/tx/".concat(n,"/out/").concat(o)})),cr),0:(ur={},u(ur,0,(function(e){return"https://mempool.space/tx/".concat(e.split(":")[0])})),u(ur,1,(function(e){return"https://mempool.space/testnet/tx/".concat(e.split(":")[0])})),ur),2:(lr={},u(lr,0,(function(e){return"https://ltc.bitaps.com/".concat(e.split(":")[0])})),u(lr,1,(function(e){return"https://sochain.com/tx/LTCTEST/".concat(e.split(":")[0])})),lr),60:(hr={},u(hr,0,(function(e){return 42===e.length?"https://etherscan.io/address/".concat(e):"https://etherscan.io/tx/".concat(e)})),u(hr,1,(function(e){return 42===e.length?"https://goerli.etherscan.io/address/".concat(e):"https://goerli.etherscan.io/tx/".concat(e)})),hr),3:(dr={},u(dr,0,(function(e){return"https://dogeblocks.com/tx/".concat(e.split(":")[0])})),u(dr,1,(function(e){return"https://blockexplorer.one/dogecoin/testnet/tx/".concat(e.split(":")[0])})),dr),145:(fr={},u(fr,0,(function(e){return"https://bch.loping.net/tx/".concat(e.split(":")[0])})),u(fr,1,(function(e){return"https://tbch4.loping.net/tx/".concat(e.split(":")[0])})),fr)};var Cr,Sr=function(e){He(m,e);var t,n,r,o,s,l,h,d,f,p=(d=m,f=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=Qe(d);if(f){var n=Qe(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return Xe(this,e)});function m(e){var t;i(this,m),u(_e(t=p.call(this)),"body",void 0),u(_e(t),"forms",void 0),u(_e(t),"currentForm",void 0),u(_e(t),"page",void 0),u(_e(t),"host",void 0),u(_e(t),"keyup",void 0),u(_e(t),"dexAddrForm",void 0),t.body=e,t.host=e.dataset.host?e.dataset.host:"";var n=t.page=Ie.idDescendants(e);t.forms=Ie.applySelector(n.forms,":scope > form"),Ie.bind(n.exportDexBtn,"click",(function(){return t.prepareAccountExport(n.authorizeAccountExportForm)})),Ie.bind(n.disableAcctBtn,"click",(function(){return t.prepareAccountDisable(n.disableAccountForm)})),Ie.bind(n.updateCertBtn,"click",(function(){return n.certFileInput.click()})),Ie.bind(n.updateHostBtn,"click",(function(){return t.prepareUpdateHost()})),Ie.bind(n.certFileInput,"change",(function(){return t.onCertFileChange()})),t.dexAddrForm=new Bt(n.dexAddrForm,function(){var e=a(v().mark((function e(t){return v().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),zt(n.authorizeAccountExportForm,n.authorizeExportAccountConfirm,(function(){return t.exportAccount()})),zt(n.disableAccountForm,n.disableAccountConfirm,(function(){return t.disableAccount()}));var r=function(){Ie.hide(n.forms)};return Ie.bind(n.forms,"mousedown",(function(e){Ie.mouseInElement(e,t.currentForm)||r()})),t.keyup=function(e){"Escape"===e.key&&r()},Ie.bind(document,"keyup",t.keyup),n.forms.querySelectorAll(".form-closer").forEach((function(e){Ie.bind(e,"click",(function(){r()}))})),at().registerNoteFeeder({conn:function(){t.setConnectionStatus()}}),t.setConnectionStatus(),t}return c(m,[{key:"showForm",value:(h=a(v().mark((function e(t){var n,r;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.page,this.currentForm=t,this.forms.forEach((function(e){return Ie.hide(e)})),t.style.right="10000px",Ie.show(n.forms,t),r=(n.forms.offsetWidth+t.offsetWidth)/2,e.next=8,Ie.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 h.apply(this,arguments)})},{key:"exportAccount",value:(l=a(v().mark((function e(){var t,n,r,o,a,i,s,c;return v().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="",o={pw:n,host:r},a=at().loading(this.body),e.next=8,et("/api/exportaccount",o);case 8:if(i=e.sent,a(),at().checkResponse(i)){e.next=14;break}return t.exportAccountErr.textContent=i.msg,Ie.show(t.exportAccountErr),e.abrupt("return");case 14:s=JSON.parse(JSON.stringify(i.account)),(c=document.createElement("a")).setAttribute("download","dcrAccount-"+r+".json"),c.setAttribute("href","data:text/json,"+JSON.stringify(s,null,2)),c.click(),Ie.hide(t.forms);case 20:case"end":return e.stop()}}),e,this)}))),function(){return l.apply(this,arguments)})},{key:"disableAccount",value:(s=a(v().mark((function e(){var t,n,r,o,a,i;return v().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="",o={pw:n,host:r},a=at().loading(this.body),e.next=8,et("/api/disableaccount",o);case 8:if(i=e.sent,a(),at().checkResponse(i,!0)){e.next=14;break}return t.disableAccountErr.textContent=i.msg,Ie.show(t.disableAccountErr),e.abrupt("return");case 14:Ie.hide(t.forms),window.location.assign("/settings");case 16:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"prepareAccountExport",value:(o=a(v().mark((function e(t){var n;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(n=this.page).exportAccountHost.textContent=this.host,n.exportAccountErr.textContent="",je.passwordIsCached()?this.exportAccount():this.showForm(t);case 4:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"prepareAccountDisable",value:(r=a(v().mark((function e(t){var n;return v().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 r.apply(this,arguments)})},{key:"prepareUpdateHost",value:(n=a(v().mark((function e(){var t;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.page,this.dexAddrForm.refresh(),this.showForm(t.dexAddrForm);case 3:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"onCertFileChange",value:(t=a(v().mark((function e(){var t,n,r,o,a,i;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.page,Ie.hide(t.errMsg),!(n=t.certFileInput.files)||!n.length){e.next=7;break}return e.next=6,n[0].text();case 6:r=e.sent;case 7:if(r){e.next=9;break}return e.abrupt("return");case 9:return o={host:this.host,cert:r},a=at().loading(this.body),e.next=13,et("/api/updatecert",o);case 13:i=e.sent,a(),at().checkResponse(i,!0)?(Ie.show(t.updateCertMsg),setTimeout((function(){Ie.hide(t.updateCertMsg)}),5e3)):(t.errMsg.textContent=i.msg,Ie.show(t.errMsg));case 16:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"setConnectionStatus",value:function(){var e=this.page,t=at().user.exchanges[this.host],n=function(t){t?(Ie.hide(e.disconnectedIcon),Ie.show(e.connectedIcon)):(Ie.show(e.disconnectedIcon),Ie.hide(e.connectedIcon))};if(t)switch(t.connectionStatus){case Ye.Connected:n(!0),e.connectionStatus.textContent="Connected";break;case Ye.Disconnected:n(!1),e.connectionStatus.textContent="Disconnected";break;case Ye.InvalidCert:n(!1),e.connectionStatus.textContent="Disconnected - Invalid Certificate"}}}]),m}(Je);function Fr(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 Er(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}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 a,i=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return i=e.done,e},e:function(e){s=!0,a=e},f:function(){try{i||null==n.return||n.return()}finally{if(s)throw a}}}}function Rr(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n-1:0),o=1;o-1,this.checkResponse(t,n)){e.next=6;break}return e.abrupt("return");case 6:o=t,this.seedGenTime=o.seedgentime,this.user=o,this.assets=o.assets,this.exchanges=o.exchanges,this.walletMap={},this.fiatRatesMap=o.fiatRates,a=Ar(Object.entries(o.assets));try{for(a.s();!(i=a.n()).done;)s=r(i.value,2),c=s[0],(u=s[1]).wallet&&(this.walletMap[c]=u.wallet)}catch(e){a.e(e)}finally{a.f()}return this.updateMenuItemsDisplay(),e.abrupt("return",o);case 17:case"end":return e.stop()}}),e,this)}))),function(){return s.apply(this,arguments)})},{key:"loadPage",value:(o=a(v().mark((function e(t,n,r){var o,a,i,s,c,u,l,h;return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.tooltip.style.left="-10000px",Ie.hide(this.page.noteBox,this.page.profileBox),o=new URL("/".concat(t),window.location.origin),a=Mr(t),e.next=6,window.fetch(o.toString());case 6:if((i=e.sent).ok){e.next=9;break}return e.abrupt("return",!1);case 9:return e.next=11,i.text();case 11:return s=e.sent,c=Ie.noderize(s),u=Ir(c,"main"),l=u.dataset.handler,r||(h=l===a?o.toString():"/".concat(l),window.history.pushState({page:t,data:n},"",h)),document.title=c.title,this.main.replaceWith(u),this.main=u,this.noteReceivers=[],this.attach(n),e.abrupt("return",!0);case 22:case"end":return e.stop()}}),e,this)}))),function(e,t,n){return o.apply(this,arguments)})},{key:"attach",value:function(e){var t=this.main.dataset.handler;if(t){this.attachCommon(this.main),this.loadedPage&&this.loadedPage.unload();var n=Pr[t];this.loadedPage=n?new n(this.main,e):null,this.bindTooltips(this.main)}else console.error("cannot attach to content with no specified handler")}},{key:"bindTooltips",value:function(e){var t=this;e.querySelectorAll("[data-tooltip]").forEach((function(e){Or(e,"mouseenter",(function(){t.tooltip.textContent=e.dataset.tooltip||"";var n=Ie.layoutMetrics(e),r=n.centerX-t.tooltip.offsetWidth/2;r<0&&(r=5),r+t.tooltip.offsetWidth>document.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")})),Or(e,"mouseleave",(function(){t.tooltip.style.left="-10000px"}))}))}},{key:"attachHeader",value:function(){var e=this;this.header=Ir(document.body,"header"),this.popupNotes=Ir(document.body,"popupNotes"),this.popupTmpl=Ie.tmplElement(this.popupNotes,"note"),this.popupTmpl?this.popupTmpl.remove():console.error("popupTmpl element not found"),this.tooltip=Ir(document.body,"tooltip");var t=this.page=Ie.idDescendants(this.header);t.noteTmpl.removeAttribute("id"),t.noteTmpl.remove(),t.pokeTmpl.removeAttribute("id"),t.pokeTmpl.remove(),t.loader.remove(),Ie.show(t.loader),Or(t.noteMenuEntry,"click",a(v().mark((function n(){var r,o,a;return v().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:Ie.hide(t.pokeList),Ie.show(t.noteList),e.ackNotes(),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),e.showDropdown(t.noteMenuEntry,t.noteBox),Ie.hide(t.noteIndicator),r=Ar(e.notes);try{for(r.s();!(o=r.n()).done;)(a=o.value).acked&&a.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)})))),Or(t.profileMenuEntry,"click",(function(){e.showDropdown(t.profileMenuEntry,t.profileBox)})),Or(t.innerNoteIcon,"click",(function(){Ie.hide(t.noteBox)})),Or(t.innerProfileIcon,"click",(function(){Ie.hide(t.profileBox)})),Or(t.profileSignout,"click",a(v().mark((function t(){return v().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)})))),Or(t.pokeCat,"click",(function(){e.setNoteTimes(t.pokeList),t.pokeCat.classList.add("active"),t.noteCat.classList.remove("active"),Ie.hide(t.noteList),Ie.show(t.pokeList),e.ackNotes()})),Or(t.noteCat,"click",(function(){e.setNoteTimes(t.noteList),t.noteCat.classList.add("active"),t.pokeCat.classList.remove("active"),Ie.hide(t.pokeList),Ie.show(t.noteList),e.ackNotes()}))}},{key:"showDropdown",value:function(e,t){var n=this,r=e.getBoundingClientRect();Ie.hide(this.page.noteBox,this.page.profileBox),Ie.show(t),t.style.right="".concat(window.innerWidth-r.left-r.width+11,"px"),t.style.top="".concat(r.top-9,"px"),Or(document,"click",(function e(r){Ie.mouseInElement(r,t)||(Ie.hide(t),Dr(document,"click",e),t===n.page.noteBox&&Ie.isDisplayed(n.page.noteList)&&n.ackNotes())}))}},{key:"ackNotes",value:function(){var e,t=[],n=Ar(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&&Rn.request("acknotes",t),Ie.hide(this.page.noteIndicator)}},{key:"setNoteTimes",value:function(e){var t,n=Ar(Array.from(e.children));try{for(n.s();!(t=n.n()).done;){var r=t.value;Ie.safeSelector(r,"span.note-time").textContent=Ie.timeSince(r.note.stamp)}}catch(e){n.e(e)}finally{n.f()}}},{key:"bindInternalNavigation",value:function(e){var t=this,n=new URL(window.location.href);e.querySelectorAll("a").forEach((function(e){if(e.href){var r=new URL(e.href);if(r.origin===n.origin){var o=r.pathname.substring(1),a={};r.search&&r.searchParams.forEach((function(e,t){a[t]=e})),Ie.bind(e,"click",(function(e){e.preventDefault(),t.loadPage(o,a)}))}}}))}},{key:"storeNotes",value:function(){je.store("notifications",this.notes.map((function(e){return{subject:e.subject,details:e.details,severity:e.severity,stamp:e.stamp,id:e.id,acked:e.acked}})))}},{key:"updateMenuItemsDisplay",value:function(){var e=this.page;e&&(this.user.authed?(Ie.show(e.noteMenuEntry,e.walletsMenuEntry,e.profileMenuEntry),Object.keys(this.user.exchanges).length>0?Ie.show(e.marketsMenuEntry):Ie.hide(e.marketsMenuEntry)):Ie.hide(e.noteMenuEntry,e.walletsMenuEntry,e.marketsMenuEntry,e.profileMenuEntry))}},{key:"attachCommon",value:function(e){this.bindInternalNavigation(e)}},{key:"updateExchangeRegistration",value:function(e,t,n){var r=this.exchanges[e],o=this.assets[n].symbol;r.pendingFee={confs:t,assetID:n,symbol:o}}},{key:"setDEXPaid",value:function(e){this.exchanges[e].pendingFee=null}},{key:"handleFeePaymentNote",value:function(e){switch(e.topic){case"RegUpdate":this.updateExchangeRegistration(e.dex,e.confirmations,e.asset);break;case"AccountRegistered":this.setDEXPaid(e.dex)}}},{key:"setNotes",value:function(e){this.log("notes","setNotes",e),this.notes=[],Ie.empty(this.page.noteList);for(var t=0;t5;)R.removeChild(R.firstChild);setTimeout(a(v().mark((function e(){return v().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Ie.animate(500,(function(e){E.style.opacity=String(1-e)}));case 2:E.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),o=1;o100;)this.pokes.shift();this.prependListElement(this.page.pokeList,o,n)}},{key:"prependNoteElement",value:function(e,t){var n=r(this.makeNote(e),2),o=n[0],a=n[1];for(this.notes.push(a);this.notes.length>100;)this.notes.shift();var i=this.page.noteList;if(this.prependListElement(i,a,o),t||this.storeNotes(),!(0===this.notes.length||Ie.isDisplayed(this.page.noteBox)&&Ie.isDisplayed(i))){var s=0,c=this.notes.reduce((function(e,t){return t.acked||s++,!t.acked&&t.severity>e?t.severity:e}),0),u=this.page.noteIndicator;Ur(u,c),s?(u.textContent=String(s>99?"".concat(99,"+"):s),Ie.show(u)):Ie.hide(u)}}},{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";Ie.safeSelector(t,"div.note-indicator").classList.add(n)}return Ie.safeSelector(t,"div.note-subject").textContent=e.subject,Ie.safeSelector(t,"div.note-details").textContent=e.details,[t,Er({el:t},e)]}},{key:"makePoke",value:function(e){var t=this.page.pokeTmpl.cloneNode(!0),n=new Date(e.stamp);return Ie.tmplElement(t,"dateTime").textContent="".concat(n.toLocaleDateString(),", ").concat(n.toLocaleTimeString()),Ie.tmplElement(t,"details").textContent="".concat(e.subject,": ").concat(e.details),[t,Er({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=this.user.exchanges[e].markets[t].orders;return n||(n=[],this.user.exchanges[e].markets[t].orders=n),n}},{key:"haveAssetOrders",value:function(e){for(var t=0,n=Object.values(this.user.exchanges);t result for the\n // current iteration.\n result.value = unwrapped;\n resolve(result);\n }, function(error) {\n // If a rejected Promise was yielded, throw the rejection back\n // into the async generator function so it can be handled there.\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new PromiseImpl(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n define(AsyncIterator.prototype, asyncIteratorSymbol, function () {\n return this;\n });\n exports.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {\n if (PromiseImpl === void 0) PromiseImpl = Promise;\n\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList),\n PromiseImpl\n );\n\n return exports.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\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\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n // Note: [\"return\"] must be used for ES3 parsing compatibility.\n if (delegate.iterator[\"return\"]) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n define(Gp, toStringTagSymbol, \"Generator\");\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n define(Gp, iteratorSymbol, function() {\n return this;\n });\n\n define(Gp, \"toString\", function() {\n return \"[object Generator]\";\n });\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n exports.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n exports.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(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 &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(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 (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n\n // Regardless of whether this script is executing as a CommonJS module\n // or not, return the runtime object so that we can declare the variable\n // regeneratorRuntime in the outer scope, which allows this module to be\n // injected easily by `bin/regenerator --include-runtime script.js`.\n return exports;\n\n}(\n // If this script is executing as a CommonJS module, use module.exports\n // as the regeneratorRuntime namespace. Otherwise create a new empty\n // object. Either way, the resulting object will be used to initialize\n // the regeneratorRuntime variable at the top of this file.\n typeof module === \"object\" ? module.exports : {}\n));\n\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n // This module should not be running in strict mode, so the above\n // assignment should always work unless something is misconfigured. Just\n // in case runtime.js accidentally runs in strict mode, in modern engines\n // we can explicitly access globalThis. In older engines we can escape\n // strict mode using a global Function call. This could conceivably fail\n // if a Content Security Policy forbids using Function, but in that case\n // the proper solution is to fix the accidental strict mode problem. If\n // you've misconfigured your bundler to force strict mode and applied a\n // CSP to forbid Function, and you're not willing to fix either of those\n // problems, please detail your unique predicament in a GitHub issue.\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\n for (var i = 0, arr2 = new Array(len); i < len; i++) {\n arr2[i] = arr[i];\n }\n\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 = arr == null ? null : typeof Symbol !== \"undefined\" && arr[Symbol.iterator] || arr[\"@@iterator\"];\n\n if (_i == null) return;\n var _arr = [];\n var _n = true;\n var _d = false;\n\n var _s, _e;\n\n try {\n for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {\n _arr.push(_s.value);\n\n if (i && _arr.length === i) break;\n }\n } catch (err) {\n _d = true;\n _e = err;\n } finally {\n try {\n if (!_n && _i[\"return\"] != null) _i[\"return\"]();\n } finally {\n if (_d) throw _e;\n }\n }\n\n return _arr;\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\n if (info.done) {\n resolve(value);\n } else {\n Promise.resolve(value).then(_next, _throw);\n }\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\n function _next(value) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value);\n }\n\n function _throw(err) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err);\n }\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}","function _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, descriptor.key, descriptor);\n }\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}","export default function _defineProperty(obj, key, value) {\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\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_LOCKED = 'ID_LOCKED'\nexport const ID_NOWALLET = 'ID_NOWALLET'\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_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_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 SETUP_NEEDED = 'SETUP_NEEDED'\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_NOWALLET]: '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_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]: 'Registration fee payment successful!',\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}\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_NOWALLET]: '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!',\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}\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_NOWALLET]: '未连接钱包', // 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_UNKNOWN]: 'unknown', // TODO\n [ID_EPOCH]: '时间',\n [ID_SETTLING]: 'settling', // TODO - \"settling\" shows in the Your Orders table when the order is doing an atomic swap with another trade\n [ID_NO_MATCH]: 'no match', // TODO - \"no match\" shows in the Your Orders table when the order did not match with another trade and is done\n [ID_CANCELED]: 'canceled', // TODO - \"canceled\" shows in the Your Orders table when the order has been canceled\n [ID_REVOKED]: 'revoked', // TODO - \"revoked\" shows in the Your Orders table when the order has failed during swap\n [ID_WAITING_FOR_CONFS]: 'Waiting for confirmations...', // TODO - shows when the registration fee transaction is waiting to be mined (needs confirmations)\n [ID_NONE_SELECTED]: 'none selected', // TODO - looks unused, but this indicates nothing is selected in some list\n [ID_REGISTRATION_FEE_SUCCESS]: 'Registration fee payment successful!', // TODO - When the registration fee transaction reaches the required number of confirmations, this is shown\n [ID_API_ERROR]: '接口错误',\n [ID_ADD]: '加',\n [ID_CREATE]: '创建',\n [ID_WALLET_READY]: 'Choose Wallet', // xxx translate\n [ID_SETUP_WALLET]: 'Setup' // xxx translate\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_NOWALLET]: '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ę!',\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}\n\nconst localesMap: Record = {\n 'en-us': enUS,\n 'pt-br': ptBR,\n 'zh-cn': zhCN,\n 'pl-pl': plPL\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 = {\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}\n\nconst BipSymbols = Object.values(BipIDs)\n\nconst intFormatter = new Intl.NumberFormat((navigator.languages as string[]))\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: (e: Event) => 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) {\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.\n */\n static mouseInElement (e: MouseEvent, el: HTMLElement): boolean {\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 /* 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 * 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 /* 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 const easer = easingAlgo ? Easing[easingAlgo] : Easing.linear\n const start = new Date().getTime()\n const end = start + duration\n const range = end - start\n const frameDuration = 1000 / FPS\n let now = start\n while (now < end) {\n f(easer((now - start) / range))\n await sleep(frameDuration)\n now = new Date().getTime()\n }\n f(1)\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 /*\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 '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 /*\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 | Element, 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\n/* Easing algorithms for animations. */\nconst 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}\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.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)\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)\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)\n Doc.show(i.unlocked)\n if (this.status) this.status.textContent = intl.prep(intl.ID_READY)\n }\n\n /* sleeping 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)\n Doc.show(i.nowallet)\n if (this.status) this.status.textContent = intl.prep(intl.ID_NOWALLET)\n }\n\n setSyncing (wallet: WalletState | null) {\n const syncIcon = this.icons.syncing\n if (!wallet || !wallet.running) {\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.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","const darkModeCK = 'darkMode'\nconst authCK = 'dexauth'\nconst popupsCK = 'popups'\nconst pwKeyCK = 'sessionkey'\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 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 /* dark sets the dark-mode cookie. */\n static dark (dark: boolean) {\n this.setCookie(darkModeCK, dark ? '1' : '0')\n if (dark) {\n document.body.classList.add('dark')\n } else {\n document.body.classList.remove('dark')\n }\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(`${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(pwKeyCK)\n }\n\n /* store puts the key-value pair into Window.localStorage. */\n static store (k: string, v: any) {\n window.localStorage.setItem(k, JSON.stringify(v))\n }\n\n /* clearAllStore remove all the key-value pair in Window.localStorage. */\n static clearAllStore () {\n window.localStorage.clear()\n }\n\n static removeAuthCK () {\n document.cookie = `${authCK}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`\n }\n\n /*\n * fetch fetches the value associated with the key in Window.localStorage, or\n * null if the no value exists for the key.\n */\n static fetch (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\n// If the dark-mode cookie is not set, set it to dark mode on.\nif (State.getCookie(darkModeCK) === null) State.setCookie(darkModeCK, '1')\nif (State.getCookie(popupsCK) === null) State.setCookie(popupsCK, '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\n return self;\n}","export default function _setPrototypeOf(o, p) {\n _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\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\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}","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 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\n return assertThisInitialized(self);\n}","export default function _getPrototypeOf(o) {\n _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {\n return o.__proto__ || Object.getPrototypeOf(o);\n };\n return _getPrototypeOf(o);\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 Exchange {\n host: string\n acctID: string\n markets: Record\n assets: Record\n connectionStatus: ConnectionStatus\n feeAsset: FeeAsset // DEPRECATED. DCR.\n regFees: Record\n pendingFee: PendingFeeState | null\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\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}\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\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 FeeAsset {\n id: number\n confs: number\n amount: number\n}\n\nexport interface PendingFeeState {\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 wallet: WalletState\n info: WalletInfo\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 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 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}\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 noauth: 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}\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 FeePaymentNote extends CoreNote {\n asset: number\n confirmations: number\n dex: string\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 SpotPriceNote extends CoreNote {\n host: string\n spots: Record\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}\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}\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 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}\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 Application {\n assets: Record\n seedGenTime: number\n user: User\n header: HTMLElement\n walletMap: Record\n exchanges: Record\n fiatRatesMap: Record\n showPopups: boolean\n commitHash: string\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 updateExchangeRegistration (dexAddr: string, confs: number, assetID: number): void\n handleFeePaymentNote (note: FeePaymentNote): 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 haveAssetOrders (assetID: number): boolean\n walletIsActive (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): number\n walletDefinition (assetID: number, walletType: string): WalletDefinition\n currentWalletDefinition (assetID: number): WalletDefinition\n fetchBalance (assetID: number): Promise\n checkResponse (resp: APIResponse, skipNote?: boolean): 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","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","/*\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","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 Doc from './doc'\nimport * as intl from './locales'\nimport {\n app,\n Order,\n TradeForm,\n PageElement,\n OrderOption as OrderOpt,\n Match,\n XYRange\n} from './registry'\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\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) { return ord.sell ? 'sell' : 'buy' }\nexport function typeString (ord: Order) { return ord.type === Limit ? (ord.tif === ImmediateTiF ? 'limit (i)' : 'limit') : 'market' }\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 * hasLiveMatches returns true if the order has matches that have not completed\n * settlement yet.\n */\nexport function hasLiveMatches (order: Order) {\n if (!order.matches) return false\n for (const match of order.matches) {\n if (!match.revoked && match.status < MakerRedeemed) return true\n }\n return false\n}\n\n/* statusString converts the order status to a string */\nexport function statusString (order: Order): string {\n const isLive = hasLiveMatches(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 return (order.filled === 0) ? intl.prep(intl.ID_NO_MATCH) : 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 ''\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/*\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 return 'Revoked - Refund PENDING' // auto-redeem also possible, but action is pending\n }\n if (m.refund) {\n return 'Revoked - Refunded'\n }\n if (m.redeem) {\n return 'Revoked - Redeemed'\n }\n return 'Revoked - Complete'\n }\n\n switch (m.status) {\n case NewlyMatched:\n return '(0 / 4) Newly Matched'\n case MakerSwapCast:\n return '(1 / 4) First Swap Sent'\n case TakerSwapCast:\n return '(2 / 4) Second Swap Sent'\n case MakerRedeemed:\n if (m.side === Maker) {\n return 'Match Complete'\n }\n return '(3 / 4) Maker Redeemed'\n case MatchComplete:\n return 'Match Complete'\n }\n return 'Unknown Match Status'\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) {\n [booleanOptTmpl, rangeOptTmpl, orderOptTmpl] = [page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl]\n}\n\ninterface OptionsReporters {\n enable: () => void\n disable: () => void\n}\n\n/*\n * OrderOption is a base class for option elements. OrderOptions stores some\n * common 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 */\nclass OrderOption {\n opt: OrderOpt\n order: TradeForm\n node: HTMLElement\n tmpl: Record\n on: boolean\n\n constructor (opt: OrderOpt, order: TradeForm, isSwapOption: boolean, report: OptionsReporters) {\n this.opt = opt\n this.order = order\n const node = this.node = orderOptTmpl.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(node)\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 tmpl.chainIcon.src = Doc.logoPath(symbol)\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 quoteSymbol () {\n return dexAssetSymbol(this.order.host, this.order.quote)\n }\n\n baseSymbol () {\n return dexAssetSymbol(this.order.host, this.order.base)\n }\n}\n\n/*\n * BooleanOrderOption is a simple on/off option with a short summary of it's\n * effects. BooleanOrderOption is the handler for a *BooleanConfig from\n * client/asset.\n */\nclass BooleanOrderOption extends OrderOption {\n control: HTMLElement\n changed: () => void\n\n constructor (opt: OrderOpt, order: TradeForm, changed: () => void, isSwapOption: boolean) {\n super(opt, order, isSwapOption, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.changed = () => changed()\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 order.options[opt.key] !== 'undefined' ? order.options[opt.key] : opt.default\n if (this.on) this.node.classList.add('selected')\n }\n\n store () {\n if (this.on === this.opt.default) delete this.order.options[this.opt.key]\n else this.order.options[this.opt.key] = this.on\n this.changed()\n }\n\n enable () {\n this.store()\n }\n\n disable () {\n this.store()\n }\n}\n\n/*\n * XYRangeOrderOption is an order option that contains an XYRangeHandler.\n * The logic for handling the slider to is defined in XYRangeHandler so\n * that the slider can be used without being contained in an order option.\n */\nclass XYRangeOrderOption extends OrderOption {\n handler: XYRangeHandler\n x: number\n changed: () => void\n\n constructor (opt: OrderOpt, order: TradeForm, changed: () => void, isSwapOption: boolean) {\n super(opt, order, isSwapOption, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.changed = changed\n const cfg = opt.xyRange\n const setVal = order.options[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.order.options[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 () {\n this.order.options[this.opt.key] = this.x\n this.changed()\n }\n\n disable () {\n delete this.order.options[this.opt.key]\n this.changed()\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 x: number\n updated: (x:number, y:number) => void\n changed: () => void\n selected: () => 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 = Doc.parseTemplate(control)\n\n this.changed = changed\n this.selected = selected\n this.updated = updated\n\n const { slider, handle } = tmpl\n\n const rangeX = cfg.end.x - cfg.start.x\n const rangeY = cfg.end.y - cfg.start.y\n const normalizeX = (x: number) => (x - cfg.start.x) / rangeX\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 let r = normalizeX(initVal)\n let x = this.x = initVal\n let y = r * rangeY + cfg.start.y\n\n const number = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 3,\n maximumSignificantDigits: 3\n })\n\n // accept needs to be called anytime a handler updates x, y, and r.\n const accept = (skipUpdate?: boolean) => {\n if (roundY) y = Math.round(y)\n tmpl.x.textContent = number.format(x)\n tmpl.y.textContent = number.format(y)\n if (roundY) tmpl.y.textContent = `${y}`\n handle.style.left = `calc(${r * 100}% - ${r * 14}px)`\n this.x = x\n if (!skipUpdate) this.updated(x, y)\n }\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 x = clamp(xx, cfg.start.x, cfg.end.x)\n r = normalizeX(x)\n y = r * rangeY + cfg.start.y\n accept()\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 = number.format(x)\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 y = clamp(yy, cfg.start.y, cfg.end.y)\n r = (y - cfg.start.y) / rangeY\n x = cfg.start.x + r * rangeX\n accept()\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 = number.format(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(x) * 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 r = left(ee) / w\n x = r * rangeX + cfg.start.x\n y = r * rangeY + cfg.start.y\n accept()\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 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 accept(true)\n }\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: OrderOpt, order: TradeForm, change: () => void, isSwap: boolean): HTMLElement {\n switch (true) {\n case !!opt.boolean:\n return new BooleanOrderOption(opt, order, change, isSwap).node\n case !!opt.xyRange:\n return new XYRangeOrderOption(opt, order, change, isSwap).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) {\n return app().exchanges[host].assets[assetID].symbol\n}\n\nconst clamp = (v: number, min: number, max: number) => v < min ? min : v > max ? max : v\n","import Doc from './doc'\nimport { postJSON } from './http'\nimport State from './state'\nimport * as intl from './locales'\nimport * as OrderUtil from './orderutil'\nimport {\n app,\n PasswordCache,\n SupportedAsset,\n PageElement,\n WalletDefinition,\n ConfigOption,\n Exchange,\n Market,\n UnitInfo,\n FeeAsset,\n WalletState,\n WalletBalance,\n BalanceNote,\n Order,\n XYRange,\n WalletStateNote\n} from './registry'\n\ninterface ConfigOptionInput extends HTMLInputElement {\n configOpt: ConfigOption\n}\n\ninterface ProgressPoint {\n stamp: number\n progress: number\n}\n\n/*\n * NewWalletForm should be used with the \"newWalletForm\" template. The enclosing\n *
element should be the second argument of the constructor.\n */\nexport class NewWalletForm {\n page: Record\n form: HTMLElement\n pwCache: PasswordCache | null\n success: (assetID: number) => void\n currentAsset: SupportedAsset\n pwHiders: HTMLElement[]\n subform: WalletConfigForm\n currentWalletType: string\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 Doc.bind(this.subform.showOther, 'click', () => Doc.show(page.walletSettingsHeader))\n\n bind(form, page.submitAdd, () => this.submit())\n bind(form, page.oneBttn, () => this.submit())\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 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 const assetID = this.currentAsset.id\n const createForm = {\n assetID: assetID,\n pass: newWalletPass.value || '',\n config: this.subform.map(),\n appPass: pw,\n walletType: this.currentWalletType\n }\n appPass.value = ''\n const loaded = app().loading(page.mainForm)\n const res = await postJSON('/api/newwallet', createForm)\n loaded()\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n newWalletPass.value = ''\n this.success(assetID)\n }\n\n async setAsset (assetID: number) {\n const page = this.page\n const asset = app().assets[assetID]\n const tabs = page.walletTypeTabs\n if (this.currentAsset && this.currentAsset.id === asset.id) return\n this.currentAsset = asset\n page.assetLogo.src = Doc.logoPath(asset.symbol)\n page.assetName.textContent = asset.info.name\n page.newWalletPass.value = ''\n\n if (asset.info.availablewallets.length > 1) page.header.classList.add('bordertop')\n else page.header.classList.remove('bordertop')\n\n const walletDef = asset.info.availablewallets[0]\n Doc.empty(tabs)\n Doc.hide(tabs, page.newWalletErr)\n\n if (asset.info.availablewallets.length > 1) {\n Doc.show(tabs)\n for (const wDef of asset.info.availablewallets) {\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(walletDef)\n }\n\n async update (walletDef: WalletDefinition) {\n const page = this.page\n this.currentWalletType = walletDef.type\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 if (appPwCached && walletDef.seeded) {\n Doc.show(page.oneBttnBox)\n } else if (walletDef.seeded) {\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 this.subform.update(configOpts)\n\n if (this.subform.dynamicOpts.children.length) Doc.show(page.walletSettingsHeader)\n else Doc.hide(page.walletSettingsHeader)\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 walletDef = app().walletDefinition(this.currentAsset.id, this.currentWalletType)\n if (walletDef.seeded) return\n if (walletDef.configpath === '') return\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/defaultwalletcfg', {\n assetID: this.currentAsset.id,\n type: this.currentWalletType\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\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: Record\n configOpts: ConfigOption[]\n sectionize: boolean\n allSettings: PageElement\n dynamicOpts: PageElement\n textInputTmpl: PageElement\n dateInputTmpl: PageElement\n checkboxTmpl: 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\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.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 /*\n * update creates the dynamic form.\n */\n update (configOpts: ConfigOption[], walletIsActive?: boolean) {\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 (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 const addOpt = (box: HTMLElement, opt: ConfigOption) => {\n const elID = 'wcfg-' + opt.key\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 el = this.textInputTmpl.cloneNode(true) as HTMLElement\n this.configElements[opt.key] = el\n const input = el.querySelector('input') as ConfigOptionInput\n input.id = elID\n input.configOpt = opt\n const label = Doc.safeSelector(el, 'label')\n label.htmlFor = elID // 'for' attribute, but 'for' is a keyword\n label.prepend(opt.displayname)\n box.appendChild(el)\n if (opt.noecho) input.type = 'password'\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 input.valueAsDate = opt.default ? new Date(opt.default * 1000) : new Date()\n } else input.value = opt.default !== null ? opt.default : ''\n input.disabled = Boolean(opt.disablewhenactive && walletIsActive)\n }\n for (const opt of this.configOpts) {\n if (this.sectionize && opt.default !== null) defaultedOpts.push(opt)\n else addOpt(this.dynamicOpts, opt)\n }\n if (defaultedOpts.length) {\n for (const opt of defaultedOpts) {\n 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) {\n const finds: HTMLElement[] = []\n this.allSettings.querySelectorAll('input').forEach((input: ConfigOptionInput) => {\n const k = input.configOpt.key\n const v = cfg[k]\n if (typeof v === 'undefined') return\n finds.push(this.configElements[k])\n if (input.configOpt.isboolean) input.checked = isTruthyString(v)\n else if (input.configOpt.isdate) input.valueAsDate = new Date(parseInt(v) * 1000)\n else input.value = v\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 (): Record {\n const config: Record = {}\n this.allSettings.querySelectorAll('input').forEach((input: ConfigOptionInput) => {\n if (input.configOpt.isboolean && input.configOpt.key) {\n config[input.configOpt.key] = input.checked ? '1' : '0'\n } else if (input.configOpt.isdate && input.configOpt.key) {\n const minDate = input.min ? toUnixDate(new Date(input.min)) : Number.MIN_SAFE_INTEGER\n const maxDate = input.max ? toUnixDate(new Date(input.max)) : Number.MAX_SAFE_INTEGER\n let date = input.value ? toUnixDate(new Date(input.value)) : 0\n if (date < minDate) date = minDate\n else if (date > maxDate) date = maxDate\n config[input.configOpt.key] = '' + date\n } else if (input.value) {\n config[input.configOpt.key] = input.value\n }\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.configOpt.key\n inputs[k] = this.configElements[k]\n })\n for (const opt of this.configOpts) {\n const input = inputs[opt.key]\n if (input) box.append(input)\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 feeAssetID: 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 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 unitInfo = asset.info.unitinfo\n this.feeAssetID = asset.id\n const page = this.page\n const regAsset = this.xc.regFees[asset.symbol]\n page.fee.textContent = Doc.formatCoinValue(regAsset.amount, unitInfo)\n page.feeUnit.textContent = unitInfo.conventional.unit.toUpperCase()\n page.logo.src = Doc.logoPath(asset.symbol)\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 if (this.feeAssetID === null) {\n page.regErr.innerText = 'You must select a valid wallet for the fee payment'\n Doc.show(page.regErr)\n return\n }\n const symbol = app().user.assets[this.feeAssetID].wallet.symbol\n Doc.hide(page.regErr)\n const feeAsset = this.xc.regFees[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 registration = {\n addr: dexAddr,\n pass: pw,\n fee: feeAsset.amount,\n asset: feeAsset.id,\n cert: cert\n }\n page.appPass.value = ''\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/register', registration)\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\n constructor (form: HTMLElement, success: (assetID: number) => void) {\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\n setExchange (xc: Exchange) {\n this.xc = xc\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.name.textContent = `${baseSymbol}-${quoteSymbol}`\n const s = Doc.formatCoinValue(mkt.lotsize, baseUnitInfo)\n marketTmpl.lotSize.textContent = `${s} ${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, feeAsset] of Object.entries(xc.regFees)) {\n const asset = app().assets[feeAsset.id]\n if (!asset) continue\n const haveWallet = asset.wallet\n const unitInfo = asset.info.unitinfo\n const assetNode = page.assetTmpl.cloneNode(true) as HTMLElement\n Doc.bind(assetNode, 'click', () => { this.success(feeAsset.id) })\n const assetTmpl = Doc.parseTemplate(assetNode)\n page.assets.appendChild(assetNode)\n assetTmpl.logo.src = Doc.logoPath(symbol)\n const fee = Doc.formatCoinValue(feeAsset.amount, unitInfo)\n assetTmpl.fee.textContent = `${fee} ${unitInfo.conventional.unit}`\n assetTmpl.confs.textContent = String(feeAsset.confs)\n assetTmpl.ready.textContent = haveWallet ? intl.prep(intl.WALLET_READY) : intl.prep(intl.SETUP_NEEDED)\n assetTmpl.ready.classList.add(haveWallet ? 'readygreen' : 'setuporange')\n\n let count = 0\n for (const mkt of Object.values(xc.markets)) {\n if (mkt.baseid !== feeAsset.id && mkt.quoteid !== feeAsset.id) continue\n const node = marketNode(mkt, feeAsset.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 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 * WalletWaitForm is a form used to track the wallet sync status and balance\n * in preparation for paying the registration fee.\n */\nexport class WalletWaitForm {\n form: HTMLElement\n success: () => void\n goBack: () => void\n page: Record\n assetID: number\n xc: Exchange\n regFee: FeeAsset\n progressCache: ProgressPoint[]\n progressed: boolean\n funded: 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.balance, 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 form. */\n setWallet (wallet: WalletState, txFee: number) {\n this.assetID = wallet.assetID\n this.progressCache = []\n this.progressed = false\n this.funded = false\n const page = this.page\n const asset = app().assets[wallet.assetID]\n const fee = this.regFee = this.xc.regFees[asset.symbol]\n\n for (const span of Doc.applySelector(this.form, '.unit')) span.textContent = asset.symbol.toUpperCase()\n page.logo.src = Doc.logoPath(asset.symbol)\n page.depoAddr.textContent = wallet.address\n page.fee.textContent = Doc.formatCoinValue(fee.amount, asset.info.unitinfo)\n\n Doc.hide(page.syncUncheck, page.syncCheck, page.balUncheck, page.balCheck, page.syncRemainBox)\n Doc.show(page.balanceBox)\n\n if (txFee > 0) {\n page.totalFees.textContent = Doc.formatCoinValue(fee.amount + txFee, asset.info.unitinfo)\n Doc.show(page.sendEnoughWithEst)\n Doc.hide(page.sendEnough)\n } else {\n Doc.show(page.sendEnough)\n Doc.hide(page.sendEnoughWithEst)\n }\n\n Doc.show(wallet.synced ? page.syncCheck : wallet.syncProgress >= 1 ? page.syncSpinner : page.syncUncheck)\n Doc.show(wallet.balance.available > fee.amount ? page.balCheck : page.balUncheck)\n\n page.progress.textContent = String(Math.round(wallet.syncProgress * 100))\n\n if (wallet.synced) {\n this.progressed = true\n }\n this.reportBalance(wallet.balance, 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 (wallet.assetID !== this.assetID) return\n if (this.progressed && this.funded) return\n this.reportProgress(wallet.synced, wallet.syncProgress)\n this.reportBalance(wallet.balance, wallet.assetID)\n }\n\n /*\n * reportBalance sets the balance display and calls success if we go over the\n * threshold.\n */\n reportBalance (bal: WalletBalance, assetID: number) {\n if (this.funded || this.assetID === -1 || this.assetID !== assetID) return\n const page = this.page\n const asset = app().assets[this.assetID]\n\n if (bal.available <= this.regFee.amount) {\n page.balance.textContent = Doc.formatCoinValue(bal.available, asset.info.unitinfo)\n return\n }\n\n Doc.show(page.balCheck)\n Doc.hide(page.balUncheck, page.balanceBox, page.sendEnough)\n this.funded = true\n\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 = String(Math.round(prog * 100))\n\n // The remaining time estimate must be based on more than one progress\n // report. We'll cache up to the last 20 and look at the difference between\n // 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 while (cache.length > cacheSize) cache.shift()\n if (cache.length === 1) return\n Doc.show(page.syncRemainBox)\n const [first, last] = [cache[0], cache[cache.length - 1]]\n const progDelta = last.progress - first.progress\n if (progDelta === 0) {\n page.syncRemain.textContent = '> 1 day'\n return\n }\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: () => void\n pwCache: PasswordCache | null\n page: Record\n currentAsset: SupportedAsset\n\n constructor (form: HTMLElement, success: () => 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.info.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 Doc.hide(this.page.unlockErr)\n const open = {\n assetID: this.currentAsset.id,\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()\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 accelerateorder 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 = `Error accelerating order: ${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 = `Error accelerating order: ${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 OrderUtil.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 accelerateestimate request to the\n // client backend using the curretly 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 = `Error estimating acceleration fee: ${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 defaultTLSText: string\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 this.defaultTLSText = 'none selected'\n\n const page = this.page = Doc.parseTemplate(form)\n\n page.selectedCert.textContent = this.defaultTLSText\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 have the password cached, we're good to go.\n if (State.passwordIsCached() || (pwCache && pwCache.pw)) return this.checkDEX(host)\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)\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 const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(page.appPWBox, page.auth)\n else Doc.show(page.appPWBox, page.auth)\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 }\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 = 'DEX address cannot be empty'\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 let pw = ''\n if (!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 = '/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, true)) {\n if (res.msg === '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 && res.paid) {\n await app().fetchUser()\n 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 = this.defaultTLSText\n Doc.hide(page.removeCert)\n Doc.show(page.addCert)\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\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 }\n app().setNotes(res.notes || [])\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 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 javscript 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// dateToString converts a javascript date object to a YYYY-MM-DD format string.\nfunction dateToString (date: Date) {\n return date.toISOString().split('T')[0]\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport {\n NewWalletForm,\n DEXAddressForm,\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 newWalletForm: NewWalletForm\n regAssetForm: FeeAssetSelectionForm\n walletWaitForm: WalletWaitForm\n confirmRegisterForm: ConfirmRegistrationForm\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n this.pwCache = { pw: '' }\n const page = this.page = Doc.idDescendants(body)\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 this.dexAddrForm.refresh()\n slideSwap(page.loginForm, page.dexAddrForm)\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.currentDEX = xc\n this.confirmRegisterForm.setExchange(xc, certFile)\n this.walletWaitForm.setExchange(xc)\n this.regAssetForm.setExchange(xc)\n this.animateRegAsset(page.dexAddrForm)\n }, this.pwCache)\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 fee = this.currentDEX.regFees[asset.symbol]\n if (wallet.synced && wallet.balance.available > fee.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 slideSwap(page.regAssetForm, page.walletWait)\n return\n }\n\n this.newWalletForm.setAsset(assetID)\n this.newWalletForm.loadDefaults()\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 }\n Doc.show(currentForm)\n\n // Attempt to load the dcrwallet configuration from the default location.\n if (app().user.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 /* 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 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, true)) {\n return 0\n }\n return res.txfee\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 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 feeAmt = this.currentDEX.regFees[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > feeAmt) {\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 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 app().loadPage('markets')\n }\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 Doc, { WalletIcons } from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport { NewWalletForm, WalletConfigForm, UnlockWalletForm, bind as bindForm } from './forms'\nimport * as ntfn from './notifications'\nimport State from './state'\nimport * as intl from './locales'\nimport {\n app,\n PageElement,\n SupportedAsset,\n WalletDefinition,\n BalanceNote,\n WalletStateNote,\n Market,\n RateNote,\n WalletState\n} from './registry'\n\nconst bind = Doc.bind\nconst animationLength = 300\nconst traitNewAddresser = 1 << 1\nconst traitLogFiler = 1 << 2\nconst traitRecoverer = 1 << 5\nconst traitWithdrawer = 1 << 6\nconst traitRestorer = 1 << 8\n\nconst activeOrdersErrCode = 35\n\ninterface Actions {\n connect: HTMLElement\n unlock: HTMLElement\n send: HTMLElement\n deposit: HTMLElement\n create: HTMLElement\n rescan: HTMLElement\n lock: HTMLElement\n settings: HTMLElement\n}\n\ninterface RowInfo {\n assetID: number\n tr: HTMLElement\n symbol: string\n name: string\n stateIcons: WalletIcons\n actions: Actions\n}\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\nexport default class WalletsPage extends BasePage {\n body: HTMLElement\n page: Record\n rowInfos: Record\n sendAsset: SupportedAsset\n newWalletForm: NewWalletForm\n reconfigForm: WalletConfigForm\n unlockForm: UnlockWalletForm\n lastFormAsset: number\n keyup: (e: KeyboardEvent) => void\n changeWalletPW: boolean\n depositAsset: number\n // Methods to switch the item displayed on the right side, with a little\n // fade-in animation.\n displayed: HTMLElement\n animation: Promise\n openAsset: number\n walletAsset: number\n reconfigAsset: number\n forms: PageElement[]\n forceReq: RescanRecoveryRequest\n forceUrl: string\n currentForm: PageElement\n restoreInfoCard: HTMLElement\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)\n this.restoreInfoCard = page.restoreInfoCard.cloneNode(true) as HTMLElement\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 Doc.bind(page.copyAddressBtn, 'click', () => { this.copyAddress() })\n\n // Read the document, storing some info about each asset's row.\n const getAction = (row: HTMLElement, name: string) => row.querySelector(`[data-action=${name}]`) as HTMLElement\n const rowInfos: Record = this.rowInfos = {}\n const rows = Doc.applySelector(page.walletTable, 'tr')\n let firstRow\n for (const tr of rows) {\n const assetID = parseInt(tr.dataset.assetID || '')\n rowInfos[assetID] = {\n assetID: assetID,\n tr: tr,\n symbol: tr.dataset.symbol || '',\n name: tr.dataset.name || '',\n stateIcons: new WalletIcons(tr),\n actions: {\n connect: getAction(tr, 'connect'),\n unlock: getAction(tr, 'unlock'),\n send: getAction(tr, 'send'),\n deposit: getAction(tr, 'deposit'),\n create: getAction(tr, 'create'),\n rescan: getAction(tr, 'rescan'),\n lock: getAction(tr, 'lock'),\n settings: getAction(tr, 'settings')\n }\n }\n if (!firstRow) firstRow = rowInfos[assetID]\n }\n\n // Prepare templates\n page.marketCard.removeAttribute('id')\n page.marketCard.remove()\n page.oneMarket.removeAttribute('id')\n page.oneMarket.remove()\n\n // Bind the new wallet form.\n this.newWalletForm = new NewWalletForm(page.newWalletForm, () => { this.createWalletSuccess() })\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, () => { this.openWalletSuccess() })\n\n // Bind the Send form.\n bindForm(page.sendForm, page.submitSendForm, () => { this.send() })\n\n // Bind the wallet reconfiguration submission.\n bindForm(page.reconfigForm, page.submitReconfig, () => this.reconfig())\n\n // Bind the row clicks, which shows the available markets for the asset.\n for (const rowInfo of Object.values(rowInfos)) {\n bind(rowInfo.tr, 'click', () => {\n this.showMarkets(rowInfo.assetID)\n })\n }\n\n page.rightBox.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => {\n this.showMarkets(this.lastFormAsset)\n })\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)) {\n this.closePopups()\n } else {\n this.showMarkets(this.lastFormAsset)\n }\n }\n }\n bind(document, 'keyup', this.keyup)\n\n bind(page.downloadLogs, 'click', async () => { this.downloadLogs() })\n bind(page.exportWallet, 'click', async () => { this.displayExportWalletAuth() })\n 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\n // Bind buttons\n for (const [k, asset] of Object.entries(rowInfos)) {\n const assetID = parseInt(k) // keys are string asset ID.\n const a = asset.actions\n const run = (e: Event, f: (assetID: number, asset: RowInfo) => void) => {\n e.stopPropagation()\n f(assetID, asset)\n }\n bind(a.connect, 'click', e => { run(e, this.doConnect.bind(this)) })\n bind(a.send, 'click', e => { run(e, this.showSendForm.bind(this)) })\n bind(a.deposit, 'click', e => { run(e, this.showDeposit.bind(this)) })\n bind(a.create, 'click', e => { run(e, this.showNewWallet.bind(this)) })\n bind(a.rescan, 'click', e => { run(e, this.rescanWallet.bind(this)) })\n bind(a.unlock, 'click', e => { run(e, this.openWallet.bind(this)) })\n bind(a.lock, 'click', async e => { run(e, this.lock.bind(this)) })\n bind(a.settings, 'click', e => { run(e, this.showReconfig.bind(this)) })\n }\n\n // New deposit address button.\n bind(page.newDepAddrBttn, 'click', async () => { this.newDepositAddress() })\n\n // Clicking on the available amount on the Send form populates the\n // amount field.\n bind(page.sendAvail, 'click', () => {\n const asset = this.sendAsset\n const bal = asset.wallet.balance.available\n page.sendAmt.value = String(bal / asset.info.unitinfo.conventional.conversionFactor)\n this.showFiatValue(asset.id, bal, page.sendValue)\n // Ensure we don't check subtract checkbox for assets that don't have a\n // withdraw method.\n if ((asset.wallet.traits & traitWithdrawer) === 0) page.subtractCheckBox.checked = false\n else page.subtractCheckBox.checked = true\n })\n\n for (const [assetID, wallet] of (Object.entries(app().walletMap) as [any, WalletState][])) {\n if (!wallet) continue\n const fiatDisplay = this.page.walletTable.querySelector(`[data-conversion-target=\"${assetID}\"]`) as PageElement\n if (!fiatDisplay) continue\n this.showFiatValue(assetID, wallet.balance.available, fiatDisplay)\n }\n\n // Display fiat value for current send amount.\n bind(page.sendAmt, 'input', () => {\n const asset = this.sendAsset\n if (!asset) return\n const amt = parseFloat(page.sendAmt.value || '0')\n const conversionFactor = asset.info.unitinfo.conventional.conversionFactor\n this.showFiatValue(asset.id, amt * conversionFactor, page.sendValue)\n })\n\n // A link on the wallet reconfiguration form to show/hide the password field.\n bind(page.showChangePW, 'click', () => {\n this.changeWalletPW = !this.changeWalletPW\n this.setPWSettingViz(this.changeWalletPW)\n })\n\n // Changing the type of wallet.\n bind(page.changeWalletTypeSelect, 'change', () => {\n this.changeWalletType()\n })\n 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.reconfigAsset)\n })\n\n if (!firstRow) return\n this.showMarkets(firstRow.assetID)\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 })\n }\n\n closePopups () {\n Doc.hide(this.page.forms)\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 /*\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 * hideBox hides the displayed box after waiting for the currently running\n * animation to complete.\n */\n async hideBox () {\n if (this.animation) await this.animation\n if (!this.displayed) return\n Doc.hide(this.displayed)\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 /*\n * Show the markets box, which lists the markets available for a selected\n * asset.\n */\n async showMarkets (assetID: number) {\n const page = this.page\n const box = page.marketsBox\n const card = page.marketsCard\n const rowInfo = this.rowInfos[assetID]\n await this.hideBox()\n Doc.empty(card)\n page.marketsFor.textContent = rowInfo.name\n page.marketsForLogo.src = Doc.logoPath(app().assets[assetID].symbol)\n for (const [host, xc] of Object.entries(app().user.exchanges)) {\n let count = 0\n if (!xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n if (market.baseid === assetID || market.quoteid === assetID) count++\n }\n if (count === 0) continue\n const marketBox = page.marketCard.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(marketBox)\n tmpl.dexTitle.textContent = host\n card.appendChild(marketBox)\n if (!xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n // Only show markets where this is the base or quote asset.\n if (market.baseid !== assetID && market.quoteid !== assetID) continue\n const mBox = page.oneMarket.cloneNode(true) as HTMLElement\n Doc.safeSelector(mBox, 'span').textContent = prettyMarketName(market)\n let counterSymbol = market.basesymbol\n if (market.baseid === assetID) counterSymbol = market.quotesymbol\n Doc.safeSelector(mBox, 'img').src = Doc.logoPath(counterSymbol)\n // Bind the click to a load of the markets page.\n const pageData = { host: host, base: market.baseid, quote: market.quoteid }\n bind(mBox, 'click', () => { app().loadPage('markets', pageData) })\n tmpl.markets.appendChild(mBox)\n }\n }\n this.animation = this.showBox(box)\n }\n\n /* Show the new wallet form. */\n async showNewWallet (assetID: number) {\n const page = this.page\n const box = page.newWalletForm\n await this.hideBox()\n this.walletAsset = this.lastFormAsset = assetID\n this.newWalletForm.setAsset(assetID)\n this.animation = this.showBox(box)\n await this.newWalletForm.loadDefaults()\n }\n\n async rescanWallet (assetID: number) {\n const loaded = app().loading(this.body)\n const url = '/api/rescanwallet'\n const req = { assetID: assetID }\n const res = await postJSON(url, req)\n loaded()\n if (res.code === activeOrdersErrCode) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n return\n }\n app().checkResponse(res)\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 this.openAsset = assetID\n const open = {\n assetID: assetID\n }\n const res = await postJSON('/api/openwallet', open)\n if (app().checkResponse(res)) {\n this.openWalletSuccess.bind(this)()\n } else {\n this.showOpen(assetID, `Error opening wallet: ${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 this.openAsset = this.lastFormAsset = assetID\n await this.hideBox()\n this.unlockForm.refresh(app().assets[assetID])\n if (errorMsg) this.unlockForm.showErrorOnly(errorMsg)\n this.animation = this.showBox(page.unlockWalletForm, page.walletPass)\n }\n\n /* Show the form used to change wallet configuration settings. */\n async showReconfig (assetID: number) {\n const page = this.page\n Doc.hide(page.changeWalletType, page.changeTypeHideIcon, page.reconfigErr, page.showChangeType, page.changeTypeHideIcon)\n Doc.hide(page.reconfigErr)\n // Hide update password section by default\n this.reconfigAsset = this.lastFormAsset = assetID\n this.changeWalletPW = false\n this.setPWSettingViz(this.changeWalletPW)\n const asset = app().assets[assetID]\n\n const currentDef = app().currentWalletDefinition(assetID)\n\n if (asset.info.availablewallets.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 asset.info.availablewallets) {\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 if ((wallet.traits & traitLogFiler) !== 0) Doc.show(page.downloadLogs)\n else Doc.hide(page.downloadLogs)\n if ((wallet.traits & traitRecoverer) !== 0) Doc.show(page.recoverWallet)\n else Doc.hide(page.recoverWallet)\n if ((wallet.traits & traitRestorer)) Doc.show(page.exportWallet)\n else Doc.hide(page.exportWallet)\n\n page.recfgAssetLogo.src = Doc.logoPath(asset.symbol)\n page.recfgAssetName.textContent = asset.info.name\n await this.hideBox()\n this.animation = this.showBox(page.reconfigForm)\n const loaded = app().loading(page.reconfigForm)\n const res = await postJSON('/api/walletsettings', {\n assetID: assetID\n })\n loaded()\n if (!app().checkResponse(res, true)) {\n page.reconfigErr.textContent = res.msg\n Doc.show(page.reconfigErr)\n return\n }\n const walletIsActive = app().walletIsActive(assetID)\n this.reconfigForm.update(currentDef.configopts || [], walletIsActive)\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.reconfigAsset, walletType)\n this.reconfigForm.update(walletDef.configopts || [])\n this.updateDisplayedReconfigFields(walletDef)\n }\n\n updateDisplayedReconfigFields (walletDef: WalletDefinition) {\n if (walletDef.seeded) {\n Doc.hide(this.page.showChangePW)\n this.changeWalletPW = false\n this.setPWSettingViz(false)\n } else Doc.show(this.page.showChangePW)\n }\n\n /* Display a deposit address. */\n async showDeposit (assetID: number) {\n const page = this.page\n Doc.hide(page.depositErr)\n const box = page.deposit\n const asset = app().assets[assetID]\n page.depositLogo.src = Doc.logoPath(asset.symbol)\n const wallet = app().walletMap[assetID]\n this.depositAsset = this.lastFormAsset = assetID\n if (!wallet) {\n app().notify(ntfn.make('Cannot retrieve deposit address.', `No wallet found for ${asset.info.name}`, ntfn.ERROR)) // TODO: translate\n return\n }\n await this.hideBox()\n page.depositName.textContent = asset.info.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 this.animation = this.showBox(box)\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(page.deposit)\n const res = await postJSON('/api/depositaddress', {\n assetID: this.depositAsset\n })\n loaded()\n if (!app().checkResponse(res, true)) {\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 /* 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 asset = this.sendAsset = app().assets[assetID]\n this.lastFormAsset = assetID\n const wallet = app().walletMap[assetID]\n if (!wallet) {\n app().notify(ntfn.make('Cannot send/withdraw.', `No wallet found for ${asset.info.name}`, ntfn.ERROR))\n }\n await this.hideBox()\n\n Doc.hide(page.senderOnlyHelpText)\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.senderOnlyHelpText)\n page.subtractCheckBox.checked = false\n } else {\n Doc.show(page.toggleSubtract)\n }\n\n page.sendAddr.value = ''\n page.sendAmt.value = ''\n page.sendPW.value = ''\n page.sendErr.textContent = ''\n\n this.showFiatValue(asset.id, 0, page.sendValue)\n page.sendAvail.textContent = Doc.formatFullPrecision(wallet.balance.available, asset.info.unitinfo)\n page.sendLogo.src = Doc.logoPath(asset.symbol)\n page.sendName.textContent = asset.info.name\n // page.sendFee.textContent = wallet.feerate\n // page.sendUnit.textContent = wallet.units\n box.dataset.assetID = String(assetID)\n this.animation = this.showBox(box, page.walletPass)\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', {\n assetID: assetID\n })\n loaded()\n if (!app().checkResponse(res)) return\n const rowInfo = this.rowInfos[assetID]\n Doc.hide(rowInfo.actions.connect)\n }\n\n /* createWalletSuccess is the success callback for wallet creation. */\n async createWalletSuccess () {\n const rowInfo = this.rowInfos[this.walletAsset]\n this.showMarkets(rowInfo.assetID)\n await app().fetchUser()\n await app().loadPage('wallets')\n }\n\n /* openWalletSuccess is the success callback for wallet unlocking. */\n async openWalletSuccess () {\n const rowInfo = this.rowInfos[this.openAsset]\n const a = rowInfo.actions\n Doc.show(a.send, a.deposit)\n Doc.hide(a.unlock, a.connect)\n if (app().walletMap[rowInfo.assetID].encrypted) {\n Doc.show(a.lock)\n }\n this.showMarkets(this.openAsset)\n }\n\n /* send submits the send form to the API. */\n async send () {\n const page = this.page\n Doc.hide(page.sendErr)\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 open = {\n assetID: assetID,\n address: page.sendAddr.value,\n subtract: subtract,\n value: Math.round(parseFloat(page.sendAmt.value || '') * conversionFactor),\n pw: page.sendPW.value\n }\n const loaded = app().loading(page.sendForm)\n const res = await postJSON('/api/send', open)\n loaded()\n if (!app().checkResponse(res, true)) {\n page.sendErr.textContent = res.msg\n Doc.show(page.sendErr)\n return\n }\n this.showMarkets(assetID)\n }\n\n /* update wallet configuration */\n async reconfig () {\n const page = this.page\n Doc.hide(page.reconfigErr)\n if (!page.appPW.value && !State.passwordIsCached()) {\n page.reconfigErr.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.reconfigErr)\n return\n }\n\n let walletType = app().currentWalletDefinition(this.reconfigAsset).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: this.reconfigAsset,\n config: this.reconfigForm.map(),\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, true)) {\n page.reconfigErr.textContent = res.msg\n Doc.show(page.reconfigErr)\n return\n }\n this.showMarkets(this.reconfigAsset)\n }\n\n /* lock instructs the API to lock the wallet. */\n async lock (assetID: number, asset: RowInfo) {\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 const a = asset.actions\n Doc.hide(a.send, a.lock, a.deposit)\n Doc.show(a.unlock)\n }\n\n async downloadLogs () {\n const search = new URLSearchParams('')\n search.append('assetid', `${this.reconfigAsset}`)\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 () {\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 () {\n const page = this.page\n const req = {\n assetID: this.reconfigAsset,\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 page.exportWalletErr.textContent = res.msg\n Doc.show(page.exportWalletErr)\n }\n }\n\n // displayRestoreWalletInfo displays the information needed to restore a\n // wallet in external wallets.\n async displayRestoreWalletInfo (info: WalletRestoration[]) {\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 () {\n const page = this.page\n Doc.hide(page.recoverWalletErr)\n const req = {\n assetID: this.reconfigAsset,\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 === activeOrdersErrCode) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n } else if (app().checkResponse(res)) {\n this.closePopups()\n } else {\n page.recoverWalletErr.textContent = res.msg\n Doc.show(page.recoverWalletErr)\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 () {\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 page.confirmForceErr.textContent = res.msg\n Doc.show(page.confirmForceErr)\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) {\n const td = Doc.safeSelector(this.page.walletTable, `[data-balance-target=\"${note.assetID}\"]`)\n td.textContent = Doc.formatFullPrecision(note.balance.available, app().unitInfo(note.assetID))\n const fiatDisplay = Doc.safeSelector(this.page.walletTable, `[data-conversion-target=\"${note.assetID}\"]`)\n if (!fiatDisplay) return\n this.showFiatValue(note.assetID, note.balance.available, fiatDisplay)\n }\n\n /* handleRatesNote handles fiat rate notifications, updating the fiat value of\n * all supported assets.\n */\n handleRatesNote (note: RateNote) {\n app().fiatRatesMap = note.fiatRates\n for (const [assetID, wallet] of (Object.entries(app().walletMap) as [any, WalletState][])) {\n if (!wallet) continue\n const fiatDisplay = this.page.walletTable.querySelector(`[data-conversion-target=\"${assetID}\"]`) as PageElement\n if (!fiatDisplay) continue\n this.showFiatValue(assetID, wallet.balance.available, fiatDisplay)\n }\n }\n\n // showFiatValue displays the fiat equivalent for the provided amount.\n showFiatValue (assetID: number, amount: number, display: PageElement) {\n if (display) {\n const rate = app().fiatRatesMap[assetID]\n display.textContent = Doc.formatFiatConversion(amount, 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 /*\n * handleWalletStateNote is a handler for both the 'walletstate' and\n * 'walletconfig' notifications.\n */\n handleWalletStateNote (note: WalletStateNote) {\n this.rowInfos[note.wallet.assetID].stateIcons.readWallet(note.wallet)\n const fiatDisplay = this.page.walletTable.querySelector(`[data-conversion-target=\"${note.wallet.assetID}\"]`) as PageElement\n if (!fiatDisplay) return\n this.showFiatValue(note.wallet.assetID, note.wallet.balance.available, fiatDisplay)\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /wallets page.\n */\n unload () {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n}\n\n/*\n * Given a market object as created with makeMarket, prettyMarketName will\n * create a string ABC-XYZ, where ABC and XYZ are the upper-case ticker symbols\n * for the base and quote assets respectively.\n */\nfunction prettyMarketName (market: Market) {\n return `${market.basesymbol.toUpperCase()}-${market.quotesymbol.toUpperCase()}`\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'\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 defaultTLSText: string\n keyup: (e: KeyboardEvent) => void\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n this.defaultTLSText = 'none selected'\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.dark(page.darkMode.checked || false)\n if (page.darkMode.checked) {\n document.body.classList.add('dark')\n } else {\n document.body.classList.remove('dark')\n }\n })\n\n Doc.bind(page.showPokes, 'click', () => {\n const show = page.showPokes.checked || false\n State.setCookie('popups', 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 => {\n this.confirmRegisterForm.setAsset(assetID)\n\n const asset = app().assets[assetID]\n const wallet = asset.wallet\n if (wallet) {\n const fee = this.currentDEX.regFees[asset.symbol]\n if (wallet.synced && wallet.balance.available > fee.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.newWalletForm.loadDefaults()\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, true)) {\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 feeAmt = this.currentDEX.regFees[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > feeAmt) {\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 = '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 req = {\n pw: pw,\n account: account\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 const loginResponse = await postJSON('/api/login', { pass: pw })\n if (!app().checkResponse(loginResponse)) {\n page.importAccountErr.textContent = loginResponse.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, true)) {\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","import Doc 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 unbind = Doc.unbind\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: '#3a3a3a',\n gridLines: '#2a2a2a',\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: '#3a3a3a',\n gridLines: '#dadada',\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.\nclass Chart {\n parent: HTMLElement\n report: ChartReporters\n theme: Theme\n canvas: HTMLCanvasElement\n visible: 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\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 this.setZoomBttns()\n // Mouse handling\n this.mousePos = null\n bind(this.canvas, 'mousemove', (e: MouseEvent) => {\n // this.rect will be set in resize().\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 // 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 this.boundResizer = () => { this.resize(parent.clientHeight) }\n bind(window, 'resize', this.boundResizer)\n bind(this.canvas, 'click', (e: MouseEvent) => { this.click(e) })\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 // setZoomBttns is run before drawing and should be used for setup of zoom\n // buttons.\n setZoomBttns () {\n // should be implemented by inheriting class.\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 programatically after the caller sets a style.height\n * but before the clientHeight has been updated.\n */\n resize (parentHeight: number) {\n this.canvas.width = this.parent.clientWidth\n this.canvas.height = parentHeight - 20 // magic number derived from a soup of css values.\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 /* hide hides the canvas */\n hide () {\n this.visible = false\n Doc.hide(this.canvas)\n }\n\n /* show shows the canvas */\n show () {\n this.visible = true\n Doc.show(this.canvas)\n this.resize(this.parent.clientHeight)\n }\n\n /* The market handler will call unattach when the markets page is unloaded. */\n unattach () {\n unbind(window, 'resize', this.boundResizer)\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 () {\n this.ctx.textAlign = 'center'\n this.ctx.textBaseline = 'middle'\n this.ctx.font = '12px \\'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 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) * 1.5\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.resize(parent.clientHeight)\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) return\n\n this.clear()\n // if (!this.book || this.book.empty()) return\n\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(parent.clientHeight)\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) return\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 * bFactor / qFactor\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\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 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","// MessageSocket is a WebSocket manager that uses the Decred DEX Mesage 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} from './charts'\nimport { postJSON } from './http'\nimport { NewWalletForm, UnlockWalletForm, AccelerateOrderForm, bind as bindForm } 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 SpotPriceNote,\n FeePaymentNote,\n OrderNote,\n EpochNote,\n BalanceNote,\n MiniOrder,\n RemainderUpdate,\n ConnEventNote,\n Spot,\n OrderOption,\n ConnectionStatus\n} from './registry'\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'\n\nconst lastMarketKey = 'selectedMarket'\nconst chartRatioKey = 'chartRatio'\nconst depthZoomKey = 'depthZoom'\n\nconst animationLength = 500\n\nconst anHour = 60 * 60 * 1000 // milliseconds\n\nconst depthChart = 'depth_chart'\nconst candleChart = 'candle_chart'\n\nconst check = document.createElement('span')\ncheck.classList.add('ico-check')\n\nconst percentFormatter = new Intl.NumberFormat(document.documentElement.lang, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 2\n})\n\ninterface MetaOrder {\n row: HTMLElement\n order: 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 BalanceWidgetElement {\n id: number\n cfg: Asset | null\n logo: PageElement\n avail: PageElement\n newWalletRow: PageElement\n newWalletBttn: PageElement\n locked: PageElement\n immature: PageElement\n unsupported: PageElement\n expired: PageElement\n connect: PageElement\n spinner: PageElement\n iconBox: PageElement\n stateIcons: WalletIcons\n}\n\ninterface LoadTracker {\n loaded: () => void,\n timer: number\n}\n\ninterface OrderRow extends HTMLElement {\n manager: OrderTableRowManager\n}\n\nexport default class MarketsPage extends BasePage {\n page: Record\n main: HTMLElement\n loaded: (() => void) | null\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 ordersSortKey: string\n ordersSortDirection: 1 | -1\n ogTitle: string\n depthChart: DepthChart\n candleChart: CandleChart\n currentChart: string\n candleDur: string\n balanceWgt: BalanceWidget\n marketList: MarketList\n quoteUnits: NodeListOf\n baseUnits: NodeListOf\n unlockForm: UnlockWalletForm\n newWalletForm: NewWalletForm\n keyup: (e: KeyboardEvent) => void\n secondTicker: number\n candlesLoading: LoadTracker | null\n accelerateOrderForm: AccelerateOrderForm\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 this.loaded = app().loading(this.main.parentElement)\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.preorderCache = {}\n this.depthLines = {\n hover: [],\n input: []\n }\n this.hovers = []\n // 'Your Orders' list sort key and direction.\n this.ordersSortKey = 'submitTime'\n // 1 if sorting ascendingly, -1 if sorting descendingly.\n this.ordersSortDirection = 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.marketChart, depthReporters, State.fetch(depthZoomKey))\n\n const candleReporters: CandleReporters = {\n mouse: c => { this.reportMouseCandle(c) }\n }\n this.candleChart = new CandleChart(page.marketChart, 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.candleChart.hide()\n this.currentChart = depthChart\n this.candleDur = ''\n\n // Set up the BalanceWidget.\n {\n const wgt = this.balanceWgt = new BalanceWidget(page.balanceTable)\n const baseIcons = wgt.base.stateIcons.icons\n const quoteIcons = wgt.quote.stateIcons.icons\n bind(wgt.base.connect, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.connect, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(wgt.base.expired, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.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(wgt.base.newWalletBttn, 'click', () => { this.showCreate(this.market.base) })\n bind(wgt.quote.newWalletBttn, 'click', () => { this.showCreate(this.market.quote) })\n }\n\n // Prepare templates for the buy and sell tables and the user's order table.\n OrderUtil.setOptionTemplates(page)\n Doc.cleanTemplates(page.rowTemplate, page.liveTemplate, page.durBttnTemplate, page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl)\n\n // Prepare the list of markets.\n this.marketList = new MarketList(page.marketList)\n for (const xc of this.marketList.xcSections) {\n if (!xc.marketRows) continue\n for (const row of xc.marketRows) {\n bind(row.node, 'click', () => {\n this.setMarket(xc.host, row.mkt.baseid, row.mkt.quoteid)\n })\n }\n }\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('sellred')\n page.submitBttn.classList.add('buygreen')\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('sellred')\n page.submitBttn.classList.remove('buygreen')\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 bind(page.depthBttn, 'click', () => {\n this.depthChartSelected()\n })\n bind(page.candlestickBttn, 'click', () => {\n this.candleChartSelected()\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 intial candlestick data on the 'candles' route.\n ws.registerRoute(candleUpdateRoute, (data: BookUpdate) => { this.handleCandleUpdateRoute(data) })\n // Handle the candles update on the 'candles' route.\n ws.registerRoute(candlesRoute, (data: BookUpdate) => { this.handleCandlesRoute(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.liveTable.querySelectorAll('[data-ordercol]')\n .forEach((th: HTMLElement) => bind(th, 'click', () => setOrdersSortCol(th.dataset.ordercol || '')))\n\n const setOrdersSortCol = (key: string) => {\n // First unset header's current sorted col classes.\n unsetOrdersSortColClasses()\n // If already sorting by key change sort direction.\n if (this.ordersSortKey === key) {\n this.ordersSortDirection *= -1\n } else {\n // Otherwise update sort key and set default direction to ascending.\n this.ordersSortKey = key\n this.ordersSortDirection = 1\n }\n this.refreshActiveOrders()\n // Set header's new sorted col classes.\n setOrdersSortColClasses()\n }\n\n const sortClassByDirection = () => {\n if (this.ordersSortDirection === 1) return 'sorted-asc'\n return 'sorted-dsc'\n }\n\n const unsetOrdersSortColClasses = () => {\n page.liveTable.querySelectorAll('[data-ordercol]')\n .forEach(th => th.classList.remove('sorted-asc', 'sorted-dsc'))\n }\n\n const setOrdersSortColClasses = () => {\n const key = this.ordersSortKey\n const sortCls = sortClassByDirection()\n Doc.safeSelector(page.liveTable, `[data-ordercol=${key}]`).classList.add(sortCls)\n }\n\n // Set default's sorted col header classes.\n setOrdersSortColClasses()\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.marketSearch, 'change', () => { this.filterMarkets() })\n bind(page.marketSearch, 'keyup', () => { this.filterMarkets() })\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.liveList, 'mouseleave', () => {\n this.activeMarkerRate = null\n this.setDepthMarkers()\n })\n\n // Load the user's layout preferences.\n const setChartRatio = (r: number) => {\n if (r > 0.7) r = 0.7\n else if (r < 0.25) r = 0.25\n\n const h = r * (this.main.clientHeight - app().header.offsetHeight)\n page.marketChart.style.height = `${h}px`\n\n this.depthChart.resize(h)\n this.candleChart.resize(h)\n }\n const chartDivRatio = State.fetch(chartRatioKey)\n if (chartDivRatio) {\n setChartRatio(chartDivRatio)\n }\n // Bind chart resizing.\n bind(page.chartResizer, 'mousedown', (e: MouseEvent) => {\n if (e.button !== 0) return\n e.preventDefault()\n let chartRatio: number\n const trackMouse = (ee: MouseEvent) => {\n ee.preventDefault()\n const box = page.rightSide.getBoundingClientRect()\n const h = box.bottom - box.top\n chartRatio = (ee.pageY - box.top) / h\n setChartRatio(chartRatio)\n }\n bind(document, 'mousemove', trackMouse)\n bind(document, 'mouseup', () => {\n if (chartRatio) State.store(chartRatioKey, chartRatio)\n Doc.unbind(document, 'mousemove', trackMouse)\n })\n })\n\n // Notification filters.\n app().registerNoteFeeder({\n order: (note: OrderNote) => { this.handleOrderNote(note) },\n epoch: (note: EpochNote) => { this.handleEpochNote(note) },\n conn: (note: ConnEventNote) => { this.handleConnNote(note) },\n balance: (note: BalanceNote) => { this.handleBalanceNote(note) },\n feepayment: (note: FeePaymentNote) => { this.handleFeePayment(note) },\n spots: (note: SpotPriceNote) => { this.handlePriceUpdate(note) }\n })\n\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.fetch(lastMarketKey)\n }\n if (!selected || !this.marketList.exists(selected.host, selected.base, selected.quote)) {\n selected = this.marketList.first()\n }\n this.setMarket(selected.host, selected.base, selected.quote)\n\n // Start a ticker to update time-since values.\n this.secondTicker = window.setInterval(() => {\n for (const metaOrder of Object.values(this.metaOrders)) {\n const td = Doc.tmplElement(metaOrder.row, 'age')\n td.textContent = Doc.timeSince(metaOrder.order.submitTime)\n }\n }, 1000)\n\n // set the initial state for the registration status\n this.setRegistrationStatusVisibility()\n this.setBalanceVisibility()\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 /* hasFeePending is true if the fee payment is pending */\n hasFeePending () {\n return !!this.market.dex.pendingFee\n }\n\n /* assetsAreSupported is true if all the assets of the current market are\n * supported\n */\n assetsAreSupported () {\n const [b, q] = [this.market.base, this.market.quote]\n return b && q\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)\n const feePaid = !this.hasFeePending()\n const assetsAreSupported = this.assetsAreSupported()\n const { base, quote } = this.market\n const hasWallets = base && app().assets[base.id].wallet && quote && app().assets[quote.id].wallet\n\n if (feePaid && assetsAreSupported && hasWallets) {\n Doc.show(page.orderForm)\n }\n }\n\n /* setLoaderMsgVisibility displays a message in case a dex asset is not\n * supported\n */\n setLoaderMsgVisibility () {\n const { page, market } = this\n\n if (this.assetsAreSupported()) {\n // make sure to hide the loader msg\n Doc.hide(page.loaderMsg)\n return\n }\n const symbol = market.base ? market.quoteCfg.symbol : market.baseCfg.symbol\n page.loaderMsg.textContent = intl.prep(intl.ID_NOT_SUPPORTED, { asset: symbol.toUpperCase() })\n Doc.show(page.loaderMsg)\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 const pending = dex.pendingFee\n if (!pending) {\n this.setRegistrationStatusView(intl.prep(intl.ID_REGISTRATION_FEE_SUCCESS), '', 'completed')\n return\n }\n\n const confirmationsRequired = dex.regFees[pending.symbol].confs\n page.confReq.textContent = String(confirmationsRequired)\n const confStatusMsg = `${pending.confs} / ${confirmationsRequired}`\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: { dex } } = this\n\n // If dex is not connected to server, is not possible to know fee\n // registration status.\n if (dex.connectionStatus !== ConnectionStatus.Connected) return\n\n this.updateRegistrationStatusView()\n\n if (dex.pendingFee) {\n Doc.show(page.registrationStatus)\n } else {\n const toggle = () => {\n Doc.hide(page.registrationStatus)\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 }\n }\n\n setOrderBttnText () {\n if (this.isSell()) {\n this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_SELL, { asset: this.market.baseCfg.symbol.toUpperCase() })\n } else this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_BUY, { asset: this.market.baseCfg.symbol.toUpperCase() })\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 // 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.connectionStatus !== ConnectionStatus.Connected) {\n // TODO: Figure out why this was like this.\n // this.market = { dex: dex }\n\n page.chartErrMsg.textContent = intl.prep(intl.ID_CONNECTION_FAILED)\n Doc.show(page.chartErrMsg)\n if (this.loaded) this.loaded()\n this.main.style.opacity = '1'\n Doc.hide(page.marketLoader)\n return\n }\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 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: app().assets[base],\n quote: app().assets[quote],\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.show(page.marketLoader)\n if (!dex.candleDurs || dex.candleDurs.length === 0) this.currentChart = depthChart\n if (this.currentChart === depthChart) ws.request('loadmarket', makeMarket(host, base, quote))\n else {\n if (dex.candleDurs.indexOf(this.candleDur) === -1) this.candleDur = dex.candleDurs[0]\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)\n return\n }\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 metaOrd of Object.values(this.metaOrders)) {\n const [row, ord] = [metaOrd.row, metaOrd.order]\n if (ord.status !== OrderUtil.StatusBooked) continue\n if (r.hoverMarkers.indexOf(ord.rate) > -1) {\n row.classList.add('hover')\n this.hovers.push(row)\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 Doc.show(page.hoverData)\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.store(depthZoomKey, zoom)\n }\n\n reportMouseCandle (candle: Candle | null) {\n const page = this.page\n if (!candle) {\n Doc.hide(page.hoverData)\n return\n }\n\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 Doc.show(page.hoverData)\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 if (!limit && !sell) {\n qtyField = page.mktBuyField\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 || '', market.baseUnitInfo.conventional.conversionFactor),\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, true)) {\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 = lots === 1 ? 'lot' : '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, toAsset] = sell ? [this.market.base, this.market.quote] : [this.market.quote, this.market.base]\n page.maxFromAmt.textContent = Doc.formatCoinValue(maxOrder.value || 0, fromAsset.info.unitinfo)\n page.maxFromTicker.textContent = fromAsset.symbol.toUpperCase()\n // Could subtract the maxOrder.redemptionFees here.\n const toConversion = sell ? this.adjustedRate() / OrderUtil.RateEncodingFactor : OrderUtil.RateEncodingFactor / this.adjustedRate()\n page.maxToAmt.textContent = Doc.formatCoinValue((maxOrder.value || 0) * toConversion, toAsset.info.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 this.depthChart.set(this.book, cfg.lotsize, cfg.ratestep, baseUnitInfo, quoteUnitInfo)\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 /*\n * ordersSortCompare returns sort compare function according to the active\n * sort key and direction.\n */\n ordersSortCompare () {\n switch (this.ordersSortKey) {\n case 'submitTime':\n return (a: Order, b: Order) => this.ordersSortDirection * (b.submitTime - a.submitTime)\n case 'rate':\n return (a: Order, b: Order) => this.ordersSortDirection * (a.rate - b.rate)\n case 'qty':\n return (a: Order, b: Order) => this.ordersSortDirection * (a.qty - b.qty)\n case 'type':\n return (a: Order, b: Order) => this.ordersSortDirection *\n OrderUtil.typeString(a).localeCompare(OrderUtil.typeString(b))\n case 'sell':\n return (a: Order, b: Order) => this.ordersSortDirection *\n (OrderUtil.sellString(a)).localeCompare(OrderUtil.sellString(b))\n case 'status':\n return (a: Order, b: Order) => this.ordersSortDirection *\n (OrderUtil.statusString(a)).localeCompare(OrderUtil.statusString(b))\n case 'settled':\n return (a: Order, b: Order) => this.ordersSortDirection *\n ((OrderUtil.settled(a) * 100 / a.qty) - (OrderUtil.settled(b) * 100 / b.qty))\n case 'filled':\n return (a: Order, b: Order) => this.ordersSortDirection *\n ((OrderUtil.filled(a) * 100 / a.qty) - (OrderUtil.filled(b) * 100 / b.qty))\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 const compare = this.ordersSortCompare()\n orders.sort(compare)\n\n Doc.empty(page.liveList)\n for (const ord of orders) {\n const row = page.liveTemplate.cloneNode(true) as HTMLElement\n metaOrders[ord.id] = {\n row: row,\n order: ord\n }\n Doc.bind(row, 'mouseenter', () => {\n this.activeMarkerRate = ord.rate\n this.setDepthMarkers()\n })\n this.updateUserOrderRow(row, ord)\n if (ord.type === OrderUtil.Limit && (ord.tif === OrderUtil.StandingTiF && ord.status < OrderUtil.StatusExecuted)) {\n const icon = Doc.tmplElement(row, 'cancelBttn')\n Doc.show(icon)\n bind(icon, 'click', e => {\n e.stopPropagation()\n this.showCancel(row, ord.id)\n })\n }\n\n const accelerateBttn = Doc.tmplElement(row, 'accelerateBttn')\n bind(accelerateBttn, 'click', e => {\n e.stopPropagation()\n this.showAccelerate(ord)\n })\n if (app().canAccelerateOrder(ord)) {\n Doc.show(accelerateBttn)\n }\n\n const side = Doc.tmplElement(row, 'side')\n side.classList.add(ord.sell ? 'sellcolor' : 'buycolor')\n const link = Doc.tmplElement(row, 'link')\n link.href = `order/${ord.id}`\n app().bindInternalNavigation(row)\n page.liveList.appendChild(row)\n app().bindTooltips(row)\n }\n this.setDepthMarkers()\n }\n\n /*\n * updateUserOrderRow sets the td contents of the user's order table row.\n */\n updateUserOrderRow (tr: HTMLElement, ord: Order) {\n updateDataCol(tr, 'type', OrderUtil.typeString(ord))\n updateDataCol(tr, 'side', OrderUtil.sellString(ord))\n updateDataCol(tr, 'age', Doc.timeSince(ord.submitTime))\n updateDataCol(tr, 'rate', Doc.formatCoinValue(ord.rate / this.market.rateConversionFactor))\n updateDataCol(tr, 'qty', Doc.formatCoinValue(ord.qty, this.market.baseUnitInfo))\n updateDataCol(tr, 'filled', `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`)\n updateDataCol(tr, 'settled', `${(OrderUtil.settled(ord) / ord.qty * 100).toFixed(1)}%`)\n updateDataCol(tr, 'status', OrderUtil.statusString(ord))\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 mo of Object.values(this.metaOrders)) {\n const ord = mo.order\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 Doc.hide(page.marketLoader)\n this.marketList.select(host, b.id, q.id)\n\n State.store(lastMarketKey, {\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 => { el.textContent = b.symbol.toUpperCase() })\n this.quoteUnits.forEach(el => { el.textContent = q.symbol.toUpperCase() })\n this.balanceWgt.setWallets(host, b.id, q.id)\n this.setMarketBuyOrderEstimate()\n this.refreshActiveOrders()\n if (this.loaded) {\n this.loaded()\n this.loaded = null\n Doc.animate(250, progress => {\n this.main.style.opacity = String(progress)\n })\n }\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 this.depthChart.hide()\n this.candleChart.show()\n if (data.host !== this.market.dex.host) return\n const dur = data.payload.dur\n this.market.candleCaches[dur] = data.payload\n if (this.currentChart !== candleChart || this.candleDur !== dur) return\n this.candleChart.setCandles(data.payload, this.market.cfg, this.market.baseUnitInfo, this.market.quoteUnitInfo)\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.currentChart !== candleChart || 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(page.unlockWalletForm, page.verifyForm, page.newWalletForm,\n page.cancelForm, page.vDetailPane, 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 = '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 /* 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 // Set the to and from icons in the fee details pane.\n for (const icon of Doc.applySelector(page.vDetailPane, '[data-icon]')) {\n switch (icon.dataset.icon) {\n case 'from':\n icon.src = Doc.logoPath(fromAsset.symbol)\n break\n case 'to':\n icon.src = Doc.logoPath(toAsset.symbol)\n }\n }\n\n Doc.hide(page.vUnlockPreorder, page.vPreorderErr)\n Doc.show(page.vPreorder)\n\n page.vBuySell.textContent = isSell ? 'Selling' : '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.info.unitinfo)\n const total = order.rate / OrderUtil.RateEncodingFactor * order.qty\n page.vTotal.textContent = Doc.formatCoinValue(total, quoteAsset.info.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.info.unitinfo)\n page.vmToAsset.textContent = toAsset.symbol.toUpperCase()\n // Format recieved 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 const buyBtnClass = 'buygreen'\n const sellBtnClass = 'sellred'\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, true)) {\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, true)) 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 /* preOrder loads the options and fetches pre-order estimates */\n async preOrder (order: TradeForm) {\n // if (!this.validateOrder(order)) return\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 Doc.empty(page.vOrderOpts)\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 const addOption = (opt: OrderOption, isSwap: boolean) => page.vOrderOpts.appendChild(OrderUtil.optionElement(opt, order, changed, isSwap))\n for (const opt of swap.options || []) addOption(opt, true)\n for (const opt of redeem.options || []) addOption(opt, false)\n app().bindTooltips(page.vOrderOpts)\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 const { baseUnitInfo, quoteUnitInfo, rateConversionFactor } = market\n const swapped = swap.estimate.value || 0\n const fmtPct = percentFormatter.format\n\n let [toUI, fromUI] = [baseUnitInfo, quoteUnitInfo]\n if (this.currentOrder.sell) {\n [fromUI, toUI] = [toUI, fromUI]\n }\n\n // Set swap fee estimates in the details pane.\n const bestSwapPct = swap.estimate.realisticBestCase / swapped * 100\n page.vSwapFeesLowPct.textContent = `${fmtPct(bestSwapPct)}%`\n page.vSwapFeesLow.textContent = Doc.formatCoinValue(swap.estimate.realisticBestCase, fromUI)\n const worstSwapPct = swap.estimate.realisticWorstCase / swapped * 100\n page.vSwapFeesHighPct.textContent = `${fmtPct(worstSwapPct)}%`\n page.vSwapFeesHigh.textContent = Doc.formatCoinValue(swap.estimate.realisticWorstCase, fromUI)\n page.vSwapFeesMaxPct.textContent = `${fmtPct(swap.estimate.maxFees / swapped * 100)}%`\n page.vSwapFeesMax.textContent = Doc.formatCoinValue(swap.estimate.maxFees, fromUI)\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 bestRedeemPct = redeem.estimate.realisticBestCase / received * 100\n page.vRedeemFeesLowPct.textContent = `${fmtPct(bestRedeemPct)}%`\n page.vRedeemFeesLow.textContent = Doc.formatCoinValue(redeem.estimate.realisticBestCase, toUI)\n const worstRedeemPct = redeem.estimate.realisticWorstCase / received * 100\n page.vRedeemFeesHighPct.textContent = `${fmtPct(worstRedeemPct)}%`\n page.vRedeemFeesHigh.textContent = Doc.formatCoinValue(redeem.estimate.realisticWorstCase, toUI)\n\n // Set the summary percent, which is a simple addition of swap and redeem\n // loss percents.\n page.vFeeSummaryLow.textContent = fmtPct(bestSwapPct + bestRedeemPct)\n page.vFeeSummaryHigh.textContent = fmtPct(worstSwapPct + worstRedeemPct)\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, true)) {\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 order = this.metaOrders[orderID].order\n const page = this.page\n const remaining = order.qty - order.filled\n const asset = OrderUtil.isMarketBuy(order) ? this.market.quote : this.market.base\n page.cancelRemain.textContent = Doc.formatCoinValue(remaining, asset.info.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: order\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 this.newWalletForm.loadDefaults()\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 handlePriceUpdate (note: SpotPriceNote) {\n const xcSection = this.marketList.xcSection(note.host)\n if (!xcSection) return\n for (const spot of Object.values(note.spots)) {\n const marketRow = xcSection.marketRow(spot.baseID, spot.quoteID)\n if (marketRow) marketRow.setSpot(spot)\n }\n }\n\n /*\n * handleFeePayment is the handler for the 'feepayment' notification type.\n * This is used to update the registration status of the current exchange.\n */\n handleFeePayment (note: FeePaymentNote) {\n const dexAddr = note.dex\n if (dexAddr !== this.market.dex.host) return\n // update local dex\n this.market.dex = app().exchanges[dexAddr]\n this.setRegistrationStatusVisibility()\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 metaOrder = this.metaOrders[order.id]\n // If metaOrder doesn't exist for the given order it means it was\n // created via dexcctl and the GUI isn't aware of it.\n // Call refreshActiveOrders to grab the order.\n if (!metaOrder) return this.refreshActiveOrders()\n const oldStatus = metaOrder.status\n metaOrder.order = order\n const cancelBttn = Doc.tmplElement(metaOrder.row, 'cancelBttn')\n if (note.topic === 'MissedCancel') Doc.show(cancelBttn)\n if (order.filled === order.qty) Doc.hide(cancelBttn)\n const accelerateBttn = Doc.tmplElement(metaOrder.row, 'accelerateBttn')\n if (app().canAccelerateOrder(order)) Doc.show(accelerateBttn)\n else Doc.hide(accelerateBttn)\n this.updateUserOrderRow(metaOrder.row, order)\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 metaOrder of Object.values(this.metaOrders)) {\n const order = metaOrder.order\n const alreadyMatched = note.epoch > order.epoch\n const statusTD = Doc.tmplElement(metaOrder.row, 'status')\n switch (true) {\n case order.type === OrderUtil.Limit && order.status === OrderUtil.StatusEpoch && alreadyMatched:\n statusTD.textContent = order.tif === OrderUtil.ImmediateTiF ? intl.prep(intl.ID_EXECUTED) : intl.prep(intl.ID_BOOKED)\n order.status = order.tif === OrderUtil.ImmediateTiF ? OrderUtil.StatusExecuted : OrderUtil.StatusBooked\n break\n case order.type === OrderUtil.Market && order.status === OrderUtil.StatusEpoch:\n // Technically don't know if this should be 'executed' or 'settling'.\n statusTD.textContent = intl.prep(intl.ID_EXECUTED)\n order.status = OrderUtil.StatusExecuted\n break\n }\n }\n }\n\n setBalanceVisibility () {\n if (this.market.dex.connectionStatus === ConnectionStatus.Connected) {\n Doc.show(this.page.balanceTable)\n } else {\n Doc.hide(this.page.balanceTable)\n }\n }\n\n /* handleBalanceNote handles notifications updating a wallet's balance. */\n handleBalanceNote (note: BalanceNote) {\n this.setBalanceVisibility()\n // if connection to dex server fails, it is not possible to retrieve\n // markets.\n if (this.market.dex.connectionStatus !== ConnectionStatus.Connected) return\n // If there's a balance update, refresh the max order section.\n const mkt = this.market\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/trade', req)\n // Hide loader and show submit button.\n page.vSubmit.classList.remove('d-hide')\n page.vLoader.classList.add('d-hide')\n // If errors display error on confirmation modal.\n if (!app().checkResponse(res, true)) {\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 this.depthChart.draw()\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 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 // 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 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.marketSearch.value\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 /*\n * depthChartSelected is called when the user clicks a button to show the\n * depth chart.\n */\n depthChartSelected () {\n const page = this.page\n Doc.hide(page.depthBttn, page.durBttnBox, page.candleHoverData)\n Doc.show(page.candlestickBttn, page.epochLine, page.depthHoverData, page.depthSummary)\n this.currentChart = depthChart\n this.depthChart.show()\n this.candleChart.hide()\n }\n\n /*\n * candleChartSelected is called when the user clicks a button to show the\n * historical market data (candlestick) chart.\n */\n candleChartSelected () {\n const page = this.page\n const dex = this.market.dex\n this.currentChart = candleChart\n Doc.hide(page.candlestickBttn, page.epochLine, page.depthHoverData, page.depthSummary)\n Doc.show(page.depthBttn, page.durBttnBox, page.candleHoverData)\n if (dex.candleDurs.indexOf(this.candleDur) === -1) this.candleDur = dex.candleDurs[0]\n this.loadCandles()\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: () => { Doc.hide(this.page.marketLoader) },\n timer: window.setTimeout(() => {\n if (this.candlesLoading) {\n this.candlesLoading = null\n Doc.hide(this.page.marketLoader)\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(epochOrderRoute)\n ws.deregisterRoute(bookOrderRoute)\n ws.deregisterRoute(unbookOrderRoute)\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 selected: MarketRow\n\n constructor (div: HTMLElement) {\n const xcTmpl = Doc.tmplElement(div, 'xc')\n Doc.cleanTemplates(xcTmpl)\n this.xcSections = []\n for (const dex of Object.values(app().user.exchanges)) {\n this.xcSections.push(new ExchangeSection(xcTmpl, dex))\n }\n // Initial sort is alphabetical.\n for (const xc of this.sortedSections()) {\n div.appendChild(xc.node)\n }\n }\n\n /*\n * sortedSections returns a list of ExchangeSection sorted alphabetically by\n * host.\n */\n sortedSections () {\n return [...this.xcSections].sort((a, b) => a.host < b.host ? -1 : 1)\n }\n\n /*\n * xcSection is a getter for the ExchangeSection for a specified host.\n */\n xcSection (host: string) {\n for (const xc of this.xcSections) {\n if (xc.host === host) return xc\n }\n return null\n }\n\n /* exists will be true if the specified market exists. */\n exists (host: string, baseID: number, quoteID: number) {\n const xc = this.xcSection(host)\n // If connecting to an offline server the client is not able to get xc.marketRows.\n // Therefore we must check for it.\n if (!xc || !xc.marketRows) return false\n for (const mkt of xc.marketRows) {\n if (mkt.baseID === baseID && mkt.quoteID === quoteID) return true\n }\n return false\n }\n\n /* first gets the first market from the first exchange, alphabetically. */\n first () {\n const firstXC = this.sortedSections()[0]\n const firstMkt = firstXC.first()\n // Cannot find markets if server connection failed.\n if (!firstMkt) return makeMarket(firstXC.host)\n return makeMarket(firstXC.host, firstMkt.baseID, firstMkt.quoteID)\n }\n\n /* select sets the specified market as selected. */\n select (host: string, baseID: number, quoteID: number) {\n if (this.selected) this.selected.node.classList.remove('selected')\n const xcSection = this.xcSection(host)\n if (!xcSection) return console.error(`select: no exchange section for ${host}`)\n const marketRow = xcSection.marketRow(baseID, quoteID)\n if (!marketRow) return console.error(`select: no market row for ${host}, ${baseID}-${quoteID}`)\n this.selected = marketRow\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 const xcSection = this.xcSection(note.host)\n if (!xcSection) return console.error(`setConnectionStatus: no exchange section for ${note.host}`)\n xcSection.setConnected(note.connectionStatus === ConnectionStatus.Connected)\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 xc of this.xcSections) {\n xc.setFilter(filter)\n }\n }\n}\n\n/*\n * ExchangeSection is a top level section of the MarketList.\n */\nclass ExchangeSection {\n marketRows: MarketRow[]\n host: string\n dex: Exchange\n node: HTMLElement\n disconnectedIco: PageElement\n\n constructor (template: HTMLElement, dex: Exchange) {\n this.dex = dex\n this.host = dex.host\n this.node = template.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(this.node)\n tmpl.header.textContent = dex.host\n\n this.disconnectedIco = tmpl.disconnected\n if (dex.connectionStatus === ConnectionStatus.Connected) Doc.hide(tmpl.disconnected)\n\n tmpl.mkts.removeChild(tmpl.mktrow)\n // If disconnected is not possible to get the markets from the server.\n if (!dex.markets) return\n\n this.marketRows = Object.values(dex.markets).map(mkt => {\n const bui = app().unitInfo(mkt.baseid, dex)\n const qui = app().unitInfo(mkt.quoteid, dex)\n const rateConversionFactor = OrderUtil.RateEncodingFactor / bui.conventional.conversionFactor * qui.conventional.conversionFactor\n return new MarketRow(tmpl.mktrow, mkt, rateConversionFactor)\n })\n\n // for (const mkt of Object.values(dex.markets)) {\n // this.marketRows.push(new MarketRow(tmpl.mktrow, mkt))\n // }\n for (const market of this.sortedMarkets()) {\n tmpl.mkts.appendChild(market.node)\n }\n }\n\n /*\n * sortedMarkets is the list of MarketRow sorted alphabetically by the base\n * symbol first, quote symbol second.\n */\n sortedMarkets () {\n if (!this.marketRows) return []\n return [...this.marketRows].sort((a, b) => a.name < b.name ? -1 : 1)\n }\n\n /*\n * first returns the first market in the alphabetically-sorted list of\n * markets.\n */\n first () {\n return this.sortedMarkets()[0]\n }\n\n /*\n * marketRow gets the MarketRow for the specified market.\n */\n marketRow (baseID: number, quoteID: number) {\n for (const mkt of this.marketRows) {\n if (mkt.baseID === baseID && mkt.quoteID === quoteID) return mkt\n }\n }\n\n /* setConnected sets the visiblity of the disconnected icon. */\n setConnected (isConnected: boolean) {\n if (isConnected) Doc.hide(this.disconnectedIco)\n else Doc.show(this.disconnectedIco)\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 mkt of this.marketRows) {\n if (filter(mkt)) Doc.show(mkt.node)\n else Doc.hide(mkt.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: Market\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: Market, 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.textContent = mkt.basesymbol.toUpperCase()\n tmpl.quoteSymbol.textContent = mkt.quotesymbol.toUpperCase()\n this.setSpot(mkt.spot)\n }\n\n setSpot (spot: Spot) {\n if (!spot) return\n const { tmpl, mkt } = this\n\n Doc.show(tmpl.pctChange)\n const pct = spot.change24 * 100\n const num = percentFormatter.format(pct)\n const sign = pct > 0 ? '+' : ''\n tmpl.pctChange.textContent = `${sign}${num}%`\n tmpl.pctChange.classList.remove('upgreen', 'downred', 'grey')\n tmpl.pctChange.classList.add(pct === 0 ? 'grey' : pct > 0 ? 'upgreen' : 'downred')\n const baseAsset = app().assets[mkt.baseid]\n if (baseAsset) {\n Doc.show(tmpl.bottomRow)\n tmpl.assetName.textContent = baseAsset.info.name\n tmpl.price.textContent = Doc.formatCoinValue(spot.rate / this.rateConversionFactor)\n }\n }\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 dex: Exchange\n\n constructor (table: HTMLElement) {\n const els = Doc.idDescendants(table)\n this.base = {\n id: 0,\n cfg: null,\n logo: els.baseImg,\n avail: els.baseAvail,\n newWalletRow: els.baseNewWalletRow,\n newWalletBttn: els.baseNewButton,\n locked: els.baseLocked,\n immature: els.baseImmature,\n unsupported: els.baseUnsupported,\n expired: els.baseExpired,\n connect: els.baseConnect,\n spinner: els.baseSpinner,\n iconBox: els.baseWalletState,\n stateIcons: new WalletIcons(els.baseWalletState)\n }\n this.quote = {\n id: 0,\n cfg: null,\n logo: els.quoteImg,\n avail: els.quoteAvail,\n newWalletRow: els.quoteNewWalletRow,\n newWalletBttn: els.quoteNewButton,\n locked: els.quoteLocked,\n immature: els.quoteImmature,\n unsupported: els.quoteUnsupported,\n expired: els.quoteExpired,\n connect: els.quoteConnect,\n spinner: els.quoteSpinner,\n iconBox: els.quoteWalletState,\n stateIcons: new WalletIcons(els.quoteWalletState)\n }\n\n app().registerNoteFeeder({\n balance: (note: BalanceNote) => { this.updateAsset(note.assetID) },\n walletstate: (note: WalletStateNote) => { this.updateAsset(note.wallet.assetID) }\n })\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 this.dex = app().user.exchanges[host]\n this.base.id = baseID\n this.base.cfg = this.dex.assets[baseID]\n this.quote.id = 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 if (!side.cfg) return // no wallet set yet\n const asset = app().assets[side.id]\n // Just hide everything to start.\n Doc.hide(\n side.newWalletRow, side.avail, side.immature, side.locked,\n side.expired, side.unsupported, side.connect, side.spinner, side.iconBox\n )\n side.logo.src = Doc.logoPath(side.cfg.symbol)\n // Handle an unsupported asset.\n if (!asset) {\n Doc.show(side.unsupported)\n return\n }\n Doc.show(side.iconBox)\n const wallet = asset.wallet\n side.stateIcons.readWallet(wallet)\n // Handle no wallet configured.\n if (!wallet) {\n Doc.show(side.newWalletRow)\n return\n }\n const bal = wallet.balance\n // Handle not connected and no balance known for the DEX.\n if (!bal && !wallet.running) {\n Doc.show(side.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(side.id)\n Doc.show(side.spinner)\n return\n }\n // We have a wallet and a DEX-specific balance. Set all of the fields.\n Doc.show(side.avail, side.immature, side.locked)\n side.avail.textContent = Doc.formatCoinValue(bal.available, asset.info.unitinfo)\n side.locked.textContent = Doc.formatCoinValue((bal.locked + bal.contractlocked), asset.info.unitinfo)\n side.immature.textContent = Doc.formatCoinValue(bal.immature, asset.info.unitinfo)\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) {\n Doc.show(side.expired)\n if (wallet.running) app().fetchBalance(side.id)\n } else Doc.hide(side.expired)\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 }\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 * updateDataCol sets the textContent of descendent template element.\n */\nfunction updateDataCol (tr: HTMLElement, col: string, s: string) {\n Doc.tmplElement(tr, col).textContent = s\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","import Doc from './doc'\nimport BasePage from './basepage'\nimport * as OrderUtil from './orderutil'\nimport { postJSON } from './http'\nimport {\n app,\n PageElement,\n OrderFilter,\n Order\n} from './registry'\n\nconst orderBatchSize = 50\n\nexport default class OrdersPage extends BasePage {\n main: HTMLElement\n offset: string\n loading: boolean\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 Doc.bind(page.exportOrders, 'click', () => {\n this.exportOrders()\n })\n\n this.submitFilter()\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 /*\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'\n\nconst Mainnet = 0\nconst Testnet = 1\n// const Regtest = 3\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\n constructor (main: HTMLElement) {\n super()\n const stampers = Doc.applySelector(main, '[data-stamp]')\n net = parseInt(main.dataset.net || '')\n // Find the order\n this.orderID = main.dataset.oid || ''\n const 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 this.fetchOrder()\n\n const page = this.page = Doc.idDescendants(main)\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 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 this.showAccelerationButton()\n const success = () => {\n this.refreshOnPopupClose = true\n }\n // Do not call cleanTemplates before creating the AccelerateOrderForm\n OrderUtil.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 main.querySelectorAll('[data-explorer-id]').forEach((link: PageElement) => {\n setCoinHref(link)\n })\n\n const setStamp = () => {\n for (const span of stampers) {\n span.textContent = Doc.timeSince(parseInt(span.dataset.stamp || ''))\n }\n }\n setStamp()\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\n unload () {\n clearInterval(this.secondTicker)\n }\n\n /* fetchOrder fetches the order from the client. */\n async fetchOrder () {\n const res = await postJSON('/api/order', this.orderID)\n if (!app().checkResponse(res)) return\n this.order = res.order\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.info.unitinfo)\n page.cancelUnit.textContent = asset.info.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 * showAccelerationButton shows the acceleration button if the order can\n * be accelerated.\n */\n showAccelerationButton () {\n const order = this.order\n if (!order) return\n const page = this.page\n if (app().canAccelerateOrder(order)) Doc.show(page.accelerateBttn, page.actionsLabel)\n else Doc.hide(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.showAccelerationButton()\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 }\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 // TO DO: Create a new card from template.\n return\n }\n\n const setCoin = (divName: string, linkName: string, coin: Coin) => {\n if (!card) return // Ugh\n if (!coin) return\n Doc.show(Doc.tmplElement(card, divName))\n const coinLink = Doc.tmplElement(card, linkName)\n coinLink.textContent = coin.stringID\n coinLink.dataset.explorerCoin = coin.stringID\n setCoinHref(coinLink)\n }\n\n setCoin('swap', 'swapCoin', m.swap)\n setCoin('counterSwap', 'counterSwapCoin', m.counterSwap)\n setCoin('redeem', 'redeemCoin', m.redeem)\n setCoin('counterRedeem', 'counterRedeemCoin', m.counterRedeem)\n setCoin('refund', 'refundCoin', m.refund)\n\n const swapSpan = Doc.tmplElement(card, 'swapMsg')\n const cSwapSpan = Doc.tmplElement(card, 'counterSwapMsg')\n\n if (inCounterSwapCast(m)) {\n cSwapSpan.textContent = confirmationString(m.counterSwap)\n Doc.hide(Doc.tmplElement(card, 'swapMsg'))\n Doc.show(cSwapSpan)\n } else if (inSwapCast(m)) {\n swapSpan.textContent = confirmationString(m.swap)\n Doc.hide(Doc.tmplElement(card, 'counterSwapMsg'))\n Doc.show(swapSpan)\n } else {\n Doc.hide(swapSpan, cSwapSpan)\n }\n\n Doc.tmplElement(card, 'status').textContent = OrderUtil.matchStatusString(m)\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) return ''\n return `${coin.confs.count} / ${coin.confs.required} confirmations`\n}\n\n/*\n * inCounterSwapCast will be true if we are waiting on confirmations for the\n * counterparty's swap.\n */\nfunction inCounterSwapCast (m: Match) {\n return (m.side === OrderUtil.Taker && m.status === OrderUtil.MakerSwapCast) || (m.side === OrderUtil.Maker && m.status === OrderUtil.TakerSwapCast)\n}\n\n/*\n * inCounterSwapCast will be true if we are waiting on confirmations for our own\n * swap.\n */\nfunction inSwapCast (m: Match) {\n return (m.side === OrderUtil.Maker && m.status === OrderUtil.MakerSwapCast) || (m.side === OrderUtil.Taker && m.status === OrderUtil.TakerSwapCast)\n}\n\n/*\n * setCoinHref sets the hyperlink element's href attribute based on its\n * data-explorer-id and data-explorer-coin values.\n */\nfunction setCoinHref (link: PageElement) {\n const assetExplorer = CoinExplorers[parseInt(link.dataset.explorerId || '')]\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 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: { // eth\n [Mainnet]: (cid: string) => {\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.length === 42) {\n return `https://goerli.etherscan.io/address/${cid}`\n }\n return `https://goerli.etherscan.io/tx/${cid}`\n }\n },\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'\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.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.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 /* 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 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, true)) {\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 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, true)) {\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 = 'Connected'\n break\n case ConnectionStatus.Disconnected:\n displayIcons(false)\n page.connectionStatus.textContent = 'Disconnected'\n break\n case ConnectionStatus.InvalidCert:\n displayIcons(false)\n page.connectionStatus.textContent = 'Disconnected - Invalid Certificate'\n }\n }\n }\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 { RateEncodingFactor, StatusExecuted, hasLiveMatches } from './orderutil'\nimport { getJSON, postJSON } 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 FeePaymentNote,\n CoreNote,\n OrderNote,\n Market,\n Order,\n Match,\n BalanceNote,\n WalletConfigNote,\n MatchNote,\n ConnEventNote,\n SpotPriceNote,\n UnitInfo,\n WalletDefinition,\n WalletBalance,\n LogMessage,\n NoteElement,\n BalanceResponse,\n APIResponse,\n RateNote\n} from './registry'\n\nconst idel = Doc.idel // = element by id\nconst bind = Doc.bind\nconst unbind = Doc.unbind\n\nconst notificationRoute = 'notify'\nconst loggersKey = 'loggers'\nconst recordersKey = 'recorders'\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}\n\n// unathedPages are pages that don't require authorization to load.\n// These are endpoints outside of the requireLogin block in webserver.New.\nconst unauthedPages = ['register', 'login', 'settings']\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 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 // The \"user\" is a large data structure that contains nearly all state\n // information, including exchanges, markets, wallets, orders and exchange\n // rates for assets.\n this.user = {\n exchanges: {},\n inited: false,\n seedgentime: 0,\n assets: {},\n fiatRates: {},\n authed: false,\n ok: true\n }\n this.seedGenTime = 0\n this.commitHash = process.env.COMMITHASH || ''\n this.noteReceivers = []\n this.showPopups = State.getCookie('popups') === '1'\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.fetch(loggersKey) || {}\n window.enableLogger = (loggerID, state) => {\n if (state) this.loggers[loggerID] = true\n else delete this.loggers[loggerID]\n State.store(loggersKey, 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.fetch(recordersKey) || []\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.store(recordersKey, 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.fetch('notifications')\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 it's not a page that requires auth, skip the error notification.\n const skipNote = unauthedPages.indexOf(this.main.dataset.handler || '') > -1\n if (!this.checkResponse(resp, skipNote)) 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 /* 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 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.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.noteMenuEntry, '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.noteMenuEntry, 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.profileMenuEntry, 'click', () => {\n this.showDropdown(page.profileMenuEntry, page.profileBox)\n })\n\n bind(page.innerNoteIcon, 'click', () => { Doc.hide(page.noteBox) })\n bind(page.innerProfileIcon, '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 + 11}px`\n dialog.style.top = `${ico.top - 9}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.store('notifications', 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 = this.page\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 if (!this.user.authed) {\n Doc.hide(page.noteMenuEntry, page.walletsMenuEntry, page.marketsMenuEntry, page.profileMenuEntry)\n return\n }\n Doc.show(page.noteMenuEntry, page.walletsMenuEntry, page.profileMenuEntry)\n if (Object.keys(this.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 * updateExchangeRegistration updates the information for the exchange\n * registration payment\n */\n updateExchangeRegistration (dexAddr: string, confs: number, assetID: number) {\n const dex = this.exchanges[dexAddr]\n const symbol = this.assets[assetID].symbol\n dex.pendingFee = { confs, assetID, symbol }\n }\n\n setDEXPaid (host: string) {\n // setting the null value in the 'confs' field indicates that the fee\n // payment was completed\n this.exchanges[host].pendingFee = null\n }\n\n /*\n * handleFeePaymentNote is the handler for the 'feepayment'-type notification, which\n * is used to update the dex registration status.\n */\n handleFeePaymentNote (note: FeePaymentNote) {\n switch (note.topic) {\n case 'RegUpdate':\n this.updateExchangeRegistration(note.dex, note.confirmations, note.asset)\n break\n case 'AccountRegistered':\n this.setDEXPaid(note.dex)\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 /*\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 switch (note.type) {\n case 'order': {\n const order = (note as OrderNote).order\n const mkt = this.user.exchanges[order.host].markets[order.market]\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 = this.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 'feepayment':\n this.handleFeePaymentNote(note as FeePaymentNote)\n break\n case 'walletstate':\n case 'walletconfig': {\n // assets can be null if failed to connect to dex server.\n if (!this.assets) return\n const wallet = (note as WalletConfigNote).wallet\n const asset = this.assets[wallet.assetID]\n asset.wallet = wallet\n this.walletMap[wallet.assetID] = wallet\n const bal = wallet.balance.available\n const balances = this.main.querySelectorAll(`[data-balance-target=\"${wallet.assetID}\"]`)\n balances.forEach(el => { el.textContent = Doc.formatFullPrecision(bal, asset.info.unitinfo) })\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 = this.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 = this.user.exchanges[n.host]\n // Spots can come before the user is fetched after login.\n if (!xc) 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 }\n }\n\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 }\n }\n // Discard data notifications.\n if (note.severity < ntfn.POKE) return\n // Poke notifications have their own display.\n if (this.showPopups) {\n const span = this.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 const pn = this.popupNotes\n pn.appendChild(span)\n // These take up screen space. Only show max 5 at a time.\n while (pn.children.length > 5) pn.removeChild(pn.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 orders (host: string, mktID: string): Order[] {\n let o = this.user.exchanges[host].markets[mktID].orders\n if (!o) {\n o = []\n this.user.exchanges[host].markets[mktID].orders = o\n }\n return o\n }\n\n /*\n * haveActiveOrders returns whether or not the there are active orders\n * involving a certain asset.\n */\n haveAssetOrders (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 || hasLiveMatches(ord))) return true\n }\n }\n }\n return false\n }\n\n walletIsActive (assetID: number): boolean {\n if (this.haveAssetOrders(assetID)) return true\n for (const xc of Object.values(this.user.exchanges)) {\n if (xc.pendingFee && xc.pendingFee.assetID === assetID) {\n return true\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 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.info.unitinfo\n if (!xc) {\n throw Error(`no supported asset info for id = ${assetID}, and no exchange info provided`)\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): number {\n const [b, q] = [this.unitInfo(baseID), this.unitInfo(quoteID)]\n const r = b.conventional.conversionFactor / q.conventional.conversionFactor\n return encRate / RateEncodingFactor * r\n }\n\n walletDefinition (assetID: number, walletType: string): WalletDefinition {\n const assetInfo = this.assets[assetID].info\n if (walletType === '') return assetInfo.availablewallets[assetInfo.emptyidx]\n return assetInfo.availablewallets.filter(def => def.type === walletType)[0]\n }\n\n currentWalletDefinition (assetID: number): WalletDefinition {\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, a\n * message will be displayed in the drop-down notifications and false will be\n * returned.\n */\n checkResponse (resp: APIResponse, skipNote?: boolean): boolean {\n if (!resp.requestSuccessful || !resp.ok) {\n if (this.user.inited && !skipNote) this.notify(ntfn.make(intl.prep(intl.ID_API_ERROR), resp.msg, ntfn.ERROR))\n return false\n }\n return true\n }\n\n /**\n * signOut call to /api/logout, if response with no errors occurred clear all\n * store, remove auth, darkMode cookies and reload the page, otherwise will\n * show a notification\n */\n async signOut () {\n const res = await postJSON('/api/logout')\n if (!this.checkResponse(res)) {\n Doc.hide(this.page.profileBox)\n return\n }\n State.clearAllStore()\n State.removeAuthCK()\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 Application from './js/app'\nimport { registerApplication } from './js/registry'\nimport './css/application.scss'\n\nconst app = new Application()\nregisterApplication(app)\napp.start()\n"],"names":["module","exports","runtime","undefined","Op","Object","prototype","hasOwn","hasOwnProperty","$Symbol","Symbol","iteratorSymbol","iterator","asyncIteratorSymbol","asyncIterator","toStringTagSymbol","toStringTag","define","obj","key","value","defineProperty","enumerable","configurable","writable","err","wrap","innerFn","outerFn","self","tryLocsList","protoGenerator","Generator","generator","create","context","Context","_invoke","state","GenStateSuspendedStart","method","arg","GenStateExecuting","Error","GenStateCompleted","doneResult","delegate","delegateResult","maybeInvokeDelegate","ContinueSentinel","sent","_sent","dispatchException","abrupt","record","tryCatch","type","done","GenStateSuspendedYield","makeInvokeMethod","fn","call","GeneratorFunction","GeneratorFunctionPrototype","IteratorPrototype","this","getProto","getPrototypeOf","NativeIteratorPrototype","values","Gp","defineIteratorMethods","forEach","AsyncIterator","PromiseImpl","invoke","resolve","reject","result","__await","then","unwrapped","error","previousPromise","callInvokeWithMethodAndArg","TypeError","info","resultName","next","nextLoc","pushTryEntry","locs","entry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","iterable","iteratorMethod","isNaN","length","i","displayName","isGeneratorFunction","genFun","ctor","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","iter","keys","object","reverse","pop","skipTempReset","prev","charAt","slice","stop","rootRecord","rval","exception","handle","loc","caught","hasCatch","hasFinally","finallyEntry","complete","finish","thrown","delegateYield","regeneratorRuntime","accidentalStrictMode","globalThis","Function","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","__esModule","d","a","definition","o","get","prop","_arrayLikeToArray","arr","len","arr2","Array","_unsupportedIterableToArray","minLen","toString","from","test","_slicedToArray","isArray","_i","_s","_e","_arr","_n","_d","asyncGeneratorStep","gen","_next","_throw","_asyncToGenerator","args","arguments","apply","_classCallCheck","instance","Constructor","_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_LOCKED","ID_NOWALLET","ID_WALLET_SYNC_PROGRESS","ID_HIDE_ADDITIONAL_SETTINGS","ID_SHOW_ADDITIONAL_SETTINGS","ID_BUY","ID_SELL","ID_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_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","SETUP_NEEDED","enUS","ptBR","zhCN","plPL","localesMap","defaultLocale","prep","k","expression","replace","_","stringTemplateParser","window","localeDiscrepancies","ref","entries","lang","dict","s","console","log","parser","DOMParser","BipSymbols","intFormatter","Intl","NumberFormat","navigator","languages","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","rect","getBoundingClientRect","pageX","left","right","pageY","top","bottom","box","docEl","document","documentElement","scrollTop","scrollLeft","w","offsetWidth","h","offsetHeight","bodyTop","bodyLeft","width","height","centerX","centerY","els","firstChild","removeChild","classList","add","remove","contains","duration","easingAlgo","easer","Easing","linear","start","Date","getTime","range","end","frameDuration","now","sleep","ancestor","querySelectorAll","children","warn","createElement","applySelector","vAtomic","Number","isInteger","format","decimalFormatter","rate","symbol","indexOf","substring","tmpls","tmpl","removeAttribute","dataset","t","formatDuration","dur","y","mo","m","seconds","floor","count","timeMod","aYear","aMonth","aDay","anHour","aMinute","inputFields","inputField","preventDefault","easeIn","easeOut","easeInHard","easeOutHard","WalletIcons","stateElement","icons","sleeping","locked","unlocked","nowallet","syncing","nopeers","status","hide","show","textContent","intl","wallet","syncIcon","running","peerCount","synced","tooltip","syncProgress","toFixed","setSyncing","open","ms","setTimeout","darkModeCK","popupsCK","State","cname","cvalue","setTime","expires","toUTCString","cookie","split","trim","dark","setCookie","body","filter","item","includes","getCookie","localStorage","setItem","JSON","stringify","clear","getItem","parse","_assertThisInitialized","ReferenceError","_setPrototypeOf","p","_inherits","subClass","superClass","_typeof","_possibleConstructorReturn","_getPrototypeOf","ConnectionStatus","application","BasePage","requestJSON","addr","reqBody","fetch","headers","Headers","response","json","requestSuccessful","text","msg","postJSON","data","getJSON","_toConsumableArray","app","orderOptTmpl","booleanOptTmpl","rangeOptTmpl","RateEncodingFactor","sellString","ord","sell","typeString","tif","isMarketBuy","hasLiveMatches","order","matches","match","revoked","statusString","isLive","cancelling","filled","qty","reduce","isCancel","settled","side","setOptionTemplates","page","OrderOption","opt","isSwapOption","report","node","cloneNode","parseTemplate","optName","displayname","description","baseSymbol","quoteSymbol","chainIcon","src","logoPath","on","bind","enable","toggle","stopPropagation","disable","dexAssetSymbol","host","quote","base","BooleanOrderOption","changed","cfg","control","controls","appendChild","reason","options","store","XYRangeOrderOption","xyRange","setVal","x","handler","XYRangeHandler","initVal","updated","selected","roundY","slider","rangeX","rangeY","normalizeX","r","number","minimumSignificantDigits","maximumSignificantDigits","accept","skipUpdate","style","clickOutX","xInput","xx","parseFloat","clamp","unbind","focus","clickOutY","yInput","yy","button","startX","clientWidth","startLeft","trackMouse","ee","mouseUp","rangeLblStart","label","rangeLblEnd","xUnit","yUnit","optionElement","change","isSwap","assetID","exchanges","assets","NewWalletForm","form","success","pwCache","backFunc","pwHiders","refresh","goBack","empty","walletTabTmpl","subform","WalletConfigForm","walletSettings","showOther","walletSettingsHeader","submitAdd","submit","oneBttn","passwordIsCached","pw","appPass","newWalletPass","newWalletErr","currentAsset","createForm","pass","config","map","walletType","currentWalletType","loaded","loading","mainForm","res","checkResponse","setError","asset","tabs","walletTypeTabs","assetLogo","assetName","availablewallets","header","walletDef","wDef","tab","kids","update","bindTooltips","appPwCached","auth","oneBttnBox","newWalletPassBox","configOpts","configopts","isBirthdayConfig","seedGenTime","toUnixDate","seeded","noauth","dynamicOpts","loadDefaults","errMsg","walletDefinition","configpath","setLoadedConfig","sectionize","configElements","allSettings","tmplElement","textInputTmpl","dateInputTmpl","checkboxTmpl","fileSelector","fileInput","showIcon","hideIcon","showHideMsg","otherSettings","loadedSettingsMsg","loadedSettings","defaultSettingsMsg","defaultSettings","click","fileInputChanged","setOtherSettingsViz","files","configtext","append","setConfig","reorder","defaultOpts","loadedOpts","walletIsActive","defaultedOpts","addOpt","elID","isboolean","isdate","input","configOpt","safeSelector","htmlFor","prepend","noecho","checked","getMinMaxVal","minMax","toISOString","valueAsDate","disabled","Boolean","disablewhenactive","visible","finds","toLowerCase","parseInt","minDate","MIN_SAFE_INTEGER","maxDate","MAX_SAFE_INTEGER","date","inputs","ConfirmRegistrationForm","certFile","submitForm","xc","passBox","unitinfo","feeAssetID","regAsset","regFees","fee","formatCoinValue","amount","feeUnit","unit","toUpperCase","logo","animate","prog","transform","opacity","String","pow","offset","regErr","innerText","user","feeAsset","cert","dexAddr","registration","FeeAssetSelectionForm","cleanTemplates","marketTmpl","assetTmpl","allMarkets","cFactor","ui","marketNode","mkt","excludeIcon","baseAsset","baseid","baseUnitInfo","quoteAsset","quoteid","quoteUnitInfo","excludeBase","otherSymbol","otherLogo","parent","parentNode","insertBefore","nextSibling","lotsize","lotSize","spot","quoteLotSize","quoteLot","OrderUtil","haveWallet","assetNode","confs","ready","markets","fader","setExchange","how","regAssetElements","allmkts","marginTop","paddingTop","fontSize","WalletWaitForm","progressCache","progressed","funded","registerNoteFeeder","walletstate","note","reportWalletState","balance","reportBalance","txFee","regFee","depoAddr","address","syncUncheck","syncCheck","balUncheck","balCheck","syncRemainBox","balanceBox","totalFees","sendEnoughWithEst","sendEnough","syncSpinner","available","progress","reportProgress","bal","cache","stamp","shift","first","last","progDelta","progRate","toGoTime","syncRemain","UnlockWalletForm","idDescendants","submitUnlock","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","assetSymbol","quoteID","feeEstimate","DEXAddressForm","dexToUpdate","defaultTLSText","selectedCert","onCertFileChange","removeCert","clearCertFile","addCert","showCustom","customBox","knownExchanges","knownXCs","div","checkDEX","appPW","addDexHdr","updateDexHdr","appPWBox","pickServerMsg","addCustomMsg","endpoint","newHost","oldHost","needCert","paid","fetchUser","loadPage","LoginForm","headerTxt","rememberPass","notes","setNotes","animationLength","slideSwap","form1","form2","submitBttn","wrapper","RegistrationPage","bindForm","appPWForm","appPWSubmit","setAppPass","showSeedRestore","seedRestore","loginForm","dexAddrForm","newWalletForm","newWalletCreated","animateRegAsset","currentDEX","confirmRegisterForm","walletWaitForm","regAssetForm","setAsset","animateConfirmForm","getRegistrationTxFeeEstimate","setWallet","walletWait","confirmRegForm","registerDEXSuccess","currentForm","forms","authed","oldForm","getCertFile","txfee","appPWErrMsg","pwAgain","appPWAgain","seed","seedInput","updateMenuItemsDisplay","feeAmt","LoginPage","idel","loggedIn","make","subject","details","severity","acked","topic","WalletsPage","restoreInfoCard","closePopups","cancelForce","copyAddressBtn","copyAddress","firstRow","getAction","row","rowInfos","walletTable","tr","stateIcons","actions","connect","unlock","send","deposit","rescan","lock","settings","marketCard","oneMarket","createWalletSuccess","reconfigForm","reconfigInputs","unlockForm","unlockWalletForm","openWalletSuccess","sendForm","submitSendForm","submitReconfig","reconfig","rowInfo","showMarkets","rightBox","lastFormAsset","mouseInElement","keyup","isDisplayed","downloadLogs","exportWallet","displayExportWalletAuth","recoverWallet","showRecoverWallet","exportWalletAuth","exportWalletAuthSubmit","recoverWalletConfirm","recoverWalletSubmit","confirmForce","confirmForceSubmit","run","doConnect","showSendForm","showDeposit","showNewWallet","rescanWallet","openWallet","showReconfig","newDepAddrBttn","newDepositAddress","sendAvail","sendAsset","sendAmt","showFiatValue","sendValue","traits","subtractCheckBox","walletMap","fiatDisplay","amt","showChangePW","changeWalletPW","setPWSettingViz","changeWalletTypeSelect","changeWalletType","showChangeType","isHidden","changeTypeHideIcon","changeTypeShowIcon","changeTypeMsg","reconfigAsset","fiatrateupdate","handleRatesNote","handleBalanceNote","handleWalletStateNote","walletconfig","clipboard","writeText","depositAddress","copyAlert","changePW","switchPWMsg","animation","displayed","focuser","marketsBox","card","marketsCard","hideBox","marketsFor","marketsForLogo","market","marketBox","dexTitle","mBox","prettyMarketName","counterSymbol","basesymbol","quotesymbol","pageData","showBox","walletAsset","url","code","forceUrl","forceReq","showConfirmForce","confirmForceErr","showForm","recoverWalletErr","showOpen","openAsset","errorMsg","showErrorOnly","walletPass","reconfigErr","currentDef","currentWalletDefinition","option","recfgAssetLogo","recfgAssetName","updateDisplayedReconfigFields","depositErr","depositLogo","depositAsset","notify","ntfn","depositName","qrcode","senderOnlyHelpText","toggleSubtract","sendAddr","sendPW","sendErr","formatFullPrecision","sendLogo","sendName","encrypted","subtract","newWalletPW","newPW","search","URLSearchParams","URL","location","href","pathname","exportWalletErr","exportWalletPW","displayRestoreWalletInfo","restorationinfo","restoreInfoCardsList","wr","seedName","instructions","restoreWalletInfo","recoverWalletPW","force","fiatRatesMap","fiatRates","display","formatFiatConversion","parentElement","readWallet","SettingsPage","fiatRateSources","darkMode","showPokes","showPopups","commitHash","addADex","source","importAccount","prepareAccountImport","authorizeAccountImportForm","authorizeImportAccountConfirm","changeAppPW","changeAppPWForm","submitNewPW","accountFile","onAccountFileChange","removeAccount","clearAccountFile","addAccount","exportSeed","exportSeedAuth","exportSeedSubmit","submitExportSeedReq","exportSeedPW","seedDiv","selectedAccount","importAccountErr","importAccountAppPass","accountString","account","message","importResponse","loginResponse","reload","exportAccountErr","exportSeedE","authorizeSeedDisplay","changePWErrMsg","clearValues","newAppPW","confirmNewPW","OrderBook","mktBook","buys","book","sells","qtyAtomic","splice","less","findIdx","token","removeFromSide","findOrder","updateRemainingSide","epochIdx","approve","epoch","best","bestGapOrder","PIPI","PI","plusChar","fromCharCode","minusChar","darkTheme","axisLabel","gridBorder","gridLines","gapLine","zoom","zoomHover","sellLine","buyLine","sellFill","buyFill","crosshairs","legendFill","legendText","lightTheme","Chart","reporters","theme","isDark","canvas","ctx","getContext","textAlign","textBaseline","setZoomBttns","mousePos","clientX","clientY","draw","wheelLimiter","wheel","boundResizer","resize","clientHeight","clearRect","render","deltaY","parentHeight","plotExtents","Extents","xLblExtents","yLblExtents","plotRegion","Region","xRegion","yRegion","requestAnimationFrame","bigger","font","fillStyle","labels","minX","maxX","unitLines","extents","plot","tools","applyLabelStyle","lastX","unitCenter","lbls","lbl","fillText","txt","val","lineWidth","strokeStyle","line","region","minY","maxY","lastY","step","valFmt","yLabels","makeLabels","dataExtents","yAxisWidth","widest","plotYLabels","beginPath","dataCoords","moveTo","lineTo","stroke","DepthChart","resized","clicked","zoomed","zoomLevel","lines","markers","zoomInBttn","zoomOutBttn","wheeled","translator","unx","rateStep","qFactor","bFactor","baseUnit","quoteUnit","gap","midGap","gapWidth","minZoom","halfWindow","high","low","buyMarkers","sellMarkers","sort","b","buyDepth","buyEpoch","sellDepth","sellEpoch","volumeReport","buyBase","buyQuote","sellBase","sellQuote","sum","epochSum","floatCompare","active","buySum","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","highRate","lowRate","matchVolume","highVol","ratestep","rFactor","rateConversionFactor","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","desc","startRate","endRate","cx","maxCandles","xMin","xMax","yMin","yMax","screenMinX","screenMaxY","screenH","xFactor","yFactor","uny","drawFunc","skipMask","clip","tx","ty","tickGuess","absMax","abs","sigFigs","toPrecision","unitW","x0","y0","x1","y1","skipStroke","labelSpecs","forward","route","payload","handlers","MessageSocket","queue","maxQlength","connection","readyState","WebSocket","OPEN","close","uri","reloader","retrys","go","conn","timeout","onmessage","evt","onclose","delay","onopen","request","onerror","bookRoute","bookOrderRoute","unbookOrderRoute","epochOrderRoute","candlesRoute","candleUpdateRoute","lastMarketKey","chartRatioKey","depthZoomKey","depthChart","candleChart","check","percentFormatter","MarketsPage","main","maxOrderUpdateCounter","metaOrders","preorderCache","depthLines","hovers","ordersSortKey","ordersSortDirection","ogTitle","title","depthReporters","reportDepthClick","reportDepthVolume","reportDepthMouse","z","reportDepthZoom","marketChart","candleReporters","reportMouseCandle","accelerateOrderForm","accelerateForm","currentChart","candleDur","wgt","balanceWgt","BalanceWidget","balanceTable","baseIcons","quoteIcons","walletUnlocked","expired","newWalletBttn","showCreate","rowTemplate","liveTemplate","durBttnTemplate","marketList","MarketList","xcSections","marketRows","setMarket","quoteUnits","baseUnits","buyBttn","swapBttns","sellBttn","maxLbl","setOrderBttnText","setOrderVisibility","drawChartLines","limitBttn","marketBttn","rateField","isSell","setMarketBuyOrderEstimate","maxOrd","maxSell","lotField","swap","lots","maxBuys","adjustedRate","lotChanged","depthBttn","depthChartSelected","candlestickBttn","candleChartSelected","disableMouseWheel","qtyField","mktBuyField","ws","handleBookRoute","handleBookOrderRoute","handleUnbookOrderRoute","handleUpdateRemainingRoute","handleEpochOrderRoute","handleCandleUpdateRoute","handleCandlesRoute","openFunc","createWallet","orderForm","stepSubmit","verifyForm","vSubmit","submitOrder","vUnlockSubmit","submitEstimateUnlock","cancelForm","cancelSubmit","submitCancel","vFeeDetails","vDetailPane","closeDetailPane","showVerifyForm","liveTable","th","setOrdersSortCol","ordercol","unsetOrdersSortColClasses","refreshActiveOrders","setOrdersSortColClasses","sortCls","vPass","cancelPass","quantityChanged","marketBuyChanged","rateFieldChanged","previewQuoteAmt","marketSearch","filterMarkets","clearChartLines","buyRows","sellRows","liveList","activeMarkerRate","setDepthMarkers","setChartRatio","chartDivRatio","chartResizer","chartRatio","rightSide","handleOrderNote","handleEpochNote","handleConnNote","feepayment","handleFeePayment","spots","handlePriceUpdate","makeMarket","exists","secondTicker","setInterval","metaOrder","timeSince","submitTime","setRegistrationStatusVisibility","setBalanceVisibility","dex","pendingFee","isLimit","priceBox","tifBox","qtyBox","maxBox","mktBuyBox","feePaid","hasFeePending","assetsAreSupported","hasWallets","loaderMsg","quoteCfg","baseCfg","titleContent","confStatusMsg","titleClass","regStatusTitle","regStatusConfsDisplay","registrationStatus","regStatusDex","pending","confirmationsRequired","confReq","setRegistrationStatusView","connectionStatus","Connected","updateRegistrationStatusView","resolveOrderFormVisibility","durBttnBox","candleDurs","bttn","candleDurationSelected","chartErrMsg","marketLoader","qui","bui","maxEstimateTimer","mktId","marketID","sid","maxSellRequested","candleCaches","sellBalance","buyBalance","loadCandles","setLoaderMsgVisibility","setCandleDurBttns","q","sellBookedBase","sellBookedQuote","buyBookedBase","buyBookedQuote","metaOrd","hoverPrice","hoverVolume","hoverData","candle","candleStart","candleEnd","candleHigh","candleLow","candleVol","limit","convertToAtoms","tifnow","tifNow","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","toAsset","maxFromAmt","maxFromTicker","toConversion","maxToAmt","maxToTicker","loadTable","addTableOrder","set","msgRate","buffer","buybuffer","midGapConventional","minMktBuy","localeCompare","oid","orders","compare","ordersSortCompare","updateUserOrderRow","icon","showCancel","accelerateBttn","showAccelerate","canAccelerateOrder","bindInternalNavigation","updateDataCol","rateFactor","setMarkers","midGapValue","baseSymb","quoteSymb","handleBook","updateTitle","select","setWallets","removeTableOrder","updateRemaining","updateTableOrder","candlesLoading","timer","setCandles","startStamp","currentOrder","vUnlockPreorder","vPreorderErr","vPreorder","vBuySell","buySellStr","vSideSubmit","vOrderHost","verifyLimit","verifyMarket","orderDesc","vOrderType","vRate","vQty","vTotal","vFiatTotal","vmFromTotal","vmFromAsset","vmFromTotalFiat","vMarketEstimate","received","vmToTotal","vmToAsset","vmTotalFiat","buyBtnClass","sellBtnClass","vHeader","preOrder","unlockWalletsForEstimates","vErr","vUnlockPass","attemptWalletUnlock","setPreorderErr","assetIDs","cacheKey","cached","wireOrder","estimate","vPreorderErrTip","refreshPreorder","fetchPreorder","est","redeem","vOrderOpts","setFeeEstimates","vFeeSummary","backgroundColor","addOption","swapped","fmtPct","toUI","fromUI","bestSwapPct","realisticBestCase","vSwapFeesLowPct","vSwapFeesLow","worstSwapPct","realisticWorstCase","vSwapFeesHighPct","vSwapFeesHigh","vSwapFeesMaxPct","maxFees","vSwapFeesMax","estRate","bestRedeemPct","vRedeemFeesLowPct","vRedeemFeesLow","worstRedeemPct","vRedeemFeesHighPct","vRedeemFeesHigh","vFeeSummaryLow","vFeeSummaryHigh","cancelData","cancelErr","remaining","cancelRemain","cancelUnit","currentCreate","validateOrder","showVerify","xcSection","marketRow","setSpot","oldStatus","cancelBttn","setEpoch","clearOrderTableEpochs","alreadyMatched","statusTD","avail","vLoader","updateAsset","finalize","mktBuyLots","mktBuyScore","NaN","loadTableSide","bins","currEpochBin","currNonEpochBin","currRate","bin","bookSide","tbody","binOrdersByRateAndEpoch","orderTableRow","manager","getRate","insertOrder","removeOrder","u","updateOrderQty","clearOrderTableEpochSide","removeEpochOrders","orderBin","OrderTableRowManager","chart","setConnectionStatus","filterTxt","setFilter","setLines","candleHoverData","epochLine","depthHoverData","depthSummary","requestCandles","unattach","clearInterval","xcTmpl","ExchangeSection","sortedSections","firstXC","firstMkt","setConnected","template","disconnectedIco","disconnected","mkts","mktrow","MarketRow","sortedMarkets","isConnected","baseIcon","quoteIcon","pctChange","pct","change24","num","sign","bottomRow","price","table","baseImg","baseAvail","newWalletRow","baseNewWalletRow","baseNewButton","baseLocked","immature","baseImmature","unsupported","baseUnsupported","baseExpired","baseConnect","spinner","baseSpinner","iconBox","baseWalletState","quoteImg","quoteAvail","quoteNewWalletRow","quoteNewButton","quoteLocked","quoteImmature","quoteUnsupported","quoteExpired","quoteConnect","quoteSpinner","quoteWalletState","updateWallet","fetchBalance","contractlocked","before","col","stringyOptions","assign","tableRow","setRateEl","setEpochEl","updateQtyNumOrdersEl","epochEl","isEpoch","rateEl","cssClass","curr","numOrders","qtyEl","numOrdersEl","setAttribute","index","findIndex","newEpoch","OrdersPage","orderTmpl","rowTmpl","filterState","hosts","statuses","readFilter","filterKey","subFilter","hostFilter","assetFilter","statusFilter","applyButtons","monitorFilter","applyBttn","submitFilter","filter1","filter2","compareSubFilter","parseSubFilter","ordersTable","nextPage","exportOrders","tableBody","appendOrders","tmplID","mktID","to","fromQty","toQty","conventionalRate","dateTime","fetchOrders","setOrders","currentFilter","setQuery","orderLoader","net","OrderPage","stampers","fetchOrder","refreshOnPopupClose","showAccelerateForm","showAccelerationButton","link","setCoinHref","setStamp","span","handleMatchNote","actionsLabel","processMatch","matchBox","matchID","setCoin","divName","linkName","coin","coinLink","stringID","explorerCoin","counterSwap","counterRedeem","refund","swapSpan","cSwapSpan","inCounterSwapCast","inSwapCast","confirmationString","required","assetExplorer","CoinExplorers","explorerId","cid","txid","vout","DexSettingsPage","exportDexBtn","prepareAccountExport","authorizeAccountExportForm","disableAcctBtn","prepareAccountDisable","disableAccountForm","updateCertBtn","certFileInput","updateHostBtn","prepareUpdateHost","authorizeExportAccountConfirm","exportAccount","disableAccountConfirm","disableAccount","exportAccountAppPass","exportAccountHost","accountForExport","disableAccountAppPW","disableAccountHost","disableAccountErr","updateCertMsg","exchange","displayIcons","connected","disconnectedIcon","connectedIcon","Disconnected","InvalidCert","loggersKey","recordersKey","constructors","login","register","wallets","dexsettings","unauthedPages","Application","pokes","inited","seedgentime","ok","process","noteReceivers","loggers","enableLogger","loggerID","recorderKeys","recorders","recordLogger","dumpLogger","btoa","download","handlerFromPath","history","replaceState","attachHeader","attachCommon","attach","protocol","reconnected","resp","skipNote","skipPush","noteBox","profileBox","origin","requestedHandler","doc","noderize","delivered","pushState","replaceWith","handlerID","loadedPage","unload","lyt","layoutMetrics","popupNotes","popupTmpl","noteTmpl","pokeTmpl","loader","noteMenuEntry","pokeList","noteList","ackNotes","noteCat","pokeCat","showDropdown","noteIndicator","setNoteTimes","storeNotes","profileMenuEntry","innerNoteIcon","innerProfileIcon","profileSignout","signOut","dialog","ico","innerWidth","acks","pageURL","params","searchParams","walletsMenuEntry","marketsMenuEntry","updateExchangeRegistration","confirmations","setDEXPaid","prependNoteElement","updateOrder","handleFeePaymentNote","updateMatch","mktName","indicator","setSeverityClass","pn","prependPokeElement","receivers","nowString","time","cn","makePoke","prependListElement","skipSave","makeNote","unacked","ni","noteCacheSize","lastChild","cls","toLocaleDateString","toLocaleTimeString","haveAssetOrders","fromAssetID","supportedAsset","encRate","assetInfo","emptyidx","def","clearAllStore","removeAuthCK","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/regenerator/index.js","webpack://dexclient/./node_modules/regenerator-runtime/runtime.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/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/typeof.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/registry.ts","webpack://dexclient/./src/js/basepage.ts","webpack://dexclient/./src/js/http.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/forms.ts","webpack://dexclient/./src/js/register.ts","webpack://dexclient/./src/js/login.ts","webpack://dexclient/./src/js/notifications.ts","webpack://dexclient/./src/js/wallets.ts","webpack://dexclient/./src/js/settings.ts","webpack://dexclient/./src/js/orderbook.ts","webpack://dexclient/./src/js/charts.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/app.ts","webpack://dexclient/./src/index.ts"],"sourcesContent":["module.exports = require(\"regenerator-runtime\");\n","/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nvar runtime = (function (exports) {\n \"use strict\";\n\n var Op = Object.prototype;\n var hasOwn = Op.hasOwnProperty;\n var undefined; // More compressible than void 0.\n var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n function define(obj, key, value) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n return obj[key];\n }\n try {\n // IE 8 has a broken Object.defineProperty that only works on DOM objects.\n define({}, \"\");\n } catch (err) {\n define = function(obj, key, value) {\n return obj[key] = value;\n };\n }\n\n function wrap(innerFn, outerFn, self, tryLocsList) {\n // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n var generator = Object.create(protoGenerator.prototype);\n var context = new Context(tryLocsList || []);\n\n // The ._invoke method unifies the implementations of the .next,\n // .throw, and .return methods.\n generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n return generator;\n }\n exports.wrap = wrap;\n\n // Try/catch helper to minimize deoptimizations. Returns a completion\n // record like context.tryEntries[i].completion. This interface could\n // have been (and was previously) designed to take a closure to be\n // invoked without arguments, but in all the cases we care about we\n // already have an existing method we want to call, so there's no need\n // to create a new function object. We can even get away with assuming\n // the method takes exactly one argument, since that happens to be true\n // in every case, so we don't have to touch the arguments object. The\n // only additional allocation required is the completion record, which\n // has a stable shape and so hopefully should be cheap to allocate.\n function tryCatch(fn, obj, arg) {\n try {\n return { type: \"normal\", arg: fn.call(obj, arg) };\n } catch (err) {\n return { type: \"throw\", arg: err };\n }\n }\n\n var GenStateSuspendedStart = \"suspendedStart\";\n var GenStateSuspendedYield = \"suspendedYield\";\n var GenStateExecuting = \"executing\";\n var GenStateCompleted = \"completed\";\n\n // Returning this object from the innerFn has the same effect as\n // breaking out of the dispatch switch statement.\n var ContinueSentinel = {};\n\n // Dummy constructor functions that we use as the .constructor and\n // .constructor.prototype properties for functions that return Generator\n // objects. For full spec compliance, you may wish to configure your\n // minifier not to mangle the names of these two functions.\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n\n // This is a polyfill for %IteratorPrototype% for environments that\n // don't natively support it.\n var IteratorPrototype = {};\n define(IteratorPrototype, iteratorSymbol, function () {\n return this;\n });\n\n var getProto = Object.getPrototypeOf;\n var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n if (NativeIteratorPrototype &&\n NativeIteratorPrototype !== Op &&\n hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n // This environment has a native %IteratorPrototype%; use it instead\n // of the polyfill.\n IteratorPrototype = NativeIteratorPrototype;\n }\n\n var Gp = GeneratorFunctionPrototype.prototype =\n Generator.prototype = Object.create(IteratorPrototype);\n GeneratorFunction.prototype = GeneratorFunctionPrototype;\n define(Gp, \"constructor\", GeneratorFunctionPrototype);\n define(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction);\n GeneratorFunction.displayName = define(\n GeneratorFunctionPrototype,\n toStringTagSymbol,\n \"GeneratorFunction\"\n );\n\n // Helper for defining the .next, .throw, and .return methods of the\n // Iterator interface in terms of a single ._invoke method.\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\n exports.isGeneratorFunction = function(genFun) {\n var ctor = typeof genFun === \"function\" && genFun.constructor;\n return ctor\n ? ctor === GeneratorFunction ||\n // For the native GeneratorFunction constructor, the best we can\n // do is to check its .name property.\n (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n : false;\n };\n\n exports.mark = function(genFun) {\n if (Object.setPrototypeOf) {\n Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n } else {\n genFun.__proto__ = GeneratorFunctionPrototype;\n define(genFun, toStringTagSymbol, \"GeneratorFunction\");\n }\n genFun.prototype = Object.create(Gp);\n return genFun;\n };\n\n // Within the body of any async function, `await x` is transformed to\n // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n // meant to be awaited.\n exports.awrap = function(arg) {\n return { __await: arg };\n };\n\n function AsyncIterator(generator, PromiseImpl) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (record.type === \"throw\") {\n reject(record.arg);\n } else {\n var result = record.arg;\n var value = result.value;\n if (value &&\n typeof value === \"object\" &&\n hasOwn.call(value, \"__await\")) {\n return PromiseImpl.resolve(value.__await).then(function(value) {\n invoke(\"next\", value, resolve, reject);\n }, function(err) {\n invoke(\"throw\", err, resolve, reject);\n });\n }\n\n return PromiseImpl.resolve(value).then(function(unwrapped) {\n // When a yielded Promise is resolved, its final value becomes\n // the .value of the Promise<{value,done}> result for the\n // current iteration.\n result.value = unwrapped;\n resolve(result);\n }, function(error) {\n // If a rejected Promise was yielded, throw the rejection back\n // into the async generator function so it can be handled there.\n return invoke(\"throw\", error, resolve, reject);\n });\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new PromiseImpl(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n define(AsyncIterator.prototype, asyncIteratorSymbol, function () {\n return this;\n });\n exports.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {\n if (PromiseImpl === void 0) PromiseImpl = Promise;\n\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList),\n PromiseImpl\n );\n\n return exports.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\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\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n // Note: [\"return\"] must be used for ES3 parsing compatibility.\n if (delegate.iterator[\"return\"]) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n define(Gp, toStringTagSymbol, \"Generator\");\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n define(Gp, iteratorSymbol, function() {\n return this;\n });\n\n define(Gp, \"toString\", function() {\n return \"[object Generator]\";\n });\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n exports.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n exports.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(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 &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(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 (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n\n // Regardless of whether this script is executing as a CommonJS module\n // or not, return the runtime object so that we can declare the variable\n // regeneratorRuntime in the outer scope, which allows this module to be\n // injected easily by `bin/regenerator --include-runtime script.js`.\n return exports;\n\n}(\n // If this script is executing as a CommonJS module, use module.exports\n // as the regeneratorRuntime namespace. Otherwise create a new empty\n // object. Either way, the resulting object will be used to initialize\n // the regeneratorRuntime variable at the top of this file.\n typeof module === \"object\" ? module.exports : {}\n));\n\ntry {\n regeneratorRuntime = runtime;\n} catch (accidentalStrictMode) {\n // This module should not be running in strict mode, so the above\n // assignment should always work unless something is misconfigured. Just\n // in case runtime.js accidentally runs in strict mode, in modern engines\n // we can explicitly access globalThis. In older engines we can escape\n // strict mode using a global Function call. This could conceivably fail\n // if a Content Security Policy forbids using Function, but in that case\n // the proper solution is to fix the accidental strict mode problem. If\n // you've misconfigured your bundler to force strict mode and applied a\n // CSP to forbid Function, and you're not willing to fix either of those\n // problems, please detail your unique predicament in a GitHub issue.\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\n for (var i = 0, arr2 = new Array(len); i < len; i++) {\n arr2[i] = arr[i];\n }\n\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 = arr == null ? null : typeof Symbol !== \"undefined\" && arr[Symbol.iterator] || arr[\"@@iterator\"];\n\n if (_i == null) return;\n var _arr = [];\n var _n = true;\n var _d = false;\n\n var _s, _e;\n\n try {\n for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {\n _arr.push(_s.value);\n\n if (i && _arr.length === i) break;\n }\n } catch (err) {\n _d = true;\n _e = err;\n } finally {\n try {\n if (!_n && _i[\"return\"] != null) _i[\"return\"]();\n } finally {\n if (_d) throw _e;\n }\n }\n\n return _arr;\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\n if (info.done) {\n resolve(value);\n } else {\n Promise.resolve(value).then(_next, _throw);\n }\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\n function _next(value) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value);\n }\n\n function _throw(err) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err);\n }\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}","function _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, descriptor.key, descriptor);\n }\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}","export default function _defineProperty(obj, key, value) {\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\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_LOCKED = 'ID_LOCKED'\nexport const ID_NOWALLET = 'ID_NOWALLET'\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_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_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 SETUP_NEEDED = 'SETUP_NEEDED'\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_NOWALLET]: '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_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]: 'Registration fee payment successful!',\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}\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_NOWALLET]: '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!',\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}\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_NOWALLET]: '未连接钱包', // 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_UNKNOWN]: 'unknown', // TODO\n [ID_EPOCH]: '时间',\n [ID_SETTLING]: 'settling', // TODO - \"settling\" shows in the Your Orders table when the order is doing an atomic swap with another trade\n [ID_NO_MATCH]: 'no match', // TODO - \"no match\" shows in the Your Orders table when the order did not match with another trade and is done\n [ID_CANCELED]: 'canceled', // TODO - \"canceled\" shows in the Your Orders table when the order has been canceled\n [ID_REVOKED]: 'revoked', // TODO - \"revoked\" shows in the Your Orders table when the order has failed during swap\n [ID_WAITING_FOR_CONFS]: 'Waiting for confirmations...', // TODO - shows when the registration fee transaction is waiting to be mined (needs confirmations)\n [ID_NONE_SELECTED]: 'none selected', // TODO - looks unused, but this indicates nothing is selected in some list\n [ID_REGISTRATION_FEE_SUCCESS]: 'Registration fee payment successful!', // TODO - When the registration fee transaction reaches the required number of confirmations, this is shown\n [ID_API_ERROR]: '接口错误',\n [ID_ADD]: '加',\n [ID_CREATE]: '创建',\n [ID_WALLET_READY]: 'Choose Wallet', // xxx translate\n [ID_SETUP_WALLET]: 'Setup' // xxx translate\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_NOWALLET]: '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ę!',\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}\n\nconst localesMap: Record = {\n 'en-us': enUS,\n 'pt-br': ptBR,\n 'zh-cn': zhCN,\n 'pl-pl': plPL\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 = {\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}\n\nconst BipSymbols = Object.values(BipIDs)\n\nconst intFormatter = new Intl.NumberFormat((navigator.languages as string[]))\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: (e: Event) => 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) {\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.\n */\n static mouseInElement (e: MouseEvent, el: HTMLElement): boolean {\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 /* 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 * 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 /* 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 const easer = easingAlgo ? Easing[easingAlgo] : Easing.linear\n const start = new Date().getTime()\n const end = start + duration\n const range = end - start\n const frameDuration = 1000 / FPS\n let now = start\n while (now < end) {\n f(easer((now - start) / range))\n await sleep(frameDuration)\n now = new Date().getTime()\n }\n f(1)\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 /*\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 '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 /*\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 | Element, 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\n/* Easing algorithms for animations. */\nconst 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}\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.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)\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)\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)\n Doc.show(i.unlocked)\n if (this.status) this.status.textContent = intl.prep(intl.ID_READY)\n }\n\n /* sleeping 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)\n Doc.show(i.nowallet)\n if (this.status) this.status.textContent = intl.prep(intl.ID_NOWALLET)\n }\n\n setSyncing (wallet: WalletState | null) {\n const syncIcon = this.icons.syncing\n if (!wallet || !wallet.running) {\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.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","const darkModeCK = 'darkMode'\nconst authCK = 'dexauth'\nconst popupsCK = 'popups'\nconst pwKeyCK = 'sessionkey'\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 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 /* dark sets the dark-mode cookie. */\n static dark (dark: boolean) {\n this.setCookie(darkModeCK, dark ? '1' : '0')\n if (dark) {\n document.body.classList.add('dark')\n } else {\n document.body.classList.remove('dark')\n }\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(`${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(pwKeyCK)\n }\n\n /* store puts the key-value pair into Window.localStorage. */\n static store (k: string, v: any) {\n window.localStorage.setItem(k, JSON.stringify(v))\n }\n\n /* clearAllStore remove all the key-value pair in Window.localStorage. */\n static clearAllStore () {\n window.localStorage.clear()\n }\n\n static removeAuthCK () {\n document.cookie = `${authCK}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`\n }\n\n /*\n * fetch fetches the value associated with the key in Window.localStorage, or\n * null if the no value exists for the key.\n */\n static fetch (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\n// If the dark-mode cookie is not set, set it to dark mode on.\nif (State.getCookie(darkModeCK) === null) State.setCookie(darkModeCK, '1')\nif (State.getCookie(popupsCK) === null) State.setCookie(popupsCK, '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\n return self;\n}","export default function _setPrototypeOf(o, p) {\n _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\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\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}","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 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\n return assertThisInitialized(self);\n}","export default function _getPrototypeOf(o) {\n _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {\n return o.__proto__ || Object.getPrototypeOf(o);\n };\n return _getPrototypeOf(o);\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 Exchange {\n host: string\n acctID: string\n markets: Record\n assets: Record\n connectionStatus: ConnectionStatus\n feeAsset: FeeAsset // DEPRECATED. DCR.\n regFees: Record\n pendingFee: PendingFeeState | null\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\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}\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\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 FeeAsset {\n id: number\n confs: number\n amount: number\n}\n\nexport interface PendingFeeState {\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 wallet: WalletState\n info: WalletInfo\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 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 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}\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 noauth: 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}\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 FeePaymentNote extends CoreNote {\n asset: number\n confirmations: number\n dex: string\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 SpotPriceNote extends CoreNote {\n host: string\n spots: Record\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}\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}\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 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}\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 Application {\n assets: Record\n seedGenTime: number\n user: User\n header: HTMLElement\n walletMap: Record\n exchanges: Record\n fiatRatesMap: Record\n showPopups: boolean\n commitHash: string\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 updateExchangeRegistration (dexAddr: string, confs: number, assetID: number): void\n handleFeePaymentNote (note: FeePaymentNote): 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 haveAssetOrders (assetID: number): boolean\n walletIsActive (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): number\n walletDefinition (assetID: number, walletType: string): WalletDefinition\n currentWalletDefinition (assetID: number): WalletDefinition\n fetchBalance (assetID: number): Promise\n checkResponse (resp: APIResponse, skipNote?: boolean): 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","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","/*\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","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 Doc from './doc'\nimport * as intl from './locales'\nimport {\n app,\n Order,\n TradeForm,\n PageElement,\n OrderOption as OrderOpt,\n Match,\n XYRange\n} from './registry'\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\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) { return ord.sell ? 'sell' : 'buy' }\nexport function typeString (ord: Order) { return ord.type === Limit ? (ord.tif === ImmediateTiF ? 'limit (i)' : 'limit') : 'market' }\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 * hasLiveMatches returns true if the order has matches that have not completed\n * settlement yet.\n */\nexport function hasLiveMatches (order: Order) {\n if (!order.matches) return false\n for (const match of order.matches) {\n if (!match.revoked && match.status < MakerRedeemed) return true\n }\n return false\n}\n\n/* statusString converts the order status to a string */\nexport function statusString (order: Order): string {\n const isLive = hasLiveMatches(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 return (order.filled === 0) ? intl.prep(intl.ID_NO_MATCH) : 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 ''\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/*\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 return 'Revoked - Refund PENDING' // auto-redeem also possible, but action is pending\n }\n if (m.refund) {\n return 'Revoked - Refunded'\n }\n if (m.redeem) {\n return 'Revoked - Redeemed'\n }\n return 'Revoked - Complete'\n }\n\n switch (m.status) {\n case NewlyMatched:\n return '(0 / 4) Newly Matched'\n case MakerSwapCast:\n return '(1 / 4) First Swap Sent'\n case TakerSwapCast:\n return '(2 / 4) Second Swap Sent'\n case MakerRedeemed:\n if (m.side === Maker) {\n return 'Match Complete'\n }\n return '(3 / 4) Maker Redeemed'\n case MatchComplete:\n return 'Match Complete'\n }\n return 'Unknown Match Status'\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) {\n [booleanOptTmpl, rangeOptTmpl, orderOptTmpl] = [page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl]\n}\n\ninterface OptionsReporters {\n enable: () => void\n disable: () => void\n}\n\n/*\n * OrderOption is a base class for option elements. OrderOptions stores some\n * common 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 */\nclass OrderOption {\n opt: OrderOpt\n order: TradeForm\n node: HTMLElement\n tmpl: Record\n on: boolean\n\n constructor (opt: OrderOpt, order: TradeForm, isSwapOption: boolean, report: OptionsReporters) {\n this.opt = opt\n this.order = order\n const node = this.node = orderOptTmpl.cloneNode(true) as HTMLElement\n const tmpl = this.tmpl = Doc.parseTemplate(node)\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 tmpl.chainIcon.src = Doc.logoPath(symbol)\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 quoteSymbol () {\n return dexAssetSymbol(this.order.host, this.order.quote)\n }\n\n baseSymbol () {\n return dexAssetSymbol(this.order.host, this.order.base)\n }\n}\n\n/*\n * BooleanOrderOption is a simple on/off option with a short summary of it's\n * effects. BooleanOrderOption is the handler for a *BooleanConfig from\n * client/asset.\n */\nclass BooleanOrderOption extends OrderOption {\n control: HTMLElement\n changed: () => void\n\n constructor (opt: OrderOpt, order: TradeForm, changed: () => void, isSwapOption: boolean) {\n super(opt, order, isSwapOption, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.changed = () => changed()\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 order.options[opt.key] !== 'undefined' ? order.options[opt.key] : opt.default\n if (this.on) this.node.classList.add('selected')\n }\n\n store () {\n if (this.on === this.opt.default) delete this.order.options[this.opt.key]\n else this.order.options[this.opt.key] = this.on\n this.changed()\n }\n\n enable () {\n this.store()\n }\n\n disable () {\n this.store()\n }\n}\n\n/*\n * XYRangeOrderOption is an order option that contains an XYRangeHandler.\n * The logic for handling the slider to is defined in XYRangeHandler so\n * that the slider can be used without being contained in an order option.\n */\nclass XYRangeOrderOption extends OrderOption {\n handler: XYRangeHandler\n x: number\n changed: () => void\n\n constructor (opt: OrderOpt, order: TradeForm, changed: () => void, isSwapOption: boolean) {\n super(opt, order, isSwapOption, {\n enable: () => this.enable(),\n disable: () => this.disable()\n })\n this.changed = changed\n const cfg = opt.xyRange\n const setVal = order.options[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.order.options[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 () {\n this.order.options[this.opt.key] = this.x\n this.changed()\n }\n\n disable () {\n delete this.order.options[this.opt.key]\n this.changed()\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 x: number\n updated: (x:number, y:number) => void\n changed: () => void\n selected: () => 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 = Doc.parseTemplate(control)\n\n this.changed = changed\n this.selected = selected\n this.updated = updated\n\n const { slider, handle } = tmpl\n\n const rangeX = cfg.end.x - cfg.start.x\n const rangeY = cfg.end.y - cfg.start.y\n const normalizeX = (x: number) => (x - cfg.start.x) / rangeX\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 let r = normalizeX(initVal)\n let x = this.x = initVal\n let y = r * rangeY + cfg.start.y\n\n const number = new Intl.NumberFormat((navigator.languages as string[]), {\n minimumSignificantDigits: 3,\n maximumSignificantDigits: 3\n })\n\n // accept needs to be called anytime a handler updates x, y, and r.\n const accept = (skipUpdate?: boolean) => {\n if (roundY) y = Math.round(y)\n tmpl.x.textContent = number.format(x)\n tmpl.y.textContent = number.format(y)\n if (roundY) tmpl.y.textContent = `${y}`\n handle.style.left = `calc(${r * 100}% - ${r * 14}px)`\n this.x = x\n if (!skipUpdate) this.updated(x, y)\n }\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 x = clamp(xx, cfg.start.x, cfg.end.x)\n r = normalizeX(x)\n y = r * rangeY + cfg.start.y\n accept()\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 = number.format(x)\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 y = clamp(yy, cfg.start.y, cfg.end.y)\n r = (y - cfg.start.y) / rangeY\n x = cfg.start.x + r * rangeX\n accept()\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 = number.format(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(x) * 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 r = left(ee) / w\n x = r * rangeX + cfg.start.x\n y = r * rangeY + cfg.start.y\n accept()\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 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 accept(true)\n }\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: OrderOpt, order: TradeForm, change: () => void, isSwap: boolean): HTMLElement {\n switch (true) {\n case !!opt.boolean:\n return new BooleanOrderOption(opt, order, change, isSwap).node\n case !!opt.xyRange:\n return new XYRangeOrderOption(opt, order, change, isSwap).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) {\n return app().exchanges[host].assets[assetID].symbol\n}\n\nconst clamp = (v: number, min: number, max: number) => v < min ? min : v > max ? max : v\n","import Doc from './doc'\nimport { postJSON } from './http'\nimport State from './state'\nimport * as intl from './locales'\nimport * as OrderUtil from './orderutil'\nimport {\n app,\n PasswordCache,\n SupportedAsset,\n PageElement,\n WalletDefinition,\n ConfigOption,\n Exchange,\n Market,\n UnitInfo,\n FeeAsset,\n WalletState,\n WalletBalance,\n BalanceNote,\n Order,\n XYRange,\n WalletStateNote\n} from './registry'\n\ninterface ConfigOptionInput extends HTMLInputElement {\n configOpt: ConfigOption\n}\n\ninterface ProgressPoint {\n stamp: number\n progress: number\n}\n\n/*\n * NewWalletForm should be used with the \"newWalletForm\" template. The enclosing\n * element should be the second argument of the constructor.\n */\nexport class NewWalletForm {\n page: Record\n form: HTMLElement\n pwCache: PasswordCache | null\n success: (assetID: number) => void\n currentAsset: SupportedAsset\n pwHiders: HTMLElement[]\n subform: WalletConfigForm\n currentWalletType: string\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 Doc.bind(this.subform.showOther, 'click', () => Doc.show(page.walletSettingsHeader))\n\n bind(form, page.submitAdd, () => this.submit())\n bind(form, page.oneBttn, () => this.submit())\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 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 const assetID = this.currentAsset.id\n const createForm = {\n assetID: assetID,\n pass: newWalletPass.value || '',\n config: this.subform.map(),\n appPass: pw,\n walletType: this.currentWalletType\n }\n appPass.value = ''\n const loaded = app().loading(page.mainForm)\n const res = await postJSON('/api/newwallet', createForm)\n loaded()\n if (!app().checkResponse(res)) {\n this.setError(res.msg)\n return\n }\n if (this.pwCache) this.pwCache.pw = pw\n newWalletPass.value = ''\n this.success(assetID)\n }\n\n async setAsset (assetID: number) {\n const page = this.page\n const asset = app().assets[assetID]\n const tabs = page.walletTypeTabs\n if (this.currentAsset && this.currentAsset.id === asset.id) return\n this.currentAsset = asset\n page.assetLogo.src = Doc.logoPath(asset.symbol)\n page.assetName.textContent = asset.info.name\n page.newWalletPass.value = ''\n\n if (asset.info.availablewallets.length > 1) page.header.classList.add('bordertop')\n else page.header.classList.remove('bordertop')\n\n const walletDef = asset.info.availablewallets[0]\n Doc.empty(tabs)\n Doc.hide(tabs, page.newWalletErr)\n\n if (asset.info.availablewallets.length > 1) {\n Doc.show(tabs)\n for (const wDef of asset.info.availablewallets) {\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(walletDef)\n }\n\n async update (walletDef: WalletDefinition) {\n const page = this.page\n this.currentWalletType = walletDef.type\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 if (appPwCached && walletDef.seeded) {\n Doc.show(page.oneBttnBox)\n } else if (walletDef.seeded) {\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 this.subform.update(configOpts)\n\n if (this.subform.dynamicOpts.children.length) Doc.show(page.walletSettingsHeader)\n else Doc.hide(page.walletSettingsHeader)\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 walletDef = app().walletDefinition(this.currentAsset.id, this.currentWalletType)\n if (walletDef.seeded) return\n if (walletDef.configpath === '') return\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/defaultwalletcfg', {\n assetID: this.currentAsset.id,\n type: this.currentWalletType\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\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: Record\n configOpts: ConfigOption[]\n sectionize: boolean\n allSettings: PageElement\n dynamicOpts: PageElement\n textInputTmpl: PageElement\n dateInputTmpl: PageElement\n checkboxTmpl: 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\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.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 /*\n * update creates the dynamic form.\n */\n update (configOpts: ConfigOption[], walletIsActive?: boolean) {\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 (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 const addOpt = (box: HTMLElement, opt: ConfigOption) => {\n const elID = 'wcfg-' + opt.key\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 el = this.textInputTmpl.cloneNode(true) as HTMLElement\n this.configElements[opt.key] = el\n const input = el.querySelector('input') as ConfigOptionInput\n input.id = elID\n input.configOpt = opt\n const label = Doc.safeSelector(el, 'label')\n label.htmlFor = elID // 'for' attribute, but 'for' is a keyword\n label.prepend(opt.displayname)\n box.appendChild(el)\n if (opt.noecho) input.type = 'password'\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 input.valueAsDate = opt.default ? new Date(opt.default * 1000) : new Date()\n } else input.value = opt.default !== null ? opt.default : ''\n input.disabled = Boolean(opt.disablewhenactive && walletIsActive)\n }\n for (const opt of this.configOpts) {\n if (this.sectionize && opt.default !== null) defaultedOpts.push(opt)\n else addOpt(this.dynamicOpts, opt)\n }\n if (defaultedOpts.length) {\n for (const opt of defaultedOpts) {\n 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) {\n const finds: HTMLElement[] = []\n this.allSettings.querySelectorAll('input').forEach((input: ConfigOptionInput) => {\n const k = input.configOpt.key\n const v = cfg[k]\n if (typeof v === 'undefined') return\n finds.push(this.configElements[k])\n if (input.configOpt.isboolean) input.checked = isTruthyString(v)\n else if (input.configOpt.isdate) input.valueAsDate = new Date(parseInt(v) * 1000)\n else input.value = v\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 (): Record {\n const config: Record = {}\n this.allSettings.querySelectorAll('input').forEach((input: ConfigOptionInput) => {\n if (input.configOpt.isboolean && input.configOpt.key) {\n config[input.configOpt.key] = input.checked ? '1' : '0'\n } else if (input.configOpt.isdate && input.configOpt.key) {\n const minDate = input.min ? toUnixDate(new Date(input.min)) : Number.MIN_SAFE_INTEGER\n const maxDate = input.max ? toUnixDate(new Date(input.max)) : Number.MAX_SAFE_INTEGER\n let date = input.value ? toUnixDate(new Date(input.value)) : 0\n if (date < minDate) date = minDate\n else if (date > maxDate) date = maxDate\n config[input.configOpt.key] = '' + date\n } else if (input.value) {\n config[input.configOpt.key] = input.value\n }\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.configOpt.key\n inputs[k] = this.configElements[k]\n })\n for (const opt of this.configOpts) {\n const input = inputs[opt.key]\n if (input) box.append(input)\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 feeAssetID: 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 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 unitInfo = asset.info.unitinfo\n this.feeAssetID = asset.id\n const page = this.page\n const regAsset = this.xc.regFees[asset.symbol]\n page.fee.textContent = Doc.formatCoinValue(regAsset.amount, unitInfo)\n page.feeUnit.textContent = unitInfo.conventional.unit.toUpperCase()\n page.logo.src = Doc.logoPath(asset.symbol)\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 if (this.feeAssetID === null) {\n page.regErr.innerText = 'You must select a valid wallet for the fee payment'\n Doc.show(page.regErr)\n return\n }\n const symbol = app().user.assets[this.feeAssetID].wallet.symbol\n Doc.hide(page.regErr)\n const feeAsset = this.xc.regFees[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 registration = {\n addr: dexAddr,\n pass: pw,\n fee: feeAsset.amount,\n asset: feeAsset.id,\n cert: cert\n }\n page.appPass.value = ''\n const loaded = app().loading(this.form)\n const res = await postJSON('/api/register', registration)\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\n constructor (form: HTMLElement, success: (assetID: number) => void) {\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\n setExchange (xc: Exchange) {\n this.xc = xc\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.name.textContent = `${baseSymbol}-${quoteSymbol}`\n const s = Doc.formatCoinValue(mkt.lotsize, baseUnitInfo)\n marketTmpl.lotSize.textContent = `${s} ${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, feeAsset] of Object.entries(xc.regFees)) {\n const asset = app().assets[feeAsset.id]\n if (!asset) continue\n const haveWallet = asset.wallet\n const unitInfo = asset.info.unitinfo\n const assetNode = page.assetTmpl.cloneNode(true) as HTMLElement\n Doc.bind(assetNode, 'click', () => { this.success(feeAsset.id) })\n const assetTmpl = Doc.parseTemplate(assetNode)\n page.assets.appendChild(assetNode)\n assetTmpl.logo.src = Doc.logoPath(symbol)\n const fee = Doc.formatCoinValue(feeAsset.amount, unitInfo)\n assetTmpl.fee.textContent = `${fee} ${unitInfo.conventional.unit}`\n assetTmpl.confs.textContent = String(feeAsset.confs)\n assetTmpl.ready.textContent = haveWallet ? intl.prep(intl.WALLET_READY) : intl.prep(intl.SETUP_NEEDED)\n assetTmpl.ready.classList.add(haveWallet ? 'readygreen' : 'setuporange')\n\n let count = 0\n for (const mkt of Object.values(xc.markets)) {\n if (mkt.baseid !== feeAsset.id && mkt.quoteid !== feeAsset.id) continue\n const node = marketNode(mkt, feeAsset.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 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 * WalletWaitForm is a form used to track the wallet sync status and balance\n * in preparation for paying the registration fee.\n */\nexport class WalletWaitForm {\n form: HTMLElement\n success: () => void\n goBack: () => void\n page: Record\n assetID: number\n xc: Exchange\n regFee: FeeAsset\n progressCache: ProgressPoint[]\n progressed: boolean\n funded: 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.balance, 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 form. */\n setWallet (wallet: WalletState, txFee: number) {\n this.assetID = wallet.assetID\n this.progressCache = []\n this.progressed = false\n this.funded = false\n const page = this.page\n const asset = app().assets[wallet.assetID]\n const fee = this.regFee = this.xc.regFees[asset.symbol]\n\n for (const span of Doc.applySelector(this.form, '.unit')) span.textContent = asset.symbol.toUpperCase()\n page.logo.src = Doc.logoPath(asset.symbol)\n page.depoAddr.textContent = wallet.address\n page.fee.textContent = Doc.formatCoinValue(fee.amount, asset.info.unitinfo)\n\n Doc.hide(page.syncUncheck, page.syncCheck, page.balUncheck, page.balCheck, page.syncRemainBox)\n Doc.show(page.balanceBox)\n\n if (txFee > 0) {\n page.totalFees.textContent = Doc.formatCoinValue(fee.amount + txFee, asset.info.unitinfo)\n Doc.show(page.sendEnoughWithEst)\n Doc.hide(page.sendEnough)\n } else {\n Doc.show(page.sendEnough)\n Doc.hide(page.sendEnoughWithEst)\n }\n\n Doc.show(wallet.synced ? page.syncCheck : wallet.syncProgress >= 1 ? page.syncSpinner : page.syncUncheck)\n Doc.show(wallet.balance.available > fee.amount ? page.balCheck : page.balUncheck)\n\n page.progress.textContent = String(Math.round(wallet.syncProgress * 100))\n\n if (wallet.synced) {\n this.progressed = true\n }\n this.reportBalance(wallet.balance, 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 (wallet.assetID !== this.assetID) return\n if (this.progressed && this.funded) return\n this.reportProgress(wallet.synced, wallet.syncProgress)\n this.reportBalance(wallet.balance, wallet.assetID)\n }\n\n /*\n * reportBalance sets the balance display and calls success if we go over the\n * threshold.\n */\n reportBalance (bal: WalletBalance, assetID: number) {\n if (this.funded || this.assetID === -1 || this.assetID !== assetID) return\n const page = this.page\n const asset = app().assets[this.assetID]\n\n if (bal.available <= this.regFee.amount) {\n page.balance.textContent = Doc.formatCoinValue(bal.available, asset.info.unitinfo)\n return\n }\n\n Doc.show(page.balCheck)\n Doc.hide(page.balUncheck, page.balanceBox, page.sendEnough)\n this.funded = true\n\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 = String(Math.round(prog * 100))\n\n // The remaining time estimate must be based on more than one progress\n // report. We'll cache up to the last 20 and look at the difference between\n // 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 while (cache.length > cacheSize) cache.shift()\n if (cache.length === 1) return\n Doc.show(page.syncRemainBox)\n const [first, last] = [cache[0], cache[cache.length - 1]]\n const progDelta = last.progress - first.progress\n if (progDelta === 0) {\n page.syncRemain.textContent = '> 1 day'\n return\n }\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: () => void\n pwCache: PasswordCache | null\n page: Record\n currentAsset: SupportedAsset\n\n constructor (form: HTMLElement, success: () => 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.info.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 Doc.hide(this.page.unlockErr)\n const open = {\n assetID: this.currentAsset.id,\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()\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 accelerateorder 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 = `Error accelerating order: ${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 = `Error accelerating order: ${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 OrderUtil.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 accelerateestimate request to the\n // client backend using the curretly 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 = `Error estimating acceleration fee: ${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 defaultTLSText: string\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 this.defaultTLSText = 'none selected'\n\n const page = this.page = Doc.parseTemplate(form)\n\n page.selectedCert.textContent = this.defaultTLSText\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 have the password cached, we're good to go.\n if (State.passwordIsCached() || (pwCache && pwCache.pw)) return this.checkDEX(host)\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)\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 const hidePWBox = State.passwordIsCached() || (this.pwCache && this.pwCache.pw)\n if (hidePWBox) Doc.hide(page.appPWBox, page.auth)\n else Doc.show(page.appPWBox, page.auth)\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 }\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 = 'DEX address cannot be empty'\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 let pw = ''\n if (!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 = '/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, true)) {\n if (res.msg === '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 && res.paid) {\n await app().fetchUser()\n 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 = this.defaultTLSText\n Doc.hide(page.removeCert)\n Doc.show(page.addCert)\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\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 }\n app().setNotes(res.notes || [])\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 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 javscript 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// dateToString converts a javascript date object to a YYYY-MM-DD format string.\nfunction dateToString (date: Date) {\n return date.toISOString().split('T')[0]\n}\n","import Doc from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport {\n NewWalletForm,\n DEXAddressForm,\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 newWalletForm: NewWalletForm\n regAssetForm: FeeAssetSelectionForm\n walletWaitForm: WalletWaitForm\n confirmRegisterForm: ConfirmRegistrationForm\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n this.pwCache = { pw: '' }\n const page = this.page = Doc.idDescendants(body)\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 this.dexAddrForm.refresh()\n slideSwap(page.loginForm, page.dexAddrForm)\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.currentDEX = xc\n this.confirmRegisterForm.setExchange(xc, certFile)\n this.walletWaitForm.setExchange(xc)\n this.regAssetForm.setExchange(xc)\n this.animateRegAsset(page.dexAddrForm)\n }, this.pwCache)\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 fee = this.currentDEX.regFees[asset.symbol]\n if (wallet.synced && wallet.balance.available > fee.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 slideSwap(page.regAssetForm, page.walletWait)\n return\n }\n\n this.newWalletForm.setAsset(assetID)\n this.newWalletForm.loadDefaults()\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 }\n Doc.show(currentForm)\n\n // Attempt to load the dcrwallet configuration from the default location.\n if (app().user.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 /* 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 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, true)) {\n return 0\n }\n return res.txfee\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 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 feeAmt = this.currentDEX.regFees[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > feeAmt) {\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 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 app().loadPage('markets')\n }\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 Doc, { WalletIcons } from './doc'\nimport BasePage from './basepage'\nimport { postJSON } from './http'\nimport { NewWalletForm, WalletConfigForm, UnlockWalletForm, bind as bindForm } from './forms'\nimport * as ntfn from './notifications'\nimport State from './state'\nimport * as intl from './locales'\nimport {\n app,\n PageElement,\n SupportedAsset,\n WalletDefinition,\n BalanceNote,\n WalletStateNote,\n Market,\n RateNote,\n WalletState\n} from './registry'\n\nconst bind = Doc.bind\nconst animationLength = 300\nconst traitNewAddresser = 1 << 1\nconst traitLogFiler = 1 << 2\nconst traitRecoverer = 1 << 5\nconst traitWithdrawer = 1 << 6\nconst traitRestorer = 1 << 8\n\nconst activeOrdersErrCode = 35\n\ninterface Actions {\n connect: HTMLElement\n unlock: HTMLElement\n send: HTMLElement\n deposit: HTMLElement\n create: HTMLElement\n rescan: HTMLElement\n lock: HTMLElement\n settings: HTMLElement\n}\n\ninterface RowInfo {\n assetID: number\n tr: HTMLElement\n symbol: string\n name: string\n stateIcons: WalletIcons\n actions: Actions\n}\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\nexport default class WalletsPage extends BasePage {\n body: HTMLElement\n page: Record\n rowInfos: Record\n sendAsset: SupportedAsset\n newWalletForm: NewWalletForm\n reconfigForm: WalletConfigForm\n unlockForm: UnlockWalletForm\n lastFormAsset: number\n keyup: (e: KeyboardEvent) => void\n changeWalletPW: boolean\n depositAsset: number\n // Methods to switch the item displayed on the right side, with a little\n // fade-in animation.\n displayed: HTMLElement\n animation: Promise\n openAsset: number\n walletAsset: number\n reconfigAsset: number\n forms: PageElement[]\n forceReq: RescanRecoveryRequest\n forceUrl: string\n currentForm: PageElement\n restoreInfoCard: HTMLElement\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)\n this.restoreInfoCard = page.restoreInfoCard.cloneNode(true) as HTMLElement\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 Doc.bind(page.copyAddressBtn, 'click', () => { this.copyAddress() })\n\n // Read the document, storing some info about each asset's row.\n const getAction = (row: HTMLElement, name: string) => row.querySelector(`[data-action=${name}]`) as HTMLElement\n const rowInfos: Record = this.rowInfos = {}\n const rows = Doc.applySelector(page.walletTable, 'tr')\n let firstRow\n for (const tr of rows) {\n const assetID = parseInt(tr.dataset.assetID || '')\n rowInfos[assetID] = {\n assetID: assetID,\n tr: tr,\n symbol: tr.dataset.symbol || '',\n name: tr.dataset.name || '',\n stateIcons: new WalletIcons(tr),\n actions: {\n connect: getAction(tr, 'connect'),\n unlock: getAction(tr, 'unlock'),\n send: getAction(tr, 'send'),\n deposit: getAction(tr, 'deposit'),\n create: getAction(tr, 'create'),\n rescan: getAction(tr, 'rescan'),\n lock: getAction(tr, 'lock'),\n settings: getAction(tr, 'settings')\n }\n }\n if (!firstRow) firstRow = rowInfos[assetID]\n }\n\n // Prepare templates\n page.marketCard.removeAttribute('id')\n page.marketCard.remove()\n page.oneMarket.removeAttribute('id')\n page.oneMarket.remove()\n\n // Bind the new wallet form.\n this.newWalletForm = new NewWalletForm(page.newWalletForm, () => { this.createWalletSuccess() })\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, () => { this.openWalletSuccess() })\n\n // Bind the Send form.\n bindForm(page.sendForm, page.submitSendForm, () => { this.send() })\n\n // Bind the wallet reconfiguration submission.\n bindForm(page.reconfigForm, page.submitReconfig, () => this.reconfig())\n\n // Bind the row clicks, which shows the available markets for the asset.\n for (const rowInfo of Object.values(rowInfos)) {\n bind(rowInfo.tr, 'click', () => {\n this.showMarkets(rowInfo.assetID)\n })\n }\n\n page.rightBox.querySelectorAll('.form-closer').forEach(el => {\n Doc.bind(el, 'click', () => {\n this.showMarkets(this.lastFormAsset)\n })\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)) {\n this.closePopups()\n } else {\n this.showMarkets(this.lastFormAsset)\n }\n }\n }\n bind(document, 'keyup', this.keyup)\n\n bind(page.downloadLogs, 'click', async () => { this.downloadLogs() })\n bind(page.exportWallet, 'click', async () => { this.displayExportWalletAuth() })\n 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\n // Bind buttons\n for (const [k, asset] of Object.entries(rowInfos)) {\n const assetID = parseInt(k) // keys are string asset ID.\n const a = asset.actions\n const run = (e: Event, f: (assetID: number, asset: RowInfo) => void) => {\n e.stopPropagation()\n f(assetID, asset)\n }\n bind(a.connect, 'click', e => { run(e, this.doConnect.bind(this)) })\n bind(a.send, 'click', e => { run(e, this.showSendForm.bind(this)) })\n bind(a.deposit, 'click', e => { run(e, this.showDeposit.bind(this)) })\n bind(a.create, 'click', e => { run(e, this.showNewWallet.bind(this)) })\n bind(a.rescan, 'click', e => { run(e, this.rescanWallet.bind(this)) })\n bind(a.unlock, 'click', e => { run(e, this.openWallet.bind(this)) })\n bind(a.lock, 'click', async e => { run(e, this.lock.bind(this)) })\n bind(a.settings, 'click', e => { run(e, this.showReconfig.bind(this)) })\n }\n\n // New deposit address button.\n bind(page.newDepAddrBttn, 'click', async () => { this.newDepositAddress() })\n\n // Clicking on the available amount on the Send form populates the\n // amount field.\n bind(page.sendAvail, 'click', () => {\n const asset = this.sendAsset\n const bal = asset.wallet.balance.available\n page.sendAmt.value = String(bal / asset.info.unitinfo.conventional.conversionFactor)\n this.showFiatValue(asset.id, bal, page.sendValue)\n // Ensure we don't check subtract checkbox for assets that don't have a\n // withdraw method.\n if ((asset.wallet.traits & traitWithdrawer) === 0) page.subtractCheckBox.checked = false\n else page.subtractCheckBox.checked = true\n })\n\n for (const [assetID, wallet] of (Object.entries(app().walletMap) as [any, WalletState][])) {\n if (!wallet) continue\n const fiatDisplay = this.page.walletTable.querySelector(`[data-conversion-target=\"${assetID}\"]`) as PageElement\n if (!fiatDisplay) continue\n this.showFiatValue(assetID, wallet.balance.available, fiatDisplay)\n }\n\n // Display fiat value for current send amount.\n bind(page.sendAmt, 'input', () => {\n const asset = this.sendAsset\n if (!asset) return\n const amt = parseFloat(page.sendAmt.value || '0')\n const conversionFactor = asset.info.unitinfo.conventional.conversionFactor\n this.showFiatValue(asset.id, amt * conversionFactor, page.sendValue)\n })\n\n // A link on the wallet reconfiguration form to show/hide the password field.\n bind(page.showChangePW, 'click', () => {\n this.changeWalletPW = !this.changeWalletPW\n this.setPWSettingViz(this.changeWalletPW)\n })\n\n // Changing the type of wallet.\n bind(page.changeWalletTypeSelect, 'change', () => {\n this.changeWalletType()\n })\n 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.reconfigAsset)\n })\n\n if (!firstRow) return\n this.showMarkets(firstRow.assetID)\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 })\n }\n\n closePopups () {\n Doc.hide(this.page.forms)\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 /*\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 * hideBox hides the displayed box after waiting for the currently running\n * animation to complete.\n */\n async hideBox () {\n if (this.animation) await this.animation\n if (!this.displayed) return\n Doc.hide(this.displayed)\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 /*\n * Show the markets box, which lists the markets available for a selected\n * asset.\n */\n async showMarkets (assetID: number) {\n const page = this.page\n const box = page.marketsBox\n const card = page.marketsCard\n const rowInfo = this.rowInfos[assetID]\n await this.hideBox()\n Doc.empty(card)\n page.marketsFor.textContent = rowInfo.name\n page.marketsForLogo.src = Doc.logoPath(app().assets[assetID].symbol)\n for (const [host, xc] of Object.entries(app().user.exchanges)) {\n let count = 0\n if (!xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n if (market.baseid === assetID || market.quoteid === assetID) count++\n }\n if (count === 0) continue\n const marketBox = page.marketCard.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(marketBox)\n tmpl.dexTitle.textContent = host\n card.appendChild(marketBox)\n if (!xc.markets) continue\n for (const market of Object.values(xc.markets)) {\n // Only show markets where this is the base or quote asset.\n if (market.baseid !== assetID && market.quoteid !== assetID) continue\n const mBox = page.oneMarket.cloneNode(true) as HTMLElement\n Doc.safeSelector(mBox, 'span').textContent = prettyMarketName(market)\n let counterSymbol = market.basesymbol\n if (market.baseid === assetID) counterSymbol = market.quotesymbol\n Doc.safeSelector(mBox, 'img').src = Doc.logoPath(counterSymbol)\n // Bind the click to a load of the markets page.\n const pageData = { host: host, base: market.baseid, quote: market.quoteid }\n bind(mBox, 'click', () => { app().loadPage('markets', pageData) })\n tmpl.markets.appendChild(mBox)\n }\n }\n this.animation = this.showBox(box)\n }\n\n /* Show the new wallet form. */\n async showNewWallet (assetID: number) {\n const page = this.page\n const box = page.newWalletForm\n await this.hideBox()\n this.walletAsset = this.lastFormAsset = assetID\n this.newWalletForm.setAsset(assetID)\n this.animation = this.showBox(box)\n await this.newWalletForm.loadDefaults()\n }\n\n async rescanWallet (assetID: number) {\n const loaded = app().loading(this.body)\n const url = '/api/rescanwallet'\n const req = { assetID: assetID }\n const res = await postJSON(url, req)\n loaded()\n if (res.code === activeOrdersErrCode) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n return\n }\n app().checkResponse(res)\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 this.openAsset = assetID\n const open = {\n assetID: assetID\n }\n const res = await postJSON('/api/openwallet', open)\n if (app().checkResponse(res)) {\n this.openWalletSuccess.bind(this)()\n } else {\n this.showOpen(assetID, `Error opening wallet: ${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 this.openAsset = this.lastFormAsset = assetID\n await this.hideBox()\n this.unlockForm.refresh(app().assets[assetID])\n if (errorMsg) this.unlockForm.showErrorOnly(errorMsg)\n this.animation = this.showBox(page.unlockWalletForm, page.walletPass)\n }\n\n /* Show the form used to change wallet configuration settings. */\n async showReconfig (assetID: number) {\n const page = this.page\n Doc.hide(page.changeWalletType, page.changeTypeHideIcon, page.reconfigErr, page.showChangeType, page.changeTypeHideIcon)\n Doc.hide(page.reconfigErr)\n // Hide update password section by default\n this.reconfigAsset = this.lastFormAsset = assetID\n this.changeWalletPW = false\n this.setPWSettingViz(this.changeWalletPW)\n const asset = app().assets[assetID]\n\n const currentDef = app().currentWalletDefinition(assetID)\n\n if (asset.info.availablewallets.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 asset.info.availablewallets) {\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 if ((wallet.traits & traitLogFiler) !== 0) Doc.show(page.downloadLogs)\n else Doc.hide(page.downloadLogs)\n if ((wallet.traits & traitRecoverer) !== 0) Doc.show(page.recoverWallet)\n else Doc.hide(page.recoverWallet)\n if ((wallet.traits & traitRestorer)) Doc.show(page.exportWallet)\n else Doc.hide(page.exportWallet)\n\n page.recfgAssetLogo.src = Doc.logoPath(asset.symbol)\n page.recfgAssetName.textContent = asset.info.name\n await this.hideBox()\n this.animation = this.showBox(page.reconfigForm)\n const loaded = app().loading(page.reconfigForm)\n const res = await postJSON('/api/walletsettings', {\n assetID: assetID\n })\n loaded()\n if (!app().checkResponse(res, true)) {\n page.reconfigErr.textContent = res.msg\n Doc.show(page.reconfigErr)\n return\n }\n const walletIsActive = app().walletIsActive(assetID)\n this.reconfigForm.update(currentDef.configopts || [], walletIsActive)\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.reconfigAsset, walletType)\n this.reconfigForm.update(walletDef.configopts || [])\n this.updateDisplayedReconfigFields(walletDef)\n }\n\n updateDisplayedReconfigFields (walletDef: WalletDefinition) {\n if (walletDef.seeded) {\n Doc.hide(this.page.showChangePW)\n this.changeWalletPW = false\n this.setPWSettingViz(false)\n } else Doc.show(this.page.showChangePW)\n }\n\n /* Display a deposit address. */\n async showDeposit (assetID: number) {\n const page = this.page\n Doc.hide(page.depositErr)\n const box = page.deposit\n const asset = app().assets[assetID]\n page.depositLogo.src = Doc.logoPath(asset.symbol)\n const wallet = app().walletMap[assetID]\n this.depositAsset = this.lastFormAsset = assetID\n if (!wallet) {\n app().notify(ntfn.make('Cannot retrieve deposit address.', `No wallet found for ${asset.info.name}`, ntfn.ERROR)) // TODO: translate\n return\n }\n await this.hideBox()\n page.depositName.textContent = asset.info.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 this.animation = this.showBox(box)\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(page.deposit)\n const res = await postJSON('/api/depositaddress', {\n assetID: this.depositAsset\n })\n loaded()\n if (!app().checkResponse(res, true)) {\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 /* 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 asset = this.sendAsset = app().assets[assetID]\n this.lastFormAsset = assetID\n const wallet = app().walletMap[assetID]\n if (!wallet) {\n app().notify(ntfn.make('Cannot send/withdraw.', `No wallet found for ${asset.info.name}`, ntfn.ERROR))\n }\n await this.hideBox()\n\n Doc.hide(page.senderOnlyHelpText)\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.senderOnlyHelpText)\n page.subtractCheckBox.checked = false\n } else {\n Doc.show(page.toggleSubtract)\n }\n\n page.sendAddr.value = ''\n page.sendAmt.value = ''\n page.sendPW.value = ''\n page.sendErr.textContent = ''\n\n this.showFiatValue(asset.id, 0, page.sendValue)\n page.sendAvail.textContent = Doc.formatFullPrecision(wallet.balance.available, asset.info.unitinfo)\n page.sendLogo.src = Doc.logoPath(asset.symbol)\n page.sendName.textContent = asset.info.name\n // page.sendFee.textContent = wallet.feerate\n // page.sendUnit.textContent = wallet.units\n box.dataset.assetID = String(assetID)\n this.animation = this.showBox(box, page.walletPass)\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', {\n assetID: assetID\n })\n loaded()\n if (!app().checkResponse(res)) return\n const rowInfo = this.rowInfos[assetID]\n Doc.hide(rowInfo.actions.connect)\n }\n\n /* createWalletSuccess is the success callback for wallet creation. */\n async createWalletSuccess () {\n const rowInfo = this.rowInfos[this.walletAsset]\n this.showMarkets(rowInfo.assetID)\n await app().fetchUser()\n await app().loadPage('wallets')\n }\n\n /* openWalletSuccess is the success callback for wallet unlocking. */\n async openWalletSuccess () {\n const rowInfo = this.rowInfos[this.openAsset]\n const a = rowInfo.actions\n Doc.show(a.send, a.deposit)\n Doc.hide(a.unlock, a.connect)\n if (app().walletMap[rowInfo.assetID].encrypted) {\n Doc.show(a.lock)\n }\n this.showMarkets(this.openAsset)\n }\n\n /* send submits the send form to the API. */\n async send () {\n const page = this.page\n Doc.hide(page.sendErr)\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 open = {\n assetID: assetID,\n address: page.sendAddr.value,\n subtract: subtract,\n value: Math.round(parseFloat(page.sendAmt.value || '') * conversionFactor),\n pw: page.sendPW.value\n }\n const loaded = app().loading(page.sendForm)\n const res = await postJSON('/api/send', open)\n loaded()\n if (!app().checkResponse(res, true)) {\n page.sendErr.textContent = res.msg\n Doc.show(page.sendErr)\n return\n }\n this.showMarkets(assetID)\n }\n\n /* update wallet configuration */\n async reconfig () {\n const page = this.page\n Doc.hide(page.reconfigErr)\n if (!page.appPW.value && !State.passwordIsCached()) {\n page.reconfigErr.textContent = intl.prep(intl.ID_NO_APP_PASS_ERROR_MSG)\n Doc.show(page.reconfigErr)\n return\n }\n\n let walletType = app().currentWalletDefinition(this.reconfigAsset).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: this.reconfigAsset,\n config: this.reconfigForm.map(),\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, true)) {\n page.reconfigErr.textContent = res.msg\n Doc.show(page.reconfigErr)\n return\n }\n this.showMarkets(this.reconfigAsset)\n }\n\n /* lock instructs the API to lock the wallet. */\n async lock (assetID: number, asset: RowInfo) {\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 const a = asset.actions\n Doc.hide(a.send, a.lock, a.deposit)\n Doc.show(a.unlock)\n }\n\n async downloadLogs () {\n const search = new URLSearchParams('')\n search.append('assetid', `${this.reconfigAsset}`)\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 () {\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 () {\n const page = this.page\n const req = {\n assetID: this.reconfigAsset,\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 page.exportWalletErr.textContent = res.msg\n Doc.show(page.exportWalletErr)\n }\n }\n\n // displayRestoreWalletInfo displays the information needed to restore a\n // wallet in external wallets.\n async displayRestoreWalletInfo (info: WalletRestoration[]) {\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 () {\n const page = this.page\n Doc.hide(page.recoverWalletErr)\n const req = {\n assetID: this.reconfigAsset,\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 === activeOrdersErrCode) {\n this.forceUrl = url\n this.forceReq = req\n this.showConfirmForce()\n } else if (app().checkResponse(res)) {\n this.closePopups()\n } else {\n page.recoverWalletErr.textContent = res.msg\n Doc.show(page.recoverWalletErr)\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 () {\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 page.confirmForceErr.textContent = res.msg\n Doc.show(page.confirmForceErr)\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) {\n const td = Doc.safeSelector(this.page.walletTable, `[data-balance-target=\"${note.assetID}\"]`)\n td.textContent = Doc.formatFullPrecision(note.balance.available, app().unitInfo(note.assetID))\n const fiatDisplay = Doc.safeSelector(this.page.walletTable, `[data-conversion-target=\"${note.assetID}\"]`)\n if (!fiatDisplay) return\n this.showFiatValue(note.assetID, note.balance.available, fiatDisplay)\n }\n\n /* handleRatesNote handles fiat rate notifications, updating the fiat value of\n * all supported assets.\n */\n handleRatesNote (note: RateNote) {\n app().fiatRatesMap = note.fiatRates\n for (const [assetID, wallet] of (Object.entries(app().walletMap) as [any, WalletState][])) {\n if (!wallet) continue\n const fiatDisplay = this.page.walletTable.querySelector(`[data-conversion-target=\"${assetID}\"]`) as PageElement\n if (!fiatDisplay) continue\n this.showFiatValue(assetID, wallet.balance.available, fiatDisplay)\n }\n }\n\n // showFiatValue displays the fiat equivalent for the provided amount.\n showFiatValue (assetID: number, amount: number, display: PageElement) {\n if (display) {\n const rate = app().fiatRatesMap[assetID]\n display.textContent = Doc.formatFiatConversion(amount, 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 /*\n * handleWalletStateNote is a handler for both the 'walletstate' and\n * 'walletconfig' notifications.\n */\n handleWalletStateNote (note: WalletStateNote) {\n this.rowInfos[note.wallet.assetID].stateIcons.readWallet(note.wallet)\n const fiatDisplay = this.page.walletTable.querySelector(`[data-conversion-target=\"${note.wallet.assetID}\"]`) as PageElement\n if (!fiatDisplay) return\n this.showFiatValue(note.wallet.assetID, note.wallet.balance.available, fiatDisplay)\n }\n\n /*\n * unload is called by the Application when the user navigates away from\n * the /wallets page.\n */\n unload () {\n Doc.unbind(document, 'keyup', this.keyup)\n }\n}\n\n/*\n * Given a market object as created with makeMarket, prettyMarketName will\n * create a string ABC-XYZ, where ABC and XYZ are the upper-case ticker symbols\n * for the base and quote assets respectively.\n */\nfunction prettyMarketName (market: Market) {\n return `${market.basesymbol.toUpperCase()}-${market.quotesymbol.toUpperCase()}`\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'\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 defaultTLSText: string\n keyup: (e: KeyboardEvent) => void\n\n constructor (body: HTMLElement) {\n super()\n this.body = body\n this.defaultTLSText = 'none selected'\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.dark(page.darkMode.checked || false)\n if (page.darkMode.checked) {\n document.body.classList.add('dark')\n } else {\n document.body.classList.remove('dark')\n }\n })\n\n Doc.bind(page.showPokes, 'click', () => {\n const show = page.showPokes.checked || false\n State.setCookie('popups', 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 => {\n this.confirmRegisterForm.setAsset(assetID)\n\n const asset = app().assets[assetID]\n const wallet = asset.wallet\n if (wallet) {\n const fee = this.currentDEX.regFees[asset.symbol]\n if (wallet.synced && wallet.balance.available > fee.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.newWalletForm.loadDefaults()\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, true)) {\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 feeAmt = this.currentDEX.regFees[asset.symbol].amount\n\n if (wallet.synced && wallet.balance.available > feeAmt) {\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 = '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 req = {\n pw: pw,\n account: account\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 const loginResponse = await postJSON('/api/login', { pass: pw })\n if (!app().checkResponse(loginResponse)) {\n page.importAccountErr.textContent = loginResponse.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, true)) {\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","import Doc 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 unbind = Doc.unbind\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: '#3a3a3a',\n gridLines: '#2a2a2a',\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: '#3a3a3a',\n gridLines: '#dadada',\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.\nclass Chart {\n parent: HTMLElement\n report: ChartReporters\n theme: Theme\n canvas: HTMLCanvasElement\n visible: 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\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 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 // 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 this.boundResizer = () => { this.resize(parent.clientHeight) }\n bind(window, 'resize', this.boundResizer)\n bind(this.canvas, 'click', (e: MouseEvent) => { this.click(e) })\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 programatically after the caller sets a style.height\n * but before the clientHeight has been updated.\n */\n resize (parentHeight: number) {\n this.canvas.width = this.parent.clientWidth\n this.canvas.height = parentHeight - 20 // magic number derived from a soup of css values.\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 /* hide hides the canvas */\n hide () {\n this.visible = false\n Doc.hide(this.canvas)\n }\n\n /* show shows the canvas */\n show () {\n this.visible = true\n Doc.show(this.canvas)\n this.resize(this.parent.clientHeight)\n }\n\n /* The market handler will call unattach when the markets page is unloaded. */\n unattach () {\n unbind(window, 'resize', this.boundResizer)\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 () {\n this.ctx.textAlign = 'center'\n this.ctx.textBaseline = 'middle'\n this.ctx.font = '12px \\'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 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) * 1.5\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(parent.clientHeight)\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) return\n\n this.clear()\n // if (!this.book || this.book.empty()) return\n\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(parent.clientHeight)\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) return\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 * bFactor / qFactor\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\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","// MessageSocket is a WebSocket manager that uses the Decred DEX Mesage 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} from './charts'\nimport { postJSON } from './http'\nimport { NewWalletForm, UnlockWalletForm, AccelerateOrderForm, bind as bindForm } 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 SpotPriceNote,\n FeePaymentNote,\n OrderNote,\n EpochNote,\n BalanceNote,\n MiniOrder,\n RemainderUpdate,\n ConnEventNote,\n Spot,\n OrderOption,\n ConnectionStatus\n} from './registry'\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'\n\nconst lastMarketKey = 'selectedMarket'\nconst chartRatioKey = 'chartRatio'\nconst depthZoomKey = 'depthZoom'\n\nconst animationLength = 500\n\nconst anHour = 60 * 60 * 1000 // milliseconds\n\nconst depthChart = 'depth_chart'\nconst candleChart = 'candle_chart'\n\nconst check = document.createElement('span')\ncheck.classList.add('ico-check')\n\nconst percentFormatter = new Intl.NumberFormat(document.documentElement.lang, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 2\n})\n\ninterface MetaOrder {\n row: HTMLElement\n order: 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 BalanceWidgetElement {\n id: number\n cfg: Asset | null\n logo: PageElement\n avail: PageElement\n newWalletRow: PageElement\n newWalletBttn: PageElement\n locked: PageElement\n immature: PageElement\n unsupported: PageElement\n expired: PageElement\n connect: PageElement\n spinner: PageElement\n iconBox: PageElement\n stateIcons: WalletIcons\n}\n\ninterface LoadTracker {\n loaded: () => void,\n timer: number\n}\n\ninterface OrderRow extends HTMLElement {\n manager: OrderTableRowManager\n}\n\nexport default class MarketsPage extends BasePage {\n page: Record\n main: HTMLElement\n loaded: (() => void) | null\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 ordersSortKey: string\n ordersSortDirection: 1 | -1\n ogTitle: string\n depthChart: DepthChart\n candleChart: CandleChart\n currentChart: string\n candleDur: string\n balanceWgt: BalanceWidget\n marketList: MarketList\n quoteUnits: NodeListOf\n baseUnits: NodeListOf\n unlockForm: UnlockWalletForm\n newWalletForm: NewWalletForm\n keyup: (e: KeyboardEvent) => void\n secondTicker: number\n candlesLoading: LoadTracker | null\n accelerateOrderForm: AccelerateOrderForm\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 this.loaded = app().loading(this.main.parentElement)\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.preorderCache = {}\n this.depthLines = {\n hover: [],\n input: []\n }\n this.hovers = []\n // 'Your Orders' list sort key and direction.\n this.ordersSortKey = 'submitTime'\n // 1 if sorting ascendingly, -1 if sorting descendingly.\n this.ordersSortDirection = 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.marketChart, depthReporters, State.fetch(depthZoomKey))\n\n const candleReporters: CandleReporters = {\n mouse: c => { this.reportMouseCandle(c) }\n }\n this.candleChart = new CandleChart(page.marketChart, 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.candleChart.hide()\n this.currentChart = depthChart\n this.candleDur = ''\n\n // Set up the BalanceWidget.\n {\n const wgt = this.balanceWgt = new BalanceWidget(page.balanceTable)\n const baseIcons = wgt.base.stateIcons.icons\n const quoteIcons = wgt.quote.stateIcons.icons\n bind(wgt.base.connect, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.connect, 'click', () => { this.showOpen(this.market.quote, this.walletUnlocked) })\n bind(wgt.base.expired, 'click', () => { this.showOpen(this.market.base, this.walletUnlocked) })\n bind(wgt.quote.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(wgt.base.newWalletBttn, 'click', () => { this.showCreate(this.market.base) })\n bind(wgt.quote.newWalletBttn, 'click', () => { this.showCreate(this.market.quote) })\n }\n\n // Prepare templates for the buy and sell tables and the user's order table.\n OrderUtil.setOptionTemplates(page)\n Doc.cleanTemplates(page.rowTemplate, page.liveTemplate, page.durBttnTemplate, page.booleanOptTmpl, page.rangeOptTmpl, page.orderOptTmpl)\n\n // Prepare the list of markets.\n this.marketList = new MarketList(page.marketList)\n for (const xc of this.marketList.xcSections) {\n if (!xc.marketRows) continue\n for (const row of xc.marketRows) {\n bind(row.node, 'click', () => {\n this.setMarket(xc.host, row.mkt.baseid, row.mkt.quoteid)\n })\n }\n }\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('sellred')\n page.submitBttn.classList.add('buygreen')\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('sellred')\n page.submitBttn.classList.remove('buygreen')\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 bind(page.depthBttn, 'click', () => {\n this.depthChartSelected()\n })\n bind(page.candlestickBttn, 'click', () => {\n this.candleChartSelected()\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 // 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.liveTable.querySelectorAll('[data-ordercol]')\n .forEach((th: HTMLElement) => bind(th, 'click', () => setOrdersSortCol(th.dataset.ordercol || '')))\n\n const setOrdersSortCol = (key: string) => {\n // First unset header's current sorted col classes.\n unsetOrdersSortColClasses()\n // If already sorting by key change sort direction.\n if (this.ordersSortKey === key) {\n this.ordersSortDirection *= -1\n } else {\n // Otherwise update sort key and set default direction to ascending.\n this.ordersSortKey = key\n this.ordersSortDirection = 1\n }\n this.refreshActiveOrders()\n // Set header's new sorted col classes.\n setOrdersSortColClasses()\n }\n\n const sortClassByDirection = () => {\n if (this.ordersSortDirection === 1) return 'sorted-asc'\n return 'sorted-dsc'\n }\n\n const unsetOrdersSortColClasses = () => {\n page.liveTable.querySelectorAll('[data-ordercol]')\n .forEach(th => th.classList.remove('sorted-asc', 'sorted-dsc'))\n }\n\n const setOrdersSortColClasses = () => {\n const key = this.ordersSortKey\n const sortCls = sortClassByDirection()\n Doc.safeSelector(page.liveTable, `[data-ordercol=${key}]`).classList.add(sortCls)\n }\n\n // Set default's sorted col header classes.\n setOrdersSortColClasses()\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.marketSearch, 'change', () => { this.filterMarkets() })\n bind(page.marketSearch, 'keyup', () => { this.filterMarkets() })\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.liveList, 'mouseleave', () => {\n this.activeMarkerRate = null\n this.setDepthMarkers()\n })\n\n // Load the user's layout preferences.\n const setChartRatio = (r: number) => {\n if (r > 0.7) r = 0.7\n else if (r < 0.25) r = 0.25\n\n const h = r * (this.main.clientHeight - app().header.offsetHeight)\n page.marketChart.style.height = `${h}px`\n\n this.depthChart.resize(h)\n this.candleChart.resize(h)\n }\n const chartDivRatio = State.fetch(chartRatioKey)\n if (chartDivRatio) {\n setChartRatio(chartDivRatio)\n }\n // Bind chart resizing.\n bind(page.chartResizer, 'mousedown', (e: MouseEvent) => {\n if (e.button !== 0) return\n e.preventDefault()\n let chartRatio: number\n const trackMouse = (ee: MouseEvent) => {\n ee.preventDefault()\n const box = page.rightSide.getBoundingClientRect()\n const h = box.bottom - box.top\n chartRatio = (ee.pageY - box.top) / h\n setChartRatio(chartRatio)\n }\n bind(document, 'mousemove', trackMouse)\n bind(document, 'mouseup', () => {\n if (chartRatio) State.store(chartRatioKey, chartRatio)\n Doc.unbind(document, 'mousemove', trackMouse)\n })\n })\n\n // Notification filters.\n app().registerNoteFeeder({\n order: (note: OrderNote) => { this.handleOrderNote(note) },\n epoch: (note: EpochNote) => { this.handleEpochNote(note) },\n conn: (note: ConnEventNote) => { this.handleConnNote(note) },\n balance: (note: BalanceNote) => { this.handleBalanceNote(note) },\n feepayment: (note: FeePaymentNote) => { this.handleFeePayment(note) },\n spots: (note: SpotPriceNote) => { this.handlePriceUpdate(note) }\n })\n\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.fetch(lastMarketKey)\n }\n if (!selected || !this.marketList.exists(selected.host, selected.base, selected.quote)) {\n selected = this.marketList.first()\n }\n this.setMarket(selected.host, selected.base, selected.quote)\n\n // Start a ticker to update time-since values.\n this.secondTicker = window.setInterval(() => {\n for (const metaOrder of Object.values(this.metaOrders)) {\n const td = Doc.tmplElement(metaOrder.row, 'age')\n td.textContent = Doc.timeSince(metaOrder.order.submitTime)\n }\n }, 1000)\n\n // set the initial state for the registration status\n this.setRegistrationStatusVisibility()\n this.setBalanceVisibility()\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 /* hasFeePending is true if the fee payment is pending */\n hasFeePending () {\n return !!this.market.dex.pendingFee\n }\n\n /* assetsAreSupported is true if all the assets of the current market are\n * supported\n */\n assetsAreSupported () {\n const [b, q] = [this.market.base, this.market.quote]\n return b && q\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)\n const feePaid = !this.hasFeePending()\n const assetsAreSupported = this.assetsAreSupported()\n const { base, quote } = this.market\n const hasWallets = base && app().assets[base.id].wallet && quote && app().assets[quote.id].wallet\n\n if (feePaid && assetsAreSupported && hasWallets) {\n Doc.show(page.orderForm)\n }\n }\n\n /* setLoaderMsgVisibility displays a message in case a dex asset is not\n * supported\n */\n setLoaderMsgVisibility () {\n const { page, market } = this\n\n if (this.assetsAreSupported()) {\n // make sure to hide the loader msg\n Doc.hide(page.loaderMsg)\n return\n }\n const symbol = market.base ? market.quoteCfg.symbol : market.baseCfg.symbol\n page.loaderMsg.textContent = intl.prep(intl.ID_NOT_SUPPORTED, { asset: symbol.toUpperCase() })\n Doc.show(page.loaderMsg)\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 const pending = dex.pendingFee\n if (!pending) {\n this.setRegistrationStatusView(intl.prep(intl.ID_REGISTRATION_FEE_SUCCESS), '', 'completed')\n return\n }\n\n const confirmationsRequired = dex.regFees[pending.symbol].confs\n page.confReq.textContent = String(confirmationsRequired)\n const confStatusMsg = `${pending.confs} / ${confirmationsRequired}`\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: { dex } } = this\n\n // If dex is not connected to server, is not possible to know fee\n // registration status.\n if (dex.connectionStatus !== ConnectionStatus.Connected) return\n\n this.updateRegistrationStatusView()\n\n if (dex.pendingFee) {\n Doc.show(page.registrationStatus)\n } else {\n const toggle = () => {\n Doc.hide(page.registrationStatus)\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 }\n }\n\n setOrderBttnText () {\n if (this.isSell()) {\n this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_SELL, { asset: this.market.baseCfg.symbol.toUpperCase() })\n } else this.page.submitBttn.textContent = intl.prep(intl.ID_SET_BUTTON_BUY, { asset: this.market.baseCfg.symbol.toUpperCase() })\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 // 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.connectionStatus !== ConnectionStatus.Connected) {\n // TODO: Figure out why this was like this.\n // this.market = { dex: dex }\n\n page.chartErrMsg.textContent = intl.prep(intl.ID_CONNECTION_FAILED)\n Doc.show(page.chartErrMsg)\n if (this.loaded) this.loaded()\n this.main.style.opacity = '1'\n Doc.hide(page.marketLoader)\n return\n }\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 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: app().assets[base],\n quote: app().assets[quote],\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.show(page.marketLoader)\n if (!dex.candleDurs || dex.candleDurs.length === 0) this.currentChart = depthChart\n if (this.currentChart === depthChart) ws.request('loadmarket', makeMarket(host, base, quote))\n else {\n if (dex.candleDurs.indexOf(this.candleDur) === -1) this.candleDur = dex.candleDurs[0]\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)\n return\n }\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 metaOrd of Object.values(this.metaOrders)) {\n const [row, ord] = [metaOrd.row, metaOrd.order]\n if (ord.status !== OrderUtil.StatusBooked) continue\n if (r.hoverMarkers.indexOf(ord.rate) > -1) {\n row.classList.add('hover')\n this.hovers.push(row)\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 Doc.show(page.hoverData)\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.store(depthZoomKey, zoom)\n }\n\n reportMouseCandle (candle: Candle | null) {\n const page = this.page\n if (!candle) {\n Doc.hide(page.hoverData)\n return\n }\n\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 Doc.show(page.hoverData)\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 if (!limit && !sell) {\n qtyField = page.mktBuyField\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 || '', market.baseUnitInfo.conventional.conversionFactor),\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, true)) {\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 = lots === 1 ? 'lot' : '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, toAsset] = sell ? [this.market.base, this.market.quote] : [this.market.quote, this.market.base]\n page.maxFromAmt.textContent = Doc.formatCoinValue(maxOrder.value || 0, fromAsset.info.unitinfo)\n page.maxFromTicker.textContent = fromAsset.symbol.toUpperCase()\n // Could subtract the maxOrder.redemptionFees here.\n const toConversion = sell ? this.adjustedRate() / OrderUtil.RateEncodingFactor : OrderUtil.RateEncodingFactor / this.adjustedRate()\n page.maxToAmt.textContent = Doc.formatCoinValue((maxOrder.value || 0) * toConversion, toAsset.info.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 this.depthChart.set(this.book, cfg.lotsize, cfg.ratestep, baseUnitInfo, quoteUnitInfo)\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 /*\n * ordersSortCompare returns sort compare function according to the active\n * sort key and direction.\n */\n ordersSortCompare () {\n switch (this.ordersSortKey) {\n case 'submitTime':\n return (a: Order, b: Order) => this.ordersSortDirection * (b.submitTime - a.submitTime)\n case 'rate':\n return (a: Order, b: Order) => this.ordersSortDirection * (a.rate - b.rate)\n case 'qty':\n return (a: Order, b: Order) => this.ordersSortDirection * (a.qty - b.qty)\n case 'type':\n return (a: Order, b: Order) => this.ordersSortDirection *\n OrderUtil.typeString(a).localeCompare(OrderUtil.typeString(b))\n case 'sell':\n return (a: Order, b: Order) => this.ordersSortDirection *\n (OrderUtil.sellString(a)).localeCompare(OrderUtil.sellString(b))\n case 'status':\n return (a: Order, b: Order) => this.ordersSortDirection *\n (OrderUtil.statusString(a)).localeCompare(OrderUtil.statusString(b))\n case 'settled':\n return (a: Order, b: Order) => this.ordersSortDirection *\n ((OrderUtil.settled(a) * 100 / a.qty) - (OrderUtil.settled(b) * 100 / b.qty))\n case 'filled':\n return (a: Order, b: Order) => this.ordersSortDirection *\n ((OrderUtil.filled(a) * 100 / a.qty) - (OrderUtil.filled(b) * 100 / b.qty))\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 const compare = this.ordersSortCompare()\n orders.sort(compare)\n\n Doc.empty(page.liveList)\n for (const ord of orders) {\n const row = page.liveTemplate.cloneNode(true) as HTMLElement\n metaOrders[ord.id] = {\n row: row,\n order: ord\n }\n Doc.bind(row, 'mouseenter', () => {\n this.activeMarkerRate = ord.rate\n this.setDepthMarkers()\n })\n this.updateUserOrderRow(row, ord)\n if (ord.type === OrderUtil.Limit && (ord.tif === OrderUtil.StandingTiF && ord.status < OrderUtil.StatusExecuted)) {\n const icon = Doc.tmplElement(row, 'cancelBttn')\n Doc.show(icon)\n bind(icon, 'click', e => {\n e.stopPropagation()\n this.showCancel(row, ord.id)\n })\n }\n\n const accelerateBttn = Doc.tmplElement(row, 'accelerateBttn')\n bind(accelerateBttn, 'click', e => {\n e.stopPropagation()\n this.showAccelerate(ord)\n })\n if (app().canAccelerateOrder(ord)) {\n Doc.show(accelerateBttn)\n }\n\n const side = Doc.tmplElement(row, 'side')\n side.classList.add(ord.sell ? 'sellcolor' : 'buycolor')\n const link = Doc.tmplElement(row, 'link')\n link.href = `order/${ord.id}`\n app().bindInternalNavigation(row)\n page.liveList.appendChild(row)\n app().bindTooltips(row)\n }\n this.setDepthMarkers()\n }\n\n /*\n * updateUserOrderRow sets the td contents of the user's order table row.\n */\n updateUserOrderRow (tr: HTMLElement, ord: Order) {\n updateDataCol(tr, 'type', OrderUtil.typeString(ord))\n updateDataCol(tr, 'side', OrderUtil.sellString(ord))\n updateDataCol(tr, 'age', Doc.timeSince(ord.submitTime))\n updateDataCol(tr, 'rate', Doc.formatCoinValue(ord.rate / this.market.rateConversionFactor))\n updateDataCol(tr, 'qty', Doc.formatCoinValue(ord.qty, this.market.baseUnitInfo))\n updateDataCol(tr, 'filled', `${(OrderUtil.filled(ord) / ord.qty * 100).toFixed(1)}%`)\n updateDataCol(tr, 'settled', `${(OrderUtil.settled(ord) / ord.qty * 100).toFixed(1)}%`)\n updateDataCol(tr, 'status', OrderUtil.statusString(ord))\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 mo of Object.values(this.metaOrders)) {\n const ord = mo.order\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 Doc.hide(page.marketLoader)\n this.marketList.select(host, b.id, q.id)\n\n State.store(lastMarketKey, {\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 => { el.textContent = b.symbol.toUpperCase() })\n this.quoteUnits.forEach(el => { el.textContent = q.symbol.toUpperCase() })\n this.balanceWgt.setWallets(host, b.id, q.id)\n this.setMarketBuyOrderEstimate()\n this.refreshActiveOrders()\n if (this.loaded) {\n this.loaded()\n this.loaded = null\n Doc.animate(250, progress => {\n this.main.style.opacity = String(progress)\n })\n }\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 this.depthChart.hide()\n this.candleChart.show()\n if (data.host !== this.market.dex.host) return\n const dur = data.payload.dur\n this.market.candleCaches[dur] = data.payload\n if (this.currentChart !== candleChart || this.candleDur !== dur) return\n this.candleChart.setCandles(data.payload, this.market.cfg, this.market.baseUnitInfo, this.market.quoteUnitInfo)\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.currentChart !== candleChart || 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(page.unlockWalletForm, page.verifyForm, page.newWalletForm,\n page.cancelForm, page.vDetailPane, 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 = '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 /* 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 // Set the to and from icons in the fee details pane.\n for (const icon of Doc.applySelector(page.vDetailPane, '[data-icon]')) {\n switch (icon.dataset.icon) {\n case 'from':\n icon.src = Doc.logoPath(fromAsset.symbol)\n break\n case 'to':\n icon.src = Doc.logoPath(toAsset.symbol)\n }\n }\n\n Doc.hide(page.vUnlockPreorder, page.vPreorderErr)\n Doc.show(page.vPreorder)\n\n page.vBuySell.textContent = isSell ? 'Selling' : '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.info.unitinfo)\n const total = order.rate / OrderUtil.RateEncodingFactor * order.qty\n page.vTotal.textContent = Doc.formatCoinValue(total, quoteAsset.info.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.info.unitinfo)\n page.vmToAsset.textContent = toAsset.symbol.toUpperCase()\n // Format recieved 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 const buyBtnClass = 'buygreen'\n const sellBtnClass = 'sellred'\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, true)) {\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, true)) 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 /* preOrder loads the options and fetches pre-order estimates */\n async preOrder (order: TradeForm) {\n // if (!this.validateOrder(order)) return\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 Doc.empty(page.vOrderOpts)\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 const addOption = (opt: OrderOption, isSwap: boolean) => page.vOrderOpts.appendChild(OrderUtil.optionElement(opt, order, changed, isSwap))\n for (const opt of swap.options || []) addOption(opt, true)\n for (const opt of redeem.options || []) addOption(opt, false)\n app().bindTooltips(page.vOrderOpts)\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 const { baseUnitInfo, quoteUnitInfo, rateConversionFactor } = market\n const swapped = swap.estimate.value || 0\n const fmtPct = percentFormatter.format\n\n let [toUI, fromUI] = [baseUnitInfo, quoteUnitInfo]\n if (this.currentOrder.sell) {\n [fromUI, toUI] = [toUI, fromUI]\n }\n\n // Set swap fee estimates in the details pane.\n const bestSwapPct = swap.estimate.realisticBestCase / swapped * 100\n page.vSwapFeesLowPct.textContent = `${fmtPct(bestSwapPct)}%`\n page.vSwapFeesLow.textContent = Doc.formatCoinValue(swap.estimate.realisticBestCase, fromUI)\n const worstSwapPct = swap.estimate.realisticWorstCase / swapped * 100\n page.vSwapFeesHighPct.textContent = `${fmtPct(worstSwapPct)}%`\n page.vSwapFeesHigh.textContent = Doc.formatCoinValue(swap.estimate.realisticWorstCase, fromUI)\n page.vSwapFeesMaxPct.textContent = `${fmtPct(swap.estimate.maxFees / swapped * 100)}%`\n page.vSwapFeesMax.textContent = Doc.formatCoinValue(swap.estimate.maxFees, fromUI)\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 bestRedeemPct = redeem.estimate.realisticBestCase / received * 100\n page.vRedeemFeesLowPct.textContent = `${fmtPct(bestRedeemPct)}%`\n page.vRedeemFeesLow.textContent = Doc.formatCoinValue(redeem.estimate.realisticBestCase, toUI)\n const worstRedeemPct = redeem.estimate.realisticWorstCase / received * 100\n page.vRedeemFeesHighPct.textContent = `${fmtPct(worstRedeemPct)}%`\n page.vRedeemFeesHigh.textContent = Doc.formatCoinValue(redeem.estimate.realisticWorstCase, toUI)\n\n // Set the summary percent, which is a simple addition of swap and redeem\n // loss percents.\n page.vFeeSummaryLow.textContent = fmtPct(bestSwapPct + bestRedeemPct)\n page.vFeeSummaryHigh.textContent = fmtPct(worstSwapPct + worstRedeemPct)\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, true)) {\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 order = this.metaOrders[orderID].order\n const page = this.page\n const remaining = order.qty - order.filled\n const asset = OrderUtil.isMarketBuy(order) ? this.market.quote : this.market.base\n page.cancelRemain.textContent = Doc.formatCoinValue(remaining, asset.info.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: order\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 this.newWalletForm.loadDefaults()\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 handlePriceUpdate (note: SpotPriceNote) {\n const xcSection = this.marketList.xcSection(note.host)\n if (!xcSection) return\n for (const spot of Object.values(note.spots)) {\n const marketRow = xcSection.marketRow(spot.baseID, spot.quoteID)\n if (marketRow) marketRow.setSpot(spot)\n }\n }\n\n /*\n * handleFeePayment is the handler for the 'feepayment' notification type.\n * This is used to update the registration status of the current exchange.\n */\n handleFeePayment (note: FeePaymentNote) {\n const dexAddr = note.dex\n if (dexAddr !== this.market.dex.host) return\n // update local dex\n this.market.dex = app().exchanges[dexAddr]\n this.setRegistrationStatusVisibility()\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 metaOrder = this.metaOrders[order.id]\n // If metaOrder doesn't exist for the given order it means it was\n // created via dexcctl and the GUI isn't aware of it.\n // Call refreshActiveOrders to grab the order.\n if (!metaOrder) return this.refreshActiveOrders()\n const oldStatus = metaOrder.status\n metaOrder.order = order\n const cancelBttn = Doc.tmplElement(metaOrder.row, 'cancelBttn')\n if (note.topic === 'MissedCancel') Doc.show(cancelBttn)\n if (order.filled === order.qty) Doc.hide(cancelBttn)\n const accelerateBttn = Doc.tmplElement(metaOrder.row, 'accelerateBttn')\n if (app().canAccelerateOrder(order)) Doc.show(accelerateBttn)\n else Doc.hide(accelerateBttn)\n this.updateUserOrderRow(metaOrder.row, order)\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 metaOrder of Object.values(this.metaOrders)) {\n const order = metaOrder.order\n const alreadyMatched = note.epoch > order.epoch\n const statusTD = Doc.tmplElement(metaOrder.row, 'status')\n switch (true) {\n case order.type === OrderUtil.Limit && order.status === OrderUtil.StatusEpoch && alreadyMatched:\n statusTD.textContent = order.tif === OrderUtil.ImmediateTiF ? intl.prep(intl.ID_EXECUTED) : intl.prep(intl.ID_BOOKED)\n order.status = order.tif === OrderUtil.ImmediateTiF ? OrderUtil.StatusExecuted : OrderUtil.StatusBooked\n break\n case order.type === OrderUtil.Market && order.status === OrderUtil.StatusEpoch:\n // Technically don't know if this should be 'executed' or 'settling'.\n statusTD.textContent = intl.prep(intl.ID_EXECUTED)\n order.status = OrderUtil.StatusExecuted\n break\n }\n }\n }\n\n setBalanceVisibility () {\n if (this.market.dex.connectionStatus === ConnectionStatus.Connected) {\n Doc.show(this.page.balanceTable)\n } else {\n Doc.hide(this.page.balanceTable)\n }\n }\n\n /* handleBalanceNote handles notifications updating a wallet's balance. */\n handleBalanceNote (note: BalanceNote) {\n this.setBalanceVisibility()\n // if connection to dex server fails, it is not possible to retrieve\n // markets.\n if (this.market.dex.connectionStatus !== ConnectionStatus.Connected) return\n // If there's a balance update, refresh the max order section.\n const mkt = this.market\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/trade', req)\n // Hide loader and show submit button.\n page.vSubmit.classList.remove('d-hide')\n page.vLoader.classList.add('d-hide')\n // If errors display error on confirmation modal.\n if (!app().checkResponse(res, true)) {\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 this.depthChart.draw()\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 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 // 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 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.marketSearch.value\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 /*\n * depthChartSelected is called when the user clicks a button to show the\n * depth chart.\n */\n depthChartSelected () {\n const page = this.page\n Doc.hide(page.depthBttn, page.durBttnBox, page.candleHoverData)\n Doc.show(page.candlestickBttn, page.epochLine, page.depthHoverData, page.depthSummary)\n this.currentChart = depthChart\n this.depthChart.show()\n this.candleChart.hide()\n }\n\n /*\n * candleChartSelected is called when the user clicks a button to show the\n * historical market data (candlestick) chart.\n */\n candleChartSelected () {\n const page = this.page\n const dex = this.market.dex\n this.currentChart = candleChart\n Doc.hide(page.candlestickBttn, page.epochLine, page.depthHoverData, page.depthSummary)\n Doc.show(page.depthBttn, page.durBttnBox, page.candleHoverData)\n if (dex.candleDurs.indexOf(this.candleDur) === -1) this.candleDur = dex.candleDurs[0]\n this.loadCandles()\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: () => { Doc.hide(this.page.marketLoader) },\n timer: window.setTimeout(() => {\n if (this.candlesLoading) {\n this.candlesLoading = null\n Doc.hide(this.page.marketLoader)\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 selected: MarketRow\n\n constructor (div: HTMLElement) {\n const xcTmpl = Doc.tmplElement(div, 'xc')\n Doc.cleanTemplates(xcTmpl)\n this.xcSections = []\n for (const dex of Object.values(app().user.exchanges)) {\n this.xcSections.push(new ExchangeSection(xcTmpl, dex))\n }\n // Initial sort is alphabetical.\n for (const xc of this.sortedSections()) {\n div.appendChild(xc.node)\n }\n }\n\n /*\n * sortedSections returns a list of ExchangeSection sorted alphabetically by\n * host.\n */\n sortedSections () {\n return [...this.xcSections].sort((a, b) => a.host < b.host ? -1 : 1)\n }\n\n /*\n * xcSection is a getter for the ExchangeSection for a specified host.\n */\n xcSection (host: string) {\n for (const xc of this.xcSections) {\n if (xc.host === host) return xc\n }\n return null\n }\n\n /* exists will be true if the specified market exists. */\n exists (host: string, baseID: number, quoteID: number) {\n const xc = this.xcSection(host)\n // If connecting to an offline server the client is not able to get xc.marketRows.\n // Therefore we must check for it.\n if (!xc || !xc.marketRows) return false\n for (const mkt of xc.marketRows) {\n if (mkt.baseID === baseID && mkt.quoteID === quoteID) return true\n }\n return false\n }\n\n /* first gets the first market from the first exchange, alphabetically. */\n first () {\n const firstXC = this.sortedSections()[0]\n const firstMkt = firstXC.first()\n // Cannot find markets if server connection failed.\n if (!firstMkt) return makeMarket(firstXC.host)\n return makeMarket(firstXC.host, firstMkt.baseID, firstMkt.quoteID)\n }\n\n /* select sets the specified market as selected. */\n select (host: string, baseID: number, quoteID: number) {\n if (this.selected) this.selected.node.classList.remove('selected')\n const xcSection = this.xcSection(host)\n if (!xcSection) return console.error(`select: no exchange section for ${host}`)\n const marketRow = xcSection.marketRow(baseID, quoteID)\n if (!marketRow) return console.error(`select: no market row for ${host}, ${baseID}-${quoteID}`)\n this.selected = marketRow\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 const xcSection = this.xcSection(note.host)\n if (!xcSection) return console.error(`setConnectionStatus: no exchange section for ${note.host}`)\n xcSection.setConnected(note.connectionStatus === ConnectionStatus.Connected)\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 xc of this.xcSections) {\n xc.setFilter(filter)\n }\n }\n}\n\n/*\n * ExchangeSection is a top level section of the MarketList.\n */\nclass ExchangeSection {\n marketRows: MarketRow[]\n host: string\n dex: Exchange\n node: HTMLElement\n disconnectedIco: PageElement\n\n constructor (template: HTMLElement, dex: Exchange) {\n this.dex = dex\n this.host = dex.host\n this.node = template.cloneNode(true) as HTMLElement\n const tmpl = Doc.parseTemplate(this.node)\n tmpl.header.textContent = dex.host\n\n this.disconnectedIco = tmpl.disconnected\n if (dex.connectionStatus === ConnectionStatus.Connected) Doc.hide(tmpl.disconnected)\n\n tmpl.mkts.removeChild(tmpl.mktrow)\n // If disconnected is not possible to get the markets from the server.\n if (!dex.markets) return\n\n this.marketRows = Object.values(dex.markets).map(mkt => {\n const bui = app().unitInfo(mkt.baseid, dex)\n const qui = app().unitInfo(mkt.quoteid, dex)\n const rateConversionFactor = OrderUtil.RateEncodingFactor / bui.conventional.conversionFactor * qui.conventional.conversionFactor\n return new MarketRow(tmpl.mktrow, mkt, rateConversionFactor)\n })\n\n // for (const mkt of Object.values(dex.markets)) {\n // this.marketRows.push(new MarketRow(tmpl.mktrow, mkt))\n // }\n for (const market of this.sortedMarkets()) {\n tmpl.mkts.appendChild(market.node)\n }\n }\n\n /*\n * sortedMarkets is the list of MarketRow sorted alphabetically by the base\n * symbol first, quote symbol second.\n */\n sortedMarkets () {\n if (!this.marketRows) return []\n return [...this.marketRows].sort((a, b) => a.name < b.name ? -1 : 1)\n }\n\n /*\n * first returns the first market in the alphabetically-sorted list of\n * markets.\n */\n first () {\n return this.sortedMarkets()[0]\n }\n\n /*\n * marketRow gets the MarketRow for the specified market.\n */\n marketRow (baseID: number, quoteID: number) {\n for (const mkt of this.marketRows) {\n if (mkt.baseID === baseID && mkt.quoteID === quoteID) return mkt\n }\n }\n\n /* setConnected sets the visiblity of the disconnected icon. */\n setConnected (isConnected: boolean) {\n if (isConnected) Doc.hide(this.disconnectedIco)\n else Doc.show(this.disconnectedIco)\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 mkt of this.marketRows) {\n if (filter(mkt)) Doc.show(mkt.node)\n else Doc.hide(mkt.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: Market\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: Market, 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.textContent = mkt.basesymbol.toUpperCase()\n tmpl.quoteSymbol.textContent = mkt.quotesymbol.toUpperCase()\n this.setSpot(mkt.spot)\n }\n\n setSpot (spot: Spot) {\n if (!spot) return\n const { tmpl, mkt } = this\n\n Doc.show(tmpl.pctChange)\n const pct = spot.change24 * 100\n const num = percentFormatter.format(pct)\n const sign = pct > 0 ? '+' : ''\n tmpl.pctChange.textContent = `${sign}${num}%`\n tmpl.pctChange.classList.remove('upgreen', 'downred', 'grey')\n tmpl.pctChange.classList.add(pct === 0 ? 'grey' : pct > 0 ? 'upgreen' : 'downred')\n const baseAsset = app().assets[mkt.baseid]\n if (baseAsset) {\n Doc.show(tmpl.bottomRow)\n tmpl.assetName.textContent = baseAsset.info.name\n tmpl.price.textContent = Doc.formatCoinValue(spot.rate / this.rateConversionFactor)\n }\n }\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 dex: Exchange\n\n constructor (table: HTMLElement) {\n const els = Doc.idDescendants(table)\n this.base = {\n id: 0,\n cfg: null,\n logo: els.baseImg,\n avail: els.baseAvail,\n newWalletRow: els.baseNewWalletRow,\n newWalletBttn: els.baseNewButton,\n locked: els.baseLocked,\n immature: els.baseImmature,\n unsupported: els.baseUnsupported,\n expired: els.baseExpired,\n connect: els.baseConnect,\n spinner: els.baseSpinner,\n iconBox: els.baseWalletState,\n stateIcons: new WalletIcons(els.baseWalletState)\n }\n this.quote = {\n id: 0,\n cfg: null,\n logo: els.quoteImg,\n avail: els.quoteAvail,\n newWalletRow: els.quoteNewWalletRow,\n newWalletBttn: els.quoteNewButton,\n locked: els.quoteLocked,\n immature: els.quoteImmature,\n unsupported: els.quoteUnsupported,\n expired: els.quoteExpired,\n connect: els.quoteConnect,\n spinner: els.quoteSpinner,\n iconBox: els.quoteWalletState,\n stateIcons: new WalletIcons(els.quoteWalletState)\n }\n\n app().registerNoteFeeder({\n balance: (note: BalanceNote) => { this.updateAsset(note.assetID) },\n walletstate: (note: WalletStateNote) => { this.updateAsset(note.wallet.assetID) }\n })\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 this.dex = app().user.exchanges[host]\n this.base.id = baseID\n this.base.cfg = this.dex.assets[baseID]\n this.quote.id = 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 if (!side.cfg) return // no wallet set yet\n const asset = app().assets[side.id]\n // Just hide everything to start.\n Doc.hide(\n side.newWalletRow, side.avail, side.immature, side.locked,\n side.expired, side.unsupported, side.connect, side.spinner, side.iconBox\n )\n side.logo.src = Doc.logoPath(side.cfg.symbol)\n // Handle an unsupported asset.\n if (!asset) {\n Doc.show(side.unsupported)\n return\n }\n Doc.show(side.iconBox)\n const wallet = asset.wallet\n side.stateIcons.readWallet(wallet)\n // Handle no wallet configured.\n if (!wallet) {\n Doc.show(side.newWalletRow)\n return\n }\n const bal = wallet.balance\n // Handle not connected and no balance known for the DEX.\n if (!bal && !wallet.running) {\n Doc.show(side.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(side.id)\n Doc.show(side.spinner)\n return\n }\n // We have a wallet and a DEX-specific balance. Set all of the fields.\n Doc.show(side.avail, side.immature, side.locked)\n side.avail.textContent = Doc.formatCoinValue(bal.available, asset.info.unitinfo)\n side.locked.textContent = Doc.formatCoinValue((bal.locked + bal.contractlocked), asset.info.unitinfo)\n side.immature.textContent = Doc.formatCoinValue(bal.immature, asset.info.unitinfo)\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) {\n Doc.show(side.expired)\n if (wallet.running) app().fetchBalance(side.id)\n } else Doc.hide(side.expired)\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 }\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 * updateDataCol sets the textContent of descendent template element.\n */\nfunction updateDataCol (tr: HTMLElement, col: string, s: string) {\n Doc.tmplElement(tr, col).textContent = s\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","import Doc from './doc'\nimport BasePage from './basepage'\nimport * as OrderUtil from './orderutil'\nimport { postJSON } from './http'\nimport {\n app,\n PageElement,\n OrderFilter,\n Order\n} from './registry'\n\nconst orderBatchSize = 50\n\nexport default class OrdersPage extends BasePage {\n main: HTMLElement\n offset: string\n loading: boolean\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 Doc.bind(page.exportOrders, 'click', () => {\n this.exportOrders()\n })\n\n this.submitFilter()\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 /*\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'\n\nconst Mainnet = 0\nconst Testnet = 1\n// const Regtest = 3\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\n constructor (main: HTMLElement) {\n super()\n const stampers = Doc.applySelector(main, '[data-stamp]')\n net = parseInt(main.dataset.net || '')\n // Find the order\n this.orderID = main.dataset.oid || ''\n const 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 this.fetchOrder()\n\n const page = this.page = Doc.idDescendants(main)\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 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 this.showAccelerationButton()\n const success = () => {\n this.refreshOnPopupClose = true\n }\n // Do not call cleanTemplates before creating the AccelerateOrderForm\n OrderUtil.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 main.querySelectorAll('[data-explorer-id]').forEach((link: PageElement) => {\n setCoinHref(link)\n })\n\n const setStamp = () => {\n for (const span of stampers) {\n span.textContent = Doc.timeSince(parseInt(span.dataset.stamp || ''))\n }\n }\n setStamp()\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\n unload () {\n clearInterval(this.secondTicker)\n }\n\n /* fetchOrder fetches the order from the client. */\n async fetchOrder () {\n const res = await postJSON('/api/order', this.orderID)\n if (!app().checkResponse(res)) return\n this.order = res.order\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.info.unitinfo)\n page.cancelUnit.textContent = asset.info.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 * showAccelerationButton shows the acceleration button if the order can\n * be accelerated.\n */\n showAccelerationButton () {\n const order = this.order\n if (!order) return\n const page = this.page\n if (app().canAccelerateOrder(order)) Doc.show(page.accelerateBttn, page.actionsLabel)\n else Doc.hide(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.showAccelerationButton()\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 }\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 // TO DO: Create a new card from template.\n return\n }\n\n const setCoin = (divName: string, linkName: string, coin: Coin) => {\n if (!card) return // Ugh\n if (!coin) return\n Doc.show(Doc.tmplElement(card, divName))\n const coinLink = Doc.tmplElement(card, linkName)\n coinLink.textContent = coin.stringID\n coinLink.dataset.explorerCoin = coin.stringID\n setCoinHref(coinLink)\n }\n\n setCoin('swap', 'swapCoin', m.swap)\n setCoin('counterSwap', 'counterSwapCoin', m.counterSwap)\n setCoin('redeem', 'redeemCoin', m.redeem)\n setCoin('counterRedeem', 'counterRedeemCoin', m.counterRedeem)\n setCoin('refund', 'refundCoin', m.refund)\n\n const swapSpan = Doc.tmplElement(card, 'swapMsg')\n const cSwapSpan = Doc.tmplElement(card, 'counterSwapMsg')\n\n if (inCounterSwapCast(m)) {\n cSwapSpan.textContent = confirmationString(m.counterSwap)\n Doc.hide(Doc.tmplElement(card, 'swapMsg'))\n Doc.show(cSwapSpan)\n } else if (inSwapCast(m)) {\n swapSpan.textContent = confirmationString(m.swap)\n Doc.hide(Doc.tmplElement(card, 'counterSwapMsg'))\n Doc.show(swapSpan)\n } else {\n Doc.hide(swapSpan, cSwapSpan)\n }\n\n Doc.tmplElement(card, 'status').textContent = OrderUtil.matchStatusString(m)\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) return ''\n return `${coin.confs.count} / ${coin.confs.required} confirmations`\n}\n\n/*\n * inCounterSwapCast will be true if we are waiting on confirmations for the\n * counterparty's swap.\n */\nfunction inCounterSwapCast (m: Match) {\n return (m.side === OrderUtil.Taker && m.status === OrderUtil.MakerSwapCast) || (m.side === OrderUtil.Maker && m.status === OrderUtil.TakerSwapCast)\n}\n\n/*\n * inCounterSwapCast will be true if we are waiting on confirmations for our own\n * swap.\n */\nfunction inSwapCast (m: Match) {\n return (m.side === OrderUtil.Maker && m.status === OrderUtil.MakerSwapCast) || (m.side === OrderUtil.Taker && m.status === OrderUtil.TakerSwapCast)\n}\n\n/*\n * setCoinHref sets the hyperlink element's href attribute based on its\n * data-explorer-id and data-explorer-coin values.\n */\nfunction setCoinHref (link: PageElement) {\n const assetExplorer = CoinExplorers[parseInt(link.dataset.explorerId || '')]\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 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: { // eth\n [Mainnet]: (cid: string) => {\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.length === 42) {\n return `https://goerli.etherscan.io/address/${cid}`\n }\n return `https://goerli.etherscan.io/tx/${cid}`\n }\n },\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'\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.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.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 /* 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 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, true)) {\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 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, true)) {\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 = 'Connected'\n break\n case ConnectionStatus.Disconnected:\n displayIcons(false)\n page.connectionStatus.textContent = 'Disconnected'\n break\n case ConnectionStatus.InvalidCert:\n displayIcons(false)\n page.connectionStatus.textContent = 'Disconnected - Invalid Certificate'\n }\n }\n }\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 { RateEncodingFactor, StatusExecuted, hasLiveMatches } from './orderutil'\nimport { getJSON, postJSON } 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 FeePaymentNote,\n CoreNote,\n OrderNote,\n Market,\n Order,\n Match,\n BalanceNote,\n WalletConfigNote,\n MatchNote,\n ConnEventNote,\n SpotPriceNote,\n UnitInfo,\n WalletDefinition,\n WalletBalance,\n LogMessage,\n NoteElement,\n BalanceResponse,\n APIResponse,\n RateNote\n} from './registry'\n\nconst idel = Doc.idel // = element by id\nconst bind = Doc.bind\nconst unbind = Doc.unbind\n\nconst notificationRoute = 'notify'\nconst loggersKey = 'loggers'\nconst recordersKey = 'recorders'\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}\n\n// unathedPages are pages that don't require authorization to load.\n// These are endpoints outside of the requireLogin block in webserver.New.\nconst unauthedPages = ['register', 'login', 'settings']\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 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 // The \"user\" is a large data structure that contains nearly all state\n // information, including exchanges, markets, wallets, orders and exchange\n // rates for assets.\n this.user = {\n exchanges: {},\n inited: false,\n seedgentime: 0,\n assets: {},\n fiatRates: {},\n authed: false,\n ok: true\n }\n this.seedGenTime = 0\n this.commitHash = process.env.COMMITHASH || ''\n this.noteReceivers = []\n this.showPopups = State.getCookie('popups') === '1'\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.fetch(loggersKey) || {}\n window.enableLogger = (loggerID, state) => {\n if (state) this.loggers[loggerID] = true\n else delete this.loggers[loggerID]\n State.store(loggersKey, 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.fetch(recordersKey) || []\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.store(recordersKey, 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.fetch('notifications')\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 it's not a page that requires auth, skip the error notification.\n const skipNote = unauthedPages.indexOf(this.main.dataset.handler || '') > -1\n if (!this.checkResponse(resp, skipNote)) 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 /* 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 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.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.noteMenuEntry, '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.noteMenuEntry, 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.profileMenuEntry, 'click', () => {\n this.showDropdown(page.profileMenuEntry, page.profileBox)\n })\n\n bind(page.innerNoteIcon, 'click', () => { Doc.hide(page.noteBox) })\n bind(page.innerProfileIcon, '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 + 11}px`\n dialog.style.top = `${ico.top - 9}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.store('notifications', 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 = this.page\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 if (!this.user.authed) {\n Doc.hide(page.noteMenuEntry, page.walletsMenuEntry, page.marketsMenuEntry, page.profileMenuEntry)\n return\n }\n Doc.show(page.noteMenuEntry, page.walletsMenuEntry, page.profileMenuEntry)\n if (Object.keys(this.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 * updateExchangeRegistration updates the information for the exchange\n * registration payment\n */\n updateExchangeRegistration (dexAddr: string, confs: number, assetID: number) {\n const dex = this.exchanges[dexAddr]\n const symbol = this.assets[assetID].symbol\n dex.pendingFee = { confs, assetID, symbol }\n }\n\n setDEXPaid (host: string) {\n // setting the null value in the 'confs' field indicates that the fee\n // payment was completed\n this.exchanges[host].pendingFee = null\n }\n\n /*\n * handleFeePaymentNote is the handler for the 'feepayment'-type notification, which\n * is used to update the dex registration status.\n */\n handleFeePaymentNote (note: FeePaymentNote) {\n switch (note.topic) {\n case 'RegUpdate':\n this.updateExchangeRegistration(note.dex, note.confirmations, note.asset)\n break\n case 'AccountRegistered':\n this.setDEXPaid(note.dex)\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 /*\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 switch (note.type) {\n case 'order': {\n const order = (note as OrderNote).order\n const mkt = this.user.exchanges[order.host].markets[order.market]\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 = this.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 'feepayment':\n this.handleFeePaymentNote(note as FeePaymentNote)\n break\n case 'walletstate':\n case 'walletconfig': {\n // assets can be null if failed to connect to dex server.\n if (!this.assets) return\n const wallet = (note as WalletConfigNote).wallet\n const asset = this.assets[wallet.assetID]\n asset.wallet = wallet\n this.walletMap[wallet.assetID] = wallet\n const bal = wallet.balance.available\n const balances = this.main.querySelectorAll(`[data-balance-target=\"${wallet.assetID}\"]`)\n balances.forEach(el => { el.textContent = Doc.formatFullPrecision(bal, asset.info.unitinfo) })\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 = this.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 = this.user.exchanges[n.host]\n // Spots can come before the user is fetched after login.\n if (!xc) 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 }\n }\n\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 }\n }\n // Discard data notifications.\n if (note.severity < ntfn.POKE) return\n // Poke notifications have their own display.\n if (this.showPopups) {\n const span = this.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 const pn = this.popupNotes\n pn.appendChild(span)\n // These take up screen space. Only show max 5 at a time.\n while (pn.children.length > 5) pn.removeChild(pn.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 orders (host: string, mktID: string): Order[] {\n let o = this.user.exchanges[host].markets[mktID].orders\n if (!o) {\n o = []\n this.user.exchanges[host].markets[mktID].orders = o\n }\n return o\n }\n\n /*\n * haveActiveOrders returns whether or not the there are active orders\n * involving a certain asset.\n */\n haveAssetOrders (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 || hasLiveMatches(ord))) return true\n }\n }\n }\n return false\n }\n\n walletIsActive (assetID: number): boolean {\n if (this.haveAssetOrders(assetID)) return true\n for (const xc of Object.values(this.user.exchanges)) {\n if (xc.pendingFee && xc.pendingFee.assetID === assetID) {\n return true\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 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.info.unitinfo\n if (!xc) {\n throw Error(`no supported asset info for id = ${assetID}, and no exchange info provided`)\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): number {\n const [b, q] = [this.unitInfo(baseID), this.unitInfo(quoteID)]\n const r = b.conventional.conversionFactor / q.conventional.conversionFactor\n return encRate / RateEncodingFactor * r\n }\n\n walletDefinition (assetID: number, walletType: string): WalletDefinition {\n const assetInfo = this.assets[assetID].info\n if (walletType === '') return assetInfo.availablewallets[assetInfo.emptyidx]\n return assetInfo.availablewallets.filter(def => def.type === walletType)[0]\n }\n\n currentWalletDefinition (assetID: number): WalletDefinition {\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, a\n * message will be displayed in the drop-down notifications and false will be\n * returned.\n */\n checkResponse (resp: APIResponse, skipNote?: boolean): boolean {\n if (!resp.requestSuccessful || !resp.ok) {\n if (this.user.inited && !skipNote) this.notify(ntfn.make(intl.prep(intl.ID_API_ERROR), resp.msg, ntfn.ERROR))\n return false\n }\n return true\n }\n\n /**\n * signOut call to /api/logout, if response with no errors occurred clear all\n * store, remove auth, darkMode cookies and reload the page, otherwise will\n * show a notification\n */\n async signOut () {\n const res = await postJSON('/api/logout')\n if (!this.checkResponse(res)) {\n Doc.hide(this.page.profileBox)\n return\n }\n State.clearAllStore()\n State.removeAuthCK()\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 Application from './js/app'\nimport { registerApplication } from './js/registry'\nimport './css/application.scss'\n\nconst app = new Application()\nregisterApplication(app)\napp.start()\n"],"names":["module","exports","runtime","undefined","Op","Object","prototype","hasOwn","hasOwnProperty","$Symbol","Symbol","iteratorSymbol","iterator","asyncIteratorSymbol","asyncIterator","toStringTagSymbol","toStringTag","define","obj","key","value","defineProperty","enumerable","configurable","writable","err","wrap","innerFn","outerFn","self","tryLocsList","protoGenerator","Generator","generator","create","context","Context","_invoke","state","GenStateSuspendedStart","method","arg","GenStateExecuting","Error","GenStateCompleted","doneResult","delegate","delegateResult","maybeInvokeDelegate","ContinueSentinel","sent","_sent","dispatchException","abrupt","record","tryCatch","type","done","GenStateSuspendedYield","makeInvokeMethod","fn","call","GeneratorFunction","GeneratorFunctionPrototype","IteratorPrototype","this","getProto","getPrototypeOf","NativeIteratorPrototype","values","Gp","defineIteratorMethods","forEach","AsyncIterator","PromiseImpl","invoke","resolve","reject","result","__await","then","unwrapped","error","previousPromise","callInvokeWithMethodAndArg","TypeError","info","resultName","next","nextLoc","pushTryEntry","locs","entry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","iterable","iteratorMethod","isNaN","length","i","displayName","isGeneratorFunction","genFun","ctor","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","iter","keys","object","reverse","pop","skipTempReset","prev","charAt","slice","stop","rootRecord","rval","exception","handle","loc","caught","hasCatch","hasFinally","finallyEntry","complete","finish","thrown","delegateYield","regeneratorRuntime","accidentalStrictMode","globalThis","Function","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","__esModule","d","a","definition","o","get","prop","_arrayLikeToArray","arr","len","arr2","Array","_unsupportedIterableToArray","minLen","toString","from","test","_slicedToArray","isArray","_i","_s","_e","_arr","_n","_d","asyncGeneratorStep","gen","_next","_throw","_asyncToGenerator","args","arguments","apply","_classCallCheck","instance","Constructor","_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_LOCKED","ID_NOWALLET","ID_WALLET_SYNC_PROGRESS","ID_HIDE_ADDITIONAL_SETTINGS","ID_SHOW_ADDITIONAL_SETTINGS","ID_BUY","ID_SELL","ID_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_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","SETUP_NEEDED","enUS","ptBR","zhCN","plPL","localesMap","defaultLocale","prep","k","expression","replace","_","stringTemplateParser","window","localeDiscrepancies","ref","entries","lang","dict","s","console","log","parser","DOMParser","BipSymbols","intFormatter","Intl","NumberFormat","navigator","languages","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","rect","getBoundingClientRect","pageX","left","right","pageY","top","bottom","box","docEl","document","documentElement","scrollTop","scrollLeft","w","offsetWidth","h","offsetHeight","bodyTop","bodyLeft","width","height","centerX","centerY","els","firstChild","removeChild","classList","add","remove","contains","duration","easingAlgo","easer","Easing","linear","start","Date","getTime","range","end","frameDuration","now","sleep","ancestor","querySelectorAll","children","warn","createElement","applySelector","vAtomic","Number","isInteger","format","decimalFormatter","rate","symbol","indexOf","substring","tmpls","tmpl","removeAttribute","dataset","t","formatDuration","dur","y","mo","m","seconds","floor","count","timeMod","aYear","aMonth","aDay","anHour","aMinute","inputFields","inputField","preventDefault","easeIn","easeOut","easeInHard","easeOutHard","WalletIcons","stateElement","icons","sleeping","locked","unlocked","nowallet","syncing","nopeers","status","hide","show","textContent","intl","wallet","syncIcon","running","peerCount","synced","tooltip","syncProgress","toFixed","setSyncing","open","ms","setTimeout","darkModeCK","popupsCK","State","cname","cvalue","setTime","expires","toUTCString","cookie","split","trim","dark","setCookie","body","filter","item","includes","getCookie","localStorage","setItem","JSON","stringify","clear","getItem","parse","_assertThisInitialized","ReferenceError","_setPrototypeOf","p","_inherits","subClass","superClass","_typeof","_possibleConstructorReturn","_getPrototypeOf","ConnectionStatus","application","BasePage","requestJSON","addr","reqBody","fetch","headers","Headers","response","json","requestSuccessful","text","msg","postJSON","data","getJSON","_toConsumableArray","app","orderOptTmpl","booleanOptTmpl","rangeOptTmpl","RateEncodingFactor","sellString","ord","sell","typeString","tif","isMarketBuy","hasLiveMatches","order","matches","match","revoked","statusString","isLive","cancelling","filled","qty","reduce","isCancel","settled","side","setOptionTemplates","page","OrderOption","opt","isSwapOption","report","node","cloneNode","parseTemplate","optName","displayname","description","baseSymbol","quoteSymbol","chainIcon","src","logoPath","on","bind","enable","toggle","stopPropagation","disable","dexAssetSymbol","host","quote","base","BooleanOrderOption","changed","cfg","control","controls","appendChild","reason","options","store","XYRangeOrderOption","xyRange","setVal","x","handler","XYRangeHandler","initVal","updated","selected","roundY","slider","rangeX","rangeY","normalizeX","r","number","minimumSignificantDigits","maximumSignificantDigits","accept","skipUpdate","style","clickOutX","xInput","xx","parseFloat","clamp","unbind","focus","clickOutY","yInput","yy","button","startX","clientWidth","startLeft","trackMouse","ee","mouseUp","rangeLblStart","label","rangeLblEnd","xUnit","yUnit","optionElement","change","isSwap","assetID","exchanges","assets","NewWalletForm","form","success","pwCache","backFunc","pwHiders","refresh","goBack","empty","walletTabTmpl","subform","WalletConfigForm","walletSettings","showOther","walletSettingsHeader","submitAdd","submit","oneBttn","passwordIsCached","pw","appPass","newWalletPass","newWalletErr","currentAsset","createForm","pass","config","map","walletType","currentWalletType","loaded","loading","mainForm","res","checkResponse","setError","asset","tabs","walletTypeTabs","assetLogo","assetName","availablewallets","header","walletDef","wDef","tab","kids","update","bindTooltips","appPwCached","auth","oneBttnBox","newWalletPassBox","configOpts","configopts","isBirthdayConfig","seedGenTime","toUnixDate","seeded","noauth","dynamicOpts","loadDefaults","errMsg","walletDefinition","configpath","setLoadedConfig","sectionize","configElements","allSettings","tmplElement","textInputTmpl","dateInputTmpl","checkboxTmpl","fileSelector","fileInput","showIcon","hideIcon","showHideMsg","otherSettings","loadedSettingsMsg","loadedSettings","defaultSettingsMsg","defaultSettings","click","fileInputChanged","setOtherSettingsViz","files","configtext","append","setConfig","reorder","defaultOpts","loadedOpts","walletIsActive","defaultedOpts","addOpt","elID","isboolean","isdate","input","configOpt","safeSelector","htmlFor","prepend","noecho","checked","getMinMaxVal","minMax","toISOString","valueAsDate","disabled","Boolean","disablewhenactive","visible","finds","toLowerCase","parseInt","minDate","MIN_SAFE_INTEGER","maxDate","MAX_SAFE_INTEGER","date","inputs","ConfirmRegistrationForm","certFile","submitForm","xc","passBox","unitinfo","feeAssetID","regAsset","regFees","fee","formatCoinValue","amount","feeUnit","unit","toUpperCase","logo","animate","prog","transform","opacity","String","pow","offset","regErr","innerText","user","feeAsset","cert","dexAddr","registration","FeeAssetSelectionForm","cleanTemplates","marketTmpl","assetTmpl","allMarkets","cFactor","ui","marketNode","mkt","excludeIcon","baseAsset","baseid","baseUnitInfo","quoteAsset","quoteid","quoteUnitInfo","excludeBase","otherSymbol","otherLogo","parent","parentNode","insertBefore","nextSibling","lotsize","lotSize","spot","quoteLotSize","quoteLot","OrderUtil","haveWallet","assetNode","confs","ready","markets","fader","setExchange","how","regAssetElements","allmkts","marginTop","paddingTop","fontSize","WalletWaitForm","progressCache","progressed","funded","registerNoteFeeder","walletstate","note","reportWalletState","balance","reportBalance","txFee","regFee","depoAddr","address","syncUncheck","syncCheck","balUncheck","balCheck","syncRemainBox","balanceBox","totalFees","sendEnoughWithEst","sendEnough","syncSpinner","available","progress","reportProgress","bal","cache","stamp","shift","first","last","progDelta","progRate","toGoTime","syncRemain","UnlockWalletForm","idDescendants","submitUnlock","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","assetSymbol","quoteID","feeEstimate","DEXAddressForm","dexToUpdate","defaultTLSText","selectedCert","onCertFileChange","removeCert","clearCertFile","addCert","showCustom","customBox","knownExchanges","knownXCs","div","checkDEX","appPW","addDexHdr","updateDexHdr","appPWBox","pickServerMsg","addCustomMsg","endpoint","newHost","oldHost","needCert","paid","fetchUser","loadPage","LoginForm","headerTxt","rememberPass","notes","setNotes","animationLength","slideSwap","form1","form2","submitBttn","wrapper","RegistrationPage","bindForm","appPWForm","appPWSubmit","setAppPass","showSeedRestore","seedRestore","loginForm","dexAddrForm","newWalletForm","newWalletCreated","animateRegAsset","currentDEX","confirmRegisterForm","walletWaitForm","regAssetForm","setAsset","animateConfirmForm","getRegistrationTxFeeEstimate","setWallet","walletWait","confirmRegForm","registerDEXSuccess","currentForm","forms","authed","oldForm","getCertFile","txfee","appPWErrMsg","pwAgain","appPWAgain","seed","seedInput","updateMenuItemsDisplay","feeAmt","LoginPage","idel","loggedIn","make","subject","details","severity","acked","topic","WalletsPage","restoreInfoCard","closePopups","cancelForce","copyAddressBtn","copyAddress","firstRow","getAction","row","rowInfos","walletTable","tr","stateIcons","actions","connect","unlock","send","deposit","rescan","lock","settings","marketCard","oneMarket","createWalletSuccess","reconfigForm","reconfigInputs","unlockForm","unlockWalletForm","openWalletSuccess","sendForm","submitSendForm","submitReconfig","reconfig","rowInfo","showMarkets","rightBox","lastFormAsset","mouseInElement","keyup","isDisplayed","downloadLogs","exportWallet","displayExportWalletAuth","recoverWallet","showRecoverWallet","exportWalletAuth","exportWalletAuthSubmit","recoverWalletConfirm","recoverWalletSubmit","confirmForce","confirmForceSubmit","run","doConnect","showSendForm","showDeposit","showNewWallet","rescanWallet","openWallet","showReconfig","newDepAddrBttn","newDepositAddress","sendAvail","sendAsset","sendAmt","showFiatValue","sendValue","traits","subtractCheckBox","walletMap","fiatDisplay","amt","showChangePW","changeWalletPW","setPWSettingViz","changeWalletTypeSelect","changeWalletType","showChangeType","isHidden","changeTypeHideIcon","changeTypeShowIcon","changeTypeMsg","reconfigAsset","fiatrateupdate","handleRatesNote","handleBalanceNote","handleWalletStateNote","walletconfig","clipboard","writeText","depositAddress","copyAlert","changePW","switchPWMsg","animation","displayed","focuser","marketsBox","card","marketsCard","hideBox","marketsFor","marketsForLogo","market","marketBox","dexTitle","mBox","prettyMarketName","counterSymbol","basesymbol","quotesymbol","pageData","showBox","walletAsset","url","code","forceUrl","forceReq","showConfirmForce","confirmForceErr","showForm","recoverWalletErr","showOpen","openAsset","errorMsg","showErrorOnly","walletPass","reconfigErr","currentDef","currentWalletDefinition","option","recfgAssetLogo","recfgAssetName","updateDisplayedReconfigFields","depositErr","depositLogo","depositAsset","notify","ntfn","depositName","qrcode","senderOnlyHelpText","toggleSubtract","sendAddr","sendPW","sendErr","formatFullPrecision","sendLogo","sendName","encrypted","subtract","newWalletPW","newPW","search","URLSearchParams","URL","location","href","pathname","exportWalletErr","exportWalletPW","displayRestoreWalletInfo","restorationinfo","restoreInfoCardsList","wr","seedName","instructions","restoreWalletInfo","recoverWalletPW","force","fiatRatesMap","fiatRates","display","formatFiatConversion","parentElement","readWallet","SettingsPage","fiatRateSources","darkMode","showPokes","showPopups","commitHash","addADex","source","importAccount","prepareAccountImport","authorizeAccountImportForm","authorizeImportAccountConfirm","changeAppPW","changeAppPWForm","submitNewPW","accountFile","onAccountFileChange","removeAccount","clearAccountFile","addAccount","exportSeed","exportSeedAuth","exportSeedSubmit","submitExportSeedReq","exportSeedPW","seedDiv","selectedAccount","importAccountErr","importAccountAppPass","accountString","account","message","importResponse","loginResponse","reload","exportAccountErr","exportSeedE","authorizeSeedDisplay","changePWErrMsg","clearValues","newAppPW","confirmNewPW","OrderBook","mktBook","buys","book","sells","qtyAtomic","splice","less","findIdx","token","removeFromSide","findOrder","updateRemainingSide","epochIdx","approve","epoch","best","bestGapOrder","PIPI","PI","plusChar","fromCharCode","minusChar","darkTheme","axisLabel","gridBorder","gridLines","gapLine","zoom","zoomHover","sellLine","buyLine","sellFill","buyFill","crosshairs","legendFill","legendText","lightTheme","Chart","reporters","theme","isDark","canvas","ctx","getContext","textAlign","textBaseline","mousePos","clientX","clientY","draw","wheelLimiter","wheel","boundResizer","resize","clientHeight","clearRect","render","deltaY","parentHeight","plotExtents","Extents","xLblExtents","yLblExtents","plotRegion","Region","xRegion","yRegion","requestAnimationFrame","bigger","font","fillStyle","labels","minX","maxX","unitLines","extents","plot","tools","applyLabelStyle","lastX","unitCenter","lbls","lbl","fillText","txt","val","lineWidth","strokeStyle","line","region","minY","maxY","lastY","step","valFmt","yLabels","makeLabels","dataExtents","yAxisWidth","widest","plotYLabels","beginPath","dataCoords","moveTo","lineTo","stroke","DepthChart","resized","clicked","zoomed","zoomLevel","lines","markers","setZoomBttns","zoomInBttn","zoomOutBttn","wheeled","translator","unx","rateStep","qFactor","bFactor","baseUnit","quoteUnit","gap","midGap","gapWidth","minZoom","halfWindow","high","low","buyMarkers","sellMarkers","sort","b","buyDepth","buyEpoch","sellDepth","sellEpoch","volumeReport","buyBase","buyQuote","sellBase","sellQuote","sum","epochSum","floatCompare","active","buySum","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","highRate","lowRate","matchVolume","highVol","ratestep","rFactor","rateConversionFactor","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","desc","startRate","endRate","cx","maxCandles","xMin","xMax","yMin","yMax","screenMinX","screenMaxY","screenH","xFactor","yFactor","uny","drawFunc","skipMask","clip","tx","ty","tickGuess","absMax","abs","sigFigs","toPrecision","unitW","x0","y0","x1","y1","skipStroke","labelSpecs","forward","route","payload","handlers","MessageSocket","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","lastMarketKey","chartRatioKey","depthZoomKey","depthChart","candleChart","check","percentFormatter","MarketsPage","main","maxOrderUpdateCounter","metaOrders","preorderCache","depthLines","hovers","ordersSortKey","ordersSortDirection","ogTitle","title","depthReporters","reportDepthClick","reportDepthVolume","reportDepthMouse","z","reportDepthZoom","marketChart","candleReporters","reportMouseCandle","accelerateOrderForm","accelerateForm","currentChart","candleDur","wgt","balanceWgt","BalanceWidget","balanceTable","baseIcons","quoteIcons","walletUnlocked","expired","newWalletBttn","showCreate","rowTemplate","liveTemplate","durBttnTemplate","marketList","MarketList","xcSections","marketRows","setMarket","quoteUnits","baseUnits","buyBttn","swapBttns","sellBttn","maxLbl","setOrderBttnText","setOrderVisibility","drawChartLines","limitBttn","marketBttn","rateField","isSell","setMarketBuyOrderEstimate","maxOrd","maxSell","lotField","swap","lots","maxBuys","adjustedRate","lotChanged","depthBttn","depthChartSelected","candlestickBttn","candleChartSelected","disableMouseWheel","qtyField","mktBuyField","ws","handleBookRoute","handleBookOrderRoute","handleUnbookOrderRoute","handleUpdateRemainingRoute","handleEpochOrderRoute","handleCandlesRoute","handleCandleUpdateRoute","openFunc","createWallet","orderForm","stepSubmit","verifyForm","vSubmit","submitOrder","vUnlockSubmit","submitEstimateUnlock","cancelForm","cancelSubmit","submitCancel","vFeeDetails","vDetailPane","closeDetailPane","showVerifyForm","liveTable","th","setOrdersSortCol","ordercol","unsetOrdersSortColClasses","refreshActiveOrders","setOrdersSortColClasses","sortCls","vPass","cancelPass","quantityChanged","marketBuyChanged","rateFieldChanged","previewQuoteAmt","marketSearch","filterMarkets","clearChartLines","buyRows","sellRows","liveList","activeMarkerRate","setDepthMarkers","setChartRatio","chartDivRatio","chartResizer","chartRatio","rightSide","handleOrderNote","handleEpochNote","handleConnNote","feepayment","handleFeePayment","spots","handlePriceUpdate","makeMarket","exists","secondTicker","setInterval","metaOrder","timeSince","submitTime","setRegistrationStatusVisibility","setBalanceVisibility","dex","pendingFee","isLimit","priceBox","tifBox","qtyBox","maxBox","mktBuyBox","feePaid","hasFeePending","assetsAreSupported","hasWallets","loaderMsg","quoteCfg","baseCfg","titleContent","confStatusMsg","titleClass","regStatusTitle","regStatusConfsDisplay","registrationStatus","regStatusDex","pending","confirmationsRequired","confReq","setRegistrationStatusView","connectionStatus","Connected","updateRegistrationStatusView","resolveOrderFormVisibility","durBttnBox","candleDurs","bttn","candleDurationSelected","chartErrMsg","marketLoader","qui","bui","maxEstimateTimer","mktId","marketID","sid","maxSellRequested","candleCaches","sellBalance","buyBalance","loadCandles","setLoaderMsgVisibility","setCandleDurBttns","q","sellBookedBase","sellBookedQuote","buyBookedBase","buyBookedQuote","metaOrd","hoverPrice","hoverVolume","hoverData","candle","candleStart","candleEnd","candleHigh","candleLow","candleVol","limit","convertToAtoms","tifnow","tifNow","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","toAsset","maxFromAmt","maxFromTicker","toConversion","maxToAmt","maxToTicker","loadTable","addTableOrder","set","msgRate","buffer","buybuffer","midGapConventional","minMktBuy","localeCompare","oid","orders","compare","ordersSortCompare","updateUserOrderRow","icon","showCancel","accelerateBttn","showAccelerate","canAccelerateOrder","bindInternalNavigation","updateDataCol","rateFactor","setMarkers","midGapValue","baseSymb","quoteSymb","handleBook","updateTitle","select","setWallets","removeTableOrder","updateRemaining","updateTableOrder","candlesLoading","timer","setCandles","startStamp","currentOrder","vUnlockPreorder","vPreorderErr","vPreorder","vBuySell","buySellStr","vSideSubmit","vOrderHost","verifyLimit","verifyMarket","orderDesc","vOrderType","vRate","vQty","vTotal","vFiatTotal","vmFromTotal","vmFromAsset","vmFromTotalFiat","vMarketEstimate","received","vmToTotal","vmToAsset","vmTotalFiat","buyBtnClass","sellBtnClass","vHeader","preOrder","unlockWalletsForEstimates","vErr","vUnlockPass","attemptWalletUnlock","setPreorderErr","assetIDs","cacheKey","cached","wireOrder","estimate","vPreorderErrTip","refreshPreorder","fetchPreorder","est","redeem","vOrderOpts","setFeeEstimates","vFeeSummary","backgroundColor","addOption","swapped","fmtPct","toUI","fromUI","bestSwapPct","realisticBestCase","vSwapFeesLowPct","vSwapFeesLow","worstSwapPct","realisticWorstCase","vSwapFeesHighPct","vSwapFeesHigh","vSwapFeesMaxPct","maxFees","vSwapFeesMax","estRate","bestRedeemPct","vRedeemFeesLowPct","vRedeemFeesLow","worstRedeemPct","vRedeemFeesHighPct","vRedeemFeesHigh","vFeeSummaryLow","vFeeSummaryHigh","cancelData","cancelErr","remaining","cancelRemain","cancelUnit","currentCreate","validateOrder","showVerify","xcSection","marketRow","setSpot","oldStatus","cancelBttn","setEpoch","clearOrderTableEpochs","alreadyMatched","statusTD","avail","vLoader","updateAsset","finalize","mktBuyLots","mktBuyScore","NaN","loadTableSide","bins","currEpochBin","currNonEpochBin","currRate","bin","bookSide","tbody","binOrdersByRateAndEpoch","orderTableRow","manager","getRate","insertOrder","removeOrder","u","updateOrderQty","clearOrderTableEpochSide","removeEpochOrders","orderBin","OrderTableRowManager","chart","setConnectionStatus","filterTxt","setFilter","setLines","candleHoverData","epochLine","depthHoverData","depthSummary","requestCandles","unattach","clearInterval","xcTmpl","ExchangeSection","sortedSections","firstXC","firstMkt","setConnected","template","disconnectedIco","disconnected","mkts","mktrow","MarketRow","sortedMarkets","isConnected","baseIcon","quoteIcon","pctChange","pct","change24","num","sign","bottomRow","price","table","baseImg","baseAvail","newWalletRow","baseNewWalletRow","baseNewButton","baseLocked","immature","baseImmature","unsupported","baseUnsupported","baseExpired","baseConnect","spinner","baseSpinner","iconBox","baseWalletState","quoteImg","quoteAvail","quoteNewWalletRow","quoteNewButton","quoteLocked","quoteImmature","quoteUnsupported","quoteExpired","quoteConnect","quoteSpinner","quoteWalletState","updateWallet","fetchBalance","contractlocked","before","col","stringyOptions","assign","tableRow","setRateEl","setEpochEl","updateQtyNumOrdersEl","epochEl","isEpoch","rateEl","cssClass","curr","numOrders","qtyEl","numOrdersEl","setAttribute","index","findIndex","newEpoch","OrdersPage","orderTmpl","rowTmpl","filterState","hosts","statuses","readFilter","filterKey","subFilter","hostFilter","assetFilter","statusFilter","applyButtons","monitorFilter","applyBttn","submitFilter","filter1","filter2","compareSubFilter","parseSubFilter","ordersTable","nextPage","exportOrders","tableBody","appendOrders","tmplID","mktID","to","fromQty","toQty","conventionalRate","dateTime","fetchOrders","setOrders","currentFilter","setQuery","orderLoader","net","OrderPage","stampers","fetchOrder","refreshOnPopupClose","showAccelerateForm","showAccelerationButton","link","setCoinHref","setStamp","span","handleMatchNote","actionsLabel","processMatch","matchBox","matchID","setCoin","divName","linkName","coin","coinLink","stringID","explorerCoin","counterSwap","counterRedeem","refund","swapSpan","cSwapSpan","inCounterSwapCast","inSwapCast","confirmationString","required","assetExplorer","CoinExplorers","explorerId","cid","txid","vout","DexSettingsPage","exportDexBtn","prepareAccountExport","authorizeAccountExportForm","disableAcctBtn","prepareAccountDisable","disableAccountForm","updateCertBtn","certFileInput","updateHostBtn","prepareUpdateHost","authorizeExportAccountConfirm","exportAccount","disableAccountConfirm","disableAccount","exportAccountAppPass","exportAccountHost","accountForExport","disableAccountAppPW","disableAccountHost","disableAccountErr","updateCertMsg","exchange","displayIcons","connected","disconnectedIcon","connectedIcon","Disconnected","InvalidCert","loggersKey","recordersKey","constructors","login","register","wallets","dexsettings","unauthedPages","Application","pokes","inited","seedgentime","ok","process","noteReceivers","loggers","enableLogger","loggerID","recorderKeys","recorders","recordLogger","dumpLogger","btoa","download","handlerFromPath","history","replaceState","attachHeader","attachCommon","attach","protocol","reconnected","resp","skipNote","skipPush","noteBox","profileBox","origin","requestedHandler","doc","noderize","delivered","pushState","replaceWith","handlerID","loadedPage","unload","lyt","layoutMetrics","popupNotes","popupTmpl","noteTmpl","pokeTmpl","loader","noteMenuEntry","pokeList","noteList","ackNotes","noteCat","pokeCat","showDropdown","noteIndicator","setNoteTimes","storeNotes","profileMenuEntry","innerNoteIcon","innerProfileIcon","profileSignout","signOut","dialog","ico","innerWidth","acks","pageURL","params","searchParams","walletsMenuEntry","marketsMenuEntry","updateExchangeRegistration","confirmations","setDEXPaid","prependNoteElement","updateOrder","handleFeePaymentNote","updateMatch","mktName","indicator","setSeverityClass","pn","prependPokeElement","receivers","nowString","time","cn","makePoke","prependListElement","skipSave","makeNote","unacked","ni","noteCacheSize","lastChild","cls","toLocaleDateString","toLocaleTimeString","haveAssetOrders","fromAssetID","supportedAsset","encRate","assetInfo","emptyidx","def","clearAllStore","removeAuthCK","severityClassMap","getSeconds","getMilliseconds"],"sourceRoot":""} \ No newline at end of file