From a8d70403efc02b393427d3ae0d4e8036f7572077 Mon Sep 17 00:00:00 2001 From: Brian Stafford Date: Wed, 16 Oct 2024 23:30:33 -0500 Subject: [PATCH] commit web assets and bump version number --- client/app/version.go | 2 +- client/cmd/bisonw-desktop/pkg/pkg-darwin.sh | 4 +- client/cmd/bisonw-desktop/pkg/pkg-debian.sh | 2 +- client/cmd/bisonw-desktop/winres.json | 4 +- client/cmd/bisonw/winres.json | 2 +- client/cmd/bwctl/version.go | 2 +- client/webserver/site/dist/entry.js | 21155 +----------------- client/webserver/site/dist/entry.js.map | 2 +- pkg.sh | 2 +- server/cmd/dcrdex/version.go | 2 +- 10 files changed, 14 insertions(+), 21163 deletions(-) diff --git a/client/app/version.go b/client/app/version.go index 394bc4b0cd..f877341d22 100644 --- a/client/app/version.go +++ b/client/app/version.go @@ -36,7 +36,7 @@ var ( // and build metadata portions MUST only contain characters from // semanticAlphabet. // NOTE: The Version string is overridden on init. - Version = "1.0.0+release.local" + Version = "1.0.1+release.local" ) func init() { diff --git a/client/cmd/bisonw-desktop/pkg/pkg-darwin.sh b/client/cmd/bisonw-desktop/pkg/pkg-darwin.sh index 7177d0b7fc..313cd95174 100755 --- a/client/cmd/bisonw-desktop/pkg/pkg-darwin.sh +++ b/client/cmd/bisonw-desktop/pkg/pkg-darwin.sh @@ -9,9 +9,9 @@ set -o pipefail; export GOWORK=off # For release set metadata to "release". -VER="1.0.0" +VER="1.0.1" META="release" -BUILD_VER="1.0.0" # increment for every build. +BUILD_VER="1.0.1" # increment for every build. OS_FULL_VERSION="$(sw_vers | sed -n 2p | cut -d : -f 2 | tr -d '[:space:]' | cut -c1-)" OS_MAJOR_VERSION="$(echo $OS_FULL_VERSION | cut -d . -f 1)" OS_MINOR_VERSION="$(echo $OS_FULL_VERSION | cut -d . -f 2)" diff --git a/client/cmd/bisonw-desktop/pkg/pkg-debian.sh b/client/cmd/bisonw-desktop/pkg/pkg-debian.sh index b1db1103ea..1e9c2d60ec 100755 --- a/client/cmd/bisonw-desktop/pkg/pkg-debian.sh +++ b/client/cmd/bisonw-desktop/pkg/pkg-debian.sh @@ -6,7 +6,7 @@ set -ex APP="bisonw" -VER="1.0.0" +VER="1.0.1" META="release" REV="0" ARCH="amd64" diff --git a/client/cmd/bisonw-desktop/winres.json b/client/cmd/bisonw-desktop/winres.json index 4cfae1ef8a..2ab1711b8b 100644 --- a/client/cmd/bisonw-desktop/winres.json +++ b/client/cmd/bisonw-desktop/winres.json @@ -18,7 +18,7 @@ "0000": { "fixed": { "file_version": "1.0.0.0", - "product_version": "1.0.0.0" + "product_version": "1.0.1.0" }, "info": { "0409": { @@ -32,7 +32,7 @@ "OriginalFilename": "bisonw-desktop.exe", "PrivateBuild": "", "ProductName": "Bison Wallet", - "ProductVersion": "1.0.0.0", + "ProductVersion": "1.0.1.0", "SpecialBuild": "" } } diff --git a/client/cmd/bisonw/winres.json b/client/cmd/bisonw/winres.json index 3f6c77181e..e0e8311197 100644 --- a/client/cmd/bisonw/winres.json +++ b/client/cmd/bisonw/winres.json @@ -29,7 +29,7 @@ "OriginalFilename": "bisonw.exe", "PrivateBuild": "", "ProductName": "Bison Wallet", - "ProductVersion": "1.0.0.0", + "ProductVersion": "1.0.1.0", "SpecialBuild": "" } } diff --git a/client/cmd/bwctl/version.go b/client/cmd/bwctl/version.go index bfa0766c38..734193a34b 100644 --- a/client/cmd/bwctl/version.go +++ b/client/cmd/bwctl/version.go @@ -41,7 +41,7 @@ var ( // and build metadata portions MUST only contain characters from // semanticAlphabet. // NOTE: The Version string is overridden on init. - Version = "1.0.0+release.local" + Version = "1.0.1+release.local" ) func init() { diff --git a/client/webserver/site/dist/entry.js b/client/webserver/site/dist/entry.js index 8611e8c70e..5d9449bf3d 100644 --- a/client/webserver/site/dist/entry.js +++ b/client/webserver/site/dist/entry.js @@ -1,21152 +1,3 @@ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "./src/css/application.scss": -/*!**********************************!*\ - !*** ./src/css/application.scss ***! - \**********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./src/css/bootstrap.scss": -/*!********************************!*\ - !*** ./src/css/bootstrap.scss ***! - \********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -// extracted by mini-css-extract-plugin - - -/***/ }), - -/***/ "./src/js/account.ts": -/*!***************************!*\ - !*** ./src/js/account.ts ***! - \***************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "ReputationMeter": () => (/* binding */ ReputationMeter), -/* harmony export */ "bondReserveMultiplier": () => (/* binding */ bondReserveMultiplier), -/* harmony export */ "epochWeight": () => (/* binding */ epochWeight), -/* harmony export */ "likelyTaker": () => (/* binding */ likelyTaker), -/* harmony export */ "parcelLimitScoreMultiplier": () => (/* binding */ parcelLimitScoreMultiplier), -/* harmony export */ "perTierBaseParcelLimit": () => (/* binding */ perTierBaseParcelLimit), -/* harmony export */ "strongTier": () => (/* binding */ strongTier), -/* harmony export */ "tradingLimits": () => (/* binding */ tradingLimits) -/* harmony export */ }); -/* harmony import */ var _doc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./doc */ "./src/js/doc.ts"); -/* harmony import */ var _orderutil__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./orderutil */ "./src/js/orderutil.ts"); -/* harmony import */ var _registry__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./registry */ "./src/js/registry.ts"); - - - -const bondReserveMultiplier = 2; // Reserves for next bond -const perTierBaseParcelLimit = 2; -const parcelLimitScoreMultiplier = 3; -class ReputationMeter { - constructor(div) { - this.page = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(div); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].cleanTemplates(this.page.penaltyMarkerTmpl); - } - setHost(host) { - this.host = host; - } - update() { - const { page, host } = this; - const { auth, maxScore, penaltyThreshold } = (0,_registry__WEBPACK_IMPORTED_MODULE_2__.app)().exchanges[host]; - const { rep: { score } } = auth; - const displayTier = strongTier(auth); - const minScore = displayTier ? displayTier * penaltyThreshold * -1 : penaltyThreshold * -1; // Just for looks - const warnPct = 25; - const scorePct = 100 - warnPct; - page.scoreWarn.style.width = `${warnPct}%`; - const pos = score >= 0 ? warnPct + (score / maxScore) * scorePct : warnPct - (Math.min(warnPct, score / minScore * warnPct)); - page.scorePointer.style.left = `${pos}%`; - page.scoreMin.textContent = String(minScore); - page.scoreMax.textContent = String(maxScore); - const bonus = limitBonus(score, maxScore); - page.limitBonus.textContent = bonus.toFixed(1); - for (const m of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(page.scoreTray, '.penalty-marker')) - m.remove(); - if (displayTier > 1) { - const markerPct = warnPct / displayTier; - for (let i = 1; i < displayTier; i++) { - const div = page.penaltyMarkerTmpl.cloneNode(true); - page.scoreTray.appendChild(div); - div.style.left = `${markerPct * i}%`; - } - } - page.score.textContent = String(score); - page.scoreData.classList.remove('negative', 'positive'); - if (score > 0) - page.scoreData.classList.add('positive'); - else - page.scoreData.classList.add('negative'); - } -} -/* - * strongTier is the effective tier, with some respect for bond overlap, such - * that we don't count weak bonds that have already had their replacements - * confirmed. - */ -function strongTier(auth) { - const { weakStrength, targetTier, effectiveTier } = auth; - if (effectiveTier > targetTier) { - const diff = effectiveTier - targetTier; - if (weakStrength >= diff) - return targetTier; - return targetTier + (diff - weakStrength); - } - return effectiveTier; -} -function likelyTaker(ord, rate) { - if (ord.type === _orderutil__WEBPACK_IMPORTED_MODULE_1__.OrderTypeMarket || ord.tif === _orderutil__WEBPACK_IMPORTED_MODULE_1__.ImmediateTiF) - return true; - // Must cross the spread to be a taker (not so conservative). - if (rate === 0) - return false; - if (ord.sell) - return ord.rate < rate; - return ord.rate > rate; -} -const preparcelQuantity = (ord, mkt, midGap) => { - var _a, _b; - const qty = ord.qty - ord.filled; - if (ord.type === _orderutil__WEBPACK_IMPORTED_MODULE_1__.OrderTypeLimit) - return qty; - if (ord.sell) - return qty * ord.rate / _orderutil__WEBPACK_IMPORTED_MODULE_1__.RateEncodingFactor; - const rate = midGap || ((_a = mkt === null || mkt === void 0 ? void 0 : mkt.spot) === null || _a === void 0 ? void 0 : _a.rate) || 0; - // Caller should not call this for market orders without a mkt arg. - if (!mkt) - return 0; - // This is tricky. The server will use the mid-gap rate to convert the - // order qty. We don't have a mid-gap rate, only a spot rate. - if (rate && (((_b = mkt === null || mkt === void 0 ? void 0 : mkt.spot) === null || _b === void 0 ? void 0 : _b.bookVolume) || 0) > 0) - return qty * _orderutil__WEBPACK_IMPORTED_MODULE_1__.RateEncodingFactor / rate; - return mkt.lotsize; // server uses same fallback if book is empty -}; -function epochWeight(ord, mkt, midGap) { - var _a; - if (ord.status !== _orderutil__WEBPACK_IMPORTED_MODULE_1__.StatusEpoch) - return 0; - const qty = preparcelQuantity(ord, mkt, midGap); - const rate = midGap || ((_a = mkt.spot) === null || _a === void 0 ? void 0 : _a.rate) || 0; - if (likelyTaker(ord, rate)) - return qty * 2; - return qty; -} -function bookWeight(ord) { - if (ord.status !== _orderutil__WEBPACK_IMPORTED_MODULE_1__.StatusBooked) - return 0; - return preparcelQuantity(ord); -} -function settlingWeight(ord) { - let sum = 0; - for (const m of (ord.matches || [])) { - if (m.side === _orderutil__WEBPACK_IMPORTED_MODULE_1__.MatchSideMaker) { - if (m.status > _orderutil__WEBPACK_IMPORTED_MODULE_1__.MakerRedeemed) - continue; - } - else if (m.status > _orderutil__WEBPACK_IMPORTED_MODULE_1__.TakerSwapCast) - continue; - sum += m.qty; - } - return sum; -} -function parcelWeight(ord, mkt, midGap) { - if (ord.type === _orderutil__WEBPACK_IMPORTED_MODULE_1__.OrderTypeCancel) - return 0; - return epochWeight(ord, mkt, midGap) + bookWeight(ord) + settlingWeight(ord); -} -// function roundParcels (p: number): number { -// return Math.floor(Math.round((p * 1e8)) / 1e8) -// } -function limitBonus(score, maxScore) { - return score > 0 ? 1 + score / maxScore * (parcelLimitScoreMultiplier - 1) : 1; -} -function tradingLimits(host) { - const { auth, maxScore, markets } = (0,_registry__WEBPACK_IMPORTED_MODULE_2__.app)().exchanges[host]; - const { rep: { score } } = auth; - const tier = strongTier(auth); - let usedParcels = 0; - for (const mkt of Object.values(markets)) { - let mktWeight = 0; - for (const ord of (mkt.inflight || [])) - mktWeight += parcelWeight(ord, mkt); - for (const ord of (mkt.orders || [])) - mktWeight += parcelWeight(ord, mkt); - usedParcels += (mktWeight / (mkt.parcelsize * mkt.lotsize)); - } - const parcelLimit = perTierBaseParcelLimit * limitBonus(score, maxScore) * tier; - return [usedParcels, parcelLimit]; -} - - -/***/ }), - -/***/ "./src/js/app.ts": -/*!***********************!*\ - !*** ./src/js/app.ts ***! - \***********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "default": () => (/* binding */ Application) -/* harmony export */ }); -/* harmony import */ var _doc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./doc */ "./src/js/doc.ts"); -/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./state */ "./src/js/state.ts"); -/* harmony import */ var _register__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./register */ "./src/js/register.ts"); -/* harmony import */ var _login__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./login */ "./src/js/login.ts"); -/* harmony import */ var _wallets__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./wallets */ "./src/js/wallets.ts"); -/* harmony import */ var _settings__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./settings */ "./src/js/settings.ts"); -/* harmony import */ var _markets__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./markets */ "./src/js/markets.ts"); -/* harmony import */ var _orders__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./orders */ "./src/js/orders.ts"); -/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./order */ "./src/js/order.ts"); -/* harmony import */ var _mm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./mm */ "./src/js/mm.ts"); -/* harmony import */ var _mmsettings__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./mmsettings */ "./src/js/mmsettings.ts"); -/* harmony import */ var _dexsettings__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./dexsettings */ "./src/js/dexsettings.ts"); -/* harmony import */ var _mmarchives__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./mmarchives */ "./src/js/mmarchives.ts"); -/* harmony import */ var _mmlogs__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./mmlogs */ "./src/js/mmlogs.ts"); -/* harmony import */ var _init__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./init */ "./src/js/init.ts"); -/* harmony import */ var _mmutil__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./mmutil */ "./src/js/mmutil.ts"); -/* harmony import */ var _orderutil__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./orderutil */ "./src/js/orderutil.ts"); -/* harmony import */ var _http__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./http */ "./src/js/http.ts"); -/* harmony import */ var _notifications__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./notifications */ "./src/js/notifications.ts"); -/* harmony import */ var _ws__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./ws */ "./src/js/ws.ts"); -/* harmony import */ var _locales__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./locales */ "./src/js/locales.ts"); -/* harmony import */ var _coinexplorers__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./coinexplorers */ "./src/js/coinexplorers.ts"); -var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; - - - - - - - - - - - - - - - - - - - - - - -const idel = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].idel; // = element by id -const bind = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind; -const unbind = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].unbind; -const notificationRoute = 'notify'; -const noteCacheSize = 100; -/* constructors is a map to page constructors. */ -const constructors = { - login: _login__WEBPACK_IMPORTED_MODULE_3__["default"], - register: _register__WEBPACK_IMPORTED_MODULE_2__["default"], - markets: _markets__WEBPACK_IMPORTED_MODULE_6__["default"], - wallets: _wallets__WEBPACK_IMPORTED_MODULE_4__["default"], - settings: _settings__WEBPACK_IMPORTED_MODULE_5__["default"], - orders: _orders__WEBPACK_IMPORTED_MODULE_7__["default"], - order: _order__WEBPACK_IMPORTED_MODULE_8__["default"], - dexsettings: _dexsettings__WEBPACK_IMPORTED_MODULE_11__["default"], - init: _init__WEBPACK_IMPORTED_MODULE_14__["default"], - mm: _mm__WEBPACK_IMPORTED_MODULE_9__["default"], - mmsettings: _mmsettings__WEBPACK_IMPORTED_MODULE_10__["default"], - mmarchives: _mmarchives__WEBPACK_IMPORTED_MODULE_12__["default"], - mmlogs: _mmlogs__WEBPACK_IMPORTED_MODULE_13__["default"] -}; -const languageData = { - 'en-US': { - name: 'English', - flag: 'πŸ‡ΊπŸ‡Έ' // Not πŸ‡¬πŸ‡§. MURICA! - }, - 'pt-BR': { - name: 'Portugese', - flag: 'πŸ‡§πŸ‡·' - }, - 'zh-CN': { - name: 'Chinese', - flag: 'πŸ‡¨πŸ‡³' - }, - 'pl-PL': { - name: 'Polish', - flag: 'πŸ‡΅πŸ‡±' - }, - 'de-DE': { - name: 'German', - flag: 'πŸ‡©πŸ‡ͺ' - }, - 'ar': { - name: 'Arabic', - flag: 'πŸ‡ͺπŸ‡¬' // Egypt I guess - } -}; -// Application is the main javascript web application for Bison Wallet. -class Application { - constructor() { - this.notes = []; - this.pokes = []; - this.seedGenTime = 0; - this.commitHash = "e9b6f8aaf74ed2a53867b9f8183cd06da9877708" || 0; - this.noteReceivers = []; - this.fiatRatesMap = {}; - this.showPopups = _state__WEBPACK_IMPORTED_MODULE_1__["default"].fetchLocal(_state__WEBPACK_IMPORTED_MODULE_1__["default"].popupsLK) === '1'; - this.txHistoryMap = {}; - this.requiredActions = {}; - console.log('Bison Wallet, Build', this.commitHash.substring(0, 7)); - // Set Bootstrap dark theme attribute if dark mode is enabled. - if (_state__WEBPACK_IMPORTED_MODULE_1__["default"].isDark()) { - document.body.classList.add('dark'); - } - // Loggers can be enabled by setting a truthy value to the loggerID using - // enableLogger. Settings are stored across sessions. See docstring for the - // log method for more info. - this.loggers = _state__WEBPACK_IMPORTED_MODULE_1__["default"].fetchLocal(_state__WEBPACK_IMPORTED_MODULE_1__["default"].loggersLK) || {}; - window.enableLogger = (loggerID, state) => { - if (state) - this.loggers[loggerID] = true; - else - delete this.loggers[loggerID]; - _state__WEBPACK_IMPORTED_MODULE_1__["default"].storeLocal(_state__WEBPACK_IMPORTED_MODULE_1__["default"].loggersLK, this.loggers); - return `${loggerID} logger ${state ? 'enabled' : 'disabled'}`; - }; - // Enable logging from anywhere. - window.log = (loggerID, ...a) => { this.log(loggerID, ...a); }; - window.mmStatus = () => this.mmStatus; - // Recorders can record log messages, and then save them to file on request. - const recorderKeys = _state__WEBPACK_IMPORTED_MODULE_1__["default"].fetchLocal(_state__WEBPACK_IMPORTED_MODULE_1__["default"].recordersLK) || []; - this.recorders = {}; - for (const loggerID of recorderKeys) { - console.log('recording', loggerID); - this.recorders[loggerID] = []; - } - window.recordLogger = (loggerID, on) => { - if (on) - this.recorders[loggerID] = []; - else - delete this.recorders[loggerID]; - _state__WEBPACK_IMPORTED_MODULE_1__["default"].storeLocal(_state__WEBPACK_IMPORTED_MODULE_1__["default"].recordersLK, Object.keys(this.recorders)); - return `${loggerID} recorder ${on ? 'enabled' : 'disabled'}`; - }; - window.dumpLogger = loggerID => { - const record = this.recorders[loggerID]; - if (!record) - return `no recorder for logger ${loggerID}`; - const a = document.createElement('a'); - a.href = `data:application/octet-stream;base64,${window.btoa(JSON.stringify(record, null, 4))}`; - a.download = `${loggerID}.json`; - document.body.appendChild(a); - a.click(); - setTimeout(() => { - document.body.removeChild(a); - }, 0); - }; - window.user = () => this.user; - } - /** - * Start the application. This is the only thing done from the index.js entry - * point. Read the id = main element and attach handlers. - */ - start() { - return __awaiter(this, void 0, void 0, function* () { - // Handle back navigation from the browser. - bind(window, 'popstate', (e) => { - var _a; - const page = (_a = e.state) === null || _a === void 0 ? void 0 : _a.page; - if (!page && page !== '') - return; - this.loadPage(page, e.state.data, true); - }); - // The main element is the interchangeable part of the page that doesn't - // include the header. Main should define a data-handler attribute - // associated with one of the available constructors. - this.main = idel(document, 'main'); - const handler = this.main.dataset.handler; - // Don't fetch the user until we know what page we're on. - yield this.fetchUser(); - const ignoreCachedLocale = "development" === 'development'; - yield _locales__WEBPACK_IMPORTED_MODULE_20__.loadLocale(this.lang, this.commitHash, ignoreCachedLocale); - // The application is free to respond with a page that differs from the - // one requested in the omnibox, e.g. routing though a login page. Set the - // current URL state based on the actual page. - const url = new URL(window.location.href); - if (handlerFromPath(url.pathname) !== handler) { - url.pathname = `/${handler}`; - url.search = ''; - window.history.replaceState({ page: handler }, '', url); - } - // Attach stuff. - this.attachHeader(); - this.attachActions(); - this.attachCommon(this.header); - this.attach({}); - // If we are authed, populate notes, otherwise get we'll them from the login - // response. - if (this.authed) - yield this.fetchNotes(); - this.updateMenuItemsDisplay(); - // initialize desktop notifications - _notifications__WEBPACK_IMPORTED_MODULE_18__.fetchDesktopNtfnSettings(); - // Connect the websocket and register the notification route. - _ws__WEBPACK_IMPORTED_MODULE_19__["default"].connect(getSocketURI(), () => this.reconnected()); - _ws__WEBPACK_IMPORTED_MODULE_19__["default"].registerRoute(notificationRoute, (note) => { - this.notify(note); - }); - }); - } - /* - * reconnected is called by the websocket client when a reconnection is made. - */ - reconnected() { - var _a; - if (((_a = this.main) === null || _a === void 0 ? void 0 : _a.dataset.handler) === 'settings') - window.location.assign('/'); - else - window.location.reload(); // This triggers another websocket disconnect/connect (!) - // a fetchUser() and loadPage(window.history.state.page) might work - } - /* - * Fetch and save the user, which is the primary core state that must be - * maintained by the Application. - */ - fetchUser() { - return __awaiter(this, void 0, void 0, function* () { - const resp = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.getJSON)('/api/user'); - if (!this.checkResponse(resp)) - return; - this.inited = resp.inited; - this.authed = Boolean(resp.user); - this.lang = resp.lang; - this.langs = resp.langs; - this.mmStatus = resp.mmStatus; - if (!resp.user) - return; - const user = resp.user; - this.seedGenTime = user.seedgentime; - this.user = user; - this.assets = user.assets; - this.exchanges = user.exchanges; - this.walletMap = {}; - this.fiatRatesMap = user.fiatRates; - for (const [assetID, asset] of Object.entries(user.assets)) { - if (asset.wallet) { - this.walletMap[assetID] = asset.wallet; - } - } - this.updateMenuItemsDisplay(); - return user; - }); - } - fetchMMStatus() { - return __awaiter(this, void 0, void 0, function* () { - this.mmStatus = yield _mmutil__WEBPACK_IMPORTED_MODULE_15__.MM.status(); - }); - } - /* Load the page from the server. Insert and bind the DOM. */ - loadPage(page, data, skipPush) { - return __awaiter(this, void 0, void 0, function* () { - // Close some menus and tooltips. - this.tooltip.style.left = '-10000px'; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.page.noteBox, this.page.profileBox); - // Parse the request. - const url = new URL(`/${page}`, window.location.origin); - const requestedHandler = handlerFromPath(page); - // Fetch and parse the page. - const response = yield window.fetch(url.toString()); - if (!response.ok) - return false; - const html = yield response.text(); - const doc = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].noderize(html); - const main = idel(doc, 'main'); - const delivered = main.dataset.handler; - // Append the request to the page history. - if (!skipPush) { - const path = delivered === requestedHandler ? url.toString() : `/${delivered}`; - window.history.pushState({ page: page, data: data }, '', path); - } - // Insert page and attach handlers. - document.title = doc.title; - this.main.replaceWith(main); - this.main = main; - this.noteReceivers = []; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(this.headerSpace); - this.attach(data); - return true; - }); - } - /* attach binds the common handlers and calls the page constructor. */ - attach(data) { - const handlerID = this.main.dataset.handler; - if (!handlerID) { - console.error('cannot attach to content with no specified handler'); - return; - } - this.attachCommon(this.main); - if (this.loadedPage) - this.loadedPage.unload(); - const constructor = constructors[handlerID]; - if (constructor) - this.loadedPage = new constructor(this.main, data); - else - this.loadedPage = null; - // Bind the tooltips. - this.bindTooltips(this.main); - if (window.isWebview) { - // Bind webview URL handlers - this.bindUrlHandlers(this.main); - } - this.bindUnits(this.main); - } - bindTooltips(ancestor) { - ancestor.querySelectorAll('[data-tooltip]').forEach((el) => { - bind(el, 'mouseenter', () => { - this.tooltip.textContent = el.dataset.tooltip || ''; - const lyt = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].layoutMetrics(el); - let left = lyt.centerX - this.tooltip.offsetWidth / 2; - if (left < 0) - left = 5; - if (left + this.tooltip.offsetWidth > document.body.offsetWidth) { - left = document.body.offsetWidth - this.tooltip.offsetWidth - 5; - } - this.tooltip.style.left = `${left}px`; - this.tooltip.style.top = `${lyt.bodyTop - this.tooltip.offsetHeight - 5}px`; - }); - bind(el, 'mouseleave', () => { - this.tooltip.style.left = '-10000px'; - }); - }); - } - /* - * bindUnits binds a hovering unit selection menu to the value or rate - * display elements. The menu gives users an option to convert the value - * to their preferred units. - */ - bindUnits(main) { - const div = document.createElement('div'); - div.classList.add('position-absolute', 'p-3'); - // div.style.backgroundColor = 'yellow' - const rows = document.createElement('div'); - div.appendChild(rows); - rows.classList.add('body-bg', 'border'); - const addRow = (el, unit, cFactor) => { - const box = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].safeSelector(el, '[data-unit-box]'); - const atoms = parseInt(box.dataset.atoms); - const row = document.createElement('div'); - row.textContent = unit; - rows.appendChild(row); - row.classList.add('p-2', 'hoverbg', 'pointer'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(row, 'click', () => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].setText(el, '[data-value]', _doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatFourSigFigs(atoms / cFactor, Math.round(Math.log10(cFactor)))); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].setText(el, '[data-unit]', unit); - }); - }; - for (const el of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(main, '[data-conversion-value]')) { - const box = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].safeSelector(el, '[data-unit-box]'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(box, 'mouseenter', () => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(rows); - box.appendChild(div); - const lyt = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].layoutMetrics(box); - const assetID = parseInt(box.dataset.assetID); - const { unitInfo: ui } = this.assets[assetID]; - addRow(el, ui.conventional.unit, ui.conventional.conversionFactor); - for (const { unit, conversionFactor } of ui.denominations) - addRow(el, unit, conversionFactor); - addRow(el, ui.atomicUnit, 1); - if (lyt.bodyTop > (div.offsetHeight + this.header.offsetHeight)) { - div.style.bottom = 'calc(100% - 1rem)'; - div.style.top = 'auto'; - } - else { - div.style.top = 'calc(100% - 1rem)'; - div.style.bottom = 'auto'; - } - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(box, 'mouseleave', () => div.remove()); - } - } - bindUrlHandlers(ancestor) { - if (!window.openUrl) - return; - for (const link of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, 'a[target=_blank]')) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(link, 'click', (e) => { - var _a; - e.preventDefault(); - window.openUrl((_a = link.href) !== null && _a !== void 0 ? _a : ''); - }); - } - } - /* attachHeader attaches the header element, which unlike the main element, - * isn't replaced during page navigation. - */ - attachHeader() { - this.header = idel(document.body, 'header'); - const page = this.page = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].idDescendants(this.header); - this.headerSpace = page.headerSpace; - this.popupNotes = idel(document.body, 'popupNotes'); - this.popupTmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(this.popupNotes, 'note'); - if (this.popupTmpl) - this.popupTmpl.remove(); - else - console.error('popupTmpl element not found'); - this.tooltip = idel(document.body, 'tooltip'); - page.noteTmpl.removeAttribute('id'); - page.noteTmpl.remove(); - page.pokeTmpl.removeAttribute('id'); - page.pokeTmpl.remove(); - page.loader.remove(); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.loader); - bind(page.noteBell, 'click', () => __awaiter(this, void 0, void 0, function* () { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.pokeList); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.noteList); - this.ackNotes(); - page.noteCat.classList.add('active'); - page.pokeCat.classList.remove('active'); - this.showDropdown(page.noteBell, page.noteBox); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.noteIndicator); - for (const note of this.notes) { - if (note.acked) { - note.el.classList.remove('firstview'); - } - } - this.setNoteTimes(page.noteList); - this.setNoteTimes(page.pokeList); - })); - bind(page.burgerIcon, 'click', () => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.logoutErr); - this.showDropdown(page.burgerIcon, page.profileBox); - }); - bind(page.innerNoteIcon, 'click', () => { _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.noteBox); }); - bind(page.innerBurgerIcon, 'click', () => { _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.profileBox); }); - bind(page.profileSignout, 'click', () => __awaiter(this, void 0, void 0, function* () { return yield this.signOut(); })); - bind(page.pokeCat, 'click', () => { - this.setNoteTimes(page.pokeList); - page.pokeCat.classList.add('active'); - page.noteCat.classList.remove('active'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.noteList); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.pokeList); - this.ackNotes(); - }); - bind(page.noteCat, 'click', () => { - this.setNoteTimes(page.noteList); - page.noteCat.classList.add('active'); - page.pokeCat.classList.remove('active'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.pokeList); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.noteList); - this.ackNotes(); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].cleanTemplates(page.langBttnTmpl); - const { name, flag } = languageData[this.lang]; - page.langFlag.textContent = flag; - page.langName.textContent = name; - for (const lang of this.langs) { - if (lang === this.lang) - continue; - const div = page.langBttnTmpl.cloneNode(true); - const { name, flag } = languageData[lang]; - div.textContent = flag; - div.title = name; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(div, 'click', () => this.setLanguage(lang)); - page.langBttns.appendChild(div); - } - } - attachActions() { - const { page } = this; - Object.assign(page, _doc__WEBPACK_IMPORTED_MODULE_0__["default"].idDescendants(_doc__WEBPACK_IMPORTED_MODULE_0__["default"].idel(document.body, 'requiredActions'))); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].cleanTemplates(page.missingNoncesTmpl, page.actionTxTableTmpl, page.tooCheapTmpl, page.lostNonceTmpl); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.actionsCollapse, 'click', () => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.actionDialog); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.actionDialogCollapsed); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.actionDialogCollapsed, 'click', () => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.actionDialogCollapsed); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.actionDialog); - if (page.actionDialogContent.children.length === 0) - this.showOldestAction(); - }); - const showAdjacentAction = (dir) => { - const selected = Object.values(this.requiredActions).filter((r) => r.selected)[0]; - const actions = this.sortedActions(); - const idx = actions.indexOf(selected); - this.showRequestedAction(actions[idx + dir].uniqueID); - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.prevAction, 'click', () => showAdjacentAction(-1)); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.nextAction, 'click', () => showAdjacentAction(1)); - } - setRequiredActions() { - const { user: { actions }, requiredActions } = this; - if (!actions) - return; - for (const a of actions) - this.addAction(a); - if (Object.keys(requiredActions).length) { - this.showOldestAction(); - this.blinkAction(); - } - } - sortedActions() { - const actions = Object.values(this.requiredActions); - actions.sort((a, b) => a.stamp - b.stamp); - return actions; - } - showOldestAction() { - this.showRequestedAction(this.sortedActions()[0].uniqueID); - } - addAction(req) { - const { page, requiredActions } = this; - const existingAction = requiredActions[req.uniqueID]; - if (existingAction && existingAction.actionID === req.actionID) - return; - const div = this.actionForm(req); - if (existingAction) { - if (existingAction.selected) - existingAction.div.replaceWith(div); - existingAction.div = div; - } - else { - requiredActions[req.uniqueID] = { - div, - stamp: (new Date()).getTime(), - uniqueID: req.uniqueID, - actionID: req.actionID, - selected: false - }; - const n = Object.keys(requiredActions).length; - page.actionDialogCount.textContent = String(n); - page.actionCount.textContent = String(n); - if (_doc__WEBPACK_IMPORTED_MODULE_0__["default"].isHidden(page.actionDialog)) { - this.showRequestedAction(req.uniqueID); - } - } - } - blinkAction() { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].blink(this.page.actionDialog); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].blink(this.page.actionDialogCollapsed); - } - resolveAction(req) { - this.resolveActionWithID(req.uniqueID); - } - resolveActionWithID(uniqueID) { - const { page, requiredActions } = this; - const existingAction = requiredActions[uniqueID]; - if (!existingAction) - return; - delete requiredActions[uniqueID]; - const rem = Object.keys(requiredActions).length; - existingAction.div.remove(); - if (rem === 0) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.actionDialog, page.actionDialogCollapsed); - return; - } - page.actionDialogCount.textContent = String(rem); - page.actionCount.textContent = String(rem); - if (existingAction.selected) - this.showOldestAction(); - } - actionForm(req) { - switch (req.actionID) { - case 'tooCheap': - return this.tooCheapAction(req); - case 'missingNonces': - return this.missingNoncesAction(req); - case 'lostNonce': - return this.lostNonceAction(req); - case 'redeemRejected': - return this.redeemRejectedAction(req); - } - throw Error('unknown required action ID ' + req.actionID); - } - actionTxTable(req) { - const { assetID, payload } = req; - const n = payload; - const { unitInfo: ui, token } = this.assets[assetID]; - const table = this.page.actionTxTableTmpl.cloneNode(true); - const tmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(table); - tmpl.lostTxID.textContent = n.tx.id; - tmpl.lostTxID.dataset.explorerCoin = n.tx.id; - (0,_coinexplorers__WEBPACK_IMPORTED_MODULE_21__.setCoinHref)(token ? token.parentID : assetID, tmpl.lostTxID); - tmpl.txAmt.textContent = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatCoinValue(n.tx.amount, ui); - tmpl.amtUnit.textContent = ui.conventional.unit; - const parentUI = token ? this.unitInfo(token.parentID) : ui; - tmpl.type.textContent = (0,_wallets__WEBPACK_IMPORTED_MODULE_4__.txTypeString)(n.tx.type); - tmpl.feeAmount.textContent = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatCoinValue(n.tx.fees, parentUI); - tmpl.feeUnit.textContent = parentUI.conventional.unit; - switch (req.actionID) { - case 'tooCheap': { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(tmpl.newFeesRow); - tmpl.newFees.textContent = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatCoinValue(n.tx.fees, parentUI); - tmpl.newFeesUnit.textContent = parentUI.conventional.unit; - break; - } - } - return table; - } - submitAction(req, action, errMsg) { - return __awaiter(this, void 0, void 0, function* () { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(errMsg); - const loading = this.loading(this.page.actionDialog); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)('/api/takeaction', { - assetID: req.assetID, - actionID: req.actionID, - action - }); - loading(); - if (!this.checkResponse(res)) { - errMsg.textContent = res.msg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(errMsg); - return; - } - this.resolveActionWithID(req.uniqueID); - }); - } - missingNoncesAction(req) { - const { assetID } = req; - const div = this.page.missingNoncesTmpl.cloneNode(true); - const tmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(div); - const { name } = this.assets[assetID]; - tmpl.assetName.textContent = name; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.doNothingBttn, 'click', () => { - this.submitAction(req, { recover: false }, tmpl.errMsg); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.recoverBttn, 'click', () => { - this.submitAction(req, { recover: true }, tmpl.errMsg); - }); - return div; - } - tooCheapAction(req) { - const { assetID, payload } = req; - const n = payload; - const div = this.page.tooCheapTmpl.cloneNode(true); - const tmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(div); - const { name } = this.assets[assetID]; - tmpl.assetName.textContent = name; - tmpl.txTable.appendChild(this.actionTxTable(req)); - const act = (bump) => { - this.submitAction(req, { - txID: n.tx.id, - bump - }, tmpl.errMsg); - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.keepWaitingBttn, 'click', () => act(false)); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.addFeesBttn, 'click', () => act(true)); - return div; - } - lostNonceAction(req) { - const { assetID, payload } = req; - const n = payload; - const div = this.page.lostNonceTmpl.cloneNode(true); - const tmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(div); - const { name } = this.assets[assetID]; - tmpl.assetName.textContent = name; - tmpl.nonce.textContent = String(n.nonce); - tmpl.txTable.appendChild(this.actionTxTable(req)); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.abandonBttn, 'click', () => { - this.submitAction(req, { txID: n.tx.id, abandon: true }, tmpl.errMsg); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.keepWaitingBttn, 'click', () => { - this.submitAction(req, { txID: n.tx.id, abandon: false }, tmpl.errMsg); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.replaceBttn, 'click', () => { - const replacementID = tmpl.idInput.value; - if (!replacementID) { - tmpl.idInput.focus(); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].blink(tmpl.idInput); - return; - } - this.submitAction(req, { txID: n.tx.id, abandon: false, replacementID }, tmpl.errMsg); - }); - return div; - } - redeemRejectedAction(req) { - const { orderID, coinID, coinFmt, assetID } = req.payload; - const div = this.page.rejectedRedemptionTmpl.cloneNode(true); - const tmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(div); - const { name, token } = this.assets[assetID]; - tmpl.assetName.textContent = name; - tmpl.txid.textContent = coinFmt; - tmpl.txid.dataset.explorerCoin = coinID; - (0,_coinexplorers__WEBPACK_IMPORTED_MODULE_21__.setCoinHref)(token ? token.parentID : assetID, tmpl.txid); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.doNothingBttn, 'click', () => { - this.submitAction(req, { orderID, coinID, retry: false }, tmpl.errMsg); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tmpl.tryAgainBttn, 'click', () => { - this.submitAction(req, { orderID, coinID, retry: true }, tmpl.errMsg); - }); - return div; - } - showRequestedAction(uniqueID) { - const { page, requiredActions } = this; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.actionDialogCollapsed); - for (const r of Object.values(requiredActions)) - r.selected = r.uniqueID === uniqueID; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(page.actionDialogContent); - const action = requiredActions[uniqueID]; - page.actionDialogContent.appendChild(action.div); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.actionDialog); - const actions = this.sortedActions(); - if (actions.length === 1) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.actionsNavigator); - return; - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.actionsNavigator); - const idx = actions.indexOf(action); - page.currentAction.textContent = String(idx + 1); - page.prevAction.classList.toggle('invisible', idx === 0); - page.nextAction.classList.toggle('invisible', idx === actions.length - 1); - } - /* - * updateMarketElements sets the textContent for any ticker or asset name - * elements or any asset logo src attributes for descendents of ancestor. - */ - updateMarketElements(ancestor, baseID, quoteID, xc) { - const getAsset = (assetID) => { - const a = this.assets[assetID]; - if (a) - return a; - if (!xc) - throw Error(`no asset found for asset ID ${assetID}`); - const xcAsset = xc.assets[assetID]; - return { unitInfo: xcAsset.unitInfo, name: xcAsset.symbol, symbol: xcAsset.symbol }; - }; - const { unitInfo: bui, name: baseName, symbol: baseSymbol } = getAsset(baseID); - for (const el of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, '[data-base-name')) - el.textContent = baseName; - for (const img of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, '[data-base-logo]')) - img.src = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].logoPath(baseSymbol); - for (const el of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, '[data-base-ticker]')) - el.textContent = bui.conventional.unit; - const { unitInfo: qui, name: quoteName, symbol: quoteSymbol } = getAsset(quoteID); - for (const el of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, '[data-quote-name')) - el.textContent = quoteName; - for (const img of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, '[data-quote-logo]')) - img.src = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].logoPath(quoteSymbol); - for (const el of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(ancestor, '[data-quote-ticker]')) - el.textContent = qui.conventional.unit; - } - setLanguage(lang) { - return __awaiter(this, void 0, void 0, function* () { - yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)('/api/setlocale', lang); - window.location.reload(); - }); - } - /* - * showDropdown sets the position and visibility of the specified dropdown - * dialog according to the position of its icon button. - */ - showDropdown(icon, dialog) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.page.noteBox, this.page.profileBox); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(dialog); - if (window.innerWidth < 500) - Object.assign(dialog.style, { left: '0', right: '0', top: '0' }); - else { - const ico = icon.getBoundingClientRect(); - const right = `${window.innerWidth - ico.left - ico.width + 5}px`; - Object.assign(dialog.style, { left: 'auto', right, top: `${ico.top - 4}px` }); - } - const hide = (e) => { - if (!_doc__WEBPACK_IMPORTED_MODULE_0__["default"].mouseInElement(e, dialog)) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(dialog); - unbind(document, 'click', hide); - if (dialog === this.page.noteBox && _doc__WEBPACK_IMPORTED_MODULE_0__["default"].isDisplayed(this.page.noteList)) { - this.ackNotes(); - } - } - }; - bind(document, 'click', hide); - } - ackNotes() { - const acks = []; - for (const note of this.notes) { - if (note.acked) { - note.el.classList.remove('firstview'); - } - else { - note.acked = true; - if (note.id && note.severity > _notifications__WEBPACK_IMPORTED_MODULE_18__.POKE) - acks.push(note.id); - } - } - if (acks.length) - _ws__WEBPACK_IMPORTED_MODULE_19__["default"].request('acknotes', acks); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.page.noteIndicator); - } - setNoteTimes(noteList) { - for (const el of Array.from(noteList.children)) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].safeSelector(el, 'span.note-time').textContent = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].timeSince(el.note.stamp); - } - } - /* - * bindInternalNavigation hijacks navigation by click on any local links that - * are descendants of ancestor. - */ - bindInternalNavigation(ancestor) { - const pageURL = new URL(window.location.href); - ancestor.querySelectorAll('a').forEach(a => { - if (!a.href) - return; - const url = new URL(a.href); - if (url.origin === pageURL.origin) { - const token = url.pathname.substring(1); - const params = {}; - if (url.search) { - url.searchParams.forEach((v, k) => { - params[k] = v; - }); - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(a, 'click', (e) => { - e.preventDefault(); - this.loadPage(token, params); - }); - } - }); - } - /* - * updateMenuItemsDisplay should be called when the user has signed in or out, - * and when the user registers a DEX. - */ - updateMenuItemsDisplay() { - const { page, authed, mmStatus } = this; - if (!page) { - // initial page load, header elements not yet attached but menu items - // would already be hidden/displayed as appropriate. - return; - } - if (!authed) { - page.profileBox.classList.remove('authed'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.noteBell, page.walletsMenuEntry, page.marketsMenuEntry); - return; - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].setVis(Object.keys(this.exchanges).length > 0, page.marketsMenuEntry, page.mmLink); - page.profileBox.classList.add('authed'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.noteBell, page.walletsMenuEntry, page.marketsMenuEntry); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].setVis(mmStatus, page.mmLink); - } - fetchNotes() { - return __awaiter(this, void 0, void 0, function* () { - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.getJSON)('/api/notes'); - if (!this.checkResponse(res)) - return console.error('failed to fetch notes:', (res === null || res === void 0 ? void 0 : res.msg) || String(res)); - res.notes.reverse(); - this.setNotes(res.notes); - this.setPokes(res.pokes); - this.setRequiredActions(); - }); - } - /* attachCommon scans the provided node and handles some common bindings. */ - attachCommon(node) { - this.bindInternalNavigation(node); - } - /* - * updateBondConfs updates the information for a pending bond. - */ - updateBondConfs(dexAddr, coinID, confs) { - const dex = this.exchanges[dexAddr]; - for (const bond of dex.auth.pendingBonds) - if (bond.coinID === coinID) - bond.confs = confs; - } - updateTier(host, bondedTier) { - this.exchanges[host].auth.rep.bondedTier = bondedTier; - } - /* - * handleBondNote is the handler for the 'bondpost'-type notification, which - * is used to update the dex tier and registration status. - */ - handleBondNote(note) { - if (note.auth) - this.exchanges[note.dex].auth = note.auth; - switch (note.topic) { - case 'RegUpdate': - if (note.coinID !== null) { // should never be null for RegUpdate - this.updateBondConfs(note.dex, note.coinID, note.confirmations); - } - break; - case 'BondConfirmed': - if (note.tier !== null) { // should never be null for BondConfirmed - this.updateTier(note.dex, note.tier); - } - break; - default: - break; - } - } - /* - * handleTransaction either adds a new transaction to the transaction history - * or updates an existing transaction. - */ - handleTransactionNote(assetID, note) { - const txHistory = this.txHistoryMap[assetID]; - if (!txHistory) - return; - if (note.new) { - txHistory.txs.unshift(note.transaction); - return; - } - for (let i = 0; i < txHistory.txs.length; i++) { - if (txHistory.txs[i].id === note.transaction.id) { - txHistory.txs[i] = note.transaction; - break; - } - } - } - handleTxHistorySyncedNote(assetID) { - delete this.txHistoryMap[assetID]; - } - loggedIn(notes, pokes) { - this.setNotes(notes); - this.setPokes(pokes); - this.setRequiredActions(); - } - /* - * setNotes sets the current notification cache and populates the notification - * display. - */ - setNotes(notes) { - this.log('notes', 'setNotes', notes); - this.notes = []; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(this.page.noteList); - for (let i = 0; i < notes.length; i++) { - this.prependNoteElement(notes[i]); - } - } - /* - * setPokes sets the current poke cache and populates the pokes display. - */ - setPokes(pokes) { - this.log('pokes', 'setPokes', pokes); - this.pokes = []; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(this.page.pokeList); - for (let i = 0; i < pokes.length; i++) { - this.prependPokeElement(pokes[i]); - } - } - botStatus(host, baseID, quoteID) { - var _a, _b; - for (const bot of ((_b = (_a = this.mmStatus) === null || _a === void 0 ? void 0 : _a.bots) !== null && _b !== void 0 ? _b : [])) { - const { config: c } = bot; - if (host === c.host && baseID === c.baseID && quoteID === c.quoteID) { - return bot; - } - } - } - updateUser(note) { - const { user, assets, walletMap } = this; - if (note.type === 'fiatrateupdate') { - this.fiatRatesMap = note.fiatRates; - return; - } - // Some notes can be received before we get a User during login. - if (!user) - return; - switch (note.type) { - case 'order': { - const orderNote = note; - const order = orderNote.order; - const mkt = user.exchanges[order.host].markets[order.market]; - const tempID = orderNote.tempID; - // Ensure market's inflight orders list is updated. - if (note.topic === 'AsyncOrderSubmitted') { - const inFlight = order; - inFlight.tempID = tempID; - if (!mkt.inflight) - mkt.inflight = [inFlight]; - else - mkt.inflight.push(inFlight); - break; - } - else if (note.topic === 'AsyncOrderFailure') { - mkt.inflight = mkt.inflight.filter(ord => ord.tempID !== tempID); - break; - } - else { - for (const i in mkt.inflight || []) { - if (!(mkt.inflight[i].tempID === tempID)) - continue; - mkt.inflight = mkt.inflight.filter(ord => ord.tempID !== tempID); - break; - } - } - // Updates given order in market's orders list if it finds it. - // Returns a bool which indicates if order was found. - mkt.orders = mkt.orders || []; - const updateOrder = (mkt, ord) => { - const i = mkt.orders.findIndex((o) => o.id === ord.id); - if (i === -1) - return false; - if (note.topic === 'OrderRetired') - mkt.orders.splice(i, 1); - else - mkt.orders[i] = ord; - return true; - }; - // If the notification order already exists we update it. - // In case market's orders list is empty or the notification order isn't - // part of it we add it manually as this means the order was - // just placed. - if (!updateOrder(mkt, order)) - mkt.orders.push(order); - break; - } - case 'balance': { - const n = note; - const asset = user.assets[n.assetID]; - // Balance updates can come before the user is fetched after login. - if (!asset) - break; - const w = asset.wallet; - if (w) - w.balance = n.balance; - break; - } - case 'bondpost': - this.handleBondNote(note); - break; - case 'reputation': { - const n = note; - this.exchanges[n.host].auth.rep = n.rep; - break; - } - case 'walletstate': - case 'walletconfig': { - // assets can be null if failed to connect to dex server. - if (!assets) - return; - const wallet = note === null || note === void 0 ? void 0 : note.wallet; - if (!wallet) - return; - const asset = assets[wallet.assetID]; - asset.wallet = wallet; - walletMap[wallet.assetID] = wallet; - break; - } - case 'walletsync': { - const n = note; - const w = this.walletMap[n.assetID]; - if (w) { - w.syncStatus = n.syncStatus; - w.synced = w.syncStatus.synced; - w.syncProgress = n.syncProgress; - } - break; - } - case 'match': { - const n = note; - const ord = this.order(n.orderID); - if (ord) - updateMatch(ord, n.match); - break; - } - case 'conn': { - const n = note; - const xc = user.exchanges[n.host]; - if (xc) - xc.connectionStatus = n.connectionStatus; - break; - } - case 'spots': { - const n = note; - const xc = user.exchanges[n.host]; - // Spots can come before the user is fetched after login and before/while the - // markets page reload when it receives a dex conn note. - if (!xc || !xc.markets) - break; - for (const [mktName, spot] of Object.entries(n.spots)) - xc.markets[mktName].spot = spot; - break; - } - case 'fiatrateupdate': { - this.fiatRatesMap = note.fiatRates; - break; - } - case 'actionrequired': { - const n = note; - this.addAction(n.payload); - break; - } - case 'walletnote': { - const n = note; - switch (n.payload.route) { - case 'transaction': { - const txNote = n.payload; - this.handleTransactionNote(n.payload.assetID, txNote); - break; - } - case 'actionRequired': { - const req = n.payload; - this.addAction(req); - this.blinkAction(); - break; - } - case 'actionResolved': { - this.resolveAction(n.payload); - } - } - if (n.payload.route === 'transactionHistorySynced') { - this.handleTxHistorySyncedNote(n.payload.assetID); - } - break; - } - case 'runstats': { - this.log('mm', { runstats: note }); - const n = note; - const bot = this.botStatus(n.host, n.baseID, n.quoteID); - if (bot) { - bot.runStats = n.stats; - bot.running = Boolean(n.stats); - } - break; - } - case 'cexnote': { - const n = note; - switch (n.topic) { - case 'BalanceUpdate': { - const u = n.note; - this.mmStatus.cexes[n.cexName].balances[u.assetID] = u.balance; - } - } - break; - } - } - } - /* - * notify is the top-level handler for notifications received from the client. - * Notifications are propagated to the loadedPage. - */ - notify(note) { - // Handle type-specific updates. - this.log('notes', 'notify', note); - this.updateUser(note); - // Inform the page. - for (const feeder of this.noteReceivers) { - const f = feeder[note.type]; - if (!f) - continue; - try { - f(note); - } - catch (error) { - console.error('note feeder error:', error.message ? error.message : error); - console.log(note); - console.log(error.stack); - } - } - // Discard data notifications. - if (note.severity < _notifications__WEBPACK_IMPORTED_MODULE_18__.POKE) - return; - // Poke notifications have their own display. - const { popupTmpl, popupNotes, showPopups } = this; - if (showPopups) { - const span = popupTmpl.cloneNode(true); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(span, 'text').textContent = `${note.subject}: ${_notifications__WEBPACK_IMPORTED_MODULE_18__.plainNote(note.details)}`; - const indicator = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(span, 'indicator'); - if (note.severity === _notifications__WEBPACK_IMPORTED_MODULE_18__.POKE) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(indicator); - } - else - setSeverityClass(indicator, note.severity); - popupNotes.appendChild(span); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(popupNotes); - // These take up screen space. Only show max 5 at a time. - while (popupNotes.children.length > 5) - popupNotes.removeChild(popupNotes.firstChild); - setTimeout(() => __awaiter(this, void 0, void 0, function* () { - yield _doc__WEBPACK_IMPORTED_MODULE_0__["default"].animate(500, (progress) => { - span.style.opacity = String(1 - progress); - }); - span.remove(); - if (popupNotes.children.length === 0) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(popupNotes); - }), 6000); - } - // Success and higher severity go to the bell dropdown. - if (note.severity === _notifications__WEBPACK_IMPORTED_MODULE_18__.POKE) - this.prependPokeElement(note); - else - this.prependNoteElement(note); - // show desktop notification - _notifications__WEBPACK_IMPORTED_MODULE_18__.desktopNotify(note); - } - /* - * registerNoteFeeder registers a feeder for core notifications. The feeder - * will be de-registered when a new page is loaded. - */ - registerNoteFeeder(receivers) { - this.noteReceivers.push(receivers); - } - /* - * log prints to the console if a logger has been enabled. Loggers are created - * implicitly by passing a loggerID to log. i.e. you don't create a logger, - * you just log to it. Loggers are enabled by invoking a global function, - * enableLogger(loggerID, onOffBoolean), from the browser's js console. Your - * choices are stored across sessions. Some common and useful loggers are - * listed below, but this list is not meant to be comprehensive. - * - * LoggerID Description - * -------- ----------- - * notes Notifications of all levels. - * book Order book feed. - * ws.........Websocket connection status changes. - */ - log(loggerID, ...msg) { - if (this.loggers[loggerID]) - console.log(`${nowString()}[${loggerID}]:`, ...msg); - if (this.recorders[loggerID]) { - this.recorders[loggerID].push({ - time: nowString(), - msg: msg - }); - } - } - prependPokeElement(cn) { - const [el, note] = this.makePoke(cn); - this.pokes.push(note); - while (this.pokes.length > noteCacheSize) - this.pokes.shift(); - this.prependListElement(this.page.pokeList, note, el); - } - prependNoteElement(cn) { - const [el, note] = this.makeNote(cn); - this.notes.push(note); - while (this.notes.length > noteCacheSize) - this.notes.shift(); - const noteList = this.page.noteList; - this.prependListElement(noteList, note, el); - this.bindUrlHandlers(el); - // Set the indicator color. - if (this.notes.length === 0 || (_doc__WEBPACK_IMPORTED_MODULE_0__["default"].isDisplayed(this.page.noteBox) && _doc__WEBPACK_IMPORTED_MODULE_0__["default"].isDisplayed(noteList))) - return; - let unacked = 0; - const severity = this.notes.reduce((s, note) => { - if (!note.acked) - unacked++; - if (!note.acked && note.severity > s) - return note.severity; - return s; - }, _notifications__WEBPACK_IMPORTED_MODULE_18__.IGNORE); - const ni = this.page.noteIndicator; - setSeverityClass(ni, severity); - if (unacked) { - ni.textContent = String((unacked > noteCacheSize - 1) ? `${noteCacheSize - 1}+` : unacked); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(ni); - } - else - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(ni); - } - prependListElement(noteList, note, el) { - el.note = note; - noteList.prepend(el); - while (noteList.children.length > noteCacheSize) - noteList.removeChild(noteList.lastChild); - this.setNoteTimes(noteList); - } - /* - * makeNote constructs a single notification element for the drop-down - * notification list. - */ - makeNote(note) { - const el = this.page.noteTmpl.cloneNode(true); - if (note.severity > _notifications__WEBPACK_IMPORTED_MODULE_18__.POKE) { - const cls = note.severity === _notifications__WEBPACK_IMPORTED_MODULE_18__.SUCCESS ? 'good' : note.severity === _notifications__WEBPACK_IMPORTED_MODULE_18__.WARNING ? 'warn' : 'bad'; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].safeSelector(el, 'div.note-indicator').classList.add(cls); - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].safeSelector(el, 'div.note-subject').textContent = note.subject; - _notifications__WEBPACK_IMPORTED_MODULE_18__.insertRichNote(_doc__WEBPACK_IMPORTED_MODULE_0__["default"].safeSelector(el, 'div.note-details'), note.details); - const np = Object.assign({ el }, note); - return [el, np]; - } - makePoke(note) { - const el = this.page.pokeTmpl.cloneNode(true); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(el, 'subject').textContent = `${note.subject}:`; - _notifications__WEBPACK_IMPORTED_MODULE_18__.insertRichNote(_doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(el, 'details'), note.details); - const np = Object.assign({ el }, note); - return [el, np]; - } - /* - * loading appends the loader to the specified element and displays the - * loading icon. The loader will block all interaction with the specified - * element until Application.loaded is called. - */ - loading(el) { - const loader = this.page.loader.cloneNode(true); - el.appendChild(loader); - return () => { loader.remove(); }; - } - /* orders retrieves a list of orders for the specified dex and market - * including inflight orders. - */ - orders(host, mktID) { - let orders = []; - const mkt = this.user.exchanges[host].markets[mktID]; - if (mkt.orders) - orders = orders.concat(mkt.orders); - if (mkt.inflight) - orders = orders.concat(mkt.inflight); - return orders; - } - /* - * haveActiveOrders returns whether or not there are active orders involving a - * certain asset. - */ - haveActiveOrders(assetID) { - for (const xc of Object.values(this.user.exchanges)) { - if (!xc.markets) - continue; - for (const market of Object.values(xc.markets)) { - if (!market.orders) - continue; - for (const ord of market.orders) { - if ((ord.baseID === assetID || ord.quoteID === assetID) && - (ord.status < _orderutil__WEBPACK_IMPORTED_MODULE_16__.StatusExecuted || (0,_orderutil__WEBPACK_IMPORTED_MODULE_16__.hasActiveMatches)(ord))) - return true; - } - } - } - return false; - } - /* order attempts to locate an order by order ID. */ - order(oid) { - for (const xc of Object.values(this.user.exchanges)) { - if (!xc || !xc.markets) - continue; - for (const market of Object.values(xc.markets)) { - if (!market.orders) - continue; - for (const ord of market.orders) { - if (ord.id === oid) - return ord; - } - } - } - return null; - } - /* - * canAccelerateOrder returns true if the "from" wallet of the order - * supports acceleration, and if the order has unconfirmed swap - * transactions. - */ - canAccelerateOrder(order) { - var _a; - const walletTraitAccelerator = 1 << 4; - let fromAssetID; - if (order.sell) - fromAssetID = order.baseID; - else - fromAssetID = order.quoteID; - const wallet = this.walletMap[fromAssetID]; - if (!wallet || !(wallet.traits & walletTraitAccelerator)) - return false; - if (order.matches) { - for (let i = 0; i < ((_a = order.matches) === null || _a === void 0 ? void 0 : _a.length); i++) { - const match = order.matches[i]; - if (match.swap && match.swap.confs && match.swap.confs.count === 0 && !match.revoked) { - return true; - } - } - } - return false; - } - /* - * unitInfo fetches unit info [dex.UnitInfo] for the asset. If xc - * [core.Exchange] is provided, and this is not a SupportedAsset, the UnitInfo - * sent from the exchange's assets map [dex.Asset] will be used. - */ - unitInfo(assetID, xc) { - const supportedAsset = this.assets[assetID]; - if (supportedAsset) - return supportedAsset.unitInfo; - if (!xc || !xc.assets) { - throw Error(_locales__WEBPACK_IMPORTED_MODULE_20__.prep(_locales__WEBPACK_IMPORTED_MODULE_20__.ID_UNSUPPORTED_ASSET_INFO_ERR_MSG, { assetID: `${assetID}` })); - } - return xc.assets[assetID].unitInfo; - } - parentAsset(assetID) { - const asset = this.assets[assetID]; - if (!asset.token) - return asset; - return this.assets[asset.token.parentID]; - } - /* - * baseChainSymbol returns the symbol for the asset's parent if the asset is a - * token, otherwise the symbol for the asset itself. - */ - baseChainSymbol(assetID) { - const asset = this.user.assets[assetID]; - return asset.token ? this.user.assets[asset.token.parentID].symbol : asset.symbol; - } - /* - * extensionWallet returns the ExtensionConfiguredWallet for the asset, if - * it exists. - */ - extensionWallet(assetID) { - var _a; - return (_a = this.user.extensionModeConfig) === null || _a === void 0 ? void 0 : _a.restrictedWallets[this.baseChainSymbol(assetID)]; - } - /* conventionalRate converts the encoded atomic rate to a conventional rate */ - conventionalRate(baseID, quoteID, encRate, xc) { - const [b, q] = [this.unitInfo(baseID, xc), this.unitInfo(quoteID, xc)]; - const r = b.conventional.conversionFactor / q.conventional.conversionFactor; - return encRate * r / _orderutil__WEBPACK_IMPORTED_MODULE_16__.RateEncodingFactor; - } - walletDefinition(assetID, walletType) { - const asset = this.assets[assetID]; - if (asset.token) - return asset.token.definition; - if (!asset.info) - throw Error('where\'s the wallet info?'); - if (walletType === '') - return asset.info.availablewallets[asset.info.emptyidx]; - return asset.info.availablewallets.filter(def => def.type === walletType)[0]; - } - currentWalletDefinition(assetID) { - const asset = this.assets[assetID]; - if (asset.token) { - return asset.token.definition; - } - return this.walletDefinition(assetID, this.assets[assetID].wallet.type); - } - /* - * fetchBalance requests a balance update from the API. The API response does - * include the balance, but we're ignoring it, since a balance update - * notification is received via the Application anyways. - */ - fetchBalance(assetID) { - return __awaiter(this, void 0, void 0, function* () { - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)('/api/balance', { assetID: assetID }); - if (!this.checkResponse(res)) { - throw new Error(`failed to fetch balance for asset ID ${assetID}`); - } - return res.balance; - }); - } - /* - * checkResponse checks the response object as returned from the functions in - * the http module. If the response indicates that the request failed, it - * returns false, otherwise, true. - */ - checkResponse(resp) { - return (resp.requestSuccessful && resp.ok); - } - /** - * signOut call to /api/logout, if response with no errors occurred remove auth - * and other privacy-critical cookies/locals and reload the page, otherwise - * show a notification. - */ - signOut() { - return __awaiter(this, void 0, void 0, function* () { - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)('/api/logout'); - if (!this.checkResponse(res)) { - if (res.code === _http__WEBPACK_IMPORTED_MODULE_17__.Errors.activeOrdersErr) { - this.page.logoutErr.textContent = _locales__WEBPACK_IMPORTED_MODULE_20__.prep(_locales__WEBPACK_IMPORTED_MODULE_20__.ID_ACTIVE_ORDERS_LOGOUT_ERR_MSG); - } - else { - this.page.logoutErr.textContent = res.msg; - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(this.page.logoutErr); - return; - } - _state__WEBPACK_IMPORTED_MODULE_1__["default"].removeCookie(_state__WEBPACK_IMPORTED_MODULE_1__["default"].authCK); - _state__WEBPACK_IMPORTED_MODULE_1__["default"].removeCookie(_state__WEBPACK_IMPORTED_MODULE_1__["default"].pwKeyCK); - _state__WEBPACK_IMPORTED_MODULE_1__["default"].removeLocal(_state__WEBPACK_IMPORTED_MODULE_1__["default"].notificationsLK); // Notification storage was DEPRECATED pre-v1. - window.location.href = '/login'; - }); - } - /* - * txHistory loads the tx history for an asset. If the results are not - * already cached, they are cached. If we have reached the oldest tx, - * this fact is also cached. If the exact amount of transactions as have been - * made are requested, we will not know if we have reached the last tx until - * a subsequent call. - */ - txHistory(assetID, n, after) { - return __awaiter(this, void 0, void 0, function* () { - const url = '/api/txhistory'; - const cachedTxHistory = this.txHistoryMap[assetID]; - if (!cachedTxHistory) { - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)(url, { - n: n, - assetID: assetID - }); - if (!this.checkResponse(res)) { - throw new Error(res.msg); - } - let txs = res.txs; - if (!txs) { - txs = []; - } - this.txHistoryMap[assetID] = { - txs: txs, - lastTx: txs.length < n - }; - return this.txHistoryMap[assetID]; - } - const txs = []; - let lastTx = false; - const startIndex = after ? cachedTxHistory.txs.findIndex(tx => tx.id === after) + 1 : 0; - if (after && startIndex === -1) { - throw new Error('invalid after tx ' + after); - } - let lastIndex = startIndex; - for (let i = startIndex; i < cachedTxHistory.txs.length && txs.length < n; i++) { - txs.push(cachedTxHistory.txs[i]); - lastIndex = i; - after = cachedTxHistory.txs[i].id; - } - if (cachedTxHistory.lastTx && lastIndex === cachedTxHistory.txs.length - 1) { - lastTx = true; - } - if (txs.length < n && !cachedTxHistory.lastTx) { - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)(url, { - n: n - txs.length + 1, - assetID: assetID, - refID: after, - past: true - }); - if (!this.checkResponse(res)) { - throw new Error(res.msg); - } - let resTxs = res.txs; - if (!resTxs) { - resTxs = []; - } - if (resTxs.length > 0 && after) { - if (resTxs[0].id === after) { - resTxs.shift(); - } - else { - // Implies a bug in the client - console.error('First tx history element != refID'); - } - } - cachedTxHistory.lastTx = resTxs.length < n - txs.length; - lastTx = cachedTxHistory.lastTx; - txs.push(...resTxs); - cachedTxHistory.txs.push(...resTxs); - } - return { txs, lastTx }; - }); - } - getWalletTx(assetID, txID) { - const cachedTxHistory = this.txHistoryMap[assetID]; - if (!cachedTxHistory) - return undefined; - return cachedTxHistory.txs.find(tx => tx.id === txID); - } - clearTxHistory(assetID) { - delete this.txHistoryMap[assetID]; - } - needsCustomProvider(assetID) { - var _a, _b, _c; - return __awaiter(this, void 0, void 0, function* () { - const baseChainID = (_c = (_b = (_a = this.assets[assetID]) === null || _a === void 0 ? void 0 : _a.token) === null || _b === void 0 ? void 0 : _b.parentID) !== null && _c !== void 0 ? _c : assetID; - if (!baseChainID) - return false; - const w = this.walletMap[baseChainID]; - if (!w) - return false; - const traitAccountLocker = 1 << 14; - if ((w.traits & traitAccountLocker) === 0) - return false; - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_17__.postJSON)('/api/walletsettings', { assetID: baseChainID }); - if (!this.checkResponse(res)) { - console.error(res.msg); - return false; - } - const settings = res.map; - return !settings.providers; - }); - } -} -/* getSocketURI returns the websocket URI for the client. */ -function getSocketURI() { - const protocol = (window.location.protocol === 'https:') ? 'wss' : 'ws'; - return `${protocol}://${window.location.host}/ws`; -} -/* - * severityClassMap maps a notification severity level to a CSS class that - * assigns a background color. - */ -const severityClassMap = { - [_notifications__WEBPACK_IMPORTED_MODULE_18__.SUCCESS]: 'good', - [_notifications__WEBPACK_IMPORTED_MODULE_18__.ERROR]: 'bad', - [_notifications__WEBPACK_IMPORTED_MODULE_18__.WARNING]: 'warn' -}; -/* handlerFromPath parses the handler name from the path. */ -function handlerFromPath(path) { - return path.replace(/^\//, '').split('/')[0].split('?')[0].split('#')[0]; -} -/* nowString creates a string formatted like HH:MM:SS.xxx */ -function nowString() { - const stamp = new Date(); - const h = stamp.getHours().toString().padStart(2, '0'); - const m = stamp.getMinutes().toString().padStart(2, '0'); - const s = stamp.getSeconds().toString().padStart(2, '0'); - const ms = stamp.getMilliseconds().toString().padStart(3, '0'); - return `${h}:${m}:${s}.${ms}`; -} -function setSeverityClass(el, severity) { - el.classList.remove('bad', 'warn', 'good'); - el.classList.add(severityClassMap[severity]); -} -/* updateMatch updates the match in or adds the match to the order. */ -function updateMatch(order, match) { - for (const i in order.matches) { - const m = order.matches[i]; - if (m.matchID === match.matchID) { - order.matches[i] = match; - return; - } - } - order.matches = order.matches || []; - order.matches.push(match); -} - - -/***/ }), - -/***/ "./src/js/basepage.ts": -/*!****************************!*\ - !*** ./src/js/basepage.ts ***! - \****************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "default": () => (/* binding */ BasePage) -/* harmony export */ }); -class BasePage { - /* unload is called when the user navigates away from the page. */ - unload() { - // should be implemented by inheriting class. - } -} - - -/***/ }), - -/***/ "./src/js/charts.ts": -/*!**************************!*\ - !*** ./src/js/charts.ts ***! - \**************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "CandleChart": () => (/* binding */ CandleChart), -/* harmony export */ "Chart": () => (/* binding */ Chart), -/* harmony export */ "DepthChart": () => (/* binding */ DepthChart), -/* harmony export */ "Extents": () => (/* binding */ Extents), -/* harmony export */ "Region": () => (/* binding */ Region), -/* harmony export */ "Wave": () => (/* binding */ Wave) -/* harmony export */ }); -/* harmony import */ var _doc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./doc */ "./src/js/doc.ts"); -/* harmony import */ var _orderutil__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./orderutil */ "./src/js/orderutil.ts"); -/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./state */ "./src/js/state.ts"); -/* harmony import */ var _registry__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./registry */ "./src/js/registry.ts"); - - - - -const bind = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind; -const PIPI = 2 * Math.PI; -const plusChar = String.fromCharCode(59914); -const minusChar = String.fromCharCode(59915); -const darkTheme = { - body: '#0b2031', - axisLabel: '#b1b1b1', - gridBorder: '#383f4b', - gridLines: '#383f4b', - gapLine: '#6b6b6b', - value: '#9a9a9a', - zoom: '#5b5b5b', - zoomHover: '#aaa', - sellLine: '#ae3333', - buyLine: '#05a35a', - sellFill: '#591a1a', - buyFill: '#02572f', - crosshairs: '#888', - legendFill: 'black', - legendText: '#d5d5d5' -}; -const lightTheme = { - body: '#f4f4f4', - axisLabel: '#1b1b1b', - gridBorder: '#ddd', - gridLines: '#ddd', - gapLine: '#595959', - value: '#4d4d4d', - zoom: '#777', - zoomHover: '#333', - sellLine: '#99302b', - buyLine: '#207a46', - sellFill: '#bd5959', - buyFill: '#4cad75', - crosshairs: '#595959', - legendFill: '#e6e6e6', - legendText: '#1b1b1b' -}; -// Chart is the base class for charts. -class Chart { - constructor(parent, reporters) { - this.parent = parent; - this.report = reporters; - this.theme = _state__WEBPACK_IMPORTED_MODULE_2__["default"].isDark() ? darkTheme : lightTheme; - this.canvas = document.createElement('canvas'); - this.visible = true; - parent.appendChild(this.canvas); - const ctx = this.canvas.getContext('2d'); - if (!ctx) { - console.error('error getting canvas context'); - return; - } - this.ctx = ctx; - this.ctx.textAlign = 'center'; - this.ctx.textBaseline = 'middle'; - // Mouse handling - this.mousePos = null; - bind(this.canvas, 'mousemove', (e) => { - // this.rect will be set in resize(). - if (!this.rect) - return; - this.mousePos = { - x: e.clientX - this.rect.left, - y: e.clientY - this.rect.y - }; - this.draw(); - }); - bind(this.canvas, 'mouseleave', () => { - this.mousePos = null; - this.draw(); - }); - // Bind resize. - const resizeObserver = new ResizeObserver(() => this.resize()); - resizeObserver.observe(this.parent); - // Scrolling by wheel is smoother when the rate is slightly limited. - this.wheelLimiter = null; - bind(this.canvas, 'wheel', (e) => { this.wheel(e); }, { passive: true }); - bind(this.canvas, 'click', (e) => { this.click(e); }); - const setVis = () => { - this.visible = document.visibilityState !== 'hidden'; - if (this.visible && this.renderScheduled) { - this.renderScheduled = false; - this.draw(); - } - }; - bind(document, 'visibilitychange', setVis); - this.unattachers = [() => { _doc__WEBPACK_IMPORTED_MODULE_0__["default"].unbind(document, 'visibilitychange', setVis); }]; - } - wheeled() { - this.wheelLimiter = window.setTimeout(() => { this.wheelLimiter = null; }, 100); - } - /* clear the canvas. */ - clear() { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - /* draw calls the child class's render method. */ - draw() { - this.render(); - } - /* click is the handler for a click event on the canvas. */ - click(e) { - this.report.click(e); - } - /* wheel is a mousewheel event handler. */ - wheel(e) { - this.zoom(e.deltaY < 0); - } - /* - * resize updates the chart size. The parentHeight is an argument to support - * updating the height programmatically after the caller sets a style.height - * but before the clientHeight has been updated. - */ - resize() { - this.canvas.width = this.parent.clientWidth; - this.canvas.height = this.parent.clientHeight; - const xLblHeight = 30; - const yGuess = 40; // y label width guess. Will be adjusted when drawn. - const plotExtents = new Extents(0, this.canvas.width, 0, this.canvas.height - xLblHeight); - const xLblExtents = new Extents(0, this.canvas.width, this.canvas.height - xLblHeight, this.canvas.height); - const yLblExtents = new Extents(0, yGuess, 0, this.canvas.height - xLblHeight); - this.plotRegion = new Region(this.ctx, plotExtents); - this.xRegion = new Region(this.ctx, xLblExtents); - this.yRegion = new Region(this.ctx, yLblExtents); - // After changing the visibility, this.canvas.getBoundingClientRect will - // return nonsense until a render. - window.requestAnimationFrame(() => { - this.rect = this.canvas.getBoundingClientRect(); - this.report.resize(); - }); - } - /* zoom is called when the user scrolls the mouse wheel on the canvas. */ - zoom(bigger) { - if (this.wheelLimiter) - return; - this.report.zoom(bigger); - } - /* The market handler will call unattach when the markets page is unloaded. */ - unattach() { - for (const u of this.unattachers) - u(); - this.unattachers = []; - } - /* render must be implemented by the child class. */ - render() { - console.error('child class must override render method'); - } - /* applyLabelStyle applies the style used for axis tick labels. */ - applyLabelStyle(fontSize) { - this.ctx.textAlign = 'center'; - this.ctx.textBaseline = 'middle'; - this.ctx.font = `${fontSize !== null && fontSize !== void 0 ? fontSize : '14'}px 'sans', sans-serif`; - this.ctx.fillStyle = this.theme.axisLabel; - } - /* plotXLabels applies the provided labels to the x axis and draws the grid. */ - plotXLabels(labels, minX, maxX, unitLines) { - const extents = new Extents(minX, maxX, 0, 1); - this.xRegion.plot(extents, (ctx, tools) => { - this.applyLabelStyle(); - const centerX = (maxX + minX) / 2; - let lastX = minX; - let unitCenter = centerX; - const [leftEdge, rightEdge] = [tools.x(minX), tools.x(maxX)]; - const centerY = tools.y(0.5); - labels.lbls.forEach(lbl => { - const m = ctx.measureText(lbl.txt); - const x = tools.x(lbl.val); - if (x - m.width / 2 < leftEdge || x + m.width / 2 > rightEdge) - return; - ctx.fillText(lbl.txt, x, centerY); - if (centerX >= lastX && centerX < lbl.val) { - unitCenter = (lastX + lbl.val) / 2; - } - lastX = lbl.val; - }); - ctx.font = '11px \'sans\', sans-serif'; - if (unitLines.length === 2) { - ctx.fillText(unitLines[0], tools.x(unitCenter), tools.y(0.63)); - ctx.fillText(unitLines[1], tools.x(unitCenter), tools.y(0.23)); - } - else if (unitLines.length === 1) { - ctx.fillText(unitLines[0], tools.x(unitCenter), centerY); - } - }, true); - } - plotXGrid(labels, minX, maxX) { - const extents = new Extents(minX, maxX, 0, 1); - this.plotRegion.plot(extents, (ctx, tools) => { - ctx.lineWidth = 1; - ctx.strokeStyle = this.theme.gridLines; - labels.lbls.forEach(lbl => { - line(ctx, tools.x(lbl.val), tools.y(0), tools.x(lbl.val), tools.y(1)); - }); - }, true); - } - /* - * plotYLabels applies the y labels based on the provided plot region, and - * draws the grid. - */ - plotYLabels(labels, minY, maxY, unit) { - const extents = new Extents(0, 1, minY, maxY); - const fillRect = (ctx, x, y, w, h, r) => { - ctx.save(); - ctx.fillStyle = this.theme.body; - ctx.beginPath(); - if (ctx.roundRect) - ctx.roundRect(x, y, w, h, r); // Safari < 16 doesn't support - else - ctx.rect(x, y, w, h); - ctx.fill(); - ctx.restore(); - }; - this.yRegion.plot(extents, (ctx, tools) => { - this.applyLabelStyle(); - this.ctx.textAlign = 'left'; - const centerY = maxY / 2; - let lastY = 0; - let unitCenter = centerY; - const x = tools.x(0); - const [xPad, yPad] = [3, 3]; - labels.lbls.forEach(lbl => { - const y = tools.y(lbl.val); - if (y < tools.y(maxY) + yPad + 7 || y > tools.y(minY) - yPad - 7) - return; - const m = ctx.measureText(lbl.txt); - fillRect(ctx, x, y - 7 - yPad, m.width + xPad * 2, 14 + yPad * 3, 3); - ctx.fillText(lbl.txt, x + xPad, y + 2); - if (centerY >= lastY && centerY < lbl.val) { - unitCenter = (lastY + lbl.val) / 2; - } - lastY = lbl.val; - }); - const m = ctx.measureText(unit); - const y = tools.y(unitCenter); - fillRect(ctx, x, y - yPad - 7, m.width + xPad * 2, 14 + yPad * 2, 3); - ctx.fillText(unit, x + xPad, tools.y(unitCenter)); - }, true); - } - plotYGrid(region, labels, minY, maxY) { - const extents = new Extents(0, 1, minY, maxY); - region.plot(extents, (ctx, tools) => { - ctx.lineWidth = 1; - ctx.strokeStyle = this.theme.gridLines; - labels.lbls.forEach(lbl => { - line(ctx, tools.x(0), tools.y(lbl.val), tools.x(1), tools.y(lbl.val)); - }); - }, true); - } - /* - * doYLabels generates and applies the y-axis labels, based upon the - * provided plot region. - */ - makeYLabels(region, step, unit, valFmt) { - this.applyLabelStyle(); - const yLabels = makeLabels(this.ctx, region.height(), this.dataExtents.y.min, this.dataExtents.y.max, 50, step, unit, valFmt); - // Reassign the width of the y-label column to accommodate the widest text. - const yAxisWidth = (yLabels.widest || 0) + 20; /* x padding */ - this.yRegion.extents.x.max = yAxisWidth; - this.yRegion.extents.y.max = region.extents.y.max; - return yLabels; - } - line(x0, y0, x1, y1, skipStroke) { - line(this.ctx, x0, y0, x1, y1, skipStroke); - } - /* dot draws a circle with the provided context. */ - dot(x, y, color, radius) { - dot(this.ctx, x, y, color, radius); - } -} -/* DepthChart is a javascript Canvas-based depth chart renderer. */ -class DepthChart extends Chart { - constructor(parent, reporters, zoom) { - super(parent, { - resize: () => this.resized(), - click: (e) => this.clicked(e), - zoom: (bigger) => this.zoomed(bigger) - }); - this.reporters = reporters; - this.zoomLevel = zoom; - this.lines = []; - this.markers = { - buys: [], - sells: [] - }; - this.setZoomBttns(); // can't wait for requestAnimationFrame -> resized - this.resize(); - } - // setZoomBttns creates new regions for zoom in and zoom out buttons. It is - // used in initiation of the buttons and resizing. - setZoomBttns() { - this.zoomInBttn = new Region(this.ctx, new Extents(0, 0, 0, 0)); - this.zoomOutBttn = new Region(this.ctx, new Extents(0, 0, 0, 0)); - } - /* resized is called when the window or parent element are resized. */ - resized() { - // The button region extents are set during drawing. - this.setZoomBttns(); - if (this.book) - this.draw(); - } - /* zoomed zooms the current view in or out. bigger=true is zoom in. */ - zoomed(bigger) { - if (!this.zoomLevel) - return; - if (!this.book.buys || !this.book.sells) - return; - this.wheeled(); - // Zoom in to 66%, but out to 150% = 1 / (2/3) so that the same zoom levels - // are hit when reversing direction. - this.zoomLevel *= bigger ? 2 / 3 : 3 / 2; - this.zoomLevel = (0,_doc__WEBPACK_IMPORTED_MODULE_0__.clamp)(this.zoomLevel, 0.005, 2); - this.draw(); - this.reporters.zoom(this.zoomLevel); - } - /* clicked is the canvas 'click' event handler. */ - clicked(e) { - if (!this.dataExtents) - return; - const x = e.clientX - this.rect.left; - const y = e.clientY - this.rect.y; - if (this.zoomInBttn.contains(x, y)) { - this.zoom(true); - return; - } - if (this.zoomOutBttn.contains(x, y)) { - this.zoom(false); - return; - } - const translator = this.plotRegion.translator(this.dataExtents); - this.reporters.click(translator.unx(x)); - } - // set sets the current data set and draws. - set(book, lotSize, rateStepEnc, baseUnitInfo, quoteUnitInfo) { - this.book = book; - this.lotSize = lotSize / baseUnitInfo.conventional.conversionFactor; - this.conventionalRateStep = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].conventionalRateStep(rateStepEnc, baseUnitInfo, quoteUnitInfo); - this.baseUnit = baseUnitInfo.conventional.unit; - this.quoteUnit = quoteUnitInfo.conventional.unit; - if (!this.zoomLevel) { - const [midGap, gapWidth] = this.gap(); - // Default to 5% zoom, but with a minimum of 5 * midGap, but still observing - // the hard cap of 200%. - const minZoom = Math.max(gapWidth / midGap * 5, 0.05); - this.zoomLevel = Math.min(minZoom, 2); - } - this.draw(); - } - /* - * render draws the chart. - * 1. Calculate the data extents and translate the order book data to a - * cumulative form. - * 2. Draw axis ticks and grid, mid-gap line and value, zoom buttons, mouse - * position indicator... - * 4. Tick labels. - * 5. Data. - * 6. Epoch line legend. - * 7. Hover legend. - */ - render() { - // if connection fails it is not possible to get book. - if (!this.book || !this.visible || this.canvas.width === 0) { - this.renderScheduled = true; - return; - } - this.clear(); - // if (!this.book || this.book.empty()) return - const ctx = this.ctx; - const mousePos = this.mousePos; - const buys = this.book.buys; - const sells = this.book.sells; - const [midGap, gapWidth] = this.gap(); - const halfWindow = this.zoomLevel * midGap / 2; - const high = midGap + halfWindow; - const low = midGap - halfWindow; - // Get a sorted copy of the markers list. - const buyMarkers = [...this.markers.buys]; - const sellMarkers = [...this.markers.sells]; - buyMarkers.sort((a, b) => b.rate - a.rate); - sellMarkers.sort((a, b) => a.rate - b.rate); - const markers = []; - const buyDepth = []; - const buyEpoch = []; - const sellDepth = []; - const sellEpoch = []; - const volumeReport = { - buyBase: 0, - buyQuote: 0, - sellBase: 0, - sellQuote: 0 - }; - let sum = 0; - // The epoch line is above the non-epoch region, so the epochSum y value - // must account for non-epoch orders too. - let epochSum = 0; - for (let i = 0; i < buys.length; i++) { - const ord = buys[i]; - epochSum += ord.qty; - if (ord.rate >= low) - buyEpoch.push([ord.rate, epochSum]); - if (ord.epoch) - continue; - sum += ord.qty; - buyDepth.push([ord.rate, sum]); - volumeReport.buyBase += ord.qty; - volumeReport.buyQuote += ord.qty * ord.rate; - while (buyMarkers.length && floatCompare(buyMarkers[0].rate, ord.rate)) { - const mark = buyMarkers.shift(); - if (!mark) - continue; - markers.push({ - rate: mark.rate, - qty: ord.epoch ? epochSum : sum, - sell: ord.sell, - active: mark.active - }); - } - } - const buySum = buyDepth.length ? last(buyDepth)[1] : 0; - buyDepth.push([low, buySum]); - const epochBuySum = buyEpoch.length ? last(buyEpoch)[1] : 0; - buyEpoch.push([low, epochBuySum]); - epochSum = sum = 0; - for (let i = 0; i < sells.length; i++) { - const ord = sells[i]; - epochSum += ord.qty; - if (ord.rate <= high) - sellEpoch.push([ord.rate, epochSum]); - if (ord.epoch) - continue; - sum += ord.qty; - sellDepth.push([ord.rate, sum]); - volumeReport.sellBase += ord.qty; - volumeReport.sellQuote += ord.qty * ord.rate; - while (sellMarkers.length && floatCompare(sellMarkers[0].rate, ord.rate)) { - const mark = sellMarkers.shift(); - if (!mark) - continue; - markers.push({ - rate: mark.rate, - qty: ord.epoch ? epochSum : sum, - sell: ord.sell, - active: mark.active - }); - } - } - // Add a data point going to the left so that the data doesn't end with a - // vertical line. - const sellSum = sellDepth.length ? last(sellDepth)[1] : 0; - sellDepth.push([high, sellSum]); - const epochSellSum = sellEpoch.length ? last(sellEpoch)[1] : 0; - sellEpoch.push([high, epochSellSum]); - // Add ~30px padding to the top of the chart. - const h = this.xRegion.extents.y.min; - const growthFactor = (h + 40) / h; - const maxY = (epochSellSum && epochBuySum ? Math.max(epochBuySum, epochSellSum) : epochSellSum || epochBuySum || 1) * growthFactor; - const dataExtents = new Extents(low, high, 0, maxY); - this.dataExtents = dataExtents; - // A function to be run at the end if there is legend data to display. - let mouseData = null; - // Draw the grid. - const xLabels = makeLabels(ctx, this.plotRegion.width(), dataExtents.x.min, dataExtents.x.max, 100, this.conventionalRateStep, ''); - this.plotXGrid(xLabels, low, high); - const yLabels = this.makeYLabels(this.plotRegion, this.lotSize, this.baseUnit); - this.plotYGrid(this.plotRegion, yLabels, this.dataExtents.y.min, this.dataExtents.y.max); - this.plotRegion.plot(dataExtents, (ctx, tools) => { - ctx.lineWidth = 1; - // first, a square around the plot area. - ctx.strokeStyle = this.theme.gridBorder; - // draw a line to indicate mid-gap - ctx.lineWidth = 2.5; - ctx.strokeStyle = this.theme.gapLine; - line(ctx, tools.x(midGap), tools.y(0), tools.x(midGap), tools.y(0.3 * dataExtents.y.max)); - ctx.font = '30px \'demi-sans\', sans-serif'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.theme.value; - const y = 0.5 * dataExtents.y.max; - ctx.fillText(_doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatFourSigFigs(midGap), tools.x(midGap), tools.y(y)); - ctx.font = '12px \'sans\', sans-serif'; - // ctx.fillText('mid-market price', tools.x(midGap), tools.y(y) + 24) - ctx.fillText(`${(gapWidth / midGap * 100).toFixed(2)}% spread`, tools.x(midGap), tools.y(y) + 24); - // Draw zoom buttons. - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - const topCenterX = this.plotRegion.extents.midX; - const topCenterY = tools.y(maxY * 0.9); - const zoomPct = dataExtents.xRange / midGap * 100; - const zoomText = `${zoomPct.toFixed(1)}%`; - const w = ctx.measureText(zoomText).width; - ctx.font = '13px \'sans\', sans-serif'; - ctx.fillText(zoomText, topCenterX, topCenterY + 1); - // define the region for the zoom button - const bttnSize = 20; - const xPad = 10; - let bttnLeft = topCenterX - w / 2 - xPad - bttnSize; - const bttnTop = topCenterY - bttnSize / 2; - this.zoomOutBttn.setExtents(bttnLeft, bttnLeft + bttnSize, bttnTop, bttnTop + bttnSize); - let hover = mousePos && this.zoomOutBttn.contains(mousePos.x, mousePos.y); - this.zoomOutBttn.plot(new Extents(0, 1, 0, 1), ctx => { - ctx.font = '12px \'icomoon\''; - ctx.fillStyle = this.theme.zoom; - if (hover) { - ctx.fillStyle = this.theme.zoomHover; - ctx.font = '13px \'icomoon\''; - } - ctx.fillText(minusChar, this.zoomOutBttn.extents.midX, this.zoomOutBttn.extents.midY); - }); - bttnLeft = topCenterX + w / 2 + xPad; - this.zoomInBttn.setExtents(bttnLeft, bttnLeft + bttnSize, bttnTop, bttnTop + bttnSize); - hover = mousePos && this.zoomInBttn.contains(mousePos.x, mousePos.y); - this.zoomInBttn.plot(new Extents(0, 1, 0, 1), ctx => { - ctx.font = '12px \'icomoon\''; - ctx.fillStyle = this.theme.zoom; - if (hover) { - ctx.fillStyle = this.theme.zoomHover; - ctx.font = '14px \'icomoon\''; - } - ctx.fillText(plusChar, this.zoomInBttn.extents.midX, this.zoomInBttn.extents.midY); - }); - // Draw a dotted vertical line where the mouse is, and a dot at the level - // of the depth line. - const drawLine = (x, color) => { - if (x > high || x < low) - return; - ctx.save(); - ctx.setLineDash([3, 5]); - ctx.lineWidth = 1.5; - ctx.strokeStyle = color; - line(ctx, tools.x(x), tools.y(0), tools.x(x), tools.y(maxY)); - ctx.restore(); - }; - // for (const line of this.lines || []) { - // drawLine(line.rate, line.color) - // } - const tolerance = (high - low) * 0.005; - const hoverMarkers = []; - for (const marker of markers || []) { - const hovered = (mousePos && withinTolerance(marker.rate, tools.unx(mousePos.x), tolerance)); - if (hovered) - hoverMarkers.push(marker.rate); - ctx.save(); - ctx.lineWidth = (hovered || marker.active) ? 5 : 3; - ctx.strokeStyle = marker.sell ? this.theme.sellLine : this.theme.buyLine; - ctx.fillStyle = marker.sell ? this.theme.sellFill : this.theme.buyFill; - const size = (hovered || marker.active) ? 10 : 8; - ctx.beginPath(); - const tip = { - x: tools.x(marker.rate), - y: tools.y(marker.qty) - 8 - }; - const top = tip.y - (Math.sqrt(3) * size / 2); // cos(30) - ctx.moveTo(tip.x, tip.y); - ctx.lineTo(tip.x - size / 2, top); - ctx.lineTo(tip.x + size / 2, top); - ctx.closePath(); - ctx.stroke(); - ctx.fill(); - ctx.restore(); - } - // If the mouse is in the chart area, draw the crosshairs. - if (!mousePos) - return; - if (!this.plotRegion.contains(mousePos.x, mousePos.y)) - return; - // The mouse is in the plot region. Get the data coordinates and find the - // side and depth for the x value. - const dataX = tools.unx(mousePos.x); - let evalSide = sellDepth; - let trigger = (ptX) => ptX >= dataX; - let dotColor = this.theme.sellLine; - if (dataX < midGap) { - evalSide = buyDepth; - trigger = (ptX) => ptX <= dataX; - dotColor = this.theme.buyLine; - } - let bestDepth = evalSide[0]; - for (let i = 0; i < evalSide.length; i++) { - const pt = evalSide[i]; - if (trigger(pt[0])) - break; - bestDepth = pt; - } - drawLine(dataX, this.theme.crosshairs); - mouseData = { - rate: dataX, - depth: bestDepth[1], - dotColor: dotColor, - hoverMarkers: hoverMarkers - }; - }); - // Draw the epoch lines - ctx.lineWidth = 1.5; - ctx.setLineDash([3, 3]); - // epoch sells - ctx.fillStyle = this.theme.sellFill; - ctx.strokeStyle = this.theme.sellLine; - this.drawDepth(sellEpoch); - // epoch buys - ctx.fillStyle = this.theme.buyFill; - ctx.strokeStyle = this.theme.buyLine; - this.drawDepth(buyEpoch); - // Draw the book depth. - ctx.lineWidth = 2.5; - ctx.setLineDash([]); - // book sells - ctx.fillStyle = this.theme.sellFill; - ctx.strokeStyle = this.theme.sellLine; - this.drawDepth(sellDepth); - // book buys - ctx.fillStyle = this.theme.buyFill; - ctx.strokeStyle = this.theme.buyLine; - this.drawDepth(buyDepth); - this.plotYLabels(yLabels, this.dataExtents.y.min, this.dataExtents.y.max, this.baseUnit); - this.plotXLabels(xLabels, low, high, [`${this.quoteUnit}/`, this.baseUnit]); - // Display the dot at the intersection of the mouse hover line and the depth - // line. This should be drawn after the depths. - if (mouseData) { - this.plotRegion.plot(dataExtents, (ctx, tools) => { - if (!mouseData) - return; // For TypeScript. Duh. - dot(ctx, tools.x(mouseData.rate), tools.y(mouseData.depth), mouseData.dotColor, 5); - }); - } - // Report the book volumes. - this.reporters.volume(volumeReport); - this.reporters.mouse(mouseData); - } - /* drawDepth draws a single side's depth chart data. */ - drawDepth(depth) { - const firstPt = depth[0]; - let x; - this.plotRegion.plot(this.dataExtents, (ctx, tools) => { - const yZero = tools.y(0); - let y = tools.y(firstPt[1]); - ctx.beginPath(); - ctx.moveTo(tools.x(firstPt[0]), tools.y(firstPt[1])); - for (let i = 0; i < depth.length; i++) { - // Set x, but don't set y until we draw the horizontal line. - x = tools.x(depth[i][0]); - ctx.lineTo(x, y); - // If this is past the render edge, quit drawing. - y = tools.y(depth[i][1]); - ctx.lineTo(x, y); - } - ctx.stroke(); - ctx.lineTo(x, yZero); - ctx.lineTo(tools.x(firstPt[0]), yZero); - ctx.closePath(); - ctx.globalAlpha = 0.25; - ctx.fill(); - }); - } - /* returns the mid-gap rate and gap width as a tuple. */ - gap() { - const [b, s] = [this.book.bestGapBuy(), this.book.bestGapSell()]; - if (!b) { - if (!s) - return [1, 0]; - return [s.rate, 0]; - } - else if (!s) - return [b.rate, 0]; - return [(s.rate + b.rate) / 2, s.rate - b.rate]; - } - /* setLines stores the indicator lines to draw. */ - setLines(lines) { - this.lines = lines; - } - /* setMarkers sets the indicator markers to draw. */ - setMarkers(markers) { - this.markers = markers; - } -} -/* CandleChart is a candlestick data renderer. */ -class CandleChart extends Chart { - constructor(parent, reporters) { - super(parent, { - resize: () => this.resized(), - click: ( /* e: MouseEvent */) => { this.clicked(); }, - zoom: (bigger) => this.zoomed(bigger) - }); - this.reporters = reporters; - this.zoomLevel = 1; - this.numToShow = 100; - this.resize(); - } - /* resized is called when the window or parent element are resized. */ - resized() { - const ext = this.plotRegion.extents; - const candleExtents = new Extents(ext.x.min, ext.x.max, ext.y.min, ext.y.min + ext.yRange * 0.85); - this.candleRegion = new Region(this.ctx, candleExtents); - const volumeExtents = new Extents(ext.x.min, ext.x.max, ext.y.min + 0.85 * ext.yRange, ext.y.max); - this.volumeRegion = new Region(this.ctx, volumeExtents); - // Set a delay on the render to prevent lag. - if (this.resizeTimer) - clearTimeout(this.resizeTimer); - this.resizeTimer = window.setTimeout(() => this.draw(), 100); - } - clicked( /* e: MouseEvent */) { - // handle clicks - } - /* zoomed zooms the current view in or out. bigger=true is zoom in. */ - zoomed(bigger) { - // bigger actually means fewer candles -> reduce zoomLevels index. - const idx = this.zoomLevels.indexOf(this.numToShow); - if (bigger) { - if (idx === 0) - return; - this.numToShow = this.zoomLevels[idx - 1]; - } - else { - if (this.zoomLevels.length <= idx + 1 || this.numToShow > this.data.candles.length) - return; - this.numToShow = this.zoomLevels[idx + 1]; - } - this.draw(); - } - /* render draws the chart */ - render() { - var _a; - const data = this.data; - if (!data || !this.visible || this.canvas.width === 0) { - this.renderScheduled = true; - return; - } - const candleWidth = data.ms; - const mousePos = this.mousePos; - const allCandles = data.candles || []; - const n = Math.min(this.numToShow, allCandles.length); - const candles = allCandles.slice(allCandles.length - n); - this.clear(); - // If there are no candles. just don't draw anything. - if (n === 0) - return; - // padding definition and some helper functions to parse candles. - const candleWidthPadding = 0.2; - const start = (c) => truncate(c.endStamp, candleWidth); - const end = (c) => start(c) + candleWidth; - const paddedStart = (c) => start(c) + candleWidthPadding * candleWidth; - const paddedWidth = (1 - 2 * candleWidthPadding) * candleWidth; - const first = candles[0]; - const last = candles[n - 1]; - let [high, low, highVol] = [first.highRate, first.lowRate, first.matchVolume]; - for (const c of candles) { - if (c.highRate > high) - high = c.highRate; - if (c.lowRate < low) - low = c.lowRate; - if (c.matchVolume > highVol) - highVol = c.matchVolume; - } - high += (high - low) * 0.1; // a little padding - const xStart = start(first); - let xEnd = end(last); - xEnd += (xEnd - xStart) * 0.05; // a little padding - // Calculate data extents and store them. They are used to apply labels. - const rateStep = this.market.ratestep; - const dataExtents = new Extents(xStart, xEnd, low, high); - if (low === high) { - // If there is no price movement at all in the window, show a little more - // top and bottom so things render nicely. - dataExtents.y.min -= rateStep; - dataExtents.y.max += rateStep; - } - this.dataExtents = dataExtents; - let mouseCandle = null; - if (mousePos) { - this.plotRegion.plot(new Extents(dataExtents.x.min, dataExtents.x.max, 0, 1), (ctx, tools) => { - const selectedStartStamp = truncate(tools.unx(mousePos.x), candleWidth); - for (const c of candles) { - if (start(c) === selectedStartStamp) { - mouseCandle = c; - ctx.fillStyle = this.theme.gridLines; - ctx.fillRect(tools.x(start(c)), tools.y(0), tools.w(candleWidth), tools.h(1)); - break; - } - } - }); - } - // Draw the grid - const rFactor = this.rateConversionFactor; - const baseUnit = ((_a = (0,_registry__WEBPACK_IMPORTED_MODULE_3__.app)().assets[this.market.baseid]) === null || _a === void 0 ? void 0 : _a.unitInfo.conventional.unit) || this.market.basesymbol.toUpperCase(); - const xLabels = makeCandleTimeLabels(candles, candleWidth, this.plotRegion.width(), 100); - this.plotXGrid(xLabels, xStart, xEnd); - const yLabels = this.makeYLabels(this.candleRegion, rateStep, baseUnit, v => _doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatFourSigFigs(v / rFactor)); - this.plotYGrid(this.candleRegion, yLabels, this.dataExtents.y.min, this.dataExtents.y.max); - // Draw the volume bars. - const volDataExtents = new Extents(xStart, xEnd, 0, highVol); - this.volumeRegion.plot(volDataExtents, (ctx, tools) => { - ctx.fillStyle = this.theme.gridBorder; - for (const c of candles) { - ctx.fillRect(tools.x(paddedStart(c)), tools.y(0), tools.w(paddedWidth), tools.h(c.matchVolume)); - } - }); - // Draw the candles. - this.candleRegion.plot(dataExtents, (ctx, tools) => { - ctx.lineWidth = 1; - for (const c of candles) { - const desc = c.startRate > c.endRate; - const [x, y, w, h] = [tools.x(paddedStart(c)), tools.y(c.startRate), tools.w(paddedWidth), tools.h(c.endRate - c.startRate)]; - const [high, low, cx] = [tools.y(c.highRate), tools.y(c.lowRate), w / 2 + x]; - ctx.strokeStyle = desc ? this.theme.sellLine : this.theme.buyLine; - ctx.fillStyle = desc ? this.theme.sellFill : this.theme.buyFill; - ctx.beginPath(); - ctx.moveTo(cx, high); - ctx.lineTo(cx, low); - ctx.stroke(); - ctx.fillRect(x, y, w, h); - ctx.strokeRect(x, y, w, h); - } - }); - // Apply labels. - this.plotXLabels(xLabels, xStart, xEnd, []); - this.plotYLabels(yLabels, this.dataExtents.y.min, this.dataExtents.y.max, baseUnit); - // Highlight the candle if the user mouse is over the canvas. - if (mouseCandle) { - const yExt = this.xRegion.extents.y; - this.xRegion.plot(new Extents(dataExtents.x.min, dataExtents.x.max, yExt.min, yExt.max), (ctx, tools) => { - if (!mouseCandle) - return; // For TypeScript. Duh. - this.applyLabelStyle(); - const rangeTxt = `${new Date(start(mouseCandle)).toLocaleString()} - ${new Date(end(mouseCandle)).toLocaleString()}`; - const [xPad, yPad] = [25, 2]; - const rangeWidth = ctx.measureText(rangeTxt).width + 2 * xPad; - const rangeHeight = 16; - let centerX = tools.x((start(mouseCandle) + end(mouseCandle)) / 2); - let left = centerX - rangeWidth / 2; - const xExt = this.xRegion.extents.x; - if (left < xExt.min) - left = xExt.min; - else if (left + rangeWidth > xExt.max) - left = xExt.max - rangeWidth; - centerX = left + rangeWidth / 2; - const top = yExt.min + (this.xRegion.height() - rangeHeight) / 2; - ctx.fillStyle = this.theme.legendFill; - ctx.strokeStyle = this.theme.gridBorder; - const rectArgs = [left - xPad, top - yPad, rangeWidth + 2 * xPad, rangeHeight + 2 * yPad]; - ctx.fillRect(...rectArgs); - ctx.strokeRect(...rectArgs); - this.applyLabelStyle(); - ctx.fillText(rangeTxt, centerX, this.xRegion.extents.midY, rangeWidth); - }); - } - // Report the mouse candle. - this.reporters.mouse(mouseCandle); - } - /* setCandles sets the candle data and redraws the chart. */ - setCandles(data, market, baseUnitInfo, quoteUnitInfo) { - this.data = data; - if (!data.candles) - return; - this.market = market; - const [qFactor, bFactor] = [quoteUnitInfo.conventional.conversionFactor, baseUnitInfo.conventional.conversionFactor]; - this.rateConversionFactor = _orderutil__WEBPACK_IMPORTED_MODULE_1__.RateEncodingFactor * qFactor / bFactor; - let n = 25; - this.zoomLevels = []; - const maxCandles = Math.max(data.candles.length, 1000); - while (n < maxCandles) { - this.zoomLevels.push(n); - n *= 2; - } - this.numToShow = 100; - this.draw(); - } -} -/* Wave is a loading animation that displays a colorful line that oscillates */ -class Wave extends Chart { - constructor(parent, opts) { - super(parent, { - resize: () => this.resized(), - click: ( /* e: MouseEvent */) => { }, - zoom: ( /* bigger: boolean */) => { } - }); - this.canvas.classList.add('fill-abs'); - this.canvas.style.zIndex = '5'; - this.opts = opts !== null && opts !== void 0 ? opts : {}; - const period = 1500; // ms - const start = Math.random() * period; - this.colorShift = Math.random() * 360; - // y = A*cos(k*x + theta*t + c) - // combine three waves with different periods and speeds and phases. - const amplitudes = [1, 0.65, 0.75]; - const ks = [3, 3, 2]; - const speeds = [Math.PI, Math.PI * 10 / 9, Math.PI / 2.5]; - const phases = [0, 0, Math.PI * 1.5]; - const n = 75; - const single = (n, angularX, angularTime) => { - return amplitudes[n] * Math.cos(ks[n] * angularX + speeds[n] * angularTime + phases[n]); - }; - const value = (x, angularTime) => { - const angularX = x * Math.PI * 2; - return (single(0, angularX, angularTime) + single(1, angularX, angularTime) + single(2, angularX, angularTime)) / 3; - }; - this.resize(); - this.ani = new _doc__WEBPACK_IMPORTED_MODULE_0__.Animation(_doc__WEBPACK_IMPORTED_MODULE_0__.Animation.Forever, () => { - const angularTime = (new Date().getTime() - start) / period * Math.PI * 2; - const values = []; - for (let i = 0; i < n; i++) { - values.push(value(i / (n - 1), angularTime)); - } - this.drawValues(values); - }); - } - resized() { - const opts = this.opts; - const [maxW, maxH] = [150, 100]; - const [cw, ch] = [this.canvas.width, this.canvas.height]; - let [w, h] = [cw * 0.8, ch * 0.8]; - if (w > maxW) - w = maxW; - if (h > maxH) - h = maxH; - let [l, t] = [(cw - w) / 2, (ch - h) / 2]; - if (opts.message) { - this.fontSize = (0,_doc__WEBPACK_IMPORTED_MODULE_0__.clamp)(h * 0.15, 10, 14); - this.applyLabelStyle(this.fontSize); - const ypad = this.fontSize * 0.5; - const halfH = (this.fontSize / 2) + ypad; - t -= halfH; - this.msgRegion = new Region(this.ctx, new Extents(0, cw, t + h, t + h + 2 * halfH)); - } - this.region = new Region(this.ctx, new Extents(l, l + w, t, t + h)); - } - drawValues(values) { - if (!this.region) - return; - this.clear(); - const hsl = (h) => `hsl(${h}, 35%, 50%)`; - const { region, msgRegion, canvas: { width: w, height: h }, opts: { backgroundColor: bg, message: msg }, colorShift, ctx } = this; - if (bg) { - if (bg === true) - ctx.fillStyle = _state__WEBPACK_IMPORTED_MODULE_2__["default"].isDark() ? '#0a1e34' : '#f0f0f0'; - else - ctx.fillStyle = bg; - ctx.fillRect(0, 0, w, h); - } - region.plot(new Extents(0, 1, -1, 1), (ctx, t) => { - ctx.lineWidth = 4; - ctx.lineCap = 'round'; - const shift = colorShift + (new Date().getTime() % 2000) / 2000 * 360; // colors move with frequency 1 / 2s - const grad = ctx.createLinearGradient(t.x(0), 0, t.x(1), 0); - grad.addColorStop(0, hsl(shift)); - ctx.strokeStyle = grad; - ctx.beginPath(); - ctx.moveTo(t.x(0), t.y(values[0])); - for (let i = 1; i < values.length; i++) { - const prog = i / (values.length - 1); - grad.addColorStop(prog, hsl(prog * 300 + shift)); - ctx.lineTo(t.x(prog), t.y(values[i])); - } - ctx.stroke(); - }); - if (!msg) - return; - msgRegion.plot(new Extents(0, 1, 0, 1), (ctx, t) => { - this.applyLabelStyle(this.fontSize); - ctx.fillText(msg, t.x(0.5), t.y(0.5), this.msgRegion.width()); - }); - } - render() { } - stop() { - this.ani.stop(); - this.canvas.remove(); - } -} -/* - * Extents holds a min and max in both the x and y directions, and provides - * getters for related data. - */ -class Extents { - constructor(xMin, xMax, yMin, yMax) { - this.setExtents(xMin, xMax, yMin, yMax); - } - setExtents(xMin, xMax, yMin, yMax) { - this.x = { - min: xMin, - max: xMax - }; - this.y = { - min: yMin, - max: yMax - }; - } - get xRange() { - return this.x.max - this.x.min; - } - get midX() { - return (this.x.max + this.x.min) / 2; - } - get yRange() { - return this.y.max - this.y.min; - } - get midY() { - return (this.y.max + this.y.min) / 2; - } -} -/* - * Region applies an Extents to the canvas, providing utilities for coordinate - * transformations and restricting drawing to a specified region of the canvas. - */ -class Region { - constructor(context, extents) { - this.context = context; - this.extents = extents; - } - setExtents(xMin, xMax, yMin, yMax) { - this.extents.setExtents(xMin, xMax, yMin, yMax); - } - width() { - return this.extents.xRange; - } - height() { - return this.extents.yRange; - } - contains(x, y) { - const ext = this.extents; - return (x < ext.x.max && x > ext.x.min && - y < ext.y.max && y > ext.y.min); - } - /* - * A translator provides 4 function for coordinate transformations. x and y - * translate data coordinates to canvas coordinates for the specified data - * Extents. unx and uny translate canvas coordinates to data coordinates. - */ - translator(dataExtents) { - const region = this.extents; - const xMin = dataExtents.x.min; - // const xMax = dataExtents.x.max - const yMin = dataExtents.y.min; - // const yMax = dataExtents.y.max - const yRange = dataExtents.yRange; - const xRange = dataExtents.xRange; - const screenMinX = region.x.min; - const screenW = region.x.max - screenMinX; - const screenMaxY = region.y.max; - const screenH = screenMaxY - region.y.min; - const xFactor = screenW / xRange; - const yFactor = screenH / yRange; - return { - x: (x) => (x - xMin) * xFactor + screenMinX, - y: (y) => screenMaxY - (y - yMin) * yFactor, - unx: (x) => (x - screenMinX) / xFactor + xMin, - uny: (y) => yMin - (y - screenMaxY) / yFactor, - w: (w) => w / xRange * screenW, - h: (h) => -h / yRange * screenH - }; - } - /* clear clears the region. */ - clear() { - const ext = this.extents; - this.context.clearRect(ext.x.min, ext.y.min, ext.xRange, ext.yRange); - } - /* plot prepares tools for drawing using data coordinates. */ - plot(dataExtents, drawFunc, skipMask) { - const ctx = this.context; - const region = this.extents; - ctx.save(); // Save the original state - if (!skipMask) { - ctx.beginPath(); - ctx.rect(region.x.min, region.y.min, region.xRange, region.yRange); - ctx.clip(); - } - // The drawFunc will be passed a set of tool that can be used to assist - // drawing. The tools start with the transformation functions. - const tools = this.translator(dataExtents); - // Create a transformation that allows drawing in data coordinates. It's - // not advisable to stroke or add text with this transform in place, as the - // result will be distorted. You can however use ctx.moveTo and ctx.lineTo - // with this transform in place using data coordinates, and remove the - // transform before stroking. The dataCoords method of the supplied tool - // provides this functionality. - // TODO: Figure out why this doesn't work on WebView. - // const yRange = dataExtents.yRange - // const xFactor = region.xRange / dataExtents.xRange - // const yFactor = region.yRange / yRange - // const xMin = dataExtents.x.min - // const yMin = dataExtents.y.min - // // These translation factors are complicated because the (0, 0) of the - // // region is not necessarily the (0, 0) of the canvas. - // const tx = (region.x.min + xMin) - xMin * xFactor - // const ty = -region.y.min - (yRange - yMin) * yFactor - // const setTransform = () => { - // // Data coordinates are flipped about y. Flip the coordinates and - // // translate top left corner to canvas (0, 0). - // ctx.transform(1, 0, 0, -1, -xMin, yMin) - // // Scale to data coordinates and shift into place for the region's offset - // // on the canvas. - // ctx.transform(xFactor, 0, 0, yFactor, tx, ty) - // } - // // dataCoords allows some drawing to be performed directly in data - // // coordinates. Most actual drawing functions like ctx.stroke and - // // ctx.fillRect should not be called from inside dataCoords, but - // // ctx.moveTo and ctx.LineTo are fine. - // tools.dataCoords = f => { - // ctx.save() - // setTransform() - // f() - // ctx.restore() - // } - drawFunc(this.context, tools); - ctx.restore(); - } -} -/* - * makeLabels attempts to create the appropriate labels for the specified - * screen size, context, and label spacing. - */ -function makeLabels(ctx, screenW, min, max, spacingGuess, step, unit, valFmt) { - valFmt = valFmt || _doc__WEBPACK_IMPORTED_MODULE_0__["default"].formatFourSigFigs; - const n = screenW / spacingGuess; - const diff = max - min; - if (n < 1 || diff <= 0) - return { lbls: [] }; - const tickGuess = diff / n; - // make the tick spacing a multiple of the step - const tick = tickGuess + step - (tickGuess % step); - let x = min + tick - (min % tick); - const absMax = Math.max(Math.abs(max), Math.abs(min)); - // The Math.round part is the minimum precision required to see the change in the numbers. - // The 2 accounts for the precision of the tick. - const sigFigs = Math.round(Math.log10(absMax / tick)) + 2; - const pts = []; - let widest = 0; - while (x < max) { - x = Number(x.toPrecision(sigFigs)); - const lbl = valFmt(x); - widest = Math.max(widest, ctx.measureText(lbl).width); - pts.push({ - val: x, - txt: lbl - }); - x += tick; - } - const unitW = ctx.measureText(unit).width; - if (unitW > widest) - widest = unitW; - return { - widest: widest, - lbls: pts - }; -} -const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']; -/* makeCandleTimeLabels prepares labels for candlestick data. */ -function makeCandleTimeLabels(candles, dur, screenW, spacingGuess) { - const first = candles[0]; - const last = candles[candles.length - 1]; - const start = truncate(first.endStamp, dur); - const end = truncate(last.endStamp, dur) + dur; - const diff = end - start; - const n = Math.min(candles.length, screenW / spacingGuess); - const tick = truncate(diff / n, dur); - if (tick === 0) { - console.error('zero tick', dur, diff, n); // probably won't happen, but it'd suck if it did - return { lbls: [] }; - } - let x = start; - const zoneOffset = new Date().getTimezoneOffset(); - const dayStamp = (x) => { - x = x - zoneOffset * 60000; - return x - (x % 86400000); - }; - let lastDay = dayStamp(start); - let lastYear = 0; // new Date(start).getFullYear() - if (dayStamp(first.endStamp) === dayStamp(last.endStamp)) - lastDay = 0; // Force at least one day stamp. - const pts = []; - let label; - if (dur < 86400000) { - label = (d, x) => { - const day = dayStamp(x); - if (day !== lastDay) - return `${months[d.getMonth()]}${d.getDate()} ${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`; - else - return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`; - }; - } - else { - label = (d) => { - const year = d.getFullYear(); - if (year !== lastYear) - return `${months[d.getMonth()]}${d.getDate()} '${String(year).slice(2, 4)}`; - else - return `${months[d.getMonth()]}${d.getDate()}`; - }; - } - while (x <= end) { - const d = new Date(x); - pts.push({ - val: x, - txt: label(d, x) - }); - lastDay = dayStamp(x); - lastYear = d.getFullYear(); - x += tick; - } - return { lbls: pts }; -} -/* The last element of an array. */ -function last(arr) { - return arr[arr.length - 1]; -} -/* line draws a line with the provided context. */ -function line(ctx, x0, y0, x1, y1, skipStroke) { - ctx.beginPath(); - ctx.moveTo(x0, y0); - ctx.lineTo(x1, y1); - if (!skipStroke) - ctx.stroke(); -} -/* dot draws a circle with the provided context. */ -function dot(ctx, x, y, color, radius) { - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(x, y, radius, 0, PIPI); - ctx.fill(); -} -/* floatCompare compares two floats to within a tolerance of 1e-8. */ -function floatCompare(a, b) { - return withinTolerance(a, b, 1e-8); -} -/* - * withinTolerance returns true if the difference between a and b are with - * the specified tolerance. - */ -function withinTolerance(a, b, tolerance) { - return Math.abs(a - b) < Math.abs(tolerance); -} -function truncate(v, w) { - return v - (v % w); -} - - -/***/ }), - -/***/ "./src/js/coinexplorers.ts": -/*!*********************************!*\ - !*** ./src/js/coinexplorers.ts ***! - \*********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "CoinExplorers": () => (/* binding */ CoinExplorers), -/* harmony export */ "Mainnet": () => (/* binding */ Mainnet), -/* harmony export */ "Simnet": () => (/* binding */ Simnet), -/* harmony export */ "Testnet": () => (/* binding */ Testnet), -/* harmony export */ "formatCoinID": () => (/* binding */ formatCoinID), -/* harmony export */ "setCoinHref": () => (/* binding */ setCoinHref) -/* harmony export */ }); -/* harmony import */ var _registry__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./registry */ "./src/js/registry.ts"); -/* harmony import */ var _locales__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./locales */ "./src/js/locales.ts"); - - -const Mainnet = 0; -const Testnet = 1; -const Simnet = 2; -const coinIDTakerFoundMakerRedemption = 'TakerFoundMakerRedemption:'; -/* ethBasedExplorerArg returns the explorer argument for ETH, ERC20 and EVM -Compatible assets and whether the return value is an address. */ -function ethBasedExplorerArg(cid) { - if (cid.startsWith(coinIDTakerFoundMakerRedemption)) - return [cid.substring(coinIDTakerFoundMakerRedemption.length), true]; - else if (cid.length === 42) - return [cid, true]; - else - return [cid, false]; -} -const ethExplorers = { - [Mainnet]: (cid) => { - const [arg, isAddr] = ethBasedExplorerArg(cid); - return isAddr ? `https://etherscan.io/address/${arg}` : `https://etherscan.io/tx/${arg}`; - }, - [Testnet]: (cid) => { - const [arg, isAddr] = ethBasedExplorerArg(cid); - return isAddr ? `https://sepolia.etherscan.io/address/${arg}` : `https://sepolia.etherscan.io/tx/${arg}`; - }, - [Simnet]: (cid) => { - const [arg, isAddr] = ethBasedExplorerArg(cid); - return isAddr ? `https://etherscan.io/address/${arg}` : `https://etherscan.io/tx/${arg}`; - } -}; -const polygonExplorers = { - [Mainnet]: (cid) => { - const [arg, isAddr] = ethBasedExplorerArg(cid); - return isAddr ? `https://polygonscan.com/address/${arg}` : `https://polygonscan.com/tx/${arg}`; - }, - [Testnet]: (cid) => { - const [arg, isAddr] = ethBasedExplorerArg(cid); - return isAddr ? `https://amoy.polygonscan.com/address/${arg}` : `https://amoy.polygonscan.com/tx/${arg}`; - }, - [Simnet]: (cid) => { - const [arg, isAddr] = ethBasedExplorerArg(cid); - return isAddr ? `https://polygonscan.com/address/${arg}` : `https://polygonscan.com/tx/${arg}`; - } -}; -const CoinExplorers = { - 42: { - [Mainnet]: (cid) => { - const [txid, vout] = cid.split(':'); - if (vout !== undefined) - return `https://explorer.dcrdata.org/tx/${txid}/out/${vout}`; - return `https://explorer.dcrdata.org/tx/${txid}`; - }, - [Testnet]: (cid) => { - const [txid, vout] = cid.split(':'); - if (vout !== undefined) - return `https://testnet.dcrdata.org/tx/${txid}/out/${vout}`; - return `https://testnet.dcrdata.org/tx/${txid}`; - }, - [Simnet]: (cid) => { - const [txid, vout] = cid.split(':'); - if (vout !== undefined) - return `http://127.0.0.1:17779/tx/${txid}/out/${vout}`; - return `https://127.0.0.1:17779/tx/${txid}`; - } - }, - 0: { - [Mainnet]: (cid) => `https://mempool.space/tx/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://mempool.space/testnet/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://mempool.space/tx/${cid.split(':')[0]}` - }, - 2: { - [Mainnet]: (cid) => `https://ltc.bitaps.com/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://sochain.com/tx/LTCTEST/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://ltc.bitaps.com/${cid.split(':')[0]}` - }, - 20: { - [Mainnet]: (cid) => `https://digiexplorer.info/tx/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://testnetexplorer.digibyteservers.io/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://digiexplorer.info/tx/${cid.split(':')[0]}` - }, - 60: ethExplorers, - 60001: ethExplorers, - 60002: ethExplorers, - 3: { - [Mainnet]: (cid) => `https://dogeblocks.com/tx/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://blockexplorer.one/dogecoin/testnet/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://dogeblocks.com/tx/${cid.split(':')[0]}` - }, - 5: { - [Mainnet]: (cid) => `https://blockexplorer.one/dash/mainnet/tx/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://blockexplorer.one/dash/testnet/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://blockexplorer.one/dash/mainnet/tx/${cid.split(':')[0]}` - }, - 133: { - [Mainnet]: (cid) => `https://zcashblockexplorer.com/transactions/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://blockexplorer.one/zcash/testnet/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://zcashblockexplorer.com/transactions/${cid.split(':')[0]}` - }, - 147: { - [Mainnet]: (cid) => `https://explorer.zcl.zelcore.io/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://explorer.zcl.zelcore.io/tx/${cid.split(':')[0]}` - }, - 136: { - [Mainnet]: (cid) => `https://explorer.firo.org/tx/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://testexplorer.firo.org/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://explorer.firo.org/tx/${cid.split(':')[0]}` - }, - 145: { - [Mainnet]: (cid) => `https://bch.loping.net/tx/${cid.split(':')[0]}`, - [Testnet]: (cid) => `https://tbch4.loping.net/tx/${cid.split(':')[0]}`, - [Simnet]: (cid) => `https://bch.loping.net/tx/${cid.split(':')[0]}` - }, - 966: polygonExplorers, - 966001: polygonExplorers, - 966002: polygonExplorers, - 966003: polygonExplorers, - 966004: polygonExplorers -}; -function formatCoinID(cid) { - if (cid.startsWith(coinIDTakerFoundMakerRedemption)) { - const makerAddr = cid.substring(coinIDTakerFoundMakerRedemption.length); - return _locales__WEBPACK_IMPORTED_MODULE_1__.prep(_locales__WEBPACK_IMPORTED_MODULE_1__.ID_TAKER_FOUND_MAKER_REDEMPTION, { makerAddr: makerAddr }); - } - return cid; -} -/* - * baseChainID returns the asset ID for the asset's parent if the asset is a - * token, otherwise the ID for the asset itself. - */ -function baseChainID(assetID) { - const asset = (0,_registry__WEBPACK_IMPORTED_MODULE_0__.app)().user.assets[assetID]; - return asset.token ? asset.token.parentID : assetID; -} -/* - * setCoinHref sets the hyperlink element's href attribute based on provided - * assetID and data-explorer-coin value present on supplied link element. - */ -function setCoinHref(assetID, link) { - const net = (0,_registry__WEBPACK_IMPORTED_MODULE_0__.app)().user.net; - const assetExplorer = CoinExplorers[baseChainID(assetID)]; - if (!assetExplorer) - return; - const formatter = assetExplorer[net]; - if (!formatter) - return; - link.classList.remove('plainlink'); - link.classList.add('subtlelink'); - link.href = formatter(link.dataset.explorerCoin || ''); -} - - -/***/ }), - -/***/ "./src/js/dexsettings.ts": -/*!*******************************!*\ - !*** ./src/js/dexsettings.ts ***! - \*******************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "default": () => (/* binding */ DexSettingsPage) -/* harmony export */ }); -/* harmony import */ var _doc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./doc */ "./src/js/doc.ts"); -/* harmony import */ var _basepage__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basepage */ "./src/js/basepage.ts"); -/* harmony import */ var _http__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./http */ "./src/js/http.ts"); -/* harmony import */ var _forms__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./forms */ "./src/js/forms.ts"); -/* harmony import */ var _locales__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./locales */ "./src/js/locales.ts"); -/* harmony import */ var _account__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./account */ "./src/js/account.ts"); -/* harmony import */ var _registry__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./registry */ "./src/js/registry.ts"); -var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; - - - - - - - -const animationLength = 300; -class DexSettingsPage extends _basepage__WEBPACK_IMPORTED_MODULE_1__["default"] { - constructor(body) { - super(); - this.body = body; - const host = this.host = body.dataset.host ? body.dataset.host : ''; - const xc = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[host]; - const page = this.page = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].idDescendants(body); - this.forms = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(page.forms, ':scope > form'); - this.confirmRegisterForm = new _forms__WEBPACK_IMPORTED_MODULE_3__.ConfirmRegistrationForm(page.confirmRegForm, () => __awaiter(this, void 0, void 0, function* () { - this.showSuccess(_locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_TRADING_TIER_UPDATED)); - this.renewToggle.setState(this.confirmRegisterForm.tier > 0); - yield (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().fetchUser(); - (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().updateMenuItemsDisplay(); - }), () => { - this.runAnimation(this.regAssetForm, page.regAssetForm); - }); - this.confirmRegisterForm.setExchange(xc, ''); - this.walletWaitForm = new _forms__WEBPACK_IMPORTED_MODULE_3__.WalletWaitForm(page.walletWait, () => { - this.runAnimation(this.confirmRegisterForm, page.confirmRegForm); - }, () => { - this.runAnimation(this.regAssetForm, page.regAssetForm); - }); - this.walletWaitForm.setExchange(xc); - this.newWalletForm = new _forms__WEBPACK_IMPORTED_MODULE_3__.NewWalletForm(page.newWalletForm, assetID => this.newWalletCreated(assetID, this.confirmRegisterForm.tier), () => this.runAnimation(this.regAssetForm, page.regAssetForm)); - this.regAssetForm = new _forms__WEBPACK_IMPORTED_MODULE_3__.FeeAssetSelectionForm(page.regAssetForm, (assetID, tier) => __awaiter(this, void 0, void 0, function* () { - if (assetID === _registry__WEBPACK_IMPORTED_MODULE_6__.PrepaidBondID) { - yield (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().fetchUser(); - this.updateReputation(); - this.showSuccess(_locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_TRADING_TIER_UPDATED)); - return; - } - const asset = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().assets[assetID]; - const wallet = asset.wallet; - if (wallet) { - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(page.regAssetForm); - const bondsFeeBuffer = yield this.getBondsFeeBuffer(assetID, page.regAssetForm); - this.confirmRegisterForm.setAsset(assetID, tier, bondsFeeBuffer); - loaded(); - this.progressTierFormsWithWallet(assetID, wallet); - return; - } - this.confirmRegisterForm.setAsset(assetID, tier, 0); - this.newWalletForm.setAsset(assetID); - this.showForm(page.newWalletForm); - })); - this.regAssetForm.setExchange(xc, ''); - this.reputationMeter = new _account__WEBPACK_IMPORTED_MODULE_5__.ReputationMeter(page.repMeter); - this.reputationMeter.setHost(host); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.exportDexBtn, 'click', () => this.exportAccount()); - this.accountDisabled = body.dataset.disabled === 'true'; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.toggleAccountStatusBtn, 'click', () => { - if (!this.accountDisabled) - this.prepareAccountDisable(page.disableAccountForm); - else - this.toggleAccountStatus(false); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.updateCertBtn, 'click', () => page.certFileInput.click()); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.updateHostBtn, 'click', () => this.prepareUpdateHost()); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.certFileInput, 'change', () => this.onCertFileChange()); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.goBackToSettings, 'click', () => (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loadPage('settings')); - const showTierForm = () => { - this.regAssetForm.setExchange((0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[host], ''); // reset form - this.showForm(page.regAssetForm); - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.changeTier, 'click', () => { showTierForm(); }); - const willAutoRenew = xc.auth.targetTier > 0; - this.renewToggle = new _doc__WEBPACK_IMPORTED_MODULE_0__.AniToggle(page.toggleAutoRenew, page.renewErr, willAutoRenew, (newState) => __awaiter(this, void 0, void 0, function* () { - if (this.accountDisabled) - return; - if (newState) - showTierForm(); - else - return this.disableAutoRenew(); - })); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.autoRenewBox, 'click', (e) => { - e.stopPropagation(); - if (!this.accountDisabled) - page.toggleAutoRenew.click(); - }); - page.penaltyComps.textContent = String(xc.auth.penaltyComps); - const hideCompInput = () => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.penaltyCompInput); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.penaltyComps); - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.penaltyCompBox, 'click', (e) => { - e.stopPropagation(); - const xc = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[this.host]; - page.penaltyCompInput.value = String(xc.auth.penaltyComps); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.penaltyComps); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.penaltyCompInput); - page.penaltyCompInput.focus(); - const checkClick = (e) => { - if (_doc__WEBPACK_IMPORTED_MODULE_0__["default"].mouseInElement(e, page.penaltyCompBox)) - return; - hideCompInput(); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].unbind(document, 'click', checkClick); - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(document, 'click', checkClick); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.penaltyCompInput, 'keyup', (e) => __awaiter(this, void 0, void 0, function* () { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.penaltyCompsErr); - if (e.key === 'Escape') { - hideCompInput(); - return; - } - if (!(e.key === 'Enter')) - return; - const penaltyComps = parseInt(page.penaltyCompInput.value || ''); - if (isNaN(penaltyComps)) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.penaltyCompsErr); - page.penaltyCompsErr.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_INVALID_COMPS_VALUE); - return; - } - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(page.otherBondSettings); - try { - yield this.updateBondOptions({ penaltyComps }); - loaded(); - page.penaltyComps.textContent = String(penaltyComps); - } - catch (e) { - loaded(); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.penaltyCompsErr); - page.penaltyCompsErr.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_API_ERROR, { msg: e.msg }); - } - hideCompInput(); - })); - this.dexAddrForm = new _forms__WEBPACK_IMPORTED_MODULE_3__.DEXAddressForm(page.dexAddrForm, (xc) => __awaiter(this, void 0, void 0, function* () { - (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loadPage(`/dexsettings/${xc.host}`); - }), this.host); - // forms.bind(page.bondDetailsForm, page.updateBondOptionsConfirm, () => this.updateBondOptions()) - _forms__WEBPACK_IMPORTED_MODULE_3__.bind(page.disableAccountForm, page.disableAccountConfirm, () => this.toggleAccountStatus(true)); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.forms, 'mousedown', (e) => { - if (!_doc__WEBPACK_IMPORTED_MODULE_0__["default"].mouseInElement(e, this.currentForm)) { - this.closePopups(); - } - }); - this.keyup = (e) => { - if (e.key === 'Escape') { - this.closePopups(); - } - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(document, 'keyup', this.keyup); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].applySelector(page.forms, '.form-closer').forEach(el => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(el, 'click', () => { this.closePopups(); }); - }); - (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().registerNoteFeeder({ - conn: () => { this.setConnectionStatus(); }, - reputation: () => { this.updateReputation(); }, - feepayment: () => { this.updateReputation(); }, - bondpost: () => { this.updateReputation(); } - }); - this.setConnectionStatus(); - this.updateReputation(); - } - unload() { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].unbind(document, 'keyup', this.keyup); - } - progressTierFormsWithWallet(assetID, wallet) { - return __awaiter(this, void 0, void 0, function* () { - const { page, confirmRegisterForm: { fees } } = this; - const asset = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().assets[assetID]; - const { bondAssets } = this.regAssetForm.xc; - const bondAsset = bondAssets[asset.symbol]; - if (!wallet.open) { - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(page.forms); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_2__.postJSON)('/api/openwallet', { assetID: assetID }); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - this.regAssetForm.setAssetError(`error unlocking wallet: ${res.msg}`); - this.runAnimation(this.regAssetForm, page.regAssetForm); - } - return; - } - if (wallet.synced && wallet.balance.available >= 2 * bondAsset.amount + fees) { - // If we are raising our tier, we'll show a confirmation form - this.progressTierFormWithSyncedFundedWallet(assetID); - return; - } - this.walletWaitForm.setWallet(assetID, fees, this.confirmRegisterForm.tier); - this.showForm(page.walletWait); - }); - } - progressTierFormWithSyncedFundedWallet(bondAssetID) { - return __awaiter(this, void 0, void 0, function* () { - const xc = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[this.host]; - const targetTier = this.confirmRegisterForm.tier; - const page = this.page; - const strongTier = xc.auth.liveStrength + xc.auth.pendingStrength - xc.auth.weakStrength; - if (targetTier > xc.auth.targetTier && targetTier > strongTier) { - this.runAnimation(this.confirmRegisterForm, page.confirmRegForm); - return; - } - // Lowering tier - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.body); - try { - yield this.updateBondOptions({ bondAssetID, targetTier }); - loaded(); - } - catch (e) { - loaded(); - this.regAssetForm.setTierError(e.msg); - return; - } - // this.animateConfirmForm(page.regAssetForm) - this.showSuccess(_locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_TRADING_TIER_UPDATED)); - }); - } - updateReputation() { - const page = this.page; - const auth = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[this.host].auth; - const { rep: { penalties }, targetTier, expiredBonds } = auth; - const displayTier = (0,_account__WEBPACK_IMPORTED_MODULE_5__.strongTier)(auth); - page.targetTier.textContent = String(targetTier); - page.effectiveTier.textContent = String(displayTier); - page.penalties.textContent = String(penalties); - page.bondsPendingRefund.textContent = `${(expiredBonds === null || expiredBonds === void 0 ? void 0 : expiredBonds.length) || 0}`; - this.reputationMeter.update(); - } - /* showForm shows a modal form with a little animation. */ - showForm(form) { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - this.currentForm = form; - this.forms.forEach(form => _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(form)); - form.style.right = '10000px'; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.forms, form); - const shift = (page.forms.offsetWidth + form.offsetWidth) / 2; - yield _doc__WEBPACK_IMPORTED_MODULE_0__["default"].animate(animationLength, progress => { - form.style.right = `${(1 - progress) * shift}px`; - }, 'easeOutHard'); - form.style.right = '0'; - }); - } - runAnimation(ani, form) { - return __awaiter(this, void 0, void 0, function* () { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.currentForm); - yield ani.animate(); - this.currentForm = form; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(form); - }); - } - closePopups() { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.page.forms); - if (this.animation) - this.animation.stop(); - } - showSuccess(msg) { - return __awaiter(this, void 0, void 0, function* () { - this.forms.forEach(form => _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(form)); - this.currentForm = this.page.checkmarkForm; - this.animation = _forms__WEBPACK_IMPORTED_MODULE_3__.showSuccess(this.page, msg); - yield this.animation.wait(); - this.animation = new _doc__WEBPACK_IMPORTED_MODULE_0__.Animation(1500, () => { }, '', () => { - if (this.currentForm === this.page.checkmarkForm) - this.closePopups(); - }); - }); - } - // exportAccount exports and downloads the account info. - exportAccount() { - return __awaiter(this, void 0, void 0, function* () { - const { page, host } = this; - const req = { host }; - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.body); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_2__.postJSON)('/api/exportaccount', req); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - page.exportAccountErr.textContent = res.msg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.exportAccountErr); - return; - } - res.account.bonds = res.bonds; // maintain backward compat of JSON file - const accountForExport = JSON.parse(JSON.stringify(res.account)); - const a = document.createElement('a'); - a.setAttribute('download', 'dcrAccount-' + host + '.json'); - a.setAttribute('href', 'data:text/json,' + JSON.stringify(accountForExport, null, 2)); - a.click(); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.forms); - }); - } - // toggleAccountStatus enables or disables the account associated with the - // provided host. - toggleAccountStatus(disable) { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.errMsg); - let host = this.host; - if (disable) - host = page.disableAccountHost.textContent; - const req = { host, disable: disable }; - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.body); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_2__.postJSON)('/api/toggleaccountstatus', req); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - if (disable) { - page.disableAccountErr.textContent = res.msg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.disableAccountErr); - } - else { - page.errMsg.textContent = res.msg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.errMsg); - } - return; - } - if (disable) { - this.page.toggleAccountStatusBtn.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_ENABLE_ACCOUNT); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.forms); - } - else - this.page.toggleAccountStatusBtn.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_DISABLE_ACCOUNT); - this.accountDisabled = disable; - (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loadPage(`dexsettings/${host}`); - }); - } - prepareAccountDisable(disableAccountForm) { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - page.disableAccountHost.textContent = this.host; - page.disableAccountErr.textContent = ''; - this.showForm(disableAccountForm); - }); - } - // Retrieve an estimate for the tx fee needed to create new bond reserves. - getBondsFeeBuffer(assetID, form) { - return __awaiter(this, void 0, void 0, function* () { - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(form); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_2__.postJSON)('/api/bondsfeebuffer', { assetID }); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - return 0; - } - return res.feeBuffer; - }); - } - prepareUpdateHost() { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - this.dexAddrForm.refresh(); - this.showForm(page.dexAddrForm); - }); - } - onCertFileChange() { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.errMsg); - const files = page.certFileInput.files; - let cert; - if (files && files.length) - cert = yield files[0].text(); - if (!cert) - return; - const req = { host: this.host, cert: cert }; - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.body); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_2__.postJSON)('/api/updatecert', req); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - page.errMsg.textContent = res.msg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.errMsg); - } - else { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.updateCertMsg); - setTimeout(() => { _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.updateCertMsg); }, 5000); - } - }); - } - setConnectionStatus() { - const page = this.page; - const exchange = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().user.exchanges[this.host]; - const displayIcons = (connected) => { - if (connected) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.disconnectedIcon); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.connectedIcon); - } - else { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.disconnectedIcon); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.connectedIcon); - } - }; - if (exchange) { - switch (exchange.connectionStatus) { - case _registry__WEBPACK_IMPORTED_MODULE_6__.ConnectionStatus.Connected: - displayIcons(true); - page.connectionStatus.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_CONNECTED); - break; - case _registry__WEBPACK_IMPORTED_MODULE_6__.ConnectionStatus.Disconnected: - displayIcons(false); - if (this.accountDisabled) - page.connectionStatus.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_ACCOUNT_DISABLED_MSG); - else - page.connectionStatus.textContent = _locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_DISCONNECTED); - break; - case _registry__WEBPACK_IMPORTED_MODULE_6__.ConnectionStatus.InvalidCert: - displayIcons(false); - page.connectionStatus.textContent = `${_locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_DISCONNECTED)} - ${_locales__WEBPACK_IMPORTED_MODULE_4__.prep(_locales__WEBPACK_IMPORTED_MODULE_4__.ID_INVALID_CERTIFICATE)}`; - } - } - } - disableAutoRenew() { - return __awaiter(this, void 0, void 0, function* () { - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.page.otherBondSettings); - try { - this.updateBondOptions({ targetTier: 0 }); - loaded(); - } - catch (e) { - loaded(); - throw e; - } - }); - } - /* - * updateBondOptions is called when the form to update bond options is - * submitted. - */ - updateBondOptions(conf) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - conf.host = this.host; - yield (0,_http__WEBPACK_IMPORTED_MODULE_2__.postJSON)('/api/updatebondoptions', conf); - const targetTier = (_a = conf.targetTier) !== null && _a !== void 0 ? _a : (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[this.host].auth.targetTier; - this.renewToggle.setState(targetTier > 0); - }); - } - newWalletCreated(assetID, tier) { - return __awaiter(this, void 0, void 0, function* () { - this.regAssetForm.refresh(); - const user = yield (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().fetchUser(); - if (!user) - return; - const page = this.page; - const asset = user.assets[assetID]; - const wallet = asset.wallet; - const xc = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().exchanges[this.host]; - const bondAmt = xc.bondAssets[asset.symbol].amount; - const bondsFeeBuffer = yield this.getBondsFeeBuffer(assetID, page.newWalletForm); - this.confirmRegisterForm.setFees(assetID, bondsFeeBuffer); - if (wallet.synced && wallet.balance.available >= 2 * bondAmt + bondsFeeBuffer) { - this.progressTierFormWithSyncedFundedWallet(assetID); - return; - } - this.walletWaitForm.setWallet(assetID, bondsFeeBuffer, tier); - yield this.showForm(page.walletWait); - }); - } -} - - -/***/ }), - -/***/ "./src/js/doc.ts": -/*!***********************!*\ - !*** ./src/js/doc.ts ***! - \***********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "AniToggle": () => (/* binding */ AniToggle), -/* harmony export */ "Animation": () => (/* binding */ Animation), -/* harmony export */ "Easing": () => (/* binding */ Easing), -/* harmony export */ "IncrementalInput": () => (/* binding */ IncrementalInput), -/* harmony export */ "MiniSlider": () => (/* binding */ MiniSlider), -/* harmony export */ "NumberInput": () => (/* binding */ NumberInput), -/* harmony export */ "WalletIcons": () => (/* binding */ WalletIcons), -/* harmony export */ "clamp": () => (/* binding */ clamp), -/* harmony export */ "default": () => (/* binding */ Doc), -/* harmony export */ "parseFloatDefault": () => (/* binding */ parseFloatDefault), -/* harmony export */ "setupCopyBtn": () => (/* binding */ setupCopyBtn), -/* harmony export */ "toFourSigFigs": () => (/* binding */ toFourSigFigs), -/* harmony export */ "toPrecision": () => (/* binding */ toPrecision) -/* harmony export */ }); -/* harmony import */ var _locales__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locales */ "./src/js/locales.ts"); -/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./state */ "./src/js/state.ts"); -var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; - - -const parser = new window.DOMParser(); -const FPS = 30; -const BipIDs = { - 0: 'btc', - 42: 'dcr', - 2: 'ltc', - 5: 'dash', - 20: 'dgb', - 22: 'mona', - 28: 'vtc', - 3: 'doge', - 145: 'bch', - 60: 'eth', - 60001: 'usdc.eth', - 60002: 'usdt.eth', - 60003: 'matic.eth', - 136: 'firo', - 133: 'zec', - 966: 'polygon', - 966001: 'usdc.polygon', - 966002: 'weth.polygon', - 966003: 'wbtc.polygon', - 966004: 'usdt.polygon', - 147: 'zcl' -}; -const BipSymbolIDs = {}; -(function () { - for (const k of Object.keys(BipIDs)) { - BipSymbolIDs[BipIDs[parseInt(k)]] = parseInt(k); - } -})(); -const BipSymbols = Object.values(BipIDs); -const RateEncodingFactor = 1e8; // same as value defined in ./orderutil -const log10RateEncodingFactor = Math.round(Math.log10(RateEncodingFactor)); -const languages = navigator.languages.filter((locale) => locale !== 'c'); -const intFormatter = new Intl.NumberFormat(languages, { maximumFractionDigits: 0 }); -const fourSigFigs = new Intl.NumberFormat(languages, { - minimumSignificantDigits: 4, - maximumSignificantDigits: 4 -}); -/* A cache for formatters used for Doc.formatCoinValue. */ -const decimalFormatters = {}; -/* - * decimalFormatter gets the formatCoinValue formatter for the specified decimal - * precision. - */ -function decimalFormatter(prec) { - return formatter(decimalFormatters, 2, prec); -} -/* A cache for formatters used for Doc.formatFullPrecision. */ -const fullPrecisionFormatters = {}; -/* - * fullPrecisionFormatter gets the formatFullPrecision formatter for the - * specified decimal precision. - */ -function fullPrecisionFormatter(prec, locales) { - return formatter(fullPrecisionFormatters, prec, prec, locales); -} -/* - * formatter gets the formatter from the supplied cache if it already exists, - * else creates it. - */ -function formatter(formatters, min, max, locales) { - const k = `${min}-${max}`; - let fmt = formatters[k]; - if (!fmt) { - fmt = new Intl.NumberFormat(locales !== null && locales !== void 0 ? locales : languages, { - minimumFractionDigits: min, - maximumFractionDigits: max - }); - formatters[k] = fmt; - } - return fmt; -} -/* - * convertToConventional converts the value in atomic units to conventional - * units. - */ -function convertToConventional(v, unitInfo) { - let prec = 8; - if (unitInfo) { - const f = unitInfo.conventional.conversionFactor; - v /= f; - prec = Math.round(Math.log10(f)); - } - return [v, prec]; -} -/* - * bestDisplayOrder is used in bestConversion, and is the order of magnitude - * that is considered the best for display. For example, if bestDisplayOrder is - * 1, and the choices for display are 1,000 BTC or 0.00001 Sats, the algorithm - * will look at the orders of the conversions, 1000 => 10^3 => order 3, and - * 0.00001 => 10^-5 => order 5, and see which is closest to bestDisplayOrder and - * choose that conversion. In the example, 3 - bestDisplayOrder = 2 and - * 1 - (-5) = 6, so the conversion that has the order closest to - * bestDisplayOrder is the first one, 1,000 BTC. - */ -const bestDisplayOrder = 1; // 10^1 => 1 -/* - * resolveUnitConversions creates a lookup object mapping unit -> conversion - * factor. By default, resolveUnitConversions only maps the atomic and - * conventional units. If a prefs dict is provided, additional units can be - * included. - */ -function resolveUnitConversions(ui, prefs) { - const unitFactors = { - [ui.atomicUnit]: 1, - [ui.conventional.unit]: ui.conventional.conversionFactor - }; - if (ui.denominations && prefs) { - for (const alt of ui.denominations) - if (prefs[alt.unit]) - unitFactors[alt.unit] = alt.conversionFactor; - } - return unitFactors; -} -// Helpers for working with the DOM. -class Doc { - /* - * idel is the element with the specified id that is the descendent of the - * specified node. - */ - static idel(el, id) { - return el.querySelector(`#${id}`); - } - /* bind binds the function to the event for the element. */ - static bind(el, ev, f, opts /* EventListenerOptions */) { - for (const e of (Array.isArray(ev) ? ev : [ev])) - el.addEventListener(e, f, opts); - } - /* unbind removes the handler for the event from the element. */ - static unbind(el, ev, f) { - el.removeEventListener(ev, f); - } - /* noderize creates a Document object from a string of HTML. */ - static noderize(html) { - return parser.parseFromString(html, 'text/html'); - } - /* - * mouseInElement returns true if the position of mouse event, e, is within - * the bounds of the specified element or any of its descendents. - */ - static mouseInElement(e, el) { - if (el.contains(e.target)) - return true; - const rect = el.getBoundingClientRect(); - return e.pageX >= rect.left && e.pageX <= rect.right && - e.pageY >= rect.top && e.pageY <= rect.bottom; - } - /* - * layoutMetrics gets information about the elements position on the page. - */ - static layoutMetrics(el) { - const box = el.getBoundingClientRect(); - const docEl = document.documentElement; - const top = box.top + docEl.scrollTop; - const left = box.left + docEl.scrollLeft; - const w = el.offsetWidth; - const h = el.offsetHeight; - return { - bodyTop: top, - bodyLeft: left, - width: w, - height: h, - centerX: left + w / 2, - centerY: top + h / 2 - }; - } - static descendentMetrics(parent, kid) { - const parentMetrics = Doc.layoutMetrics(parent); - const kidMetrics = Doc.layoutMetrics(kid); - return { - bodyTop: kidMetrics.bodyTop - parentMetrics.bodyTop, - bodyLeft: kidMetrics.bodyLeft - parentMetrics.bodyLeft, - width: kidMetrics.width, - height: kidMetrics.height, - centerX: kidMetrics.centerX - parentMetrics.bodyLeft, - centerY: kidMetrics.centerY - parentMetrics.bodyTop - }; - } - /* empty removes all child nodes from the specified element. */ - static empty(...els) { - for (const el of els) - while (el.firstChild) - el.removeChild(el.firstChild); - } - /* - * setContent removes all child nodes from the specified element and appends - * passed elements. - */ - static setContent(ancestor, ...kids) { - Doc.empty(ancestor); - for (const k of kids) - ancestor.appendChild(k); - } - /* - * hide hides the specified elements. This is accomplished by adding the - * bootstrap d-hide class to the element. Use Doc.show to undo. - */ - static hide(...els) { - for (const el of els) - el.classList.add('d-hide'); - } - /* - * show shows the specified elements. This is accomplished by removing the - * bootstrap d-hide class as added with Doc.hide. - */ - static show(...els) { - for (const el of els) - el.classList.remove('d-hide'); - } - /* - * show or hide the specified elements, based on value of the truthiness of - * vis. - */ - static setVis(vis, ...els) { - if (vis) - Doc.show(...els); - else - Doc.hide(...els); - } - /* isHidden returns true if the specified element is hidden */ - static isHidden(el) { - return el.classList.contains('d-hide'); - } - /* isDisplayed returns true if the specified element is not hidden */ - static isDisplayed(el) { - return !el.classList.contains('d-hide'); - } - /* - * animate runs the supplied function, which should be a "progress" function - * accepting one argument. The progress function will be called repeatedly - * with the argument varying from 0.0 to 1.0. The exact path that animate - * takes from 0.0 to 1.0 will vary depending on the choice of easing - * algorithm. See the Easing object for the available easing algo choices. The - * default easing algorithm is linear. - */ - static animate(duration, f, easingAlgo) { - return __awaiter(this, void 0, void 0, function* () { - yield new Animation(duration, f, easingAlgo).wait(); - }); - } - static blink(el) { - return __awaiter(this, void 0, void 0, function* () { - const [r, g, b] = _state__WEBPACK_IMPORTED_MODULE_1__["default"].isDark() ? [255, 255, 255] : [0, 0, 0]; - const cycles = 2; - Doc.animate(1000, (p) => { - el.style.outline = `2px solid rgba(${r}, ${g}, ${b}, ${(cycles - p * cycles) % 1})`; - }); - }); - } - static applySelector(ancestor, k) { - return Array.from(ancestor.querySelectorAll(k)); - } - static kids(ancestor) { - return Array.from(ancestor.children); - } - static safeSelector(ancestor, k) { - const el = ancestor.querySelector(k); - if (el) - return el; - console.warn(`no element found for selector '${k}' on element ->`, ancestor); - return document.createElement('div'); - } - /* - * idDescendants creates an object mapping to elements which are descendants - * of the ancestor and have id attributes. Elements are keyed by their id - * value. - */ - static idDescendants(ancestor) { - const d = {}; - for (const el of Doc.applySelector(ancestor, '[id]')) - d[el.id] = el; - return d; - } - /* - * formatCoinValue formats the value in atomic units into a string - * representation in conventional units. If the value happens to be an - * integer, no decimals are displayed. Trailing zeros may be truncated. - */ - static formatCoinValue(vAtomic, unitInfo) { - const [v, prec] = convertToConventional(vAtomic, unitInfo); - if (Number.isInteger(v)) - return intFormatter.format(v); - return decimalFormatter(prec).format(v); - } - static conventionalCoinValue(vAtomic, unitInfo) { - const [v] = convertToConventional(vAtomic, unitInfo); - return v; - } - /* - * formatRateFullPrecision formats rate to represent it exactly at rate step - * precision, trimming non-effectual zeros if there are any. - */ - static formatRateFullPrecision(encRate, bui, qui, rateStepEnc) { - const r = bui.conventional.conversionFactor / qui.conventional.conversionFactor; - const convRate = encRate * r / RateEncodingFactor; - const rateStepDigits = log10RateEncodingFactor - Math.floor(Math.log10(rateStepEnc)) - - Math.floor(Math.log10(bui.conventional.conversionFactor) - Math.log10(qui.conventional.conversionFactor)); - if (rateStepDigits <= 0) - return intFormatter.format(convRate); - return fullPrecisionFormatter(rateStepDigits).format(convRate); - } - static formatFourSigFigs(n, maxDecimals) { - return formatSigFigsWithFormatters(intFormatter, fourSigFigs, n, maxDecimals); - } - static formatInt(i) { - return intFormatter.format(i); - } - /* - * formatFullPrecision formats the value in atomic units into a string - * representation in conventional units using the full decimal precision - * associated with the conventional unit's conversion factor. - */ - static formatFullPrecision(vAtomic, unitInfo) { - const [v, prec] = convertToConventional(vAtomic, unitInfo); - return fullPrecisionFormatter(prec).format(v); - } - /* - * formatFiatConversion formats the value in atomic units to its representation in - * conventional units and returns the fiat value as a string. - */ - static formatFiatConversion(vAtomic, rate, unitInfo) { - if (!rate || rate === 0) - return _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_UNAVAILABLE); - const prec = 2; - const [v] = convertToConventional(vAtomic, unitInfo); - const value = v * rate; - return fullPrecisionFormatter(prec).format(value); - } - static languages() { - return languages; - } - static formatFiatValue(value) { - return fullPrecisionFormatter(2).format(value); - } - /* - * bestConversion picks the best conversion factor for the atomic value. The - * best is the one in which log10(converted_value) is closest to - * bestDisplayOrder. Return: [converted_value, precision, unit]. - */ - static bestConversion(atoms, ui, prefs) { - const unitFactors = resolveUnitConversions(ui, prefs); - const logDiffs = []; - const entryDiff = (entry) => Math.abs(Math.log10(atoms / entry[1]) - bestDisplayOrder); - for (const entry of Object.entries(unitFactors)) - logDiffs.push([entry[0], entryDiff(entry)]); - const best = logDiffs.reduce((best, entry) => entry[1] < best[1] ? entry : best); - const unit = best[0]; - const cFactor = unitFactors[unit]; - const v = atoms / cFactor; - return [v, Math.round(Math.log10(cFactor)), unit]; - } - /* - * formatBestUnitsFullPrecision formats the value with the best choice of - * units, at full precision. - */ - static formatBestUnitsFullPrecision(atoms, ui, prefs) { - const [v, prec, unit] = this.bestConversion(atoms, ui, prefs); - if (Number.isInteger(v)) - return [intFormatter.format(v), unit]; - return [fullPrecisionFormatter(prec).format(v), unit]; - } - /* - * formatBestUnitsFourSigFigs formats the value with the best choice of - * units and rounded to four significant figures. - */ - static formatBestUnitsFourSigFigs(atoms, ui, prefs) { - const [v, prec, unit] = this.bestConversion(atoms, ui, prefs); - return [Doc.formatFourSigFigs(v, prec), unit]; - } - /* - * formatBestRateElement formats a rate using the best available units and - * updates the UI element. The ancestor should have descendents with data - * attributes [best-value, data-unit, data-unit-box, data-denom]. - */ - static formatBestRateElement(ancestor, assetID, atoms, ui, prefs) { - Doc.formatBestValueElement(ancestor, assetID, atoms, ui, prefs); - Doc.setText(ancestor, '[data-denom]', ui.feeRateDenom); - } - /* - * formatBestRateElement formats a value using the best available units and - * updates the UI element. The ancestor should have descendents with data - * attributes [best-value, data-unit, data-unit-box]. - */ - static formatBestValueElement(ancestor, assetID, atoms, ui, prefs) { - const [v, unit] = this.formatBestUnitsFourSigFigs(atoms, ui, prefs); - Doc.setText(ancestor, '[data-value]', v); - Doc.setText(ancestor, '[data-unit]', unit); - const span = Doc.safeSelector(ancestor, '[data-unit-box]'); - span.dataset.atoms = String(atoms); - span.dataset.assetID = String(assetID); - } - static conventionalRateStep(rateStepEnc, baseUnitInfo, quoteUnitInfo) { - const [qFactor, bFactor] = [quoteUnitInfo.conventional.conversionFactor, baseUnitInfo.conventional.conversionFactor]; - return rateStepEnc / RateEncodingFactor * (bFactor / qFactor); - } - /* - * logoPath creates a path to a png logo for the specified ticker symbol. If - * the symbol is not a supported asset, the generic letter logo will be - * requested instead. - */ - static logoPath(symbol) { - if (BipSymbols.indexOf(symbol) === -1) - symbol = symbol.substring(0, 1); - symbol = symbol.split('.')[0]; // e.g. usdc.eth => usdc - return `/img/coins/${symbol}.png`; - } - static bipSymbol(assetID) { - return BipIDs[assetID]; - } - static bipIDFromSymbol(symbol) { - return BipSymbolIDs[symbol]; - } - static bipCEXSymbol(assetID) { - const bipSymbol = BipIDs[assetID]; - if (!bipSymbol || bipSymbol === '') - return ''; - const parts = bipSymbol.split('.'); - if (parts[0] === 'weth') - return 'eth'; - return parts[0]; - } - static logoPathFromID(assetID) { - return Doc.logoPath(BipIDs[assetID]); - } - /* - * symbolize creates a token-aware symbol element for the asset's symbol. For - * non-token assets, this is simply a SYMBOL. For tokens, it'll - * be SYMBOLPARENT. - */ - static symbolize(asset, useLogo) { - const ticker = asset.unitInfo.conventional.unit; - const symbolSpan = document.createElement('span'); - symbolSpan.textContent = ticker.toUpperCase(); - const parts = asset.symbol.split('.'); - const isToken = parts.length === 2; - if (!isToken) - return symbolSpan; - const parentSymbol = parts[1]; - const span = document.createElement('span'); - span.appendChild(symbolSpan); - if (useLogo) { - const parentLogo = document.createElement('img'); - parentLogo.src = Doc.logoPath(parentSymbol); - parentLogo.classList.add('token-parent'); - span.appendChild(parentLogo); - return span; - } - const parentSup = document.createElement('sup'); - parentSup.textContent = parentSymbol.toUpperCase(); - parentSup.classList.add('token-parent'); - span.appendChild(parentSup); - return span; - } - /* - * shortSymbol removes the short format of a symbol, with any parent chain - * identifier removed - */ - static shortSymbol(symbol) { - return symbol.split('.')[0].toUpperCase(); - } - /* - * setText sets the textContent for all descendant elements that match the - * specified CSS selector. - */ - static setText(ancestor, selector, textContent) { - for (const el of Doc.applySelector(ancestor, selector)) - el.textContent = textContent; - } - static setSrc(ancestor, selector, textContent) { - for (const img of Doc.applySelector(ancestor, selector)) - img.src = textContent; - } - /* - * cleanTemplates removes the elements from the DOM and deletes the id - * attribute. - */ - static cleanTemplates(...tmpls) { - tmpls.forEach(tmpl => { - tmpl.remove(); - tmpl.removeAttribute('id'); - }); - } - /* - * tmplElement is a helper function for grabbing sub-elements of the market list - * template. - */ - static tmplElement(ancestor, s) { - return ancestor.querySelector(`[data-tmpl="${s}"]`) || document.createElement('div'); - } - /* - * parseTemplate returns an object of data-tmpl elements, keyed by their - * data-tmpl values. - */ - static parseTemplate(ancestor) { - const d = {}; - for (const el of Doc.applySelector(ancestor, '[data-tmpl]')) - d[el.dataset.tmpl || ''] = el; - return d; - } - /* - * timeSince returns a string representation of the duration since the - * specified unix timestamp (milliseconds). - */ - static timeSince(ms) { - return Doc.formatDuration((new Date().getTime()) - ms); - } - /* - * hmsSince returns a time duration since the specified unix timestamp - * formatted as HH:MM:SS - */ - static hmsSince(secs) { - let r = (new Date().getTime() / 1000) - secs; - const h = String(Math.floor(r / 3600)); - r = r % 3600; - const m = String(Math.floor(r / 60)); - const s = String(Math.floor(r % 60)); - return `${h.padStart(2, '0')}:${m.padStart(2, '0')}:${s.padStart(2, '0')}`; - } - /* formatDuration returns a string representation of the duration */ - static formatDuration(dur) { - let seconds = Math.floor(dur); - let result = ''; - let count = 0; - const add = (n, s) => { - if (n > 0 || count > 0) - count++; - if (n > 0) - result += `${n} ${s} `; - return count >= 2; - }; - let y, mo, d, h, m, s; - [y, seconds] = timeMod(seconds, aYear); - if (add(y, 'y')) { - return result; - } - [mo, seconds] = timeMod(seconds, aMonth); - if (add(mo, 'mo')) { - return result; - } - [d, seconds] = timeMod(seconds, aDay); - if (add(d, 'd')) { - return result; - } - [h, seconds] = timeMod(seconds, anHour); - if (add(h, 'h')) { - return result; - } - [m, seconds] = timeMod(seconds, aMinute); - if (add(m, 'm')) { - return result; - } - [s, seconds] = timeMod(seconds, 1000); - add(s, 's'); - return result || '0 s'; - } - /* - * disableMouseWheel can be used to disable the mouse wheel for any - * input. It is very easy to unknowingly scroll up on a number input - * and then submit an unexpected value. This function prevents the - * scroll increment/decrement behavior for a wheel action on a - * number input. - */ - static disableMouseWheel(...inputFields) { - for (const inputField of inputFields) { - Doc.bind(inputField, 'wheel', () => { }, { passive: true }); - } - } - // showFormError can be used to set and display error message on forms. - static showFormError(el, msg) { - el.textContent = msg; - Doc.show(el); - } - // showFiatValue displays the fiat equivalent for the provided amount. - static showFiatValue(display, amount, rate, ui) { - if (rate) { - display.textContent = Doc.formatFiatConversion(amount, rate, ui); - Doc.show(display.parentElement); - } - else - Doc.hide(display.parentElement); - } -} -/* - * Animation is a handler for starting and stopping animations. - */ -class Animation { - constructor(duration, f, easingAlgo, done) { - this.done = done; - this.thread = this.run(duration, f, easingAlgo); - } - /* - * run runs the animation function, increasing progress from 0 to 1 in a - * manner dictated by easingAlgo. - */ - run(duration, f, easingAlgo) { - return __awaiter(this, void 0, void 0, function* () { - duration = duration >= 0 ? duration : 1000 * 86400 * 365 * 10; // 10 years, in ms - const easer = easingAlgo ? Easing[easingAlgo] : Easing.linear; - const start = new Date().getTime(); - const end = (duration === Animation.Forever) ? Number.MAX_SAFE_INTEGER : start + duration; - const range = end - start; - const frameDuration = 1000 / FPS; - let now = start; - this.endAnimation = false; - while (now < end) { - if (this.endAnimation) - return this.runCompletionFunction(); - f(easer((now - start) / range)); - yield sleep(frameDuration); - now = new Date().getTime(); - } - f(1); - this.runCompletionFunction(); - }); - } - /* wait returns a promise that will resolve when the animation completes. */ - wait() { - return __awaiter(this, void 0, void 0, function* () { - yield this.thread; - }); - } - /* stop schedules the animation to exit at its next frame. */ - stop() { - this.endAnimation = true; - } - /* - * stopAndWait stops the animations and returns a promise that will resolve - * when the animation exits. - */ - stopAndWait() { - return __awaiter(this, void 0, void 0, function* () { - this.stop(); - yield this.wait(); - }); - } - /* runCompletionFunction runs any registered callback function */ - runCompletionFunction() { - if (this.done) - this.done(); - } -} -Animation.Forever = -1; -/* Easing algorithms for animations. */ -const Easing = { - linear: t => t, - easeIn: t => t * t, - easeOut: t => t * (2 - t), - easeInHard: t => t * t * t, - easeOutHard: t => (--t) * t * t + 1, - easeOutElastic: t => { - const c4 = (2 * Math.PI) / 3; - return t === 0 - ? 0 - : t === 1 - ? 1 - : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1; - } -}; -/* WalletIcons are used for controlling wallets in various places. */ -class WalletIcons { - constructor(box) { - const stateElement = (name) => box.querySelector(`[data-state=${name}]`); - this.icons = {}; - this.icons.sleeping = stateElement('sleeping'); - this.icons.locked = stateElement('locked'); - this.icons.unlocked = stateElement('unlocked'); - this.icons.nowallet = stateElement('nowallet'); - this.icons.syncing = stateElement('syncing'); - this.icons.nopeers = stateElement('nopeers'); - this.icons.disabled = stateElement('disabled'); - this.status = stateElement('status'); - } - /* sleeping sets the icons to indicate that the wallet is not connected. */ - sleeping() { - const i = this.icons; - Doc.hide(i.locked, i.unlocked, i.nowallet, i.syncing, i.disabled); - Doc.show(i.sleeping); - if (this.status) - this.status.textContent = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_OFF); - } - /* - * locked sets the icons to indicate that the wallet is connected, but locked. - */ - locked() { - const i = this.icons; - Doc.hide(i.unlocked, i.nowallet, i.sleeping, i.disabled); - Doc.show(i.locked); - if (this.status) - this.status.textContent = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_LOCKED); - } - /* - * unlocked sets the icons to indicate that the wallet is connected and - * unlocked. - */ - unlocked() { - const i = this.icons; - Doc.hide(i.locked, i.nowallet, i.sleeping, i.disabled); - Doc.show(i.unlocked); - if (this.status) - this.status.textContent = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_READY); - } - /* nowallet sets the icons to indicate that no wallet exists. */ - nowallet() { - const i = this.icons; - Doc.hide(i.locked, i.unlocked, i.sleeping, i.syncing, i.disabled); - Doc.show(i.nowallet); - if (this.status) - this.status.textContent = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_NO_WALLET); - } - /* set the icons to indicate that the wallet is disabled */ - disabled() { - const i = this.icons; - Doc.hide(i.locked, i.unlocked, i.sleeping, i.syncing, i.nowallet, i.nopeers); - Doc.show(i.disabled); - i.disabled.dataset.tooltip = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_DISABLED_MSG); - } - setSyncing(wallet) { - const syncIcon = this.icons.syncing; - if (!wallet || !wallet.running || wallet.disabled) { - Doc.hide(syncIcon); - return; - } - if (wallet.peerCount === 0) { - Doc.show(this.icons.nopeers); - Doc.hide(syncIcon); // potentially misleading with no peers - return; - } - Doc.hide(this.icons.nopeers); - if (!wallet.synced) { - Doc.show(syncIcon); - syncIcon.dataset.tooltip = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_WALLET_SYNC_PROGRESS, { syncProgress: (wallet.syncProgress * 100).toFixed(1) }); - return; - } - Doc.hide(syncIcon); - } - /* reads the core.Wallet state and sets the icon visibility. */ - readWallet(wallet) { - this.setSyncing(wallet); - if (!wallet) - return this.nowallet(); - switch (true) { - case (wallet.disabled): - this.disabled(); - break; - case (!wallet.running): - this.sleeping(); - break; - case (!wallet.open): - this.locked(); - break; - case (wallet.open): - this.unlocked(); - break; - default: - console.error('wallet in unknown state', wallet); - } - } -} -/* - * AniToggle is a small toggle switch, defined in HTML with the element - *
. The animations are defined in the anitoggle - * CSS class. AniToggle triggers the callback on click events, but does not - * update toggle appearance, so the caller must call the setState method from - * the callback or elsewhere if the newState - * is accepted. - */ -class AniToggle { - constructor(toggle, errorEl, initialState, callback) { - this.toggle = toggle; - if (toggle.children.length === 0) - toggle.appendChild(document.createElement('div')); - Doc.bind(toggle, 'click', (e) => __awaiter(this, void 0, void 0, function* () { - e.stopPropagation(); - Doc.hide(errorEl); - const newState = !toggle.classList.contains('on'); - this.toggling = true; - try { - yield callback(newState); - } - catch (e) { - this.toggling = false; - Doc.show(errorEl); - errorEl.textContent = _locales__WEBPACK_IMPORTED_MODULE_0__.prep(_locales__WEBPACK_IMPORTED_MODULE_0__.ID_API_ERROR, { msg: e.msg || String(e) }); - return; - } - this.toggling = false; - })); - this.setState(initialState); - } - setState(state) { - if (state) - this.toggle.classList.add('on'); - else - this.toggle.classList.remove('on'); - } -} -/* sleep can be used by async functions to pause for a specified period. */ -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} -const aYear = 31536000000; -const aMonth = 2592000000; -const aDay = 86400000; -const anHour = 3600000; -const aMinute = 60000; -/* timeMod returns the quotient and remainder of t / dur. */ -function timeMod(t, dur) { - const n = Math.floor(t / dur); - return [n, t - n * dur]; -} -function formatSigFigsWithFormatters(intFormatter, sigFigFormatter, n, maxDecimals, locales) { - var _a, _b; - if (n >= 1000) - return intFormatter.format(n); - const s = sigFigFormatter.format(n); - if (typeof maxDecimals !== 'number') - return s; - const fractional = (_b = (_a = sigFigFormatter.formatToParts(n).filter((part) => part.type === 'fraction')[0]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ''; - if (fractional.length <= maxDecimals) - return s; - return fullPrecisionFormatter(maxDecimals, locales).format(n); -} -if (true) { - // Code will only appear in dev build. - // https://webpack.js.org/guides/production/ - window.testFormatFourSigFigs = () => { - const tests = [ - ['en-US', '1.234567', undefined, '1.235'], - ['en-US', '1.234567', 2, '1.23'], - ['en-US', '1234', undefined, '1,234.0'], - ['en-US', '12', undefined, '12.00'], - ['fr-FR', '123.45678', undefined, '123,5'], - ['fr-FR', '1234.5', undefined, '1β€―234,5'], - // For Arabic, https://www.saitak.com/number is useful, but seems to use - // slightly different unicode points and no thousands separator. I think - // the Arabic decimal separator is supposed to be more like a point, not - // a comma, but Google Chrome uses U+066B (Arabic Decimal Separator), - // which looks like a comma to me. Β―\_(ツ)_/Β― - ['ar-EG', '123.45678', undefined, 'Ω‘Ω’Ω£Ω«Ω₯'], - ['ar-EG', '1234', undefined, 'Ω‘Ω¬Ω’Ω£Ω€Ω«Ω '], - ['ar-EG', '0.12345', 3, 'Ω Ω«Ω‘Ω’Ω£'] - ]; - // Reproduce the NumberFormats with ONLY our desired language. - for (const [code, unformatted, maxDecimals, expected] of tests) { - const intFormatter = new Intl.NumberFormat(code, { - minimumFractionDigits: 1, - maximumFractionDigits: 1 - }); - const sigFigFormatter = new Intl.NumberFormat(code, { - minimumSignificantDigits: 4, - maximumSignificantDigits: 4 - }); - for (const k in decimalFormatters) - delete decimalFormatters[k]; // cleanup - for (const k in fullPrecisionFormatters) - delete fullPrecisionFormatters[k]; // cleanup - const s = formatSigFigsWithFormatters(intFormatter, sigFigFormatter, parseFloatDefault(unformatted), maxDecimals, code); - if (s !== expected) - console.log(`TEST FAILED: f('${code}', ${unformatted}, ${maxDecimals}) => '${s}' != '${expected}'}`); - else - console.log(`βœ”οΈ f('${code}', ${unformatted}, ${maxDecimals}) => ${s} βœ”οΈ`); - } - }; - window.testFormatRateFullPrecision = () => { - const tests = [ - // Two utxo assets with a conventional rate of 0.15. Conventional rate - // step is 100 / 1e8 = 1e-6, so there should be 6 decimal digits. - [1.5e7, 100, 1e8, 1e8, '0.150000'], - // USDC quote -> utxo base with a rate of $10 / 1 XYZ. USDC has an - // conversion factor of 1e6, so $10 encodes to 1e7, 1 XYZ encodes to 1e8, - // encoded rate is 1e7 / 1e8 * 1e8 = 1e7, bFactor / qFactor is 1e2. - // The conventional rate step is 200 / 1e8 * 1e2 = 2e-4, so using - // rateStepDigits, we should get 4 decimal digits. - [1e7, 200, 1e6, 1e8, '10.0000'], - // Set a rate of 1 atom USDC for 0.01 BTC. That atomic rate will be 1 / - // 1e6 = 1e-6. The encoded rate will be 1e-6 * 1e8 = 1e2. As long as our - // rate step divides evenly into 100, this should work. The conventional - // rate is 1e-6 / 1e-2 = 1e-4, so expect 4 decimal digits. - [1e2, 100, 1e6, 1e8, '0.0001'], - // DCR-ETH, expect 6 decimals. - [1.5e7, 1000, 1e9, 1e8, '0.015000'], - [1e6, 1000, 1e9, 1e8, '0.001000'], - [1e3, 1000, 1e9, 1e8, '0.000001'], - [100001000, 1000, 1e9, 1e8, '0.100001'], - [1000001000, 1000, 1e9, 1e8, '1.000001'], - // DCR-USDC, expect 3 decimals. - [1.5e7, 1000, 1e6, 1e8, '15.000'], - [1e6, 1000, 1e6, 1e8, '1.000'], - [1e3, 1000, 1e6, 1e8, '0.001'], - [101000, 1000, 1e6, 1e8, '0.101'], - [1001000, 1000, 1e6, 1e8, '1.001'], - // UTXO assets but with a rate step that's not a perfect power of 10. - // For a rate step of 500, a min rate would be e.g. rate step = 500. - // 5e2 / 1e8 = 5e-6 = 0.000005 - [5e2, 500, 1e8, 1e8, '0.000005'] - ]; - for (const [encRate, rateStep, qFactor, bFactor, expEncoding] of tests) { - for (const k in fullPrecisionFormatters) - delete fullPrecisionFormatters[k]; // cleanup - const bui = { conventional: { conversionFactor: bFactor } }; - const qui = { conventional: { conversionFactor: qFactor } }; - const enc = Doc.formatRateFullPrecision(encRate, bui, qui, rateStep); - if (enc !== expEncoding) - console.log(`TEST FAILED: f(${encRate}, ${bFactor}, ${qFactor}, ${rateStep}) => ${enc} != ${expEncoding}`); - else - console.log(`βœ”οΈ f(${encRate}, ${bFactor}, ${qFactor}, ${rateStep}) => ${enc} βœ”οΈ`); - } - }; -} -class NumberInput { - constructor(input, opts) { - var _a, _b, _c; - this.input = input; - this.prec = (_a = opts.prec) !== null && _a !== void 0 ? _a : 0; - this.fmt = opts.sigFigs ? toFourSigFigs : toPrecision; - this.changed = (_b = opts.changed) !== null && _b !== void 0 ? _b : (() => { }); - this.set = opts.set; - this.min = (_c = opts.min) !== null && _c !== void 0 ? _c : 0; - Doc.bind(input, 'change', () => { this.inputChanged(); }); - } - inputChanged() { - const { changed } = this; - if (changed) - changed(this.value()); - } - setValue(v) { - this.input.value = String(v); - v = this.value(); - if (this.set) - this.set(v, this.input.value); - } - value() { - const { input, min, prec, fmt } = this; - const rawV = Math.max(parseFloatDefault(input.value, min !== null && min !== void 0 ? min : 0), min !== null && min !== void 0 ? min : 0); - const [v, s] = fmt(rawV, prec !== null && prec !== void 0 ? prec : 0); - input.value = s; - return v; - } -} -class IncrementalInput extends NumberInput { - constructor(box, opts) { - var _a; - super(Doc.safeSelector(box, 'input'), opts); - this.opts = opts; - this.inc = (_a = opts.inc) !== null && _a !== void 0 ? _a : 1; - const up = Doc.safeSelector(box, '.ico-arrowup'); - const down = Doc.safeSelector(box, '.ico-arrowdown'); - Doc.bind(up, 'click', () => { this.increment(1); }); - Doc.bind(down, 'click', () => { this.increment(-1); }); - } - setIncrementAndMinimum(inc, min) { - this.inc = inc; - this.min = min; - } - increment(sign) { - const { inc, min, input } = this; - input.value = String(Math.max(this.value() + sign * inc, min)); - this.inputChanged(); - } -} -class MiniSlider { - constructor(box, changed) { - this.changed = changed; - this.r = 0; - const color = document.createElement('div'); - color.dataset.tmpl = 'color'; - box.appendChild(color); - const track = this.track = document.createElement('div'); - track.dataset.tmpl = 'track'; - color.appendChild(track); - const ball = this.ball = document.createElement('div'); - ball.dataset.tmpl = 'ball'; - track.appendChild(ball); - Doc.bind(box, 'mousedown', (e) => { - if (e.button !== 0) - return; - e.preventDefault(); - e.stopPropagation(); - const startX = e.pageX; - const w = track.clientWidth; - const startLeft = this.r * w; - const left = (ee) => Math.max(Math.min(startLeft + (ee.pageX - startX), w), 0); - const trackMouse = (ee) => { - ee.preventDefault(); - const l = left(ee); - this.r = l / w; - ball.style.left = `${this.r * 100}%`; - this.changed(this.r); - }; - const mouseUp = (ee) => { - trackMouse(ee); - Doc.unbind(document, 'mousemove', trackMouse); - Doc.unbind(document, 'mouseup', mouseUp); - }; - Doc.bind(document, 'mousemove', trackMouse); - Doc.bind(document, 'mouseup', mouseUp); - }); - Doc.bind(box, 'click', (e) => { - if (e.button !== 0) - return; - const x = e.pageX; - const m = Doc.layoutMetrics(track); - this.r = clamp((x - m.bodyLeft) / m.width, 0, 1); - ball.style.left = `${this.r * m.width}px`; - this.changed(this.r); - }); - } - setValue(r) { - this.r = clamp(r, 0, 1); - this.ball.style.left = `${this.r * 100}%`; - } -} -function toPrecision(v, prec) { - const ord = Math.pow(10, prec !== null && prec !== void 0 ? prec : 0); - v = Math.round(v * ord) / ord; - let s = v.toFixed(prec); - if (prec > 0) { - while (s.endsWith('0')) - s = s.substring(0, s.length - 1); - if (s.endsWith('.')) - s = s.substring(0, s.length - 1); - } - return [v, s]; -} -function toFourSigFigs(v, maxPrec) { - const ord = Math.floor(Math.log10(Math.abs(v))); - if (ord >= 3) - return [Math.round(v), v.toFixed(0)]; - const prec = Math.min(4 - ord, maxPrec); - return toPrecision(v, prec); -} -function parseFloatDefault(inputValue, defaultValue) { - const v = parseFloat((inputValue !== null && inputValue !== void 0 ? inputValue : '').replace(/,/g, '')); - if (!isNaN(v)) - return v; - return defaultValue !== null && defaultValue !== void 0 ? defaultValue : 0; -} -/* clamp returns v if min <= v <= max, else min or max. */ -function clamp(v, min, max) { - if (v < min) - return min; - if (v > max) - return max; - return v; -} -function setupCopyBtn(txt, textEl, btnEl, color) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield navigator.clipboard.writeText(txt); - } - catch (err) { - console.error('Unable to copy: ', err); - } - const textOriginalColor = textEl.style.color; - const btnOriginalColor = btnEl.style.color; - textEl.style.color = color; - btnEl.style.color = color; - setTimeout(() => { - textEl.style.color = textOriginalColor; - btnEl.style.color = btnOriginalColor; - }, 350); - }); -} - - -/***/ }), - -/***/ "./src/js/forms.ts": -/*!*************************!*\ - !*** ./src/js/forms.ts ***! - \*************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "AccelerateOrderForm": () => (/* binding */ AccelerateOrderForm), -/* harmony export */ "AppPassResetForm": () => (/* binding */ AppPassResetForm), -/* harmony export */ "CEXConfigurationForm": () => (/* binding */ CEXConfigurationForm), -/* harmony export */ "CertificatePicker": () => (/* binding */ CertificatePicker), -/* harmony export */ "ConfirmRegistrationForm": () => (/* binding */ ConfirmRegistrationForm), -/* harmony export */ "DEXAddressForm": () => (/* binding */ DEXAddressForm), -/* harmony export */ "DepositAddress": () => (/* binding */ DepositAddress), -/* harmony export */ "DiscoverAccountForm": () => (/* binding */ DiscoverAccountForm), -/* harmony export */ "FeeAssetSelectionForm": () => (/* binding */ FeeAssetSelectionForm), -/* harmony export */ "Forms": () => (/* binding */ Forms), -/* harmony export */ "LoginForm": () => (/* binding */ LoginForm), -/* harmony export */ "NewWalletForm": () => (/* binding */ NewWalletForm), -/* harmony export */ "TokenApprovalForm": () => (/* binding */ TokenApprovalForm), -/* harmony export */ "WalletConfigForm": () => (/* binding */ WalletConfigForm), -/* harmony export */ "WalletWaitForm": () => (/* binding */ WalletWaitForm), -/* harmony export */ "bind": () => (/* binding */ bind), -/* harmony export */ "showSuccess": () => (/* binding */ showSuccess), -/* harmony export */ "slideSwap": () => (/* binding */ slideSwap) -/* harmony export */ }); -/* harmony import */ var _doc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./doc */ "./src/js/doc.ts"); -/* harmony import */ var _http__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./http */ "./src/js/http.ts"); -/* harmony import */ var _state__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./state */ "./src/js/state.ts"); -/* harmony import */ var _locales__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./locales */ "./src/js/locales.ts"); -/* harmony import */ var _charts__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./charts */ "./src/js/charts.ts"); -/* harmony import */ var _account__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./account */ "./src/js/account.ts"); -/* harmony import */ var _registry__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./registry */ "./src/js/registry.ts"); -/* harmony import */ var _opts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./opts */ "./src/js/opts.ts"); -/* harmony import */ var _coinexplorers__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./coinexplorers */ "./src/js/coinexplorers.ts"); -/* harmony import */ var _mmutil__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./mmutil */ "./src/js/mmutil.ts"); -var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; - - - - - - - - - - -class Forms { - constructor(formsDiv, cfg) { - this.formsDiv = formsDiv; - this.closed = cfg === null || cfg === void 0 ? void 0 : cfg.closed; - formsDiv.querySelectorAll('.form-closer').forEach(el => { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(el, 'click', () => { this.close(); }); - }); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(formsDiv, 'mousedown', (e) => { - if (!_doc__WEBPACK_IMPORTED_MODULE_0__["default"].mouseInElement(e, this.currentForm)) { - this.close(); - } - }); - this.keyup = (e) => { - if (e.key === 'Escape') { - this.close(); - } - }; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(document, 'keyup', this.keyup); - } - /* showForm shows a modal form with a little animation. */ - show(form) { - return __awaiter(this, void 0, void 0, function* () { - this.currentForm = form; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(...Array.from(this.formsDiv.children)); - form.style.right = '10000px'; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(this.formsDiv, form); - const shift = (this.formsDiv.offsetWidth + form.offsetWidth) / 2; - yield _doc__WEBPACK_IMPORTED_MODULE_0__["default"].animate(animationLength, progress => { - form.style.right = `${(1 - progress) * shift}px`; - }, 'easeOutHard'); - form.style.right = '0'; - }); - } - close() { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.formsDiv); - if (this.closed) - this.closed(); - } - exit() { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].unbind(document, 'keyup', this.keyup); - } -} -/* - * NewWalletForm should be used with the "newWalletForm" template. The enclosing - *
element should be the first argument of the constructor. - */ -class NewWalletForm { - constructor(form, success, backFunc) { - this.form = form; - this.success = success; - const page = this.page = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].parseTemplate(form); - if (backFunc) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.goBack); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(page.goBack, 'click', () => { backFunc(); }); - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(page.walletTabTmpl); - page.walletTabTmpl.removeAttribute('id'); - // WalletConfigForm will set the global app variable. - this.subform = new WalletConfigForm(page.walletSettings, true); - this.walletCfgGuide = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'walletCfgGuide'); - bind(form, page.submitAdd, () => this.submit()); - bind(form, page.oneBttn, () => this.submit()); - (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().registerNoteFeeder({ - walletstate: (note) => { this.reportWalletState(note.wallet); }, - walletsync: (note) => { if (this.parentSyncer) - this.parentSyncer((0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().walletMap[note.assetID]); }, - createwallet: (note) => { this.reportCreationUpdate(note); } - }); - } - /* - * reportWalletState should be called when a 'walletstate' notification is - * received. - * TODO: Let form classes register for notifications. - */ - reportWalletState(w) { - if (this.parentSyncer) - this.parentSyncer(w); - } - /* - * reportWalletState should be called when a 'createwallet' notification is - * received. - */ - reportCreationUpdate(note) { - if (this.createUpdater) - this.createUpdater(note); - } - createWallet(assetID, walletType, parentForm) { - return __awaiter(this, void 0, void 0, function* () { - const createForm = { - assetID: assetID, - pass: this.page.newWalletPass.value || '', - config: this.subform.map(assetID), - walletType: walletType, - parentForm: parentForm - }; - const ani = new _charts__WEBPACK_IMPORTED_MODULE_4__.Wave(this.form, { backgroundColor: true }); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_1__.postJSON)('/api/newwallet', createForm); - ani.stop(); - return res; - }); - } - submit() { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - const newWalletPass = page.newWalletPass; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.newWalletErr); - const { asset, parentAsset } = this.current; - const selectedDef = this.current.selectedDef; - let parentForm; - let walletType = selectedDef.type; - if (parentAsset) { - walletType = asset.token.definition.type; - parentForm = { - assetID: parentAsset.id, - config: this.subform.map(parentAsset.id), - walletType: selectedDef.type - }; - } - // Register the selected asset. - const res = yield this.createWallet(asset.id, walletType, parentForm); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - this.setError(res.msg); - return; - } - newWalletPass.value = ''; - if (parentAsset) - yield this.runParentSync(); - else - this.success(this.current.asset.id); - }); - } - /* - * runParentSync shows a syncing sub-dialog that tracks the parent asset's - * syncProgress and informs the user that the token wallet will be created - * after sync is complete. - */ - runParentSync() { - return __awaiter(this, void 0, void 0, function* () { - const { page, current: { parentAsset, asset } } = this; - if (!parentAsset) - return; - page.parentSyncPct.textContent = '0'; - page.parentName.textContent = parentAsset.name; - page.parentLogo.src = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].logoPath(parentAsset.symbol); - page.childName.textContent = asset.name; - page.childLogo.src = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].logoPath(asset.symbol); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.mainForm); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.parentSyncing); - try { - yield this.syncParent(parentAsset); - this.success(this.current.asset.id); - } - catch (error) { - this.setError(error.message || error); - } - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.mainForm); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.parentSyncing); - }); - } - /* - * syncParent monitors the sync progress of a token's parent asset, generating - * an Error if the token wallet creation does not complete successfully. - */ - syncParent(parentAsset) { - const { page, current: { asset } } = this; - return new Promise((resolve, reject) => { - // First, check if it's already synced. - const w = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().assets[parentAsset.id].wallet; - if (w && w.synced) - return resolve(); - // Not synced, so create a syncer to update the parent sync pane. - this.parentSyncer = (w) => { - if (w.assetID !== parentAsset.id) - return; - page.parentSyncPct.textContent = (w.syncProgress * 100).toFixed(1); - }; - // Handle the async result. - this.createUpdater = (note) => { - if (note.assetID !== asset.id) - return; - switch (note.topic) { - case 'QueuedCreationFailed': - reject(new Error(`${note.subject}: ${note.details}`)); - break; - case 'QueuedCreationSuccess': - resolve(); - break; - default: - return; - } - this.parentSyncer = null; - this.createUpdater = null; - }; - }); - } - /* setAsset sets the current asset of the NewWalletForm */ - setAsset(assetID) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.parseAsset(assetID)) - return; // nothing to change - const page = this.page; - const tabs = page.walletTypeTabs; - const { winfo, asset, parentAsset } = this.current; - page.assetName.textContent = winfo.name; - page.newWalletPass.value = ''; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].empty(tabs); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(tabs, page.newWalletErr, page.tokenMsgBox); - this.page.assetLogo.src = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].logoPath(asset.symbol); - if (parentAsset) { - page.tokenParentLogo.src = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].logoPath(parentAsset.symbol); - page.tokenParentName.textContent = parentAsset.name; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.tokenMsgBox); - } - const pinfo = parentAsset ? parentAsset.info : null; - const walletDefs = pinfo ? pinfo.availablewallets : winfo.availablewallets ? winfo.availablewallets : [winfo.definition]; - if (walletDefs.length > 1) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(tabs); - for (const wDef of walletDefs) { - const tab = page.walletTabTmpl.cloneNode(true); - tab.dataset.tooltip = wDef.description; - tab.textContent = wDef.tab; - tabs.appendChild(tab); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(tab, 'click', () => { - for (const t of _doc__WEBPACK_IMPORTED_MODULE_0__["default"].kids(tabs)) - t.classList.remove('selected'); - tab.classList.add('selected'); - this.update(wDef); - }); - } - (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().bindTooltips(tabs); - const first = tabs.firstChild; - first.classList.add('selected'); - } - yield this.update(this.current.selectedDef); - if (asset.walletCreationPending) - yield this.runParentSync(); - }); - } - /* - * parseAsset parses the current data for the asset ID. - */ - parseAsset(assetID) { - if (this.current && this.current.asset.id === assetID) - return false; - const asset = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().assets[assetID]; - const token = asset.token; - if (!token) { - if (!asset.info) - throw Error('this non-token asset has no wallet info!'); - this.current = { asset, winfo: asset.info, selectedDef: asset.info.availablewallets[0] }; - return true; - } - const parentAsset = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().user.assets[token.parentID]; - if (parentAsset.wallet) { - // If the parent asset already has a wallet, there's no need to configure - // the parent too. Just configure the token. - this.current = { asset, winfo: token, selectedDef: token.definition }; - return true; - } - if (!parentAsset.info) - throw Error('this parent has no wallet info!'); - this.current = { asset, parentAsset, winfo: token, selectedDef: parentAsset.info.availablewallets[0] }; - return true; - } - update(walletDef) { - return __awaiter(this, void 0, void 0, function* () { - const page = this.page; - this.current.selectedDef = walletDef; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.walletPassAndSubmitBttn, page.oneBttnBox, page.newWalletPassBox); - const guideLink = walletDef.guidelink; - const configOpts = walletDef.configopts || []; - // If a config represents a wallet's birthday, we update the default - // selection to the current date if this installation of the client - // generated a seed. - configOpts.map((opt) => { - if (opt.isBirthdayConfig && (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().seedGenTime > 0) { - opt.default = toUnixDate(new Date()); - } - return opt; - }); - // Either this is a walletDef for a token's uncreated parent asset, or this - // is the definition for the token. - let containsRequired = false; - for (const opt of configOpts) { - if (opt.required) { - containsRequired = true; - break; - } - } - const { asset, parentAsset, winfo } = this.current; - const displayCreateBtn = walletDef.seeded || Boolean(asset.token); - if (displayCreateBtn && !containsRequired) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(page.walletSettingsHeader); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.oneBttnBox); - } - else if (displayCreateBtn) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.walletPassAndSubmitBttn, page.walletSettingsHeader); - page.newWalletPass.value = ''; - page.submitAdd.textContent = _locales__WEBPACK_IMPORTED_MODULE_3__.prep(_locales__WEBPACK_IMPORTED_MODULE_3__.ID_CREATE); - } - else { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.walletPassAndSubmitBttn, page.walletSettingsHeader); - if (!walletDef.noauth) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(page.newWalletPassBox); - page.submitAdd.textContent = _locales__WEBPACK_IMPORTED_MODULE_3__.prep(_locales__WEBPACK_IMPORTED_MODULE_3__.ID_ADD); - } - if (parentAsset) { - const parentAndTokenOpts = JSON.parse(JSON.stringify(configOpts)); - // Add the regAsset field to the configurations so proper logos will be displayed - // next to them, and map can filter them out. The opts are copied here so the originals - // do not have the regAsset field added to them. - for (const opt of parentAndTokenOpts) - opt.regAsset = parentAsset.id; - const tokenOpts = winfo.definition.configopts || []; - if (tokenOpts.length > 0) { - const tokenOptsCopy = JSON.parse(JSON.stringify(tokenOpts)); - for (const opt of tokenOptsCopy) - opt.regAsset = asset.id; - parentAndTokenOpts.push(...tokenOptsCopy); - } - this.subform.update(asset.id, parentAndTokenOpts, false); - } - else - this.subform.update(asset.id, configOpts, false); - this.setGuideLink(guideLink); - // A seeded or token wallet is internal to Bison Wallet and as such does - // not have an external config file to select. - if (walletDef.seeded || Boolean(this.current.asset.token)) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.subform.fileSelector); - else - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(this.subform.fileSelector); - yield this.loadDefaults(); - }); - } - setGuideLink(guideLink) { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.walletCfgGuide); - if (guideLink !== '') { - this.walletCfgGuide.href = guideLink; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(this.walletCfgGuide); - } - } - /* setError sets and shows the in-form error message. */ - setError(errMsg) { - return __awaiter(this, void 0, void 0, function* () { - this.page.newWalletErr.textContent = errMsg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(this.page.newWalletErr); - }); - } - /* - * loadDefaults attempts to load the ExchangeWallet configuration from the - * default wallet config path on the server and will auto-fill the page on - * the subform if settings are found. - */ - loadDefaults() { - return __awaiter(this, void 0, void 0, function* () { - // No default config files for seeded assets right now. - const { asset, parentAsset, selectedDef } = this.current; - if (!selectedDef.configpath) - return; - let configID = asset.id; - if (parentAsset) { - if (selectedDef.seeded) - return; - configID = parentAsset.id; - } - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.form); - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_1__.postJSON)('/api/defaultwalletcfg', { - assetID: configID, - type: selectedDef.type - }); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - this.setError(res.msg); - return; - } - this.subform.setLoadedConfig(res.config); - }); - } -} -let dynamicInputCounter = 0; -/* - * WalletConfigForm is a dynamically generated sub-form for setting - * asset-specific wallet configuration options. -*/ -class WalletConfigForm { - constructor(form, sectionize) { - this.page = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].idDescendants(form); - this.form = form; - // A configElement is a div containing an input and its label. - this.configElements = []; - // configOpts is the wallet options provided by core. - this.configOpts = []; - this.sectionize = sectionize; - // Get template elements - this.allSettings = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'allSettings'); - this.dynamicOpts = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'dynamicOpts'); - this.textInputTmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'textInput'); - this.textInputTmpl.remove(); - this.dateInputTmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'dateInput'); - this.dateInputTmpl.remove(); - this.checkboxTmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'checkbox'); - this.checkboxTmpl.remove(); - this.repeatableTmpl = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'repeatableInput'); - this.repeatableTmpl.remove(); - this.fileSelector = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'fileSelector'); - this.fileInput = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'fileInput'); - this.errMsg = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'errMsg'); - this.showOther = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'showOther'); - this.showIcon = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'showIcon'); - this.hideIcon = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'hideIcon'); - this.showHideMsg = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'showHideMsg'); - this.otherSettings = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'otherSettings'); - this.loadedSettingsMsg = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'loadedSettingsMsg'); - this.loadedSettings = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'loadedSettings'); - this.defaultSettingsMsg = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'defaultSettingsMsg'); - this.defaultSettings = _doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(form, 'defaultSettings'); - if (!sectionize) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.showOther); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(this.fileSelector, 'click', () => this.fileInput.click()); - // config file upload - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(this.fileInput, 'change', () => __awaiter(this, void 0, void 0, function* () { return this.fileInputChanged(); })); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(this.showOther, 'click', () => { - this.setOtherSettingsViz(this.hideIcon.classList.contains('d-hide')); - }); - } - /* - * fileInputChanged will read the selected file and attempt to load the - * configuration settings. All loaded settings will be made visible for - * inspection by the user. - */ - fileInputChanged() { - return __awaiter(this, void 0, void 0, function* () { - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.errMsg); - if (!this.fileInput.value) - return; - const files = this.fileInput.files; - if (!files || files.length === 0) - return; - const loaded = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().loading(this.form); - const config = yield files[0].text(); - if (!config) - return; - const res = yield (0,_http__WEBPACK_IMPORTED_MODULE_1__.postJSON)('/api/parseconfig', { - configtext: config - }); - loaded(); - if (!(0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().checkResponse(res)) { - this.errMsg.textContent = res.msg; - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].show(this.errMsg); - return; - } - if (Object.keys(res.map).length === 0) - return; - this.dynamicOpts.append(...this.setConfig(res.map)); - this.reorder(this.dynamicOpts); - const [loadedOpts, defaultOpts] = [this.loadedSettings.children.length, this.defaultSettings.children.length]; - if (loadedOpts === 0) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.loadedSettings, this.loadedSettingsMsg); - if (defaultOpts === 0) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.defaultSettings, this.defaultSettingsMsg); - if (loadedOpts + defaultOpts === 0) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(this.showOther, this.otherSettings); - }); - } - addOpt(box, opt, insertAfter, skipRepeatN) { - var _a; - let el; - if (opt.isboolean) - el = this.checkboxTmpl.cloneNode(true); - else if (opt.isdate) - el = this.dateInputTmpl.cloneNode(true); - else if (opt.repeatable) { - el = this.repeatableTmpl.cloneNode(true); - el.classList.add('repeatable'); - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].bind(_doc__WEBPACK_IMPORTED_MODULE_0__["default"].tmplElement(el, 'add'), 'click', () => { - this.addOpt(box, opt, el, true); - }); - if (!skipRepeatN) - for (let i = 0; i < (opt.repeatN ? opt.repeatN - 1 : 0); i++) - this.addOpt(box, opt, insertAfter, true); - } - else - el = this.textInputTmpl.cloneNode(true); - const hiddenFields = ((_a = (0,_registry__WEBPACK_IMPORTED_MODULE_6__.app)().extensionWallet(this.assetID)) === null || _a === void 0 ? void 0 : _a.hiddenFields) || []; - if (hiddenFields.indexOf(opt.key) !== -1) - _doc__WEBPACK_IMPORTED_MODULE_0__["default"].hide(el); - this.configElements.push([opt, el]); - const input = el.querySelector('input'); - input.dataset.configKey = opt.key; - // We need to generate a unique ID only for the =>