Browse Source
merge develop into master for v1.8.0 See merge request dojo/samourai-dojo!164umbrel v1.8.0
kenshin-samourai
4 years ago
66 changed files with 3715 additions and 10920 deletions
@ -0,0 +1,17 @@ |
|||
{ |
|||
// Use IntelliSense to learn about possible attributes. |
|||
// Hover to view descriptions of existing attributes. |
|||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |
|||
"version": "0.2.0", |
|||
"configurations": [ |
|||
{ |
|||
"type": "node", |
|||
"request": "launch", |
|||
"name": "Launch Program", |
|||
"skipFiles": [ |
|||
"<node_internals>/**" |
|||
], |
|||
"program": "${workspaceFolder}/accounts/index.js" |
|||
} |
|||
] |
|||
} |
@ -0,0 +1,136 @@ |
|||
/*! |
|||
* accounts/wallet-rest-api.js |
|||
* Copyright © 2019 – Katana Cryptographic Ltd. All Rights Reserved. |
|||
*/ |
|||
'use strict' |
|||
|
|||
const bodyParser = require('body-parser') |
|||
const Logger = require('../lib/logger') |
|||
const errors = require('../lib/errors') |
|||
const walletService = require('../lib/wallet/wallet-service') |
|||
const authMgr = require('../lib/auth/authorizations-manager') |
|||
const HttpServer = require('../lib/http-server/http-server') |
|||
const apiHelper = require('./api-helper') |
|||
|
|||
const debugApi = !!(process.argv.indexOf('api-debug') > -1) |
|||
|
|||
|
|||
/** |
|||
* Wallet API endpoints |
|||
*/ |
|||
class WalletRestApi { |
|||
|
|||
/** |
|||
* Constructor |
|||
* @param {pushtx.HttpServer} httpServer - HTTP server |
|||
*/ |
|||
constructor(httpServer) { |
|||
this.httpServer = httpServer |
|||
|
|||
// Establish routes
|
|||
const urlencodedParser = bodyParser.urlencoded({ extended: true }) |
|||
|
|||
this.httpServer.app.get( |
|||
'/wallet', |
|||
authMgr.checkAuthentication.bind(authMgr), |
|||
apiHelper.validateEntitiesParams.bind(apiHelper), |
|||
this.getWallet.bind(this), |
|||
HttpServer.sendAuthError |
|||
) |
|||
|
|||
this.httpServer.app.post( |
|||
'/wallet', |
|||
urlencodedParser, |
|||
authMgr.checkAuthentication.bind(authMgr), |
|||
apiHelper.validateEntitiesParams.bind(apiHelper), |
|||
this.postWallet.bind(this), |
|||
HttpServer.sendAuthError |
|||
) |
|||
} |
|||
|
|||
/** |
|||
* Handle wallet GET request |
|||
* @param {object} req - http request object |
|||
* @param {object} res - http response object |
|||
*/ |
|||
async getWallet(req, res) { |
|||
try { |
|||
// Check request params
|
|||
if (!apiHelper.checkEntitiesParams(req.query)) |
|||
return HttpServer.sendError(res, errors.multiaddr.NOACT) |
|||
|
|||
// Parse params
|
|||
const entities = apiHelper.parseEntitiesParams(req.query) |
|||
|
|||
const result = await walletService.getFullWalletInfo( |
|||
entities.active, |
|||
entities.legacy, |
|||
entities.bip49, |
|||
entities.bip84, |
|||
entities.pubkey |
|||
) |
|||
|
|||
const ret = JSON.stringify(result, null, 2) |
|||
HttpServer.sendRawData(res, ret) |
|||
|
|||
} catch(e) { |
|||
HttpServer.sendError(res, e) |
|||
|
|||
} finally { |
|||
if (debugApi) { |
|||
const strParams = |
|||
`${req.query.active ? req.query.active : ''} \
|
|||
${req.query.new ? req.query.new : ''} \ |
|||
${req.query.pubkey ? req.query.pubkey : ''} \ |
|||
${req.query.bip49 ? req.query.bip49 : ''} \ |
|||
${req.query.bip84 ? req.query.bip84 : ''}` |
|||
|
|||
Logger.info(`API : Completed GET /wallet ${strParams}`) |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Handle wallet POST request |
|||
* @param {object} req - http request object |
|||
* @param {object} res - http response object |
|||
*/ |
|||
async postWallet(req, res) { |
|||
try { |
|||
// Check request params
|
|||
if (!apiHelper.checkEntitiesParams(req.body)) |
|||
return HttpServer.sendError(res, errors.multiaddr.NOACT) |
|||
|
|||
// Parse params
|
|||
const entities = apiHelper.parseEntitiesParams(req.body) |
|||
|
|||
const result = await walletService.getFullWalletInfo( |
|||
entities.active, |
|||
entities.legacy, |
|||
entities.bip49, |
|||
entities.bip84, |
|||
entities.pubkey |
|||
) |
|||
|
|||
HttpServer.sendOkDataOnly(res, result) |
|||
|
|||
} catch(e) { |
|||
HttpServer.sendError(res, e) |
|||
|
|||
} finally { |
|||
if (debugApi) { |
|||
const strParams = |
|||
`${req.body.active ? req.body.active : ''} \
|
|||
${req.body.new ? req.body.new : ''} \ |
|||
${req.body.pubkey ? req.body.pubkey : ''} \ |
|||
${req.body.bip49 ? req.body.bip49 : ''} \ |
|||
${req.body.bip84 ? req.body.bip84 : ''}` |
|||
|
|||
Logger.info(`API : Completed POST /wallet ${strParams}`) |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
module.exports = WalletRestApi |
@ -0,0 +1,165 @@ |
|||
# Get Wallet |
|||
|
|||
Request details about a collection of HD accounts and/or loose addresses and/or pubkeys (derived in 3 formats P2PKH, P2WPKH/P2SH, P2WPKH Bech32) including a list of unspent transaction outputs. |
|||
|
|||
This endpoint merges the deprecated /multiaddr and /unspent endpoints augmented with feerates info provided by the /fees endpoint. |
|||
|
|||
|
|||
## Behavior of the active parameter |
|||
|
|||
If accounts passed to `?active` do not exist, they will be created with a relayed call to the [POST /xpub](./POST_xpub.md) mechanics if new or will be imported from external data sources. |
|||
|
|||
If loose addresses passed to `?active` do not exist, they will be imported from external data sources. |
|||
|
|||
If addresses derived from pubkeys passed to `?active` do not exist, they will be imported from external data sources. |
|||
|
|||
|
|||
## Declaration of new entities |
|||
|
|||
Instruct the server that [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) entities are new with `?new=xpub1|addr2|addr3` in the query parameters, and the server will skip importing for those entities. |
|||
|
|||
SegWit support via [BIP49](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki) is activated for new ypubs and new P2WPKH/P2SH loose addresses with `?bip49=xpub3|xpub4`. |
|||
|
|||
SegWit support via [BIP84](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki) is activated for new zpubs and new P2WPKH Bech32 loose addresses with `?bip84=xpub3|xpub4`. |
|||
|
|||
Support of [BIP47](https://github.com/bitcoin/bips/blob/master/bip-0047.mediawiki) with addresses derived in 3 formats (P2PKH, P2WPKH/P2SH, P2WPKH Bech32) is activated for new pubkeys with `?pubkey=pubkey1|pubkey2`. |
|||
|
|||
|
|||
Note that loose addresses that are also part of one of the HD accounts requested will be ignored. Their balances and transactions are listed as part of the HD account result. |
|||
|
|||
The `POST` version of `/wallet` is identical, except the parameters are in the POST body. |
|||
|
|||
|
|||
``` |
|||
GET /wallet?active=...[&new=...][&bip49=...][&bip84=...][&pubkey=...] |
|||
``` |
|||
|
|||
## Parameters |
|||
* **active** - `string` - A pipe-separated list of extended public keys and/or loose addresses and/or pubkeys (`xpub1|address1|address2|pubkey1|...`) |
|||
* **new** - `string` - A pipe-separated list of **new** extended public keys to be derived via [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and/or new P2PKH loose addresses |
|||
* **bip49** - `string` - A pipe-separated list of **new** extended public keys to be derived via [BIP49](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki) and/or new P2WPKH/P2SH loose addresses |
|||
* **bip84** - `string` - A pipe-separated list of **new** extended public keys to be derived via [BIP84](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki) and/or new P2WPKH Bech32 loose addresses |
|||
* **pubkey** - `string` - A pipe-separated list of **new** public keys to be derived as P2PKH, P2WPKH/P2SH, P2WPKH Bech32 addresses |
|||
* **at** - `string` (optional) - Access Token (json web token). Required if authentication is activated. Alternatively, the access token can be passed through the `Authorization` HTTP header (with the `Bearer` scheme). |
|||
|
|||
### Examples |
|||
|
|||
``` |
|||
GET /wallet?active=xpub0123456789&new=address2|address3&pubkey=pubkey4 |
|||
GET /wallet?active=xpub0123456789|address1|address2 |
|||
GET /wallet?bip49=xpub0123456789 |
|||
GET /wallet?bip84=xpub0123456789 |
|||
GET /wallet?pubkey=0312345678901 |
|||
``` |
|||
|
|||
#### Success |
|||
Status code 200 with JSON response: |
|||
```json |
|||
{ |
|||
"wallet": { |
|||
"final_balance": 100000000 |
|||
}, |
|||
"info": { |
|||
"latest_block": { |
|||
"height": 100000, |
|||
"hash": "abcdef", |
|||
"time": 1000000000 |
|||
}, |
|||
"fees": { |
|||
"2": 181, |
|||
"4": 150, |
|||
"6": 150, |
|||
"12": 111, |
|||
"24": 62 |
|||
} |
|||
}, |
|||
"addresses": [ |
|||
{ |
|||
"address": "xpubABCDEF -or- 1xAddress", |
|||
"pubkey": "04Pubkey -or- inexistant attribute" |
|||
"final_balance": 100000000, |
|||
"account_index": 0, |
|||
"change_index": 0, |
|||
"n_tx": 0 |
|||
} |
|||
], |
|||
"txs": [ |
|||
{ |
|||
"block_height": 100000, |
|||
"hash": "abcdef", |
|||
"version": 1, |
|||
"locktime": 0, |
|||
"result": -10000, |
|||
"balance": 90000, |
|||
"time": 1400000000, |
|||
"inputs": [ |
|||
{ |
|||
"vin": 1, |
|||
"prev_out": { |
|||
"txid": "abcdef", |
|||
"vout": 2, |
|||
"value": 20000, |
|||
"xpub": { |
|||
"m": "xpubABCDEF", |
|||
"path": "M/0/3" |
|||
}, |
|||
"addr": "1xAddress", |
|||
"pubkey": "04Pubkey" |
|||
}, |
|||
"sequence": 4294967295 |
|||
} |
|||
], |
|||
"out": [ |
|||
{ |
|||
"n": 2, |
|||
"value": 10000, |
|||
"addr": "1xAddress", |
|||
"pubkey": "03Pubkey" |
|||
"xpub": { |
|||
"m": "xpubABCDEF", |
|||
"path": "M/1/5" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
], |
|||
"unspent_outputs": [ |
|||
{ |
|||
"tx_hash": "abcdef", |
|||
"tx_output_n": 2, |
|||
"tx_version": 1, |
|||
"tx_locktime": 0, |
|||
"value": 10000, |
|||
"script": "abcdef", |
|||
"addr": "1xAddress", |
|||
"pubkey": "03Pubkey -or- inexistant attribute" |
|||
"confirmations": 10000, |
|||
"xpub": { |
|||
"m": "xpubABCDEF", |
|||
"path": "M/1/5" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
**Notes** |
|||
* The transaction `inputs` and `out` arrays are for known addresses only and do not reflect the full input and output list of the transaction on the blockchain |
|||
* `result.addresses[i].n_tx` used by BIP47 logic to detemine unused index |
|||
* `result.txs[i].block_height` should not be present for unconfirmed transactions |
|||
* `result.txs[i].result` is the change in value for the "wallet" as defined by all entries on the `active` query parameter |
|||
* `result.txs[i].inputs[j].prev_out.addr` should be present for BIP47-related addresses but may be `null` if the previous output address is unknown |
|||
* `result.txs[i].out[j].addr` should be present for BIP47-related addresses |
|||
|
|||
|
|||
#### Failure |
|||
Status code 400 with JSON response: |
|||
```json |
|||
{ |
|||
"status": "error", |
|||
"error": "<error message>" |
|||
} |
|||
``` |
|||
|
|||
## Notes |
|||
Wallet response is consumed by the wallet in the [APIFactory](https://code.samourai.io/wallet/samourai-wallet-android/-/blob/master/app/src/main/java/com/samourai/wallet/api/APIFactory.java) |
@ -0,0 +1,37 @@ |
|||
# Get import status for a HD Account |
|||
|
|||
Check if an import or a rescan is currently processed by Dojo for a given HD Account. |
|||
|
|||
``` |
|||
GET /xpub/:xpub/import/status |
|||
``` |
|||
|
|||
## Parameters |
|||
* **:xpub** - `string` - The extended public key for the HD Account |
|||
* **at** - `string` (optional) - Access Token (json web token). Required if authentication is activated. Alternatively, the access token can be passed through the `Authorization` HTTP header (with the `Bearer` scheme). |
|||
|
|||
### Example |
|||
|
|||
``` |
|||
GET /xpub/xpub0123456789/import/status |
|||
``` |
|||
|
|||
#### Success |
|||
Status code 200 with JSON response: |
|||
```json |
|||
{ |
|||
"status": "ok", |
|||
"data": { |
|||
"import_in_progress": false |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Failure |
|||
Status code 400 with JSON response: |
|||
```json |
|||
{ |
|||
"status": "error", |
|||
"error": "<error message>" |
|||
} |
|||
``` |
File diff suppressed because it is too large
@ -1,587 +0,0 @@ |
|||
/*! |
|||
* Bootstrap v3.3.7 (http://getbootstrap.com) |
|||
* Copyright 2011-2016 Twitter, Inc. |
|||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) |
|||
*/ |
|||
.btn-default, |
|||
.btn-primary, |
|||
.btn-success, |
|||
.btn-info, |
|||
.btn-warning, |
|||
.btn-danger { |
|||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); |
|||
} |
|||
.btn-default:active, |
|||
.btn-primary:active, |
|||
.btn-success:active, |
|||
.btn-info:active, |
|||
.btn-warning:active, |
|||
.btn-danger:active, |
|||
.btn-default.active, |
|||
.btn-primary.active, |
|||
.btn-success.active, |
|||
.btn-info.active, |
|||
.btn-warning.active, |
|||
.btn-danger.active { |
|||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); |
|||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); |
|||
} |
|||
.btn-default.disabled, |
|||
.btn-primary.disabled, |
|||
.btn-success.disabled, |
|||
.btn-info.disabled, |
|||
.btn-warning.disabled, |
|||
.btn-danger.disabled, |
|||
.btn-default[disabled], |
|||
.btn-primary[disabled], |
|||
.btn-success[disabled], |
|||
.btn-info[disabled], |
|||
.btn-warning[disabled], |
|||
.btn-danger[disabled], |
|||
fieldset[disabled] .btn-default, |
|||
fieldset[disabled] .btn-primary, |
|||
fieldset[disabled] .btn-success, |
|||
fieldset[disabled] .btn-info, |
|||
fieldset[disabled] .btn-warning, |
|||
fieldset[disabled] .btn-danger { |
|||
-webkit-box-shadow: none; |
|||
box-shadow: none; |
|||
} |
|||
.btn-default .badge, |
|||
.btn-primary .badge, |
|||
.btn-success .badge, |
|||
.btn-info .badge, |
|||
.btn-warning .badge, |
|||
.btn-danger .badge { |
|||
text-shadow: none; |
|||
} |
|||
.btn:active, |
|||
.btn.active { |
|||
background-image: none; |
|||
} |
|||
.btn-default { |
|||
text-shadow: 0 1px 0 #fff; |
|||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); |
|||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); |
|||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #dbdbdb; |
|||
border-color: #ccc; |
|||
} |
|||
.btn-default:hover, |
|||
.btn-default:focus { |
|||
background-color: #e0e0e0; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-default:active, |
|||
.btn-default.active { |
|||
background-color: #e0e0e0; |
|||
border-color: #dbdbdb; |
|||
} |
|||
.btn-default.disabled, |
|||
.btn-default[disabled], |
|||
fieldset[disabled] .btn-default, |
|||
.btn-default.disabled:hover, |
|||
.btn-default[disabled]:hover, |
|||
fieldset[disabled] .btn-default:hover, |
|||
.btn-default.disabled:focus, |
|||
.btn-default[disabled]:focus, |
|||
fieldset[disabled] .btn-default:focus, |
|||
.btn-default.disabled.focus, |
|||
.btn-default[disabled].focus, |
|||
fieldset[disabled] .btn-default.focus, |
|||
.btn-default.disabled:active, |
|||
.btn-default[disabled]:active, |
|||
fieldset[disabled] .btn-default:active, |
|||
.btn-default.disabled.active, |
|||
.btn-default[disabled].active, |
|||
fieldset[disabled] .btn-default.active { |
|||
background-color: #e0e0e0; |
|||
background-image: none; |
|||
} |
|||
.btn-primary { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #245580; |
|||
} |
|||
.btn-primary:hover, |
|||
.btn-primary:focus { |
|||
background-color: #265a88; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-primary:active, |
|||
.btn-primary.active { |
|||
background-color: #265a88; |
|||
border-color: #245580; |
|||
} |
|||
.btn-primary.disabled, |
|||
.btn-primary[disabled], |
|||
fieldset[disabled] .btn-primary, |
|||
.btn-primary.disabled:hover, |
|||
.btn-primary[disabled]:hover, |
|||
fieldset[disabled] .btn-primary:hover, |
|||
.btn-primary.disabled:focus, |
|||
.btn-primary[disabled]:focus, |
|||
fieldset[disabled] .btn-primary:focus, |
|||
.btn-primary.disabled.focus, |
|||
.btn-primary[disabled].focus, |
|||
fieldset[disabled] .btn-primary.focus, |
|||
.btn-primary.disabled:active, |
|||
.btn-primary[disabled]:active, |
|||
fieldset[disabled] .btn-primary:active, |
|||
.btn-primary.disabled.active, |
|||
.btn-primary[disabled].active, |
|||
fieldset[disabled] .btn-primary.active { |
|||
background-color: #265a88; |
|||
background-image: none; |
|||
} |
|||
.btn-success { |
|||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); |
|||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); |
|||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #3e8f3e; |
|||
} |
|||
.btn-success:hover, |
|||
.btn-success:focus { |
|||
background-color: #419641; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-success:active, |
|||
.btn-success.active { |
|||
background-color: #419641; |
|||
border-color: #3e8f3e; |
|||
} |
|||
.btn-success.disabled, |
|||
.btn-success[disabled], |
|||
fieldset[disabled] .btn-success, |
|||
.btn-success.disabled:hover, |
|||
.btn-success[disabled]:hover, |
|||
fieldset[disabled] .btn-success:hover, |
|||
.btn-success.disabled:focus, |
|||
.btn-success[disabled]:focus, |
|||
fieldset[disabled] .btn-success:focus, |
|||
.btn-success.disabled.focus, |
|||
.btn-success[disabled].focus, |
|||
fieldset[disabled] .btn-success.focus, |
|||
.btn-success.disabled:active, |
|||
.btn-success[disabled]:active, |
|||
fieldset[disabled] .btn-success:active, |
|||
.btn-success.disabled.active, |
|||
.btn-success[disabled].active, |
|||
fieldset[disabled] .btn-success.active { |
|||
background-color: #419641; |
|||
background-image: none; |
|||
} |
|||
.btn-info { |
|||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); |
|||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); |
|||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #28a4c9; |
|||
} |
|||
.btn-info:hover, |
|||
.btn-info:focus { |
|||
background-color: #2aabd2; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-info:active, |
|||
.btn-info.active { |
|||
background-color: #2aabd2; |
|||
border-color: #28a4c9; |
|||
} |
|||
.btn-info.disabled, |
|||
.btn-info[disabled], |
|||
fieldset[disabled] .btn-info, |
|||
.btn-info.disabled:hover, |
|||
.btn-info[disabled]:hover, |
|||
fieldset[disabled] .btn-info:hover, |
|||
.btn-info.disabled:focus, |
|||
.btn-info[disabled]:focus, |
|||
fieldset[disabled] .btn-info:focus, |
|||
.btn-info.disabled.focus, |
|||
.btn-info[disabled].focus, |
|||
fieldset[disabled] .btn-info.focus, |
|||
.btn-info.disabled:active, |
|||
.btn-info[disabled]:active, |
|||
fieldset[disabled] .btn-info:active, |
|||
.btn-info.disabled.active, |
|||
.btn-info[disabled].active, |
|||
fieldset[disabled] .btn-info.active { |
|||
background-color: #2aabd2; |
|||
background-image: none; |
|||
} |
|||
.btn-warning { |
|||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); |
|||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); |
|||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #e38d13; |
|||
} |
|||
.btn-warning:hover, |
|||
.btn-warning:focus { |
|||
background-color: #eb9316; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-warning:active, |
|||
.btn-warning.active { |
|||
background-color: #eb9316; |
|||
border-color: #e38d13; |
|||
} |
|||
.btn-warning.disabled, |
|||
.btn-warning[disabled], |
|||
fieldset[disabled] .btn-warning, |
|||
.btn-warning.disabled:hover, |
|||
.btn-warning[disabled]:hover, |
|||
fieldset[disabled] .btn-warning:hover, |
|||
.btn-warning.disabled:focus, |
|||
.btn-warning[disabled]:focus, |
|||
fieldset[disabled] .btn-warning:focus, |
|||
.btn-warning.disabled.focus, |
|||
.btn-warning[disabled].focus, |
|||
fieldset[disabled] .btn-warning.focus, |
|||
.btn-warning.disabled:active, |
|||
.btn-warning[disabled]:active, |
|||
fieldset[disabled] .btn-warning:active, |
|||
.btn-warning.disabled.active, |
|||
.btn-warning[disabled].active, |
|||
fieldset[disabled] .btn-warning.active { |
|||
background-color: #eb9316; |
|||
background-image: none; |
|||
} |
|||
.btn-danger { |
|||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); |
|||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); |
|||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-color: #b92c28; |
|||
} |
|||
.btn-danger:hover, |
|||
.btn-danger:focus { |
|||
background-color: #c12e2a; |
|||
background-position: 0 -15px; |
|||
} |
|||
.btn-danger:active, |
|||
.btn-danger.active { |
|||
background-color: #c12e2a; |
|||
border-color: #b92c28; |
|||
} |
|||
.btn-danger.disabled, |
|||
.btn-danger[disabled], |
|||
fieldset[disabled] .btn-danger, |
|||
.btn-danger.disabled:hover, |
|||
.btn-danger[disabled]:hover, |
|||
fieldset[disabled] .btn-danger:hover, |
|||
.btn-danger.disabled:focus, |
|||
.btn-danger[disabled]:focus, |
|||
fieldset[disabled] .btn-danger:focus, |
|||
.btn-danger.disabled.focus, |
|||
.btn-danger[disabled].focus, |
|||
fieldset[disabled] .btn-danger.focus, |
|||
.btn-danger.disabled:active, |
|||
.btn-danger[disabled]:active, |
|||
fieldset[disabled] .btn-danger:active, |
|||
.btn-danger.disabled.active, |
|||
.btn-danger[disabled].active, |
|||
fieldset[disabled] .btn-danger.active { |
|||
background-color: #c12e2a; |
|||
background-image: none; |
|||
} |
|||
.thumbnail, |
|||
.img-thumbnail { |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
} |
|||
.dropdown-menu > li > a:hover, |
|||
.dropdown-menu > li > a:focus { |
|||
background-color: #e8e8e8; |
|||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); |
|||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.dropdown-menu > .active > a, |
|||
.dropdown-menu > .active > a:hover, |
|||
.dropdown-menu > .active > a:focus { |
|||
background-color: #2e6da4; |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.navbar-default { |
|||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); |
|||
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); |
|||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-radius: 4px; |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); |
|||
} |
|||
.navbar-default .navbar-nav > .open > a, |
|||
.navbar-default .navbar-nav > .active > a { |
|||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); |
|||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); |
|||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); |
|||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); |
|||
} |
|||
.navbar-brand, |
|||
.navbar-nav > li > a { |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25); |
|||
} |
|||
.navbar-inverse { |
|||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); |
|||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); |
|||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); |
|||
background-repeat: repeat-x; |
|||
border-radius: 4px; |
|||
} |
|||
.navbar-inverse .navbar-nav > .open > a, |
|||
.navbar-inverse .navbar-nav > .active > a { |
|||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); |
|||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); |
|||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); |
|||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); |
|||
} |
|||
.navbar-inverse .navbar-brand, |
|||
.navbar-inverse .navbar-nav > li > a { |
|||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); |
|||
} |
|||
.navbar-static-top, |
|||
.navbar-fixed-top, |
|||
.navbar-fixed-bottom { |
|||
border-radius: 0; |
|||
} |
|||
@media (max-width: 767px) { |
|||
.navbar .navbar-nav .open .dropdown-menu > .active > a, |
|||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover, |
|||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus { |
|||
color: #fff; |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
} |
|||
.alert { |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2); |
|||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); |
|||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); |
|||
} |
|||
.alert-success { |
|||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); |
|||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); |
|||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #b2dba1; |
|||
} |
|||
.alert-info { |
|||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); |
|||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); |
|||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #9acfea; |
|||
} |
|||
.alert-warning { |
|||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); |
|||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); |
|||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #f5e79e; |
|||
} |
|||
.alert-danger { |
|||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); |
|||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); |
|||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #dca7a7; |
|||
} |
|||
.progress { |
|||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); |
|||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); |
|||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-success { |
|||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); |
|||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); |
|||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-info { |
|||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); |
|||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); |
|||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-warning { |
|||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); |
|||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); |
|||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-danger { |
|||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); |
|||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); |
|||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.progress-bar-striped { |
|||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); |
|||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); |
|||
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); |
|||
} |
|||
.list-group { |
|||
border-radius: 4px; |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075); |
|||
} |
|||
.list-group-item.active, |
|||
.list-group-item.active:hover, |
|||
.list-group-item.active:focus { |
|||
text-shadow: 0 -1px 0 #286090; |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #2b669a; |
|||
} |
|||
.list-group-item.active .badge, |
|||
.list-group-item.active:hover .badge, |
|||
.list-group-item.active:focus .badge { |
|||
text-shadow: none; |
|||
} |
|||
.panel { |
|||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); |
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05); |
|||
} |
|||
.panel-default > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); |
|||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-primary > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); |
|||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-success > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); |
|||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); |
|||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-info > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); |
|||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); |
|||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-warning > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); |
|||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); |
|||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.panel-danger > .panel-heading { |
|||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); |
|||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); |
|||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
} |
|||
.well { |
|||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); |
|||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); |
|||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); |
|||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); |
|||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); |
|||
background-repeat: repeat-x; |
|||
border-color: #dcdcdc; |
|||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); |
|||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); |
|||
} |
|||
/*# sourceMappingURL=bootstrap-theme.css.map */ |
File diff suppressed because it is too large
@ -0,0 +1,147 @@ |
|||
<div id="addresses-tool"> |
|||
<h1>ADDRESSES TOOL</h1> |
|||
|
|||
<div class="box-context">Check if an address is tracked by your Dojo. Import and track a new address. Rescan the full history of an address.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<!-- ADDRESS SEARCH FORM --> |
|||
<div id="addresses-tool-search-form" class="fullwidth box"> |
|||
<div class="box-body"> |
|||
<span>Check if </span> |
|||
<input id="address" type="text" placeholder="address"> |
|||
<span> is tracked by your Dojo </span> |
|||
<button id="btn-address-search-go" class="btn btn-success" type="button">GO</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- ADDRESS IMPORT --> |
|||
<div id="addresses-tool-import" class="fullwidth box"> |
|||
<div class="box-body"> |
|||
<div> |
|||
<span>This address isn't tracked by your Dojo.</span> |
|||
</div> |
|||
<div class="spacer20"></div> |
|||
<div> |
|||
<span>Do you want to import </span> |
|||
<span id="import-address"></span> |
|||
<span> and track its activity?</span> |
|||
<button id="btn-address-import-go" class="btn btn-success" type="button">IMPORT</button> |
|||
<button id="btn-address-import-cancel" class="btn btn-success" type="button">CANCEL</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- ADDRESS DETAILS --> |
|||
<div id="addresses-tool-details"> |
|||
<div id="addresses-tool-header" class="row box-main"> |
|||
<div class="fullwidth box"> |
|||
<div id="addr-value" class="box-body center"></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="addresses-tool-actions" class="row box-main"> |
|||
<div class="center"> |
|||
<button id="btn-address-details-rescan" class="btn btn-success" type="button">RESCAN THIS ADDRESS</button> |
|||
<button id="btn-address-details-reset" class="btn btn-success" type="button">SEARCH ANOTHER ADDRESS</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="addresses-rescans-actions" class="row box-main"> |
|||
<div class="center"> |
|||
<span>Do you want to rescan this address?</span> |
|||
<button id="btn-address-rescan-go" class="btn btn-success" type="button">RESCAN</button> |
|||
<button id="btn-address-rescan-cancel" class="btn btn-success" type="button">CANCEL</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="addresses-tool-details-row1" class="row box-main"> |
|||
<!-- GENERAL INFO --> |
|||
<div id="box-general" class="fullwidth box"> |
|||
<div class="box-header">GENERAL INFO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Balance</td> |
|||
<td class="table-value" id="addr-balance"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of Txs</td> |
|||
<td class="table-value" id="addr-nb-txs"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of UTXOs</td> |
|||
<td class="table-value" id="addr-nb-utxos"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Segwit</td> |
|||
<td class="table-value" id="addr-segwit"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Address Type</td> |
|||
<td class="table-value" id="addr-type"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="addresses-tool-details-row2" class="row box-main"> |
|||
<!-- HD ADDRESS INFO --> |
|||
<div id="box-hd" class="fullwidth box"> |
|||
<div class="box-header">DERIVATION INFO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Derivation path</td> |
|||
<td class="table-value" id="addr-deriv-path"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label" colspan="2">Derived from</td> |
|||
</tr> |
|||
<tr> |
|||
<td id="addr-xpub" colspan="2"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="addresses-tool-details-row3" class="row box-main"> |
|||
<!-- TXS LIST --> |
|||
<div id="box-txs" class="halfwidth-left box"> |
|||
<div class="box-header">MOST RECENT TRANSACTIONS</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table id="addr-table-list-txs"> |
|||
<tbody> |
|||
<tr> |
|||
<td></td> |
|||
<td></td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
<!-- UTXOS LIST --> |
|||
<div id="box-utxos" class="halfwidth-right box"> |
|||
<div class="box-header">UNSPENT TRANSACTION OUTPUTS</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table id="addr-table-list-utxos"> |
|||
<tbody> |
|||
<tr> |
|||
<td></td> |
|||
<td></td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script include-js="addresses-tools/addresses-tools.js"></script> |
@ -0,0 +1,228 @@ |
|||
const screenAddressesToolsScript = { |
|||
|
|||
explorerInfo: null, |
|||
currentAddress: null, |
|||
|
|||
initPage: function() { |
|||
this.getExplorerInfo() |
|||
// Sets the event handlers
|
|||
$('#btn-address-search-go').click(() => {this.searchAddress()}) |
|||
$('#btn-address-details-reset').click(() => {this.showSearchForm()}) |
|||
$('#btn-address-details-rescan').click(() => {this.showRescanForm()}) |
|||
$('#btn-address-rescan-go').click(() => {this.rescanAddress()}) |
|||
$('#btn-address-rescan-cancel').click(() => {this.hideRescanForm()}) |
|||
$('#btn-address-import-go').click(() => {this.importAddress()}) |
|||
$('#btn-address-import-cancel').click(() => {this.showSearchForm()}) |
|||
$('#addresses-tool').keyup(evt => { |
|||
if (evt.keyCode === 13) { |
|||
this.searchAddress() |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
preparePage: function() { |
|||
this.hideRescanForm() |
|||
this.showSearchForm() |
|||
$("#address").focus() |
|||
}, |
|||
|
|||
getExplorerInfo: function() { |
|||
lib_api.getExplorerPairingInfo().then(explorerInfo => { |
|||
this.explorerInfo = explorerInfo |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
searchAddress: function() { |
|||
lib_msg.displayMessage('Search in progress...'); |
|||
const address = $('#address').val() |
|||
this.currentAddress = address |
|||
return this._searchAddress(address).then(() => { |
|||
lib_msg.cleanMessagesUi() |
|||
}) |
|||
}, |
|||
|
|||
_searchAddress: function(address) { |
|||
return lib_api.getAddressInfo(address).then(addressInfo => { |
|||
if (addressInfo && addressInfo['tracked']) { |
|||
this.setAddressDetails(addressInfo) |
|||
this.showAddressDetails() |
|||
const jsonData = {'active': address} |
|||
return lib_api.getWallet(jsonData).then(walletInfo => { |
|||
// Display the txs
|
|||
const txs = walletInfo['txs'] |
|||
for (let tx of txs) |
|||
this.setTxDetails(tx) |
|||
// Display the UTXOs
|
|||
const utxos = walletInfo['unspent_outputs'].sort((a,b) => { |
|||
return a['confirmations'] - b['confirmations'] |
|||
}) |
|||
$('#addr-nb-utxos').text(utxos.length) |
|||
for (let utxo of utxos) |
|||
this.setUtxoDetails(utxo) |
|||
}) |
|||
} else { |
|||
lib_msg.displayErrors('address not found') |
|||
this.showImportForm(false) |
|||
} |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
throw e |
|||
}) |
|||
}, |
|||
|
|||
importAddress: function() { |
|||
lib_msg.displayMessage('Processing address import...'); |
|||
const jsonData = {'active': this.currentAddress} |
|||
return lib_api.getWallet(jsonData) |
|||
.then(result => { |
|||
this._searchAddress(this.currentAddress).then(() => { |
|||
lib_msg.displayInfo('Import complete') |
|||
}) |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
rescanAddress: function() { |
|||
lib_msg.displayMessage('Processing address rescan...'); |
|||
return lib_api.getAddressRescan(this.currentAddress) |
|||
.then(result => { |
|||
this.hideRescanForm() |
|||
this._searchAddress(this.currentAddress).then(() => { |
|||
lib_msg.displayInfo('Rescan complete') |
|||
}) |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
setAddressDetails: function(addressInfo) { |
|||
$('tr.tx-row').remove() |
|||
$('tr.utxo-row').remove() |
|||
|
|||
$('#addr-value').text(this.currentAddress) |
|||
$('#addr-nb-txs').text(addressInfo['n_tx']) |
|||
$('#addr-nb-utxos').text('-') |
|||
|
|||
const balance = parseInt(addressInfo['balance']) / 100000000 |
|||
$('#addr-balance').text(`${balance} BTC`) |
|||
|
|||
const addrType = (addressInfo['type'] == 'hd') ? 'Derived from an XPUB' : 'Loose address' |
|||
$('#addr-type').text(addrType) |
|||
|
|||
if (addressInfo['segwit']) { |
|||
$('#addr-segwit').html('✓') |
|||
$('#addr-segwit').css('color', '#76d776') |
|||
} else { |
|||
$('#addr-segwit').text('-') |
|||
$('#addr-segwit').css('color', '#f77c7c') |
|||
} |
|||
|
|||
if (addressInfo['type'] == 'hd') { |
|||
$('#addr-xpub').text(addressInfo['xpub']) |
|||
$('#addr-deriv-path').text(addressInfo['path']) |
|||
$('#addresses-tool-details-row2').show() |
|||
} else { |
|||
$('#addresses-tool-details-row2').hide() |
|||
} |
|||
}, |
|||
|
|||
setTxDetails: function(tx) { |
|||
const txid = tx['hash'] |
|||
const txidDisplay = `${txid.substring(0,50)}...` |
|||
const amount = parseInt(tx['result']) / 100000000 |
|||
const amountLabel = amount < 0 ? amount : `+${amount}` |
|||
const amountStyle = amount < 0 ? 'amount-sent' : 'amount-received' |
|||
const date = lib_fmt.unixTsToLocaleString(tx['time']) |
|||
const txUrl = lib_cmn.getExplorerTxUrl(txid, this.explorerInfo) |
|||
|
|||
const newRow = `<tr class="tx-row"><td colspan="2"> </td></tr>
|
|||
<tr class="tx-row"> |
|||
<td class="table-label" colspan="2"> |
|||
<a href="${txUrl}" target="_blank">${txidDisplay}</a> |
|||
</td> |
|||
</tr> |
|||
<tr class="tx-row"> |
|||
<td class="table-label">Amount</td> |
|||
<td class="table-value ${amountStyle}">${amountLabel} BTC</td> |
|||
</tr> |
|||
<tr class="tx-row"> |
|||
<td class="table-label">Block height</td> |
|||
<td class="table-value">${tx['block_height']}</td> |
|||
</tr> |
|||
<tr class="tx-row"> |
|||
<td class="table-label">Date</td> |
|||
<td class="table-value">${date}</td> |
|||
</tr>` |
|||
|
|||
$('#addr-table-list-txs tr:last').after(newRow) |
|||
}, |
|||
|
|||
setUtxoDetails: function(utxo) { |
|||
const txid = utxo['tx_hash'] |
|||
const txidVout = `${txid.substring(0,50)}...:${utxo['tx_output_n']}` |
|||
const amount = parseInt(utxo['value']) / 100000000 |
|||
const txUrl = lib_cmn.getExplorerTxUrl(txid, this.explorerInfo) |
|||
|
|||
const newRow = `<tr class="utxo-row"><td colspan="2"> </td></tr>
|
|||
<tr class="utxo-row"> |
|||
<td class="table-label" colspan="2"> |
|||
<a href="${txUrl}" target="_blank">${txidVout}</a> |
|||
</td> |
|||
</tr> |
|||
<tr class="utxo-row"> |
|||
<td class="table-label">Amount</td> |
|||
<td class="table-value">${amount} BTC</td> |
|||
</tr> |
|||
<tr class="utxo-row"> |
|||
<td class="table-label">Address</td> |
|||
<td class="table-value">${utxo['addr']}</td> |
|||
</tr> |
|||
<tr class="utxo-row"> |
|||
<td class="table-label">Confirmations</td> |
|||
<td class="table-value">${utxo['confirmations']}</td> |
|||
</tr>` |
|||
|
|||
$('#addr-table-list-utxos tr:last').after(newRow) |
|||
}, |
|||
|
|||
showSearchForm: function() { |
|||
$('#addresses-tool-details').hide() |
|||
$('#addresses-tool-import').hide() |
|||
$('#address').val('') |
|||
$('#addresses-tool-search-form').show() |
|||
lib_msg.cleanMessagesUi() |
|||
}, |
|||
|
|||
showImportForm: function() { |
|||
$('#addresses-tool-search-form').hide() |
|||
$('#addresses-tool-details').hide() |
|||
$('#import-address').text(this.currentAddress) |
|||
$('#addresses-tool-import').show() |
|||
}, |
|||
|
|||
showAddressDetails: function() { |
|||
$('#addresses-tool-search-form').hide() |
|||
$('#addresses-tool-import').hide() |
|||
$('#addresses-tool-details').show() |
|||
}, |
|||
|
|||
showRescanForm: function() { |
|||
$('#addresses-tool-actions').hide() |
|||
$('#addresses-rescans-actions').show() |
|||
lib_msg.cleanMessagesUi() |
|||
}, |
|||
|
|||
hideRescanForm: function() { |
|||
$('#addresses-rescans-actions').hide() |
|||
$('#addresses-tool-actions').show() |
|||
}, |
|||
} |
|||
|
|||
screenScripts.set('#screen-addresses-tools', screenAddressesToolsScript) |
@ -0,0 +1,22 @@ |
|||
<div id="blocks-rescan"> |
|||
<h1>BLOCKS RESCAN</h1> |
|||
|
|||
<div class="box-context">Force the Tracker to rescan a range of blocks.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<div id="blocks-rescan-form" class="box fullwidth"> |
|||
<div class="box-body"> |
|||
<span>Rescan blocks between</span> |
|||
<input id="rescan-from-height" type="text" placeholder="height"> |
|||
<span> and </span> |
|||
<input id="rescan-to-height" type="text" placeholder="height"> |
|||
<button id="btn-rescan-go" |
|||
class="btn btn-success" |
|||
type="button">GO</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<script include-js="blocks-rescan/blocks-rescan.js"></script> |
@ -0,0 +1,45 @@ |
|||
const screenBlocksRescanScript = { |
|||
|
|||
initPage: function() { |
|||
// Sets the event handlers
|
|||
$('#btn-rescan-go').click(() => { |
|||
this.processRescan() |
|||
}) |
|||
$('#blocks-rescan').keyup(evt => { |
|||
if (evt.keyCode === 13) { |
|||
this.processRescan() |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
preparePage: function() { |
|||
$("#rescan-from-height").focus() |
|||
}, |
|||
|
|||
processRescan: function() { |
|||
lib_msg.displayMessage('Processing...'); |
|||
|
|||
let fromHeight = $("#rescan-from-height").val() |
|||
let toHeight = $("#rescan-to-height").val() |
|||
fromHeight = parseInt(fromHeight) |
|||
toHeight = (toHeight) ? parseInt(toHeight) : fromHeight; |
|||
|
|||
lib_api.getBlocksRescan(fromHeight, toHeight).then(result => { |
|||
if (!result) |
|||
return |
|||
const fromHeightRes = result['fromHeight'] |
|||
const toHeightRes = result['toHeight'] |
|||
const msg = `successfully rescanned blocks between height ${fromHeightRes} and height ${toHeightRes}` |
|||
lib_msg.displayInfo(msg) |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}).then(() => { |
|||
$('#rescan-from-height').val('') |
|||
$('#rescan-to-height').val('') |
|||
}) |
|||
}, |
|||
|
|||
} |
|||
|
|||
screenScripts.set('#screen-blocks-rescan', screenBlocksRescanScript) |
@ -0,0 +1,152 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>DOJO // MAINTENANCE TOOL</title> |
|||
<link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css"> |
|||
<link rel="stylesheet" type="text/css" href="../css/bootstrap-theme.min.css"> |
|||
<link rel="stylesheet" type="text/css" href="../css/style.css"> |
|||
<script src="../lib/jquery-3.5.1.min.js"></script> |
|||
<script src="../lib/jquery.qrcode.min.js"></script> |
|||
<script src="../conf/index.js"></script> |
|||
<script src="../lib/common-script.js"></script> |
|||
<script src="../lib/api-wrapper.js"></script> |
|||
<script src="../lib/auth-utils.js"></script> |
|||
<script src="../lib/format-utils.js"></script> |
|||
<script src="../lib/messages.js"></script> |
|||
<script src="index.js"></script> |
|||
</head> |
|||
|
|||
<body class="dmt"> |
|||
<div id="top-container" class="container" style="display: none"> |
|||
<!-- HEADER --> |
|||
<div id="header" class="row"> |
|||
<div class="col-xs-9"> |
|||
<h1 class="title"><span>DOJO // MAINTENANCE TOOL</span> <span id="dojo-version" class="beta">beta</span></h1> |
|||
</div> |
|||
<div class="col-xs-3 login-box"> |
|||
<a id="btn-logout" style="display: inline;" href="#" title="DISCONNECT"> |
|||
<img src="../icons/ic_power_settings_new_white_24dp_1x.png" class="mini-icon"/> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="spacer30"></div> |
|||
|
|||
<!-- BODY --> |
|||
<div id="body" class="row"> |
|||
<!-- MENU --> |
|||
<div id="menu" class="col-xs-2"> |
|||
<div class="title"> |
|||
<h1>MONITORING</h1> |
|||
</div> |
|||
<ul id="tab-menu_list" class="nav nav-pills nav-stacked"> |
|||
<li id="link-welcome" style="display: none;"> |
|||
<a href="#">WELCOME</a> |
|||
</li> |
|||
<li id="link-status"> |
|||
<a href="#">DOJO STATUS</a> |
|||
</li> |
|||
<li id="link-pushtx"> |
|||
<a href="#">PUSHTX STATUS</a> |
|||
</li> |
|||
</ul> |
|||
<div class="spacer20"></div> |
|||
<div class="title"> |
|||
<h1>TOOLS</h1> |
|||
</div> |
|||
<ul id="tab-menu_list2" class="nav nav-pills nav-stacked"> |
|||
<li id="link-pairing"> |
|||
<a href="#">PAIRING</a> |
|||
</li> |
|||
<li id="link-xpubs-tools"> |
|||
<a href="#">XPUBS TOOL</a> |
|||
</li> |
|||
<li id="link-addresses-tools"> |
|||
<a href="#">ADDRESSES TOOL</a> |
|||
</li> |
|||
<li id="link-txs-tools"> |
|||
<a href="#">TRANSACTIONS TOOL</a> |
|||
</li> |
|||
<li id="link-blocks-rescan"> |
|||
<a href="#">BLOCKS RESCAN</a> |
|||
</li> |
|||
</ul> |
|||
<div class="spacer20"></div> |
|||
<div class="title"> |
|||
<h1>HELP</h1> |
|||
</div> |
|||
<ul id="tab-menu_list3" class="nav nav-pills nav-stacked"> |
|||
<li id="link-help-dmt"> |
|||
<a href="#">HELP DMT</a> |
|||
</li> |
|||
<li id="link-dojo-telegram"> |
|||
<a href="https://t.me/samourai_dojo" target="_blank">DOJO TELEGRAM CHAT</a> |
|||
</li> |
|||
<li id="link-wp-telegram"> |
|||
<a href="https://t.me/whirlpool_trollbox" target="_blank">WHIRLPOOL TELEGRAM CHAT</a> |
|||
</li> |
|||
<li id="link-sw-support"> |
|||
<a href="https://t.me/SamouraiWallet" target="_blank">SAMOURAI TELEGRAM CHAT</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="col-xs-1"></div> |
|||
<!-- MAIN AREA --> |
|||
<div id="main" class="col-xs-9"> |
|||
<!-- WELCOME --> |
|||
<div id="screen-welcome" |
|||
include-html="welcome/welcome.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- STATUS --> |
|||
<div id="screen-status" |
|||
include-html="status/status.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- PUSH TX --> |
|||
<div id="screen-pushtx" |
|||
include-html="pushtx/pushtx.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- PAIRING --> |
|||
<div id="screen-pairing" |
|||
include-html="pairing/pairing.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- XPUBS TOOLS --> |
|||
<div id="screen-xpubs-tools" |
|||
include-html="xpubs-tools/xpubs-tools.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- ADDRESSES TOOLS --> |
|||
<div id="screen-addresses-tools" |
|||
include-html="addresses-tools/addresses-tools.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- TRANSACTIONS TOOLS --> |
|||
<div id="screen-txs-tools" |
|||
include-html="txs-tools/txs-tools.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- BLOCKS RESCAN --> |
|||
<div id="screen-blocks-rescan" |
|||
include-html="blocks-rescan/blocks-rescan.html" |
|||
style="display: none"> |
|||
</div> |
|||
<!-- HELP DMT --> |
|||
<div id="screen-help-dmt" |
|||
include-html="welcome/welcome.html" |
|||
style="display: none"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- MSG BOX --> |
|||
<div id="box-msg" |
|||
include-html="msg-box/msg-box.html"> |
|||
</div> |
|||
</body> |
|||
|
|||
</html> |
@ -0,0 +1,116 @@ |
|||
/** |
|||
* Global obkjects |
|||
*/ |
|||
|
|||
// Ordered list of screens
|
|||
const screens = [ |
|||
'#screen-welcome', |
|||
'#screen-status', |
|||
'#screen-pushtx', |
|||
'#screen-pairing', |
|||
'#screen-xpubs-tools', |
|||
'#screen-addresses-tools', |
|||
'#screen-txs-tools', |
|||
'#screen-blocks-rescan', |
|||
'#screen-help-dmt' |
|||
] |
|||
|
|||
// Ordered list of menu items
|
|||
const tabs = [ |
|||
'#link-welcome', |
|||
'#link-status', |
|||
'#link-pushtx', |
|||
'#link-pairing', |
|||
'#link-xpubs-tools', |
|||
'#link-addresses-tools', |
|||
'#link-txs-tools', |
|||
'#link-blocks-rescan', |
|||
'#link-help-dmt' |
|||
] |
|||
|
|||
// Mapping of scripts associaed to screens
|
|||
const screenScripts = new Map() |
|||
|
|||
|
|||
/** |
|||
* UI initialization |
|||
*/ |
|||
function initTabs() { |
|||
// Activates the current tab
|
|||
let currentTab = sessionStorage.getItem('activeTab') |
|||
if (!currentTab) |
|||
currentTab = '#link-status' |
|||
$(currentTab).addClass('active') |
|||
|
|||
// Sets event handlers
|
|||
for (let tab of tabs) { |
|||
$(tab).click(function() { |
|||
$(sessionStorage.getItem('activeTab')).removeClass('active') |
|||
sessionStorage.setItem('activeTab', tab) |
|||
$(tab).addClass('active') |
|||
preparePage() |
|||
}) |
|||
} |
|||
} |
|||
|
|||
function initPages() { |
|||
// Dynamic loading of screens and scripts
|
|||
lib_cmn.includeHTML(_initPages) |
|||
// Dojo version
|
|||
let lblVersion = sessionStorage.getItem('lblVersion') |
|||
if (lblVersion == null) { |
|||
lib_api.getPairingInfo().then(apiInfo => { |
|||
lblVersion = 'v' + apiInfo['pairing']['version'] + ' beta' |
|||
sessionStorage.setItem('lblVersion', lblVersion) |
|||
$('#dojo-version').text(lblVersion) |
|||
}) |
|||
} else { |
|||
$('#dojo-version').text(lblVersion) |
|||
} |
|||
} |
|||
|
|||
function _initPages() { |
|||
for (let screen of screens) { |
|||
const screenScript = screenScripts.get(screen) |
|||
if (screenScript) |
|||
screenScript.initPage() |
|||
} |
|||
preparePage() |
|||
$('#top-container').show() |
|||
} |
|||
|
|||
function preparePage() { |
|||
lib_msg.cleanMessagesUi() |
|||
const activeTab = sessionStorage.getItem('activeTab') |
|||
for (let idxTab in tabs) { |
|||
const screen = screens[idxTab] |
|||
if (tabs[idxTab] == activeTab) { |
|||
$(screen).show() |
|||
if (screenScripts.has(screen)) |
|||
screenScripts.get(screen).preparePage() |
|||
} else { |
|||
$(screen).hide() |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Processing on loading completed |
|||
*/ |
|||
$(document).ready(function() { |
|||
// Refresh the access token
|
|||
lib_auth.refreshAccessToken() |
|||
setInterval(() => { |
|||
lib_auth.refreshAccessToken() |
|||
}, 300000) |
|||
|
|||
// Inits menu and pages
|
|||
initTabs() |
|||
initPages() |
|||
|
|||
// Set event handlers
|
|||
$('#btn-logout').click(function() { |
|||
lib_auth.logout() |
|||
}) |
|||
}) |
@ -0,0 +1,7 @@ |
|||
<div class="row box-msg"> |
|||
<div class="col-xs-12"> |
|||
<div id="msg" class="msg"></div> |
|||
<div id="errors" class="msg-error"></div> |
|||
<div id="info" class="msg-info"></div> |
|||
</div> |
|||
</div> |
@ -0,0 +1,33 @@ |
|||
<div id="pairing"> |
|||
<h1>PAIRING</h1> |
|||
|
|||
<div class="box-context">Pair your wallet to your Dojo and to your Block Explorer with a simple QRCode.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<div id="dojo-pairing" class="halfwidth-left box"> |
|||
<div class="box-header">DOJO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body" id="qr-container"> |
|||
<div class="center">Scan this QRCode with your wallet</div> |
|||
<div class="spacer10"></div> |
|||
<div id="qr-pairing"></div> |
|||
<div class="spacer10"></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="explorer-pairing" class="halfwidth-right box"> |
|||
<div class="box-header">BLOCK EXPLORER</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body" id="qr-explorer-container"> |
|||
<div class="center">Scan this QRCode with your wallet</div> |
|||
<div class="spacer10"></div> |
|||
<div id="qr-explorer-pairing"></div> |
|||
<div class="spacer10"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
|
|||
<script include-js="pairing/pairing.js"></script> |
@ -0,0 +1,65 @@ |
|||
const screenPairingScript = { |
|||
|
|||
initPage: function() {}, |
|||
|
|||
preparePage: function() { |
|||
this.displayQRPairing() |
|||
}, |
|||
|
|||
loadPairingPayloads: function() { |
|||
let result = { |
|||
'api': null, |
|||
'explorer': null |
|||
} |
|||
|
|||
lib_msg.displayMessage('Loading pairing payloads...'); |
|||
|
|||
return lib_api.getPairingInfo().then(apiInfo => { |
|||
if (apiInfo) { |
|||
apiInfo['pairing']['url'] = window.location.protocol + '//' + window.location.host + conf['api']['baseUri'] |
|||
result['api'] = apiInfo |
|||
} |
|||
}).then(() => { |
|||
return lib_api.getExplorerPairingInfo() |
|||
}).then(explorerInfo => { |
|||
if (explorerInfo) |
|||
result['explorer'] = explorerInfo |
|||
lib_msg.cleanMessagesUi() |
|||
return result |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
return result |
|||
}) |
|||
}, |
|||
|
|||
displayQRPairing: function() { |
|||
this.loadPairingPayloads().then( |
|||
function (result) { |
|||
if (result) { |
|||
if (result['api']) { |
|||
const textJson = JSON.stringify(result['api'], null, 4) |
|||
$("#qr-pairing").html('') // clear qrcode first
|
|||
$('#qr-pairing').qrcode({width: 256, height: 256, text: textJson}) |
|||
} |
|||
if (result['explorer'] && result['explorer']['pairing']['url']) { |
|||
const textJson = JSON.stringify(result['explorer'], null, 4) |
|||
$("#qr-explorer-pairing").html('') // clear qrcode first
|
|||
$('#qr-explorer-pairing').qrcode({width: 256, height: 256, text: textJson}) |
|||
} else { |
|||
$("#qr-label").removeClass('halfwidth') |
|||
$("#qr-label").addClass('fullwidth') |
|||
$("#qr-container").removeClass('halfwidth') |
|||
$("#qr-container").addClass('fullwidth') |
|||
$("#qr-explorer-label").hide() |
|||
$("#qr-explorer-container").hide() |
|||
} |
|||
} |
|||
}, |
|||
function (jqxhr) {} |
|||
); |
|||
} |
|||
|
|||
} |
|||
|
|||
screenScripts.set('#screen-pairing', screenPairingScript) |
@ -0,0 +1,44 @@ |
|||
<div id="pushtx-status"> |
|||
<h1>PUSHTX STATUS</h1> |
|||
|
|||
<div class="box-context">Monitor the transactions pushed through your Dojo.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<div id="txs-pushed" class="box fullwidth"> |
|||
<div class="box-header">TRANSACTIONS PUSHED</div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Uptime</td> |
|||
<td class="table-value" id="pushed-uptime"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of Transactions</td> |
|||
<td class="table-value" id="pushed-count"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Total Amount</td> |
|||
<td class="table-value" id="pushed-amount"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row box-main"> |
|||
<div id="txs-scheduled" class="box fullwidth"> |
|||
<div class="box-header">TRANSACTIONS SCHEDULED</div> |
|||
<div class="box-body"> |
|||
<table id="table-scheduled-txs"> |
|||
<thead> |
|||
<td colspan="2"></td> |
|||
</thead> |
|||
<tbody></tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
<script include-js="pushtx/pushtx.js"></script> |
@ -0,0 +1,90 @@ |
|||
const pushtxScript = { |
|||
|
|||
processedSchedTxs: new Set(), |
|||
|
|||
initPage: function() { |
|||
// Refresh PushTx status
|
|||
setInterval(() => {this.refreshPushTxStatus()}, 60000) |
|||
// Refresh ScheduledTxs list
|
|||
setInterval(() => {this.refreshScheduledTxsList()}, 60000) |
|||
}, |
|||
|
|||
preparePage: function() { |
|||
this.refreshPushTxStatus() |
|||
this.refreshScheduledTxsList() |
|||
}, |
|||
|
|||
refreshPushTxStatus: function() { |
|||
lib_msg.displayMessage('Loading PushTx status info...'); |
|||
lib_api.getPushtxStatus().then(pushTxStatus => { |
|||
if (pushTxStatus) { |
|||
const data = pushTxStatus['data'] |
|||
const uptime = lib_cmn.timePeriod(data['uptime']) |
|||
$('#pushed-uptime').text(uptime) |
|||
$('#pushed-count').text(data['push']['count']) |
|||
$('#pushed-amount').text(data['push']['amount']) |
|||
lib_msg.cleanMessagesUi() |
|||
} |
|||
}).catch(e => { |
|||
$('#pushed-uptime').text('-') |
|||
$('#pushed-count').text('-') |
|||
$('#pushed-amount').text('-') |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
refreshScheduledTxsList: function() { |
|||
lib_msg.displayMessage('Loading PushTx orchestrator status info...'); |
|||
lib_api.getOrchestratorStatus().then(orchestrStatus => { |
|||
if(orchestrStatus) { |
|||
const data = orchestrStatus['data'] |
|||
for (let tx of data['txs']) { |
|||
if (!this.processedSchedTxs.has(tx['schTxid'])) { |
|||
this.displayScheduledTx(tx) |
|||
this.processedSchedTxs.add(tx['schTxid']) |
|||
} |
|||
} |
|||
lib_msg.cleanMessagesUi() |
|||
} |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
displayScheduledTx: function(tx) { |
|||
const newRow = `<tr><td colspan="2"> </td></tr>
|
|||
<tr class="table-value"> |
|||
<td class="table-label">TXID</td> |
|||
<td class="table-value" id="scheduled-txid">${tx['schTxid']}</td> |
|||
</tr> |
|||
<tr class="table-value"> |
|||
<td class="table-label">Schedule Id</td> |
|||
<td class="table-value" id="scheduled-txid">${tx['schID']}</td> |
|||
</tr> |
|||
<tr class="table-value"> |
|||
<td class="table-label">Scheduled for block</td> |
|||
<td class="table-value" id="scheduled-trigger">${tx['schTrigger']}</td> |
|||
</tr> |
|||
<tr class="table-value"> |
|||
<td class="table-label">Created on</td> |
|||
<td class="table-value" id="scheduled-created">${lib_fmt.unixTsToLocaleString(tx['schCreated'])}</td> |
|||
</tr> |
|||
<tr class="table-value"> |
|||
<td class="table-label">Parent TXID</td> |
|||
<td class="table-value" id="scheduled-parent-txid">${tx['schParentTxid']}</td> |
|||
</tr> |
|||
<tr class="table-value"> |
|||
<td class="table-label">Raw Transaction</td> |
|||
<td class="table-value" id="scheduled-tx"> |
|||
<pre class="raw-tx">${tx['schRaw']}</pre> |
|||
</td> |
|||
</tr>` |
|||
|
|||
$('#table-scheduled-txs tr:last').after(newRow) |
|||
}, |
|||
|
|||
} |
|||
|
|||
screenScripts.set('#screen-pushtx', pushtxScript) |
@ -0,0 +1,92 @@ |
|||
<div id="status"> |
|||
<h1>DOJO STATUS</h1> |
|||
|
|||
<div class="box-context">Monitor the health of some core components of your Dojo.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<div id="left-column" class="two-columns-left"> |
|||
<div id="bitcoind-status" class="fullwidth box"> |
|||
<div class="box-header">FULL NODE</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Status</td> |
|||
<td class="table-value" id="node-status-ind"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Uptime</td> |
|||
<td class="table-value" id="node-uptime"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Latest block</td> |
|||
<td class="table-value" id="node-chaintip"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Bitcoind version</td> |
|||
<td class="table-value" id="node-version"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Network</td> |
|||
<td class="table-value" id="node-network"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Connected nodes</td> |
|||
<td class="table-value" id="node-conn"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Network relay fee</td> |
|||
<td class="table-value" id="node-relay-fee"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div id="right-column" class="two-columns-right"> |
|||
<div id="tracker-status" class="fullwidth box"> |
|||
<div class="box-header">TRACKER</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Status</td> |
|||
<td class="table-value" id="tracker-status-ind"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Uptime</td> |
|||
<td class="table-value" id="tracker-uptime"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Latest block</td> |
|||
<td class="table-value" id="tracker-chaintip"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="web-status" class="fullwidth box"> |
|||
<div class="box-header">WEB</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Tor status</td> |
|||
<td class="table-value" id="tor-status-ind">✓</td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Nginx status</td> |
|||
<td class="table-value" id="nginx-status-ind">✓</td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Node.js status</td> |
|||
<td class="table-value" id="nodejs-status-ind">✓</td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<script include-js="status/status.js"></script> |
@ -0,0 +1,68 @@ |
|||
const statusScript = { |
|||
|
|||
initPage: function() { |
|||
// Refresh API status
|
|||
setInterval(() => {this.refreshApiStatus()}, 60000) |
|||
// Refresh PushTx status
|
|||
setInterval(() => {this.refreshPushTxStatus()}, 60000) |
|||
}, |
|||
|
|||
preparePage: function() { |
|||
this.refreshApiStatus() |
|||
this.refreshPushTxStatus() |
|||
}, |
|||
|
|||
refreshApiStatus: function() { |
|||
lib_msg.displayMessage('Loading API status info...'); |
|||
return lib_api.getApiStatus().then(apiStatus => { |
|||
if (apiStatus) { |
|||
$('#tracker-status-ind').html('✓') |
|||
$('#tracker-status-ind').css('color', '#76d776') |
|||
$('#tracker-uptime').text(apiStatus['uptime']) |
|||
$('#tracker-chaintip').text(apiStatus['blocks']) |
|||
lib_msg.cleanMessagesUi() |
|||
} |
|||
}).catch(e => { |
|||
$('#tracker-status-ind').text('X') |
|||
$('#tracker-status-ind').css('color', '#f77c7c') |
|||
$('#tracker-uptime').text('-') |
|||
$('#tracker-chaintip').text('-') |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
refreshPushTxStatus: function() { |
|||
lib_msg.displayMessage('Loading Tracker status info...'); |
|||
lib_api.getPushtxStatus().then(pushTxStatus => { |
|||
if (pushTxStatus) { |
|||
const data = pushTxStatus['data'] |
|||
$('#node-status-ind').html('✓') |
|||
$('#node-status-ind').css('color', '#76d776') |
|||
const uptime = lib_cmn.timePeriod(data['uptime']) |
|||
$('#node-uptime').text(uptime) |
|||
$('#node-chaintip').text(data['bitcoind']['blocks']) |
|||
$('#node-version').text(data['bitcoind']['version']) |
|||
const network = data['bitcoind']['testnet'] == true ? 'testnet' : 'mainnet' |
|||
$('#node-network').text(network) |
|||
$('#node-conn').text(data['bitcoind']['conn']) |
|||
$('#node-relay-fee').text(data['bitcoind']['relayfee']) |
|||
lib_msg.cleanMessagesUi() |
|||
} |
|||
}).catch(e => { |
|||
$('#node-status-ind').text('-') |
|||
$('#node-status-ind').css('color', '#f77c7c') |
|||
$('#node-uptime').text('-') |
|||
$('#node-chaintip').text('-') |
|||
$('#node-version').text('-') |
|||
$('#node-network').text('-') |
|||
$('#node-conn').text('-') |
|||
$('#node-relay-fee').text('-') |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
} |
|||
|
|||
screenScripts.set('#screen-status', statusScript) |
@ -0,0 +1,101 @@ |
|||
<div id="txs-tool"> |
|||
<h1>TRANSACTIONS TOOL</h1> |
|||
|
|||
<div class="box-context">Check if a transaction is found in a block or in the mempool of your full node.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<!-- TRANSACTION SEARCH FORM --> |
|||
<div id="txs-tool-search-form" class="fullwidth box"> |
|||
<div class="box-body"> |
|||
<span>Search transaction with this </span> |
|||
<input id="txid" type="text" placeholder="TXID"> |
|||
<button id="btn-tx-search-go" class="btn btn-success" type="button">GO</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- TRANSACTION DETAILS --> |
|||
<div id="txs-tool-details"> |
|||
<div id="txs-tool-header" class="row box-main"> |
|||
<div class="fullwidth box"> |
|||
<div class="box-body center"> |
|||
<a id="txid-value" href="" target="_blank"></a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="txs-tool-actions" class="row box-main"> |
|||
<div class="center"> |
|||
<button id="btn-txs-details-reset" class="btn btn-success" type="button">SEARCH ANOTHER TRANSACTION</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="txs-tool-details-row1" class="row box-main"> |
|||
<!-- GENERAL INFO --> |
|||
<div id="box-general" class="halfwidth-left box"> |
|||
<div class="box-header">GENERAL INFO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">First-seen date</td> |
|||
<td class="table-value" id="tx-firstseen"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Found in</td> |
|||
<td class="table-value" id="tx-location"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Amount</td> |
|||
<td class="table-value" id="tx-amount"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Fees</td> |
|||
<td class="table-value" id="tx-fees"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Feerate</td> |
|||
<td class="table-value" id="tx-vfeerate"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of inputs</td> |
|||
<td class="table-value" id="tx-nb-inputs"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of outputs</td> |
|||
<td class="table-value" id="tx-nb-outputs"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
<!-- TECHNICAL INFO --> |
|||
<div id="box-technical" class="halfwidth-right box"> |
|||
<div class="box-header">TECHNICAL INFO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Virtual size</td> |
|||
<td class="table-value" id="tx-vsize"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Raw size</td> |
|||
<td class="table-value" id="tx-size"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Transaction version</td> |
|||
<td class="table-value" id="tx-version"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">nLockTime</td> |
|||
<td class="table-value" id="tx-nlocktime"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script include-js="txs-tools/txs-tools.js"></script> |
@ -0,0 +1,117 @@ |
|||
const screenTxsToolsScript = { |
|||
|
|||
explorerInfo: null, |
|||
currentTxid: null, |
|||
|
|||
initPage: function() { |
|||
this.getExplorerInfo() |
|||
// Sets the event handlers
|
|||
$('#btn-tx-search-go').click(() => {this.searchTx()}) |
|||
$('#btn-txs-details-reset').click(() => {this.showSearchForm()}) |
|||
$('#txs-tool').keyup(evt => { |
|||
if (evt.keyCode === 13) { |
|||
this.searchTx() |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
preparePage: function() { |
|||
this.showSearchForm() |
|||
$("#txid").focus() |
|||
}, |
|||
|
|||
getExplorerInfo: function() { |
|||
lib_api.getExplorerPairingInfo().then(explorerInfo => { |
|||
this.explorerInfo = explorerInfo |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
searchTx: function() { |
|||
lib_msg.displayMessage('Search in progress...'); |
|||
const txid = $('#txid').val() |
|||
this.currentTxid = txid |
|||
return this._searchTx(txid).then(() => { |
|||
lib_msg.cleanMessagesUi() |
|||
}) |
|||
}, |
|||
|
|||
_searchTx: function(txid) { |
|||
return lib_api.getTransaction(txid).then(txInfo => { |
|||
if (txInfo) { |
|||
console.log(txInfo) |
|||
this.setTxDetails(txInfo) |
|||
this.showTxDetails() |
|||
} |
|||
}).catch(e => { |
|||
lib_msg.displayErrors('No transaction found') |
|||
console.log(e) |
|||
throw e |
|||
}) |
|||
}, |
|||
|
|||
setTxDetails: function(txInfo) { |
|||
$('tr.input-row').remove() |
|||
$('tr.output-row').remove() |
|||
|
|||
const txUrl = lib_cmn.getExplorerTxUrl(this.currentTxid, this.explorerInfo) |
|||
$('#txid-value').text(this.currentTxid) |
|||
$('#txid-value').attr('href', txUrl) |
|||
|
|||
const firstseen = lib_fmt.unixTsToLocaleString(txInfo['created']) |
|||
$('#tx-firstseen').text(firstseen) |
|||
|
|||
if (txInfo.hasOwnProperty('block')) |
|||
$('#tx-location').text(` Block ${txInfo['block']['height']}`) |
|||
else |
|||
$('#tx-location').text(' Mempool') |
|||
|
|||
|
|||
const nbInputs = txInfo['inputs'].length |
|||
$('#tx-nb-inputs').text(nbInputs) |
|||
|
|||
const nbOutputs = txInfo['outputs'].length |
|||
$('#tx-nb-outputs').text(nbOutputs) |
|||
|
|||
$('#tx-vfeerate').text(`${txInfo['vfeerate']} sats/vbyte`) |
|||
|
|||
const fees = parseInt(txInfo['fees']) |
|||
$('#tx-fees').text(`${fees} sats`) |
|||
|
|||
let amount = fees |
|||
for (let o of txInfo['outputs']) { |
|||
amount += parseInt(o['value']) |
|||
} |
|||
amount = amount / 100000000 |
|||
$('#tx-amount').text(`${amount} BTC`) |
|||
|
|||
$('#tx-size').text(`${txInfo['size']} bytes`) |
|||
$('#tx-vsize').text(`${txInfo['vsize']} vbytes`) |
|||
$('#tx-version').text(txInfo['version']) |
|||
|
|||
let nlocktime = parseInt(txInfo['locktime']) |
|||
if (nlocktime < 500000000) { |
|||
$('#tx-nlocktime').text(`Block ${nlocktime}`) |
|||
} else { |
|||
locktime = lib_fmt.unixTsToLocaleString(locktime) |
|||
$('#tx-nlocktime').text(locktime) |
|||
} |
|||
}, |
|||
|
|||
showSearchForm: function() { |
|||
$('#txs-tool-details').hide() |
|||
$('#txid').val('') |
|||
$('#txs-tool-search-form').show() |
|||
lib_msg.cleanMessagesUi() |
|||
}, |
|||
|
|||
showTxDetails: function() { |
|||
$('#txs-tool-search-form').hide() |
|||
$('#txs-tool-details').show() |
|||
}, |
|||
|
|||
} |
|||
|
|||
screenScripts.set('#screen-txs-tools', screenTxsToolsScript) |
@ -0,0 +1,42 @@ |
|||
<div id="welcome"> |
|||
<h1>WELCOME!</h1> |
|||
|
|||
<span>The Dojo's Maintenance Tool (DMT for short) provides a set of tools for monitoring and maintaining your Dojo.</span> |
|||
|
|||
<span class="items-category ">MONITORING</span> |
|||
|
|||
<span class="item">DOJO STATUS</span> |
|||
<span class="item-descr">A dashboard for monitoring the health of some components of your Dojo.</span> |
|||
|
|||
<span class="item">PUSHTX STATUS</span> |
|||
<span class="item-descr">A dashboard for monitoring the transactions pushed through your Dojo.</span> |
|||
|
|||
<span class="items-category ">TOOLS</span> |
|||
|
|||
<span class="item">PAIRING</span> |
|||
<span class="item-descr">Pair your wallet to your Dojo by scanning a QRCode.</span> |
|||
|
|||
<span class="item">XPUBS TOOL</span> |
|||
<span class="item-descr">Everything you need to manage your XPUBs manually.<br/>Check if a XPUB is tracked by your Dojo. Import and track a XPUB. Rescan the full history of a XPUB.</span> |
|||
|
|||
<span class="item">ADDRESSES TOOL</span> |
|||
<span class="item-descr">Everything you need to manage your addresses manually.<br/>Check if an address is tracked by your Dojo. Import and track an address. Rescan the full history of an address.</span> |
|||
|
|||
<span class="item">TRANSACTIONS TOOL</span> |
|||
<span class="item-descr">Check if a transaction is found in a block or in the mempool of your full node.</span> |
|||
|
|||
<span class="item">BLOCKS RESCAN</span> |
|||
<span class="item-descr">Rescan the transactions confirmed by the blocks in a given range.</span> |
|||
|
|||
<span class="items-category ">HELP</span> |
|||
|
|||
<span class="item">DOJO TELEGRAM CHAT</span> |
|||
<span class="item-descr">Get support from the community for all things related to your Dojo (requires Telegram).</span> |
|||
|
|||
<span class="item">WHIRLPOOL TELEGRAM CHAT</span> |
|||
<span class="item-descr">Get support from the community for all things related to Whirlpool (requires Telegram).</span> |
|||
|
|||
<span class="item">SW TELEGRAM CHAT</span> |
|||
<span class="item-descr">Get support from the community for all things related to your Samourai Wallet (requires Telegram).</span> |
|||
|
|||
</div> |
@ -0,0 +1,183 @@ |
|||
<div id="xpubs-tool"> |
|||
<h1>XPUBS TOOL</h1> |
|||
|
|||
<div class="box-context">Check if a XPUB is tracked by your Dojo. Import and track a new XPUB. Rescan the full history of a XPUB.</div> |
|||
|
|||
<div class="row box-main"> |
|||
<!-- XPUB SEARCH FORM --> |
|||
<div id="xpubs-tool-search-form" class="fullwidth box"> |
|||
<div class="box-body"> |
|||
<span>Check if </span> |
|||
<input id="xpub" type="text" placeholder="XPUB"> |
|||
<span> is tracked by your Dojo </span> |
|||
<button id="btn-xpub-search-go" |
|||
class="btn btn-success" |
|||
type="button">GO</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- XPUB IMPORT --> |
|||
<div id="xpubs-tool-import" class="fullwidth box"> |
|||
<div class="box-body"> |
|||
<div id="import-deriv-first-import-msg"> |
|||
<span>This XPUB isn't tracked by your Dojo. Do you want to import it and track its activity?</span> |
|||
</div> |
|||
<div id="import-deriv-reimport-msg"> |
|||
<span>Do you want to reimport this XPUB with a new derivation type?</span> |
|||
<br/><br/> |
|||
<span>WARNING: Are you sure you need to retype this XPUB? Generally, the 'auto' derivation will type your XPUB correctly.</span> |
|||
<br/> |
|||
<span>Retyping your XPUB is reserved for very specific circumstances, and should not be taken lightly.</span> |
|||
<br/> |
|||
<span>If in doubt, contact <a href="mailto:support@samouraiwallet.com">support@samouraiwallet.com</a></span> |
|||
</div> |
|||
<div class="spacer20"></div> |
|||
<div> |
|||
<span>Import </span> |
|||
<span id="import-xpub"></span> |
|||
<span> with a </span> |
|||
<select id="import-deriv-type" type="select" value="auto"> |
|||
<option value="auto" selected>auto</option> |
|||
<option value="bip44">BIP44</option> |
|||
<option value="bip49">BIP49</option> |
|||
<option value="bip84">BIP84</option> |
|||
</select> |
|||
<span> derivation</span> |
|||
<button id="btn-xpub-import-go" class="btn btn-success" type="button">IMPORT</button> |
|||
<button id="btn-xpub-import-cancel" class="btn btn-success" type="button">CANCEL</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- XPUB DETAILS --> |
|||
<div id="xpubs-tool-details"> |
|||
<div id="xpubs-tool-header" class="row box-main"> |
|||
<div class="fullwidth box"> |
|||
<div id="xpub-value" class="box-body center"></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="xpubs-tool-actions" class="row box-main"> |
|||
<div class="center"> |
|||
<button id="btn-xpub-details-rescan" class="btn btn-success" type="button">RESCAN THIS XPUB</button> |
|||
<button id="btn-xpub-details-retype" class="btn btn-success" type="button">RETYPE THIS XPUB</button> |
|||
<button id="btn-xpub-details-reset" class="btn btn-success" type="button">SEARCH ANOTHER XPUB</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="xpubs-rescans-actions" class="row box-main"> |
|||
<div class="center"> |
|||
<span>Rescan this xpub starting at index</span> |
|||
<input id="rescan-start-idx" type="text" value="0" placeholder="index"> |
|||
<span> with a lookahead of </span> |
|||
<input id="rescan-lookahead" type="text" value="100" placeholder="#addresses"> |
|||
<span> addresses</span> |
|||
<button id="btn-xpub-rescan-go" class="btn btn-success" type="button">RESCAN</button> |
|||
<button id="btn-xpub-rescan-cancel" class="btn btn-success" type="button">CANCEL</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="xpubs-tool-details-row1" class="row box-main"> |
|||
<!-- GENERAL INFO --> |
|||
<div id="box-general" class="halfwidth-left box"> |
|||
<div class="box-header">GENERAL INFO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Derivation Type</td> |
|||
<td class="table-value" id="xpub-deriv-type"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Balance</td> |
|||
<td class="table-value" id="xpub-balance"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of Txs</td> |
|||
<td class="table-value" id="xpub-nb-txs"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Number of UTXOs</td> |
|||
<td class="table-value" id="xpub-nb-utxos"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Tracked since</td> |
|||
<td class="table-value" id="xpub-import-date"></td> |
|||
</tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
<!-- DERIVATION INFO --> |
|||
<div id="box-derivation" class="halfwidth-right box"> |
|||
<div class="box-header">XPUB DERIVATION INFO</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table> |
|||
<tr> |
|||
<td class="table-label">Account</td> |
|||
<td class="table-value" id="xpub-deriv-account"></td> |
|||
<td class="table-label">Depth</td> |
|||
<td class="table-value" id="xpub-deriv-depth"></td> |
|||
</tr> |
|||
</table> |
|||
<div class="spacer10"></div> |
|||
<table id="table-deriv-idx"> |
|||
<tr> |
|||
<td class="table-label" colspan="2">First unused indices</td> |
|||
<td class="table-label" colspan="2">Last derived indices</td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">External</td> |
|||
<td class="table-value" id="xpub-idx-unused-ext"></td> |
|||
<td class="table-label">External</td> |
|||
<td class="table-value" id="xpub-idx-derived-ext"></td> |
|||
</tr> |
|||
<tr> |
|||
<td class="table-label">Internal</td> |
|||
<td class="table-value" id="xpub-idx-unused-int"></td> |
|||
<td class="table-label">Internal</td> |
|||
<td class="table-value" id="xpub-idx-derived-int"></td> |
|||
</tr> |
|||
</table> |
|||
<div class="spacer10"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="xpubs-tool-details-row2" class="row box-main"> |
|||
<!-- TXS LIST --> |
|||
<div id="box-txs" class="halfwidth-left box"> |
|||
<div class="box-header">MOST RECENT TRANSACTIONS</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table id="xpub-table-list-txs"> |
|||
<tbody> |
|||
<tr> |
|||
<td></td> |
|||
<td></td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
<!-- UTXOS LIST --> |
|||
<div id="box-utxos" class="halfwidth-right box"> |
|||
<div class="box-header">UNSPENT TRANSACTION OUTPUTS</div> |
|||
<div class="spacer10"></div> |
|||
<div class="box-body"> |
|||
<table id="xpub-table-list-utxos"> |
|||
<tbody> |
|||
<tr> |
|||
<td></td> |
|||
<td></td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script include-js="xpubs-tools/xpubs-tools.js"></script> |
@ -0,0 +1,259 @@ |
|||
const screenXpubsToolsScript = { |
|||
|
|||
explorerInfo: null, |
|||
currentXpub: null, |
|||
isReimport: false, |
|||
|
|||
initPage: function() { |
|||
this.getExplorerInfo() |
|||
// Sets the event handlers
|
|||
$('#btn-xpub-search-go').click(() => {this.searchXpub()}) |
|||
$('#btn-xpub-details-reset').click(() => {this.showSearchForm()}) |
|||
$('#btn-xpub-details-rescan').click(() => {this.showRescanForm()}) |
|||
$('#btn-xpub-rescan-go').click(() => {this.rescanXpub()}) |
|||
$('#btn-xpub-rescan-cancel').click(() => {this.hideRescanForm()}) |
|||
$('#btn-xpub-import-go').click(() => {this.importXpub()}) |
|||
$('#btn-xpub-details-retype').click(() => {this.showImportForm(true)}) |
|||
$('#btn-xpub-import-cancel').click(() => {this.hideImportForm(this.isReimport)}) |
|||
$('#xpubs-tool').keyup(evt => { |
|||
if (evt.keyCode === 13) { |
|||
this.searchXpub() |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
preparePage: function() { |
|||
this.hideRescanForm() |
|||
this.showSearchForm() |
|||
$("#xpub").focus() |
|||
}, |
|||
|
|||
getExplorerInfo: function() { |
|||
lib_api.getExplorerPairingInfo().then(explorerInfo => { |
|||
this.explorerInfo = explorerInfo |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
searchXpub: function() { |
|||
lib_msg.displayMessage('Search in progress...'); |
|||
const xpub = $('#xpub').val() |
|||
this.currentXpub = xpub |
|||
return this._searchXpub(xpub).then(() => { |
|||
lib_msg.cleanMessagesUi() |
|||
}) |
|||
}, |
|||
|
|||
_searchXpub: function(xpub) { |
|||
return lib_api.getXpubInfo(xpub).then(xpubInfo => { |
|||
if (xpubInfo && xpubInfo['tracked']) { |
|||
this.setXpubDetails(xpubInfo) |
|||
this.showXpubDetails() |
|||
const jsonData = {'active': xpub} |
|||
return lib_api.getWallet(jsonData).then(walletInfo => { |
|||
// Display the txs
|
|||
const txs = walletInfo['txs'] |
|||
for (let tx of txs) |
|||
this.setTxDetails(tx) |
|||
// Display the UTXOs
|
|||
const utxos = walletInfo['unspent_outputs'].sort((a,b) => { |
|||
return a['confirmations'] - b['confirmations'] |
|||
}) |
|||
$('#xpub-nb-utxos').text(utxos.length) |
|||
for (let utxo of utxos) |
|||
this.setUtxoDetails(utxo) |
|||
}) |
|||
} else { |
|||
lib_msg.displayErrors('xpub not found') |
|||
this.showImportForm(false) |
|||
} |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
throw e |
|||
}) |
|||
}, |
|||
|
|||
importXpub: function() { |
|||
lib_msg.displayMessage('Processing xpub import...'); |
|||
|
|||
const jsonData = { |
|||
'xpub': this.currentXpub, |
|||
'type': 'restore', |
|||
'force': true |
|||
} |
|||
|
|||
const derivType = $('#import-deriv-type').val() |
|||
if (derivType == 'bip49' || derivType == 'bip84') { |
|||
jsonData['segwit'] = derivType |
|||
} else if (derivType == 'auto') { |
|||
if (this.currentXpub.startsWith('ypub')) |
|||
jsonData['segwit'] = 'bip49' |
|||
else if (this.currentXpub.startsWith('zpub')) |
|||
jsonData['segwit'] = 'bip84' |
|||
} |
|||
|
|||
return lib_api.postXpub(jsonData) |
|||
.then(result => { |
|||
this._searchXpub(this.currentXpub).then(() => { |
|||
lib_msg.displayInfo('Import complete') |
|||
}) |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
rescanXpub: function() { |
|||
lib_msg.displayMessage('Processing xpub rescan...'); |
|||
let startIdx = $('#rescan-start-idx').val() |
|||
startIdx = (startIdx == null) ? 0 : parseInt(startIdx) |
|||
let lookahead = $('#rescan-lookahead').val() |
|||
lookahead = (lookahead == null) ? 100 : parseInt(lookahead) |
|||
return lib_api.getXpubRescan(this.currentXpub, lookahead, startIdx) |
|||
.then(result => { |
|||
this.hideRescanForm() |
|||
this._searchXpub(this.currentXpub).then(() => { |
|||
lib_msg.displayInfo('Rescan complete') |
|||
}) |
|||
}).catch(e => { |
|||
lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) |
|||
console.log(e) |
|||
}) |
|||
}, |
|||
|
|||
setXpubDetails: function(xpubInfo) { |
|||
$('tr.tx-row').remove() |
|||
$('tr.utxo-row').remove() |
|||
|
|||
$('#xpub-value').text(this.currentXpub) |
|||
$('#xpub-import-date').text(xpubInfo['created']) |
|||
$('#xpub-deriv-type').text(xpubInfo['derivation']) |
|||
$('#xpub-nb-txs').text(xpubInfo['n_tx']) |
|||
$('#xpub-nb-utxos').text('-') |
|||
const balance = parseInt(xpubInfo['balance']) / 100000000 |
|||
$('#xpub-balance').text(`${balance} BTC`) |
|||
$('#xpub-deriv-account').text(xpubInfo['account']) |
|||
$('#xpub-deriv-depth').text(xpubInfo['depth']) |
|||
$('#xpub-idx-unused-ext').text(xpubInfo['unused']['external']) |
|||
$('#xpub-idx-derived-ext').text(xpubInfo['derived']['external']) |
|||
$('#xpub-idx-unused-int').text(xpubInfo['unused']['internal']) |
|||
$('#xpub-idx-derived-int').text(xpubInfo['derived']['internal']) |
|||
}, |
|||
|
|||
setTxDetails: function(tx) { |
|||
const txid = tx['hash'] |
|||
const txidDisplay = `${txid.substring(0,50)}...` |
|||
const amount = parseInt(tx['result']) / 100000000 |
|||
const amountLabel = amount < 0 ? amount : `+${amount}` |
|||
const amountStyle = amount < 0 ? 'amount-sent' : 'amount-received' |
|||
const date = lib_fmt.unixTsToLocaleString(tx['time']) |
|||
const txUrl = lib_cmn.getExplorerTxUrl(txid, this.explorerInfo) |
|||
|
|||
const newRow = `<tr class="tx-row"><td colspan="2"> </td></tr>
|
|||
<tr class="tx-row"> |
|||
<td class="table-label" colspan="2"> |
|||
<a href="${txUrl}" target="_blank">${txidDisplay}</a> |
|||
</td> |
|||
</tr> |
|||
<tr class="tx-row"> |
|||
<td class="table-label">Amount</td> |
|||
<td class="table-value ${amountStyle}">${amountLabel} BTC</td> |
|||
</tr> |
|||
<tr class="tx-row"> |
|||
<td class="table-label">Block height</td> |
|||
<td class="table-value">${tx['block_height']}</td> |
|||
</tr> |
|||
<tr class="tx-row"> |
|||
<td class="table-label">Date</td> |
|||
<td class="table-value">${date}</td> |
|||
</tr>` |
|||
|
|||
$('#xpub-table-list-txs tr:last').after(newRow) |
|||
}, |
|||
|
|||
setUtxoDetails: function(utxo) { |
|||
const txid = utxo['tx_hash'] |
|||
const txidVout = `${txid.substring(0,50)}...:${utxo['tx_output_n']}` |
|||
const amount = parseInt(utxo['value']) / 100000000 |
|||
const txUrl = lib_cmn.getExplorerTxUrl(txid, this.explorerInfo) |
|||
|
|||
const newRow = `<tr class="utxo-row"><td colspan="2"> </td></tr>
|
|||
<tr class="utxo-row"> |
|||
<td class="table-label" colspan="2"> |
|||
<a href="${txUrl}" target="_blank">${txidVout}</a> |
|||
</td> |
|||
</tr> |
|||
<tr class="utxo-row"> |
|||
<td class="table-label">Amount</td> |
|||
<td class="table-value">${amount} BTC</td> |
|||
</tr> |
|||
<tr class="utxo-row"> |
|||
<td class="table-label">Address</td> |
|||
<td class="table-value">${utxo['addr']}</td> |
|||
</tr> |
|||
<tr class="utxo-row"> |
|||
<td class="table-label">Confirmations</td> |
|||
<td class="table-value">${utxo['confirmations']}</td> |
|||
</tr>` |
|||
|
|||
$('#xpub-table-list-utxos tr:last').after(newRow) |
|||
}, |
|||
|
|||
showSearchForm: function() { |
|||
$('#xpubs-tool-details').hide() |
|||
$('#xpubs-tool-import').hide() |
|||
$('#xpub').val('') |
|||
$('#xpubs-tool-search-form').show() |
|||
lib_msg.cleanMessagesUi() |
|||
}, |
|||
|
|||
showImportForm: function(isReimport) { |
|||
this.isReimport = isReimport |
|||
|
|||
$('#xpubs-tool-search-form').hide() |
|||
$('#xpubs-tool-details').hide() |
|||
|
|||
if (isReimport) { |
|||
$('#import-deriv-first-import-msg').hide() |
|||
$('#import-deriv-reimport-msg').show() |
|||
} else { |
|||
$('#import-deriv-reimport-msg').hide() |
|||
$('#import-deriv-first-import-msg').show() |
|||
} |
|||
|
|||
const xpubLen = this.currentXpub.length |
|||
const xpubShortLbl = `"${this.currentXpub.substring(0, 20)}...${this.currentXpub.substring(xpubLen-20, xpubLen)}"` |
|||
$('#import-xpub').text(xpubShortLbl) |
|||
$('#xpubs-tool-import').show() |
|||
}, |
|||
|
|||
hideImportForm: function(isReimport) { |
|||
if (isReimport) |
|||
this.showXpubDetails() |
|||
else |
|||
this.showSearchForm() |
|||
}, |
|||
|
|||
showXpubDetails: function() { |
|||
$('#xpubs-tool-search-form').hide() |
|||
$('#xpubs-tool-import').hide() |
|||
$('#xpubs-tool-details').show() |
|||
}, |
|||
|
|||
showRescanForm: function() { |
|||
$('#xpubs-tool-actions').hide() |
|||
$('#xpubs-rescans-actions').show() |
|||
lib_msg.cleanMessagesUi() |
|||
}, |
|||
|
|||
hideRescanForm: function() { |
|||
$('#xpubs-rescans-actions').hide() |
|||
$('#xpubs-tool-actions').show() |
|||
}, |
|||
|
|||
} |
|||
|
|||
screenScripts.set('#screen-xpubs-tools', screenXpubsToolsScript) |
After Width: | Height: | Size: 17 KiB |
File diff suppressed because it is too large
@ -1,51 +1,125 @@ |
|||
lib_cmn = { |
|||
const lib_cmn = { |
|||
// Utils functions
|
|||
hasProperty: function(obj, propName) { |
|||
/* Checks if an object has a property with given name */ |
|||
if ( (obj == null) || (!propName) ) |
|||
return false; |
|||
if ( (obj == null) || (!propName) ) |
|||
return false |
|||
else if (obj.hasOwnProperty('propName') || propName in obj) |
|||
return true; |
|||
return true |
|||
else |
|||
return false; |
|||
return false |
|||
}, |
|||
|
|||
// Go to default page
|
|||
goToDefaultPage: function() { |
|||
const baseUri = conf['adminTool']['baseUri']; |
|||
sessionStorage.setItem('activeTab', '#link-pairing'); |
|||
window.location = baseUri + '/tool/'; |
|||
const baseUri = conf['adminTool']['baseUri'] |
|||
sessionStorage.setItem('activeTab', '#link-status') |
|||
window.location = baseUri + '/dmt/' |
|||
}, |
|||
|
|||
// Go to home page
|
|||
goToHomePage: function() { |
|||
sessionStorage.setItem('activeTab', null); |
|||
window.location = conf['adminTool']['baseUri'] + '/'; |
|||
sessionStorage.setItem('activeTab', null) |
|||
window.location = conf['adminTool']['baseUri'] + '/' |
|||
}, |
|||
|
|||
// Loads html snippet
|
|||
// Get Transaction url on selected explorer
|
|||
getExplorerTxUrl: function(txid, explorerInfo) { |
|||
if (explorerInfo == null) |
|||
return null |
|||
else if (explorerInfo['pairing']['type'] == 'explorer.oxt') |
|||
return `${explorerInfo['pairing']['url']}/transaction/${txid}` |
|||
else if (explorerInfo['pairing']['type'] == 'explorer.btc_rpc_explorer') |
|||
return `http://${explorerInfo['pairing']['url']}/tx/${txid}` |
|||
else |
|||
return null |
|||
}, |
|||
|
|||
// Loads html snippets
|
|||
includeHTML: function(cb) { |
|||
let self = this; |
|||
let z, i, elmnt, file, xhttp; |
|||
z = document.getElementsByTagName('*'); |
|||
let self = this |
|||
let z, i, elmnt, file, xhttp |
|||
z = document.getElementsByTagName('*') |
|||
for (i = 0; i < z.length; i++) { |
|||
elmnt = z[i] |
|||
file = elmnt.getAttribute('include-html') |
|||
if (file) { |
|||
xhttp = new XMLHttpRequest() |
|||
xhttp.onreadystatechange = function() { |
|||
if (this.readyState == 4 && this.status == 200) { |
|||
elmnt.innerHTML = this.responseText |
|||
elmnt.removeAttribute('include-html') |
|||
self.includeHTML(cb) |
|||
self.includeJs(elmnt) |
|||
} |
|||
} |
|||
xhttp.open('GET', file, true) |
|||
xhttp.send() |
|||
return |
|||
} |
|||
} |
|||
if (cb) cb() |
|||
}, |
|||
|
|||
// Loads js snippets
|
|||
includeJs: function(element) { |
|||
let self = this |
|||
let z, i, elmnt, file, xhttp |
|||
z = element.querySelectorAll('script') |
|||
for (i = 0; i < z.length; i++) { |
|||
elmnt = z[i]; |
|||
file = elmnt.getAttribute('include-html'); |
|||
elmnt = z[i] |
|||
file = elmnt.getAttribute('include-js') |
|||
if (file) { |
|||
xhttp = new XMLHttpRequest(); |
|||
xhttp = new XMLHttpRequest() |
|||
xhttp.onreadystatechange = function() { |
|||
if (this.readyState == 4 && this.status == 200) { |
|||
elmnt.innerHTML = this.responseText; |
|||
elmnt.removeAttribute('include-html'); |
|||
self.includeHTML(cb); |
|||
const newElmnt = document.createElement('script') |
|||
newElmnt.textContent = this.responseText |
|||
if (elmnt.parentNode) { |
|||
elmnt.parentNode.insertBefore(newElmnt, elmnt.nextSibling) |
|||
elmnt.parentNode.removeChild(elmnt) |
|||
} |
|||
} |
|||
} |
|||
xhttp.open('GET', file, true); |
|||
xhttp.send(); |
|||
return; |
|||
} |
|||
xhttp.open('GET', file, true) |
|||
xhttp.send() |
|||
return |
|||
} |
|||
} |
|||
if (cb) cb(); |
|||
}, |
|||
|
|||
pad10: function(v) { |
|||
return (v < 10) ? `0${v}` : `${v}` |
|||
}, |
|||
|
|||
pad100: function(v) { |
|||
if (v < 10) return `00${v}` |
|||
if (v < 100) return `0${v}` |
|||
return `${v}` |
|||
}, |
|||
|
|||
timePeriod: function(period, milliseconds) { |
|||
milliseconds = !!milliseconds |
|||
|
|||
const whole = Math.floor(period) |
|||
const ms = 1000*(period - whole) |
|||
const s = whole % 60 |
|||
const m = (whole >= 60) ? Math.floor(whole / 60) % 60 : 0 |
|||
const h = (whole >= 3600) ? Math.floor(whole / 3600) % 24 : 0 |
|||
const d = (whole >= 86400) ? Math.floor(whole / 86400) : 0 |
|||
|
|||
const parts = [this.pad10(h), this.pad10(m), this.pad10(s)] |
|||
|
|||
if (d > 0) |
|||
parts.splice(0, 0, this.pad100(d)) |
|||
|
|||
const str = parts.join(':') |
|||
|
|||
if (milliseconds) { |
|||
return str + '.' + this.pad100(ms) |
|||
} else { |
|||
return str |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,39 +1,41 @@ |
|||
var lib_msg = { |
|||
const lib_msg = { |
|||
|
|||
// Extracts jqxhr error message
|
|||
extractJqxhrErrorMsg: function(jqxhr) { |
|||
let hasErrorMsg = ('responseJSON' in jqxhr) && |
|||
(jqxhr['responseJSON'] != null) && |
|||
('message' in jqxhr['responseJSON']); |
|||
let hasErrorMsg = ('responseJSON' in jqxhr) && |
|||
(jqxhr['responseJSON'] != null) && |
|||
('error' in jqxhr['responseJSON']) |
|||
|
|||
return hasErrorMsg ? jqxhr['responseJSON']['message'] : jqxhr.statusText; |
|||
return hasErrorMsg ? jqxhr['responseJSON']['error'] : jqxhr.statusText |
|||
}, |
|||
|
|||
|
|||
// UI functions
|
|||
addTextinID: function(text, id){ |
|||
$(id).html(text.toUpperCase()); |
|||
$(id).html(text.toUpperCase()) |
|||
}, |
|||
|
|||
displayMessage: function(text){ |
|||
this.addTextinID('', '#errors'); |
|||
this.addTextinID('', '#info'); |
|||
this.addTextinID(text, '#msg'); |
|||
this.addTextinID('', '#errors') |
|||
this.addTextinID('', '#info') |
|||
this.addTextinID(text, '#msg') |
|||
}, |
|||
|
|||
displayErrors: function(text){ |
|||
this.addTextinID('', '#msg'); |
|||
this.addTextinID('', '#info'); |
|||
this.addTextinID(text, '#errors'); |
|||
this.addTextinID('', '#msg') |
|||
this.addTextinID('', '#info') |
|||
this.addTextinID(text, '#errors') |
|||
}, |
|||
|
|||
displayInfo: function(text){ |
|||
this.addTextinID('', '#msg'); |
|||
this.addTextinID('', '#errors'); |
|||
this.addTextinID(text, '#info'); |
|||
this.addTextinID('', '#msg') |
|||
this.addTextinID('', '#errors') |
|||
this.addTextinID(text, '#info') |
|||
}, |
|||
|
|||
|
|||
cleanMessagesUi: function() { |
|||
this.addTextinID('', '#msg'); |
|||
this.addTextinID('', '#errors'); |
|||
this.addTextinID('', '#info'); |
|||
this.addTextinID('', '#msg') |
|||
this.addTextinID('', '#errors') |
|||
this.addTextinID('', '#info') |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
@ -1,135 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>DOJO // MAINTENANCE TOOL</title> |
|||
<link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css"> |
|||
<link rel="stylesheet" type="text/css" href="../css/bootstrap-theme.min.css"> |
|||
<link rel="stylesheet" type="text/css" href="../css/style.css"> |
|||
<script src="../lib/jquery-3.2.1.min.js"></script> |
|||
<script src="../lib/jquery.qrcode.min.js"></script> |
|||
<script src="../conf/index.js"></script> |
|||
<script src="../lib/common-script.js"></script> |
|||
<script src="../lib/api-wrapper.js"></script> |
|||
<script src="../lib/auth-utils.js"></script> |
|||
<script src="../lib/format-utils.js"></script> |
|||
<script src="index.js"></script> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="info-xpub" class="container"> |
|||
<!-- HEADER --> |
|||
<div id="header" class="row"> |
|||
<div class="col-xs-9"> |
|||
<h1 class="title"><span>DOJO // MAINTENANCE TOOL</span> <span id="dojo-version" class="beta">beta</span></h1> |
|||
</div> |
|||
<div class="col-xs-3 login-box"> |
|||
<a id="btn-logout" style="display: inline;" href="#" title="DISCONNECT"> |
|||
<img src="../icons/ic_power_settings_new_white_24dp_1x.png" class="mini-icon"/> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="spacer60"></div> |
|||
|
|||
<!-- TAB MENU --> |
|||
<div id="tab-menu" class="row"> |
|||
<div class="col-xs-12" > |
|||
<ul id="tab-menu_list" class="nav nav-pills"> |
|||
<li id="link-pairing"> |
|||
<a href="#">PAIRING</a> |
|||
</li> |
|||
<li id="link-status-api"> |
|||
<a href="#">API</a> |
|||
</li> |
|||
<li id="link-status-pushtx"> |
|||
<a href="#">PUSHTX</a> |
|||
</li> |
|||
<li id="link-orchestrator"> |
|||
<a href="#">ORCHESTRATOR</a> |
|||
</li> |
|||
<li id="link-info-xpub"> |
|||
<a href="#">XPUB INFO</a> |
|||
</li> |
|||
<li id="link-rescan-xpub"> |
|||
<a href="#">XPUB RESCAN</a> |
|||
</li> |
|||
<li id="link-xpub"> |
|||
<a href="#">XPUB</a> |
|||
</li> |
|||
<li id="link-info-address"> |
|||
<a href="#">ADDR. INFO</a> |
|||
</li> |
|||
<li id="link-rescan-address"> |
|||
<a href="#">ADDR. RESCAN</a> |
|||
</li> |
|||
<li id="link-multiaddr"> |
|||
<a href="#">MULTIADDR</a> |
|||
</li> |
|||
<li id="link-unspent"> |
|||
<a href="#">UNSPENT</a> |
|||
</li> |
|||
<li id="link-tx"> |
|||
<a href="#">TX</a> |
|||
</li> |
|||
<li id="link-rescan-blocks"> |
|||
<a href="#">BLOCKS RESCAN</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- BODY --> |
|||
<div id="body" class="row"> |
|||
<div class="col-xs-1"></div> |
|||
<div class="col-xs-10 json-data-container"> |
|||
<!-- PAIRING --> |
|||
<div id="screen-pairing"> |
|||
<div class="row"> |
|||
<div id="qr-label" class="halfwidth"> |
|||
PAIR YOUR WALLET WITH YOUR DOJO |
|||
</div> |
|||
<div id="qr-explorer-label" class="halfwidth"> |
|||
PAIR YOUR WALLET WITH YOUR BLOCK EXPLORER |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div id="qr-container" class="halfwidth"> |
|||
<div id="qr-pairing"></div> |
|||
</div> |
|||
<div id="qr-explorer-container" class="halfwidth"> |
|||
<div id="qr-explorer-pairing"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- MAINTENANCE --> |
|||
<div id="form-maintenance"> |
|||
<div id="row-form-field"> |
|||
<div id="cell-args"> |
|||
<input type="text" id="args" placeholder=""> |
|||
</div> |
|||
<div id="cell-args2"> |
|||
<input type="text" id="args2" placeholder=""> |
|||
</div> |
|||
<div id="cell-args3"> |
|||
<input type="text" id="args3" placeholder=""> |
|||
</div> |
|||
</div> |
|||
<div id="row-form-button" class="center"> |
|||
<button id="btn-go" |
|||
class="btn btn-success" |
|||
type="button">GO</button> |
|||
</div> |
|||
<div class="center"> |
|||
<pre id="json-data" style="min-height: 300px"></pre> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-xs-1"></div> |
|||
</div> |
|||
|
|||
</div> |
|||
</body> |
|||
|
|||
</html> |
@ -1,300 +0,0 @@ |
|||
/** |
|||
* Display Messages |
|||
*/ |
|||
|
|||
function displayInfoMsg(msg) { |
|||
const htmlMsg = '<span class="info">' + msg + '</span>'; |
|||
$('#json-data').html(htmlMsg); |
|||
} |
|||
|
|||
function displayErrorMsg(msg) { |
|||
const htmlMsg = '<span class="error">' + msg + '</span>'; |
|||
$('#json-data').html(htmlMsg); |
|||
} |
|||
|
|||
function displayQRPairing() { |
|||
const activeTab = sessionStorage.getItem('activeTab'); |
|||
processAction(activeTab).then( |
|||
function (result) { |
|||
if (result) { |
|||
if (result['api']) { |
|||
const textJson = JSON.stringify(result['api'], null, 4); |
|||
$("#qr-pairing").html('') // clear qrcode first
|
|||
$('#qr-pairing').qrcode({width: 256, height: 256, text: textJson}); |
|||
} |
|||
if (result['explorer'] && result['explorer']['pairing']['url']) { |
|||
const textJson = JSON.stringify(result['explorer'], null, 4); |
|||
$("#qr-explorer-pairing").html('') // clear qrcode first
|
|||
$('#qr-explorer-pairing').qrcode({width: 256, height: 256, text: textJson}); |
|||
} else { |
|||
$("#qr-label").removeClass('halfwidth'); |
|||
$("#qr-label").addClass('fullwidth'); |
|||
$("#qr-container").removeClass('halfwidth'); |
|||
$("#qr-container").addClass('fullwidth'); |
|||
$("#qr-explorer-label").hide(); |
|||
$("#qr-explorer-container").hide(); |
|||
} |
|||
} |
|||
}, |
|||
function (jqxhr) {} |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* On tab switched |
|||
*/ |
|||
function initTabs() { |
|||
// Activates the current tab
|
|||
let currentTab = sessionStorage.getItem('activeTab'); |
|||
if (!currentTab) { |
|||
currentTab = '#link-pairing'; |
|||
} |
|||
$(currentTab).addClass('active'); |
|||
|
|||
const tabs = [ |
|||
'#link-pairing', |
|||
'#link-status-api', |
|||
'#link-status-pushtx', |
|||
'#link-orchestrator', |
|||
'#link-info-xpub', |
|||
'#link-rescan-xpub', |
|||
'#link-xpub', |
|||
'#link-info-address', |
|||
'#link-rescan-address', |
|||
'#link-rescan-blocks', |
|||
'#link-multiaddr', |
|||
'#link-unspent', |
|||
'#link-tx' |
|||
]; |
|||
|
|||
// Sets event handlers
|
|||
for (let tab of tabs) { |
|||
$(tab).click(function() { |
|||
$(sessionStorage.getItem('activeTab')).removeClass('active'); |
|||
sessionStorage.setItem('activeTab', tab); |
|||
$(tab).addClass('active'); |
|||
preparePage(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Prepares the page content |
|||
*/ |
|||
function preparePage() { |
|||
const activeTab = sessionStorage.getItem('activeTab'); |
|||
|
|||
// Dojo version
|
|||
let lblVersion = sessionStorage.getItem('lblVersion'); |
|||
if (lblVersion == null) { |
|||
lib_api.getPairingInfo().then(apiInfo => { |
|||
lblVersion = 'v' + apiInfo['pairing']['version'] + ' beta'; |
|||
sessionStorage.setItem('lblVersion', lblVersion); |
|||
$('#dojo-version').text(lblVersion); |
|||
}); |
|||
} else { |
|||
$('#dojo-version').text(lblVersion); |
|||
} |
|||
|
|||
// Pairing
|
|||
if (activeTab == '#link-pairing') { |
|||
$('#screen-pairing').show(); |
|||
$('#form-maintenance').hide(); |
|||
displayQRPairing(); |
|||
|
|||
// Maintenance screens
|
|||
} else { |
|||
$('#form-maintenance').show(); |
|||
$('#screen-pairing').hide(); |
|||
|
|||
let placeholder = '', |
|||
placeholder2 = '', |
|||
placeholder3 = ''; |
|||
|
|||
$("#cell-args").removeClass('halfwidth'); |
|||
$("#cell-args").addClass('fullwidth'); |
|||
$("#cell-args2").hide(); |
|||
$("#cell-args3").hide(); |
|||
|
|||
if (activeTab == '#link-status-api' || |
|||
activeTab == '#link-status-pushtx' || |
|||
activeTab == '#link-orchestrator' |
|||
) { |
|||
$("#row-form-field").hide(); |
|||
$("#row-form-button").hide(); |
|||
processGo(); |
|||
} else { |
|||
$("#row-form-field").show(); |
|||
$("#row-form-button").show(); |
|||
} |
|||
|
|||
if (activeTab == '#link-info-xpub') { |
|||
placeholder = 'ENTER A XPUB, YPUB OR ZPUB'; |
|||
} else if (activeTab == '#link-xpub') { |
|||
placeholder = 'ENTER /XPUB URL ARGUMENTS (e.g.: xpub=xpub0123456789&segwit=bip84&type=restore&force=true)'; |
|||
} else if (activeTab == '#link-info-address') { |
|||
placeholder = 'ENTER A BITCOIN ADDRESS'; |
|||
} else if (activeTab == '#link-rescan-address') { |
|||
placeholder = 'ENTER A BITCOIN ADDRESS'; |
|||
} else if (activeTab == '#link-rescan-blocks') { |
|||
$("#cell-args").removeClass('fullwidth'); |
|||
$("#cell-args").addClass('halfwidth'); |
|||
$("#cell-args2").show(); |
|||
placeholder = 'RESCAN BLOCKS FROM HEIGHT...'; |
|||
placeholder2 = '...TO HEIGHT (OPTIONAL)'; |
|||
} else if (activeTab == '#link-multiaddr') { |
|||
placeholder = 'ENTER /MULTIADDR URL ARGUMENTS (e.g.: active=xpub0123456789&new=address2|address3&pubkey=pubkey4)'; |
|||
} else if (activeTab == '#link-unspent') { |
|||
placeholder = 'ENTER /UNSPENT URL ARGUMENTS (e.g.: active=xpub0123456789&new=address2|address3&pubkey=pubkey4)'; |
|||
} else if (activeTab == '#link-tx') { |
|||
placeholder = 'ENTER A TRANSACTION TXID'; |
|||
} else if (activeTab == '#link-rescan-xpub') { |
|||
$("#cell-args").removeClass('fullwidth'); |
|||
$("#cell-args").addClass('halfwidth'); |
|||
$("#cell-args2").show(); |
|||
$("#cell-args3").show(); |
|||
placeholder = 'ENTER A XPUB, YPUB OR ZPUB'; |
|||
placeholder2 = 'ENTER #ADDR. (DEFAULT=100)'; |
|||
placeholder3 = 'ENTER START INDEX (DEFAULT=0)'; |
|||
} |
|||
|
|||
$("#args").attr('placeholder', placeholder); |
|||
$('#args').val(''); |
|||
$("#args2").attr('placeholder', placeholder2); |
|||
$('#args2').val(''); |
|||
$("#args3").attr('placeholder', placeholder3); |
|||
$('#args3').val(''); |
|||
$('#json-data').html(''); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Process action (api calls) |
|||
*/ |
|||
function processAction(activeTab, args, args2, args3) { |
|||
if (activeTab == '#link-pairing') { |
|||
//return lib_api.getPairingInfo();
|
|||
let result = { |
|||
'api': null, |
|||
'explorer': null |
|||
}; |
|||
return lib_api.getPairingInfo().then(apiInfo => { |
|||
if (apiInfo) { |
|||
apiInfo['pairing']['url'] = window.location.protocol + '//' + window.location.host + conf['api']['baseUri']; |
|||
result['api'] = apiInfo; |
|||
} |
|||
}).then(() => { |
|||
return lib_api.getExplorerPairingInfo(); |
|||
}).then(explorerInfo => { |
|||
if (explorerInfo) |
|||
result['explorer'] = explorerInfo; |
|||
return result |
|||
}).catch(e => { |
|||
console.log(e); |
|||
return result; |
|||
}); |
|||
} else if (activeTab == '#link-status-api') { |
|||
return lib_api.getApiStatus(); |
|||
} else if (activeTab == '#link-status-pushtx') { |
|||
return lib_api.getPushtxStatus(); |
|||
} else if (activeTab == '#link-orchestrator') { |
|||
return lib_api.getOrchestratorStatus(); |
|||
} |
|||
|
|||
if (args == '') { |
|||
alert('Argument is mandatory'); |
|||
return; |
|||
} |
|||
|
|||
if (activeTab == '#link-info-xpub') { |
|||
return lib_api.getXpubInfo(args); |
|||
} else if (activeTab == '#link-rescan-xpub') { |
|||
const nbAddr = (!args2) ? 100 : parseInt(args2); |
|||
const startIdx = (!args3) ? 0 : parseInt(args3); |
|||
return lib_api.getXpubRescan(args, nbAddr, startIdx); |
|||
} else if (activeTab == '#link-info-address') { |
|||
return lib_api.getAddressInfo(args); |
|||
} else if (activeTab == '#link-rescan-address') { |
|||
return lib_api.getAddressRescan(args); |
|||
} else if (activeTab == '#link-rescan-blocks') { |
|||
const fromHeight = parseInt(args); |
|||
const toHeight = (args2) ? parseInt(args2) : fromHeight; |
|||
return lib_api.getBlocksRescan(fromHeight, toHeight); |
|||
} else if (activeTab == '#link-tx') { |
|||
return lib_api.getTransaction(args); |
|||
} |
|||
|
|||
const jsonData = {}; |
|||
const aArgs = args.split('&'); |
|||
for (let arg of aArgs) { |
|||
const aArg = arg.split('='); |
|||
jsonData[aArg[0]] = aArg[1]; |
|||
} |
|||
|
|||
if (activeTab == '#link-multiaddr') |
|||
return lib_api.getMultiaddr(jsonData); |
|||
else if (activeTab == '#link-unspent') |
|||
return lib_api.getUnspent(jsonData); |
|||
else if (activeTab == '#link-xpub') |
|||
return lib_api.postXpub(jsonData); |
|||
} |
|||
|
|||
/** |
|||
* Retrieve information about the xpub |
|||
*/ |
|||
function processGo() { |
|||
const activeTab = sessionStorage.getItem('activeTab'); |
|||
const args = $("#args").val(); |
|||
const args2 = $("#args2").val(); |
|||
const args3 = $("#args3").val(); |
|||
|
|||
displayInfoMsg('Processing...'); |
|||
|
|||
let deferred = processAction(activeTab, args, args2, args3); |
|||
|
|||
deferred.then( |
|||
function (result) { |
|||
if (!result) |
|||
return; |
|||
let textJson = lib_fmt.cleanJson(result); |
|||
textJson = JSON.stringify(JSON.parse(textJson), null, 4); |
|||
textJson = lib_fmt.jsonSyntaxHighlight(textJson); |
|||
$('#json-data').html(textJson); |
|||
}, |
|||
function (jqxhr) { |
|||
let hasErrorMsg = |
|||
('responseJSON' in jqxhr) && |
|||
(jqxhr['responseJSON'] != null) && |
|||
('message' in jqxhr['responseJSON']); |
|||
|
|||
const msg = hasErrorMsg ? jqxhr['responseJSON']['message'] : jqxhr.statusText; |
|||
displayErrorMsg(msg); |
|||
} |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Processing on loading completed |
|||
*/ |
|||
$(document).ready(function() { |
|||
// Refresh the access token if needed
|
|||
setInterval(() => { |
|||
lib_auth.refreshAccessToken(); |
|||
}, 300000); |
|||
|
|||
initTabs(); |
|||
preparePage(); |
|||
|
|||
// Sets the event handlers
|
|||
$('#args').keyup(function(evt) { |
|||
if (evt.keyCode === 13) { |
|||
processGo(); |
|||
} |
|||
}); |
|||
$('#btn-go').click(function() { |
|||
processGo(); |
|||
}); |
|||
$('#btn-logout').click(function() { |
|||
lib_auth.logout(); |
|||
}); |
|||
}); |
Loading…
Reference in new issue