refactor: decorators, models and more broken bits
@@ -0,0 +1,33 @@
|
||||
[v-cloak] {
|
||||
display: none; }
|
||||
|
||||
.bg-lnbits-dark {
|
||||
background-color: #1f2234; }
|
||||
|
||||
body.body--dark, body.body--dark .q-drawer--dark, body.body--dark .q-menu--dark {
|
||||
background: #1f2234; }
|
||||
|
||||
body.body--dark .q-card--dark {
|
||||
background: #333646; }
|
||||
|
||||
body.body--dark .q-table--dark {
|
||||
background: transparent; }
|
||||
|
||||
body.body--light, body.body--light .q-drawer {
|
||||
background: whitesmoke; }
|
||||
|
||||
body.body--dark .q-field--error .text-negative,
|
||||
body.body--dark .q-field--error .q-field__messages {
|
||||
color: yellow !important; }
|
||||
|
||||
.lnbits__q-table__icon-td {
|
||||
padding-left: 5px !important; }
|
||||
|
||||
.lnbits-drawer__q-list .q-item {
|
||||
padding-top: 5px !important;
|
||||
padding-bottom: 5px !important;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px; }
|
||||
.lnbits-drawer__q-list .q-item.q-item--active {
|
||||
color: inherit;
|
||||
font-weight: bold; }
|
||||
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 230 KiB |
@@ -0,0 +1,146 @@
|
||||
var LOCALE = 'en'
|
||||
|
||||
var LNbits = {
|
||||
api: {
|
||||
request: function (method, url, macaroon, data) {
|
||||
return axios({
|
||||
method: method,
|
||||
url: url,
|
||||
headers: {
|
||||
'Grpc-Metadata-macaroon': macaroon
|
||||
},
|
||||
data: data
|
||||
});
|
||||
},
|
||||
createInvoice: function (wallet, amount, memo) {
|
||||
return this.request('post', '/api/v1/invoices', wallet.inkey, {
|
||||
amount: amount,
|
||||
memo: memo
|
||||
});
|
||||
},
|
||||
getInvoice: function (wallet, payhash) {
|
||||
return this.request('get', '/api/v1/invoices/' + payhash, wallet.inkey);
|
||||
},
|
||||
getTransactions: function (wallet) {
|
||||
return this.request('get', '/api/v1/transactions', wallet.inkey);
|
||||
}
|
||||
},
|
||||
href: {
|
||||
openWallet: function (wallet) {
|
||||
window.location.href = '/wallet?usr=' + wallet.user + '&wal=' + wallet.id;
|
||||
},
|
||||
createWallet: function (walletName, userId) {
|
||||
window.location.href = '/wallet?' + (userId ? 'usr=' + userId + '&' : '') + 'nme=' + walletName;
|
||||
},
|
||||
deleteWallet: function (walletId, userId) {
|
||||
window.location.href = '/deletewallet?usr=' + userId + '&wal=' + walletId;
|
||||
}
|
||||
},
|
||||
map: {
|
||||
extension: function (data) {
|
||||
var obj = _.object(['code', 'isValid', 'name', 'shortDescription', 'icon'], data);
|
||||
obj.url = ['/', obj.code, '/'].join('');
|
||||
return obj;
|
||||
},
|
||||
transaction: function (data) {
|
||||
var obj = _.object(['payhash', 'pending', 'amount', 'fee', 'memo', 'time'], data);
|
||||
obj.date = Quasar.utils.date.formatDate(new Date(obj.time * 1000), 'YYYY-MM-DD HH:mm')
|
||||
obj.msat = obj.amount;
|
||||
obj.sat = obj.msat / 1000;
|
||||
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.sat);
|
||||
return obj;
|
||||
},
|
||||
user: function (data) {
|
||||
var obj = _.object(['id', 'email', 'extensions', 'wallets'], data);
|
||||
var mapWallet = this.wallet;
|
||||
obj.wallets = obj.wallets.map(function (obj) {
|
||||
return mapWallet(obj);
|
||||
}).sort(function (a, b) {
|
||||
return a.name > b.name;
|
||||
});
|
||||
return obj;
|
||||
},
|
||||
wallet: function (data) {
|
||||
var obj = _.object(['id', 'name', 'user', 'adminkey', 'inkey', 'balance'], data);
|
||||
obj.sat = Math.round(obj.balance);
|
||||
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.sat);
|
||||
obj.url = ['/wallet?usr=', obj.user, '&wal=', obj.id].join('');
|
||||
return obj;
|
||||
}
|
||||
},
|
||||
utils: {
|
||||
formatSat: function (value) {
|
||||
return new Intl.NumberFormat(LOCALE).format(value);
|
||||
},
|
||||
notifyApiError: function (error) {
|
||||
var types = {
|
||||
400: 'warning',
|
||||
401: 'warning',
|
||||
500: 'negative'
|
||||
}
|
||||
Quasar.plugins.Notify.create({
|
||||
progress: true,
|
||||
timeout: 3000,
|
||||
type: types[error.response.status] || 'warning',
|
||||
message: error.response.data.message || null,
|
||||
caption: [error.response.status, ' ', error.response.statusText].join('') || null,
|
||||
icon: null
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var windowMixin = {
|
||||
data: function () {
|
||||
return {
|
||||
w: {
|
||||
visibleDrawer: false,
|
||||
extensions: [],
|
||||
user: null,
|
||||
wallet: null,
|
||||
transactions: [],
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleDarkMode: function () {
|
||||
this.$q.dark.toggle();
|
||||
this.$q.localStorage.set('lnbits.darkMode', this.$q.dark.isActive);
|
||||
},
|
||||
copyText: function (text, message) {
|
||||
var notify = this.$q.notify;
|
||||
Quasar.utils.copyToClipboard(text).then(function () {
|
||||
notify({message: 'Copied to clipboard!'});
|
||||
});
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode'));
|
||||
if (window.user) {
|
||||
this.w.user = Object.freeze(LNbits.map.user(window.user));
|
||||
}
|
||||
if (window.wallet) {
|
||||
this.w.wallet = Object.freeze(LNbits.map.wallet(window.wallet));
|
||||
}
|
||||
if (window.transactions) {
|
||||
this.w.transactions = window.transactions.map(function (data) {
|
||||
return LNbits.map.transaction(data);
|
||||
});
|
||||
}
|
||||
if (window.extensions) {
|
||||
var user = this.w.user;
|
||||
this.w.extensions = Object.freeze(window.extensions.map(function (data) {
|
||||
return LNbits.map.extension(data);
|
||||
}).map(function (obj) {
|
||||
if (user) {
|
||||
obj.isEnabled = user.extensions.indexOf(obj.code) != -1;
|
||||
} else {
|
||||
obj.isEnabled = false;
|
||||
}
|
||||
return obj;
|
||||
}).sort(function (a, b) {
|
||||
return a.name > b.name;
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
Vue.component('lnbits-wallet-list', {
|
||||
data: function () {
|
||||
return {
|
||||
user: null,
|
||||
activeWallet: null,
|
||||
showForm: false,
|
||||
walletName: ''
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<q-list v-if="user && user.wallets.length" dense class="lnbits-drawer__q-list">
|
||||
<q-item-label header>Wallets</q-item-label>
|
||||
<q-item v-for="wallet in user.wallets" :key="wallet.id"
|
||||
clickable
|
||||
:active="activeWallet && activeWallet.id == wallet.id"
|
||||
tag="a" :href="wallet.url">
|
||||
<q-item-section side>
|
||||
<q-avatar size="md"
|
||||
:color="(activeWallet && activeWallet.id == wallet.id)
|
||||
? (($q.dark.isActive) ? 'deep-purple-5' : 'deep-purple')
|
||||
: 'grey-5'">
|
||||
<q-icon name="flash_on" :size="($q.dark.isActive) ? '21px' : '20px'"
|
||||
:color="($q.dark.isActive) ? 'blue-grey-10' : 'grey-3'"></q-icon>
|
||||
</q-avatar>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label lines="1">{{ wallet.name }}</q-item-label>
|
||||
<q-item-label caption>{{ wallet.fsat }} sat</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side v-show="activeWallet && activeWallet.id == wallet.id">
|
||||
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="showForm = !showForm">
|
||||
<q-item-section side>
|
||||
<q-icon :name="(showForm) ? 'remove' : 'add'" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label lines="1" class="text-caption">Add a wallet</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="showForm">
|
||||
<q-item-section>
|
||||
<q-form>
|
||||
<q-input filled dense v-model="walletName" label="Name wallet *">
|
||||
<template v-slot:append>
|
||||
<q-btn round dense flat icon="send" size="sm" @click="createWallet" :disable="walletName == ''"></q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-form>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
`,
|
||||
methods: {
|
||||
createWallet: function () {
|
||||
LNbits.href.createWallet(this.walletName, this.user.id);
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
if (window.user) {
|
||||
this.user = LNbits.map.user(window.user);
|
||||
}
|
||||
if (window.wallet) {
|
||||
this.activeWallet = LNbits.map.wallet(window.wallet);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('lnbits-extension-list', {
|
||||
data: function () {
|
||||
return {
|
||||
extensions: [],
|
||||
user: null
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<q-list v-if="user" dense class="lnbits-drawer__q-list">
|
||||
<q-item-label header>Extensions</q-item-label>
|
||||
<q-item v-for="extension in userExtensions" :key="extension.code"
|
||||
clickable
|
||||
tag="a" :href="[extension.url, '?usr=', user.id].join('')">
|
||||
<q-item-section side>
|
||||
<q-avatar size="md" color="grey-5">
|
||||
<q-icon :name="extension.icon" :size="($q.dark.isActive) ? '21px' : '20px'"
|
||||
:color="($q.dark.isActive) ? 'blue-grey-10' : 'grey-3'"></q-icon>
|
||||
</q-avatar>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label lines="1">{{ extension.name }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable tag="a" :href="['/extensions?usr=', user.id].join('')">
|
||||
<q-item-section side>
|
||||
<q-icon name="clear_all" color="grey-5" size="md"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label lines="1" class="text-caption">Manage extensions</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
`,
|
||||
computed: {
|
||||
userExtensions: function () {
|
||||
if (!this.user) return [];
|
||||
var userExtensions = this.user.extensions;
|
||||
return this.extensions.filter(function (obj) {
|
||||
return userExtensions.indexOf(obj.code) !== -1;
|
||||
});
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.extensions = window.extensions.map(function (data) {
|
||||
return LNbits.map.extension(data);
|
||||
}).sort(function (a, b) {
|
||||
return a.name > b.name;
|
||||
});
|
||||
if (window.user) {
|
||||
this.user = LNbits.map.user(window.user);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,53 @@
|
||||
$dark-background: #1f2234;
|
||||
$dark-card-background: #333646;
|
||||
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg-lnbits-dark {
|
||||
background-color: $dark-background;
|
||||
}
|
||||
|
||||
body.body--dark,
|
||||
body.body--dark .q-drawer--dark,
|
||||
body.body--dark .q-menu--dark {
|
||||
background: $dark-background;
|
||||
}
|
||||
|
||||
body.body--dark .q-card--dark {
|
||||
background: $dark-card-background;
|
||||
}
|
||||
|
||||
body.body--dark .q-table--dark {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.body--light,
|
||||
body.body--light .q-drawer {
|
||||
background: whitesmoke;
|
||||
}
|
||||
|
||||
body.body--dark .q-field--error {
|
||||
.text-negative,
|
||||
.q-field__messages {
|
||||
color: yellow !important;
|
||||
}
|
||||
}
|
||||
|
||||
.lnbits__q-table__icon-td {
|
||||
padding-left: 5px !important;
|
||||
}
|
||||
|
||||
.lnbits-drawer__q-list .q-item {
|
||||
padding-top: 5px !important;
|
||||
padding-bottom: 5px !important;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
|
||||
&.q-item--active {
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,236 @@
|
||||
//TODO - A reader MUST check that the signature is valid (see the n tagged field)
|
||||
//TODO - Tagged part of type f: the fallback on-chain address should be decoded into an address format
|
||||
//TODO - A reader MUST check that the SHA-2 256 in the h field exactly matches the hashed description.
|
||||
//TODO - A reader MUST use the n field to validate the signature instead of performing signature recovery if a valid n field is provided.
|
||||
|
||||
function decode(paymentRequest) {
|
||||
let input = paymentRequest.toLowerCase();
|
||||
let splitPosition = input.lastIndexOf('1');
|
||||
let humanReadablePart = input.substring(0, splitPosition);
|
||||
let data = input.substring(splitPosition + 1, input.length - 6);
|
||||
let checksum = input.substring(input.length - 6, input.length);
|
||||
if (!verify_checksum(humanReadablePart, bech32ToFiveBitArray(data + checksum))) {
|
||||
throw 'Malformed request: checksum is incorrect'; // A reader MUST fail if the checksum is incorrect.
|
||||
}
|
||||
return {
|
||||
'human_readable_part': decodeHumanReadablePart(humanReadablePart),
|
||||
'data': decodeData(data, humanReadablePart),
|
||||
'checksum': checksum
|
||||
}
|
||||
}
|
||||
|
||||
function decodeHumanReadablePart(humanReadablePart) {
|
||||
let prefixes = ['lnbc', 'lntb', 'lnbcrt', 'lnsb'];
|
||||
let prefix;
|
||||
prefixes.forEach(value => {
|
||||
if (humanReadablePart.substring(0, value.length) === value) {
|
||||
prefix = value;
|
||||
}
|
||||
});
|
||||
if (prefix == null) throw 'Malformed request: unknown prefix'; // A reader MUST fail if it does not understand the prefix.
|
||||
let amount = decodeAmount(humanReadablePart.substring(prefix.length, humanReadablePart.length));
|
||||
return {
|
||||
'prefix': prefix,
|
||||
'amount': amount
|
||||
}
|
||||
}
|
||||
|
||||
function decodeData(data, humanReadablePart) {
|
||||
let date32 = data.substring(0, 7);
|
||||
let dateEpoch = bech32ToInt(date32);
|
||||
let signature = data.substring(data.length - 104, data.length);
|
||||
let tagData = data.substring(7, data.length - 104);
|
||||
let decodedTags = decodeTags(tagData);
|
||||
let value = bech32ToFiveBitArray(date32 + tagData);
|
||||
value = fiveBitArrayTo8BitArray(value, true);
|
||||
value = textToHexString(humanReadablePart).concat(byteArrayToHexString(value));
|
||||
return {
|
||||
'time_stamp': dateEpoch,
|
||||
'tags': decodedTags,
|
||||
'signature': decodeSignature(signature),
|
||||
'signing_data': value
|
||||
}
|
||||
}
|
||||
|
||||
function decodeSignature(signature) {
|
||||
let data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(signature));
|
||||
let recoveryFlag = data[data.length - 1];
|
||||
let r = byteArrayToHexString(data.slice(0, 32));
|
||||
let s = byteArrayToHexString(data.slice(32, data.length - 1));
|
||||
return {
|
||||
'r': r,
|
||||
's': s,
|
||||
'recovery_flag': recoveryFlag
|
||||
}
|
||||
}
|
||||
|
||||
function decodeAmount(str) {
|
||||
let multiplier = str.charAt(str.length - 1);
|
||||
let amount = str.substring(0, str.length - 1);
|
||||
if (amount.substring(0, 1) === '0') {
|
||||
throw 'Malformed request: amount cannot contain leading zeros';
|
||||
}
|
||||
amount = Number(amount);
|
||||
if (amount < 0 || !Number.isInteger(amount)) {
|
||||
throw 'Malformed request: amount must be a positive decimal integer'; // A reader SHOULD fail if amount contains a non-digit
|
||||
}
|
||||
|
||||
switch (multiplier) {
|
||||
case '':
|
||||
return 'Any amount'; // A reader SHOULD indicate if amount is unspecified
|
||||
case 'p':
|
||||
return amount / 10;
|
||||
case 'n':
|
||||
return amount * 100;
|
||||
case 'u':
|
||||
return amount * 100000;
|
||||
case 'm':
|
||||
return amount * 100000000;
|
||||
default:
|
||||
// A reader SHOULD fail if amount is followed by anything except a defined multiplier.
|
||||
throw 'Malformed request: undefined amount multiplier';
|
||||
}
|
||||
}
|
||||
|
||||
function decodeTags(tagData) {
|
||||
let tags = extractTags(tagData);
|
||||
let decodedTags = [];
|
||||
tags.forEach(value => decodedTags.push(decodeTag(value.type, value.length, value.data)));
|
||||
return decodedTags;
|
||||
}
|
||||
|
||||
function extractTags(str) {
|
||||
let tags = [];
|
||||
while (str.length > 0) {
|
||||
let type = str.charAt(0);
|
||||
let dataLength = bech32ToInt(str.substring(1, 3));
|
||||
let data = str.substring(3, dataLength + 3);
|
||||
tags.push({
|
||||
'type': type,
|
||||
'length': dataLength,
|
||||
'data': data
|
||||
});
|
||||
str = str.substring(3 + dataLength, str.length);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
function decodeTag(type, length, data) {
|
||||
switch (type) {
|
||||
case 'p':
|
||||
if (length !== 52) break; // A reader MUST skip over a 'p' field that does not have data_length 52
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'payment_hash',
|
||||
'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data)))
|
||||
};
|
||||
case 'd':
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'description',
|
||||
'value': bech32ToUTF8String(data)
|
||||
};
|
||||
case 'n':
|
||||
if (length !== 53) break; // A reader MUST skip over a 'n' field that does not have data_length 53
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'payee_public_key',
|
||||
'value': byteArrayToHexString(fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data)))
|
||||
};
|
||||
case 'h':
|
||||
if (length !== 52) break; // A reader MUST skip over a 'h' field that does not have data_length 52
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'description_hash',
|
||||
'value': data
|
||||
};
|
||||
case 'x':
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'expiry',
|
||||
'value': bech32ToInt(data)
|
||||
};
|
||||
case 'c':
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'min_final_cltv_expiry',
|
||||
'value': bech32ToInt(data)
|
||||
};
|
||||
case 'f':
|
||||
let version = bech32ToFiveBitArray(data.charAt(0))[0];
|
||||
if (version < 0 || version > 18) break; // a reader MUST skip over an f field with unknown version.
|
||||
data = data.substring(1, data.length);
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'fallback_address',
|
||||
'value': {
|
||||
'version': version,
|
||||
'fallback_address': data
|
||||
}
|
||||
};
|
||||
case 'r':
|
||||
data = fiveBitArrayTo8BitArray(bech32ToFiveBitArray(data));
|
||||
let pubkey = data.slice(0, 33);
|
||||
let shortChannelId = data.slice(33, 41);
|
||||
let feeBaseMsat = data.slice(41, 45);
|
||||
let feeProportionalMillionths = data.slice(45, 49);
|
||||
let cltvExpiryDelta = data.slice(49, 51);
|
||||
return {
|
||||
'type': type,
|
||||
'length': length,
|
||||
'description': 'routing_information',
|
||||
'value': {
|
||||
'public_key': byteArrayToHexString(pubkey),
|
||||
'short_channel_id': byteArrayToHexString(shortChannelId),
|
||||
'fee_base_msat': byteArrayToInt(feeBaseMsat),
|
||||
'fee_proportional_millionths': byteArrayToInt(feeProportionalMillionths),
|
||||
'cltv_expiry_delta': byteArrayToInt(cltvExpiryDelta)
|
||||
}
|
||||
};
|
||||
default:
|
||||
// reader MUST skip over unknown fields
|
||||
}
|
||||
}
|
||||
|
||||
function polymod(values) {
|
||||
let GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
||||
let chk = 1;
|
||||
values.forEach((value) => {
|
||||
let b = (chk >> 25);
|
||||
chk = (chk & 0x1ffffff) << 5 ^ value;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (((b >> i) & 1) === 1) {
|
||||
chk ^= GEN[i];
|
||||
} else {
|
||||
chk ^= 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
return chk;
|
||||
}
|
||||
|
||||
function expand(str) {
|
||||
let array = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
array.push(str.charCodeAt(i) >> 5);
|
||||
}
|
||||
array.push(0);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
array.push(str.charCodeAt(i) & 31);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function verify_checksum(hrp, data) {
|
||||
hrp = expand(hrp);
|
||||
let all = hrp.concat(data);
|
||||
let bool = polymod(all);
|
||||
return bool === 1;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
const bech32CharValues = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
|
||||
|
||||
function byteArrayToInt(byteArray) {
|
||||
let value = 0;
|
||||
for (let i = 0; i < byteArray.length; ++i) {
|
||||
value = (value << 8) + byteArray[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function bech32ToInt(str) {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
sum = sum * 32;
|
||||
sum = sum + bech32CharValues.indexOf(str.charAt(i));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
function bech32ToFiveBitArray(str) {
|
||||
let array = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
array.push(bech32CharValues.indexOf(str.charAt(i)));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function fiveBitArrayTo8BitArray(int5Array, includeOverflow) {
|
||||
let count = 0;
|
||||
let buffer = 0;
|
||||
let byteArray = [];
|
||||
int5Array.forEach((value) => {
|
||||
buffer = (buffer << 5) + value;
|
||||
count += 5;
|
||||
if (count >= 8) {
|
||||
byteArray.push(buffer >> (count - 8) & 255);
|
||||
count -= 8;
|
||||
}
|
||||
});
|
||||
if (includeOverflow && count > 0) {
|
||||
byteArray.push(buffer << (8 - count) & 255);
|
||||
}
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
function bech32ToUTF8String(str) {
|
||||
let int5Array = bech32ToFiveBitArray(str);
|
||||
let byteArray = fiveBitArrayTo8BitArray(int5Array);
|
||||
|
||||
let utf8String = '';
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
utf8String += '%' + ('0' + byteArray[i].toString(16)).slice(-2);
|
||||
}
|
||||
return decodeURIComponent(utf8String);
|
||||
}
|
||||
|
||||
function byteArrayToHexString(byteArray) {
|
||||
return Array.prototype.map.call(byteArray, function (byte) {
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function textToHexString(text) {
|
||||
let hexString = '';
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
hexString += text.charCodeAt(i).toString(16);
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
function epochToDate(int) {
|
||||
let date = new Date(int * 1000);
|
||||
return date.toUTCString();
|
||||
}
|
||||
|
||||
function isEmptyOrSpaces(str){
|
||||
return str === null || str.match(/^ *$/) !== null;
|
||||
}
|
||||
|
||||
function toFixed(x) {
|
||||
if (Math.abs(x) < 1.0) {
|
||||
var e = parseInt(x.toString().split('e-')[1]);
|
||||
if (e) {
|
||||
x *= Math.pow(10,e-1);
|
||||
x = '0.' + (new Array(e)).join('0') + x.toString().substring(2);
|
||||
}
|
||||
} else {
|
||||
var e = parseInt(x.toString().split('+')[1]);
|
||||
if (e > 20) {
|
||||
e -= 20;
|
||||
x /= Math.pow(10,e);
|
||||
x += (new Array(e+1)).join('0');
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
|
||||
|
Before Width: | Height: | Size: 59 KiB |