forked from bitcoinafterlife/bal-welist-website
feat: integrazione API reali (fetch /data, POST /data, QR code, weinfo link)
This commit is contained in:
238
index.html
238
index.html
@@ -5,14 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BAL Server List</title>
|
||||
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
|
||||
<script src="qrcode.min.js"></script>
|
||||
<script src="purify.min.js"></script>
|
||||
<style>
|
||||
/* =========================================================
|
||||
BAL Server List — minimal redesign ("Slate")
|
||||
Same backend fields as the original:
|
||||
url, info, base_fee, version, balance, status,
|
||||
last_block, count_win, points (+ chain, id)
|
||||
========================================================= */
|
||||
|
||||
:root{
|
||||
--maxw:1120px;
|
||||
--radius:10px;
|
||||
@@ -48,21 +43,15 @@ body{
|
||||
}
|
||||
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 20px}
|
||||
|
||||
/* ---------- Header / hero ---------- */
|
||||
header.hero{padding:48px 0 28px;border-bottom:1px solid var(--line)}
|
||||
.hero .wrap{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap}
|
||||
.brand{display:flex;align-items:center;gap:16px}
|
||||
.brand .mark{
|
||||
/* Brand mark: light, rounded container that holds the scalable SVG logo.
|
||||
Background is white so the (dark) logo is clearly visible; the SVG uses
|
||||
fill:currentColor, which inherits this color. */
|
||||
width:46px;height:46px;border-radius:12px;flex:none;
|
||||
display:grid;place-items:center;
|
||||
background:#fff;color:var(--ink);
|
||||
border:1px solid var(--line-strong);box-shadow:var(--shadow);overflow:hidden;
|
||||
}
|
||||
/* The inline logo is a vector (SVG) so it stays crisp at ANY size; we only
|
||||
set a display size here and can change it freely without quality loss. */
|
||||
.brand .mark .brand-logo{width:34px;height:34px;display:block}
|
||||
.brand h1{
|
||||
font-family:var(--display);font-weight:600;
|
||||
@@ -73,7 +62,6 @@ header.hero{padding:48px 0 28px;border-bottom:1px solid var(--line)}
|
||||
.stat .n{font-family:var(--display);font-size:26px;font-weight:600;line-height:1}
|
||||
.stat .l{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:6px}
|
||||
|
||||
/* ---------- Controls ---------- */
|
||||
.controls{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding:22px 0 14px}
|
||||
.seg{display:inline-flex;border:1px solid var(--line-strong);border-radius:8px;overflow:hidden}
|
||||
.seg button{
|
||||
@@ -103,12 +91,9 @@ header.hero{padding:48px 0 28px;border-bottom:1px solid var(--line)}
|
||||
}
|
||||
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
|
||||
|
||||
/* ---------- Table ---------- */
|
||||
.table-card{
|
||||
background:var(--panel);border:1px solid var(--line);
|
||||
border-radius:var(--radius);box-shadow:var(--shadow);
|
||||
/* Show roughly 10 rows; once the list is longer the card scrolls
|
||||
vertically instead of growing the page. The header stays pinned. */
|
||||
max-height:350px;overflow:auto;
|
||||
}
|
||||
table{width:100%;border-collapse:collapse;font-size:14px}
|
||||
@@ -117,7 +102,6 @@ thead th{
|
||||
font-size:11px;letter-spacing:.08em;text-transform:uppercase;
|
||||
padding:14px 14px;border-bottom:1px solid var(--line-strong);
|
||||
white-space:nowrap;
|
||||
/* Keep the column headers visible while the table body scrolls. */
|
||||
position:sticky;top:0;background:var(--panel);z-index:2;
|
||||
}
|
||||
thead th.num,tbody td.num{text-align:right}
|
||||
@@ -128,11 +112,6 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
|
||||
.rank.top{color:var(--accent)}
|
||||
.url a{color:var(--ink);text-decoration:none;font-weight:600}
|
||||
.url a:hover{color:var(--accent);text-decoration:underline}
|
||||
.url .tag{
|
||||
display:inline-block;margin-left:8px;font-size:10px;letter-spacing:.08em;
|
||||
text-transform:uppercase;color:var(--muted);border:1px solid var(--line-strong);
|
||||
border-radius:5px;padding:1px 6px;vertical-align:middle;
|
||||
}
|
||||
.info{color:var(--muted);font-size:13px;max-width:200px}
|
||||
.mono{font-family:var(--mono);font-size:13px}
|
||||
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
|
||||
@@ -145,7 +124,6 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
|
||||
.status.offline{color:var(--off)}
|
||||
.status.offline .dot{background:var(--off);box-shadow:0 0 0 3px var(--off-soft)}
|
||||
|
||||
/* ---------- Add server ---------- */
|
||||
.add-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:24px;margin:40px 0;align-items:start}
|
||||
.panel{
|
||||
background:var(--panel);border:1px solid var(--line);
|
||||
@@ -168,24 +146,17 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
|
||||
.note{font-size:13px;color:var(--muted);margin-top:16px;line-height:1.55}
|
||||
.note strong{color:var(--ink)}
|
||||
|
||||
/* invoice preview */
|
||||
.invoice{display:none}
|
||||
.invoice.show{display:block}
|
||||
.invoice .row{display:flex;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid var(--line);font-size:13px}
|
||||
.invoice .row:last-child{border-bottom:0}
|
||||
.invoice .row span:first-child{color:var(--muted)}
|
||||
.invoice .row span:last-child{font-family:var(--mono);text-align:right;word-break:break-all}
|
||||
.qr{margin:18px auto 4px;width:160px;height:160px;display:grid;
|
||||
grid-template-columns:repeat(21,1fr);gap:0;border:1px solid var(--line-strong);
|
||||
border-radius:8px;overflow:hidden;background:#fff}
|
||||
.qr i{background:#000}
|
||||
#qrcode{margin:18px auto 4px;width:160px;height:160px}
|
||||
#qrcode img{width:160px;height:160px}
|
||||
.ok-line{color:var(--ok);font-weight:600;font-size:14px;margin-bottom:14px}
|
||||
.err-line{color:#d32f2f;font-weight:600;font-size:14px;margin-bottom:14px}
|
||||
|
||||
/* ---------- Info section ---------- */
|
||||
/* ---------- Intro highlight card ----------
|
||||
Prominent white box that introduces the page, placed above the
|
||||
"Add your Will Executor" section. The accent stripe + larger title
|
||||
give it more visual weight than the surrounding panels. */
|
||||
.intro-card{
|
||||
background:var(--panel);border:1px solid var(--line);
|
||||
border-left:4px solid var(--accent);
|
||||
@@ -204,16 +175,14 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
|
||||
.info-block h3{font-size:15px;margin:0 0 8px;letter-spacing:.01em}
|
||||
.info-block p{font-size:14px;color:var(--muted);margin:0;line-height:1.6}
|
||||
|
||||
/* ---------- Footer ---------- */
|
||||
footer{border-top:1px solid var(--line);margin-top:48px;padding:28px 0;color:var(--muted);font-size:13px}
|
||||
footer .wrap{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px}
|
||||
footer a{color:var(--accent);text-decoration:none}
|
||||
|
||||
/* ---------- Mobile: table → cards ---------- */
|
||||
.mobile-list{display:none}
|
||||
@media (max-width:860px){
|
||||
.table-card{display:none}
|
||||
.mobile-list{display:grid;gap:12px;max-height:640px;overflow:auto;padding-right:2px}
|
||||
.mobile-list{display:grid;gap:12px;max-height:350px;overflow:auto;padding-right:2px}
|
||||
.add-grid{grid-template-columns:1fr}
|
||||
.info-grid{grid-template-columns:1fr}
|
||||
.hero-stats{gap:20px}
|
||||
@@ -242,9 +211,9 @@ footer a{color:var(--accent);text-decoration:none}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-stats">
|
||||
<div class="stat"><div class="n" id="s-online">5</div><div class="l">Online</div></div>
|
||||
<div class="stat"><div class="n" id="s-total">6</div><div class="l">Servers</div></div>
|
||||
<div class="stat"><div class="n">884,201</div><div class="l">Block</div></div>
|
||||
<div class="stat"><div class="n" id="s-online">0</div><div class="l">Online</div></div>
|
||||
<div class="stat"><div class="n" id="s-total">0</div><div class="l">Servers</div></div>
|
||||
<div class="stat"><div class="n" id="s-block">—</div><div class="l">Block</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -274,7 +243,6 @@ footer a{color:var(--accent);text-decoration:none}
|
||||
<a class="btn-ghost" id="download" href="#" download="willexecutors.json">↓ JSON</a>
|
||||
</div>
|
||||
|
||||
<!-- Desktop table -->
|
||||
<div class="table-card">
|
||||
<table id="tbl">
|
||||
<thead>
|
||||
@@ -295,17 +263,13 @@ footer a{color:var(--accent);text-decoration:none}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Mobile list -->
|
||||
<div class="mobile-list" id="mlist"></div>
|
||||
|
||||
<!-- Intro highlight: moved here from the bottom of the page and given a
|
||||
prominent white box so it reads as the page's primary description. -->
|
||||
<section class="intro-card">
|
||||
<h2>Will Executor Server List</h2>
|
||||
<p>This page displays all Will Executor servers that manage the inheritance processing for the Bitcoin After Life plugin on Electrum. Will Executors operate in a competitive environment, racing to broadcast inheritance transactions to network nodes and earn the associated transaction fees.</p>
|
||||
</section>
|
||||
|
||||
<!-- Add server -->
|
||||
<div class="add-grid">
|
||||
<div class="panel">
|
||||
<h2>Add your Will Executor</h2>
|
||||
@@ -316,12 +280,13 @@ footer a{color:var(--accent);text-decoration:none}
|
||||
</div>
|
||||
|
||||
<div class="invoice" id="invoice">
|
||||
<div class="ok-line">✓ Server added. Pay the invoice to complete.</div>
|
||||
<div class="row"><span>Minimum amount</span><span>50,000 sats</span></div>
|
||||
<div class="row"><span>Chain</span><span id="iv-chain">bitcoin</span></div>
|
||||
<div class="row"><span>Address</span><span id="iv-addr">bc1q…demo…address</span></div>
|
||||
<div class="row"><span>Status</span><span>online</span></div>
|
||||
<div class="qr" id="qr" aria-label="Invoice QR (demo)"></div>
|
||||
<div class="ok-line" id="iv-status">✓ Server added. Pay the invoice to complete.</div>
|
||||
<div class="row"><span>Minimum amount</span><span id="iv-min">—</span></div>
|
||||
<div class="row"><span>Chain</span><span id="iv-chain">—</span></div>
|
||||
<div class="row"><span>Address</span><span id="iv-addr">—</span></div>
|
||||
<div class="row"><span>Balance</span><span id="iv-balance">—</span></div>
|
||||
<div class="row"><span>Status</span><span id="iv-status2">—</span></div>
|
||||
<div id="qrcode"></div>
|
||||
</div>
|
||||
|
||||
<p class="note">
|
||||
@@ -340,7 +305,6 @@ footer a{color:var(--accent);text-decoration:none}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info section -->
|
||||
<section class="info-section">
|
||||
<div class="info-grid">
|
||||
<div class="info-block">
|
||||
@@ -379,115 +343,163 @@ footer a{color:var(--accent);text-decoration:none}
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
/* ---- Sample data (same shape as the real backend) ----
|
||||
12 example servers so the list demonstrates the scroll behaviour:
|
||||
the table/cards show ~10 entries, the rest are reachable by scrolling.
|
||||
Sorted by `points` (the bid score) at render time. */
|
||||
const DATA = [
|
||||
{url:"executor.bitcoin-after.life:9137", onion:false, info:"Primary node · EU", base_fee:1000, version:"1.2.0", balance:520000, status:"online", last_block:884201, count_win:142, points:120000},
|
||||
{url:"exec.satoshi.eu:9137", onion:false, info:"Low latency · DE", base_fee:800, version:"1.2.0", balance:410000, status:"online", last_block:884201, count_win:118, points:98000},
|
||||
{url:"node.lastwill.io:9137", onion:false, info:"High availability", base_fee:1500, version:"1.2.0", balance:360000, status:"online", last_block:884201, count_win:96, points:84000},
|
||||
{url:"exec7g3k…q.onion:9137", onion:true, info:"Privacy-first relay", base_fee:1200, version:"1.2.0", balance:300000, status:"online", last_block:884200, count_win:88, points:76000},
|
||||
{url:"heir.btcrelay.net:9137", onion:false, info:"US-East", base_fee:900, version:"1.2.0", balance:240000, status:"online", last_block:884201, count_win:71, points:65000},
|
||||
{url:"will.nodeforge.org:9137", onion:false, info:"Community operated", base_fee:1100, version:"1.1.9", balance:190000, status:"online", last_block:884199, count_win:60, points:58000},
|
||||
{url:"onx4f…z.onion:9137", onion:true, info:"Tor only", base_fee:1100, version:"1.2.0", balance:150000, status:"online", last_block:884201, count_win:52, points:52000},
|
||||
{url:"exec.coldkeep.io:9137", onion:false, info:"Backup executor · APAC", base_fee:1300, version:"1.1.9", balance:120000, status:"online", last_block:884198, count_win:44, points:47000},
|
||||
{url:"relay.inheritbtc.net:9137", onion:false, info:"", base_fee:1000, version:"1.2.0", balance:95000, status:"offline", last_block:884150, count_win:38, points:39000},
|
||||
{url:"kx9d…w.onion:9137", onion:true, info:"Privacy relay · backup", base_fee:1400, version:"1.1.9", balance:70000, status:"online", last_block:884197, count_win:29, points:31000},
|
||||
{url:"node.testator.eu:9137", onion:false, info:"Low fee", base_fee:600, version:"1.2.0", balance:48000, status:"online", last_block:884201, count_win:21, points:22000},
|
||||
{url:"exec.legacychain.io:9137", onion:false, info:"New operator", base_fee:1200, version:"1.1.9", balance:25000, status:"offline", last_block:884120, count_win:9, points:11000},
|
||||
];
|
||||
|
||||
const fmt = n => n.toLocaleString('en-US');
|
||||
let allServers = [];
|
||||
let withTor = true, q = "";
|
||||
|
||||
function rows(){
|
||||
return DATA
|
||||
.filter(d => withTor ? true : !d.onion)
|
||||
.filter(d => (d.url+" "+d.info).toLowerCase().includes(q.toLowerCase()))
|
||||
.sort((a,b)=>b.points-a.points);
|
||||
const fmt = n => (n != null ? Number(n).toLocaleString('en-US') : '—');
|
||||
|
||||
function getChain() {
|
||||
const btn = document.querySelector('#chainSeg [aria-pressed="true"]');
|
||||
return btn ? btn.dataset.c : 'bitcoin';
|
||||
}
|
||||
|
||||
function render(){
|
||||
const list = rows();
|
||||
function getFiltered() {
|
||||
return allServers
|
||||
.filter(d => withTor ? true : !d.onion)
|
||||
.filter(d => (d.url + " " + (d.info||"")).toLowerCase().includes(q.toLowerCase()))
|
||||
.sort((a, b) => (b.points||0) - (a.points||0));
|
||||
}
|
||||
|
||||
function render() {
|
||||
const list = getFiltered();
|
||||
const tb = document.getElementById('tbody');
|
||||
const ml = document.getElementById('mlist');
|
||||
tb.innerHTML = ""; ml.innerHTML = "";
|
||||
|
||||
if(!list.length){
|
||||
if (!list.length) {
|
||||
tb.innerHTML = '<tr><td colspan="10" style="padding:28px;text-align:center;color:var(--muted)">No servers found.</td></tr>';
|
||||
}
|
||||
|
||||
list.forEach((d,i)=>{
|
||||
const rank = i+1;
|
||||
const st = d.status==="online"
|
||||
list.forEach((d, i) => {
|
||||
const rank = i + 1;
|
||||
const st = d.status === "online"
|
||||
? '<span class="status online"><span class="dot"></span>Online</span>'
|
||||
: '<span class="status offline"><span class="dot"></span>Offline</span>';
|
||||
const tag = d.onion ? '<span class="tag">tor</span>' : '';
|
||||
const weLink = `/weinfo/${d.chain||getChain()}/${d.id}`;
|
||||
|
||||
tb.insertAdjacentHTML('beforeend', `
|
||||
<tr>
|
||||
<td class="num"><span class="rank ${rank<=3?'top':''}">${rank}</span></td>
|
||||
<td class="url"><a href="#">${d.url}</a>${tag}</td>
|
||||
<td class="info">${d.info||'—'}</td>
|
||||
<td class="url"><a href="${weLink}">${DOMPurify.sanitize(d.url)}</a></td>
|
||||
<td class="info">${DOMPurify.sanitize(d.info||'—')}</td>
|
||||
<td class="num">${fmt(d.base_fee)}</td>
|
||||
<td class="mono">${d.version}</td>
|
||||
<td class="mono">${DOMPurify.sanitize(d.version||'—')}</td>
|
||||
<td class="num">${fmt(d.balance)}</td>
|
||||
<td>${st}</td>
|
||||
<td class="num">${fmt(d.last_block)}</td>
|
||||
<td class="num">${d.count_win}</td>
|
||||
<td class="num">${d.count_win != null ? d.count_win : '—'}</td>
|
||||
<td class="num"><span class="score">${fmt(d.points)}</span></td>
|
||||
</tr>`);
|
||||
|
||||
ml.insertAdjacentHTML('beforeend', `
|
||||
<div class="srv">
|
||||
<div class="top">
|
||||
<div><div class="rk">#${rank} ${tag}</div><a href="#" style="color:var(--ink);font-weight:600;text-decoration:none;font-size:14px">${d.url}</a></div>
|
||||
<div><div class="rk">#${rank}</div><a href="${weLink}" style="color:var(--ink);font-weight:600;text-decoration:none;font-size:14px">${DOMPurify.sanitize(d.url)}</a></div>
|
||||
${st}
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div><span>Score</span><b>${fmt(d.points)}</b></div>
|
||||
<div><span>Wins</span><b>${d.count_win}</b></div>
|
||||
<div><span>Wins</span><b>${d.count_win != null ? d.count_win : '—'}</b></div>
|
||||
<div><span>Fee</span><b>${fmt(d.base_fee)} sat</b></div>
|
||||
<div><span>Balance</span><b>${fmt(d.balance)}</b></div>
|
||||
<div><span>Version</span><b>${d.version}</b></div>
|
||||
<div><span>Version</span><b>${DOMPurify.sanitize(d.version||'—')}</b></div>
|
||||
<div><span>Height</span><b>${fmt(d.last_block)}</b></div>
|
||||
</div>
|
||||
</div>`);
|
||||
});
|
||||
|
||||
const online = DATA.filter(d=>d.status==="online").length;
|
||||
const online = allServers.filter(d => d.status === "online").length;
|
||||
document.getElementById('s-online').textContent = online;
|
||||
document.getElementById('s-total').textContent = DATA.length;
|
||||
document.getElementById('s-total').textContent = allServers.length;
|
||||
const top = getFiltered()[0];
|
||||
document.getElementById('s-block').textContent = top ? fmt(top.last_block) : '—';
|
||||
}
|
||||
|
||||
/* demo JSON download */
|
||||
(function(){
|
||||
const blob = new Blob([JSON.stringify(DATA,null,2)],{type:'application/json'});
|
||||
document.getElementById('download').href = URL.createObjectURL(blob);
|
||||
})();
|
||||
async function fetchData() {
|
||||
const chain = getChain();
|
||||
const params = new URLSearchParams({ page: 0, limit: 100 });
|
||||
if (!withTor) params.append("onion", "no");
|
||||
try {
|
||||
const response = await fetch(`/data/${chain}?${params}`);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const obj = await response.json();
|
||||
allServers = Object.entries(obj).map(([key, s]) => ({ ...s, id: key }));
|
||||
render();
|
||||
} catch (err) {
|
||||
console.error('Fetch error:', err);
|
||||
allServers = [];
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
/* controls */
|
||||
document.getElementById('onion').addEventListener('change',e=>{withTor=e.target.checked;render();});
|
||||
document.getElementById('filter').addEventListener('input',e=>{q=e.target.value;render();});
|
||||
document.getElementById('onion').addEventListener('change', e => {
|
||||
withTor = e.target.checked;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
document.getElementById('chainSeg').addEventListener('click',e=>{
|
||||
const b=e.target.closest('button'); if(!b)return;
|
||||
[...e.currentTarget.children].forEach(x=>x.setAttribute('aria-pressed','false'));
|
||||
b.setAttribute('aria-pressed','true');
|
||||
document.getElementById('filter').addEventListener('input', e => {
|
||||
q = e.target.value;
|
||||
render();
|
||||
});
|
||||
|
||||
/* invoice demo + faux QR */
|
||||
document.getElementById('submit').addEventListener('click',()=>{
|
||||
document.getElementById('iv-addr').textContent='bc1q'+Math.random().toString(36).slice(2,10)+'demo'+Math.random().toString(36).slice(2,8);
|
||||
const inv=document.getElementById('invoice'); inv.classList.add('show');
|
||||
const qr=document.getElementById('qr'); qr.innerHTML="";
|
||||
for(let i=0;i<441;i++){ if(Math.random()>.52){qr.appendChild(document.createElement('i'));} else {qr.appendChild(document.createElement('em'));} }
|
||||
inv.scrollIntoView({behavior:'smooth',block:'nearest'});
|
||||
document.getElementById('chainSeg').addEventListener('click', e => {
|
||||
const b = e.target.closest('button');
|
||||
if (!b) return;
|
||||
[...e.currentTarget.children].forEach(x => x.setAttribute('aria-pressed', 'false'));
|
||||
b.setAttribute('aria-pressed', 'true');
|
||||
fetchData();
|
||||
});
|
||||
|
||||
render();
|
||||
document.getElementById('submit').addEventListener('click', async () => {
|
||||
const urlInput = document.getElementById('url');
|
||||
let url = urlInput.value.trim();
|
||||
if (!url) return;
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
url = 'https://' + url;
|
||||
urlInput.value = url;
|
||||
}
|
||||
const chain = getChain();
|
||||
try {
|
||||
const response = await fetch('/data', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ url, chain })
|
||||
});
|
||||
const result = await response.json();
|
||||
const inv = document.getElementById('invoice');
|
||||
if (!response.ok) {
|
||||
document.getElementById('iv-status').className = 'err-line';
|
||||
document.getElementById('iv-status').textContent = 'Error: ' + (result.message || result.error || JSON.stringify(result));
|
||||
inv.classList.add('show');
|
||||
document.getElementById('qrcode').innerHTML = '';
|
||||
return;
|
||||
}
|
||||
document.getElementById('iv-status').className = 'ok-line';
|
||||
document.getElementById('iv-status').textContent = '✓ Server added. Pay the invoice to complete.';
|
||||
document.getElementById('iv-min').textContent = (result.min_amount || '50,000') + ' sats';
|
||||
document.getElementById('iv-chain').textContent = result.chain || chain;
|
||||
document.getElementById('iv-addr').textContent = result.address || '—';
|
||||
document.getElementById('iv-balance').textContent = result.balance != null ? fmt(result.balance) : '—';
|
||||
document.getElementById('iv-status2').textContent = result.status || '—';
|
||||
const invoiceuri = `bitcoin:${result.address}?label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`;
|
||||
const qrEl = document.getElementById('qrcode');
|
||||
qrEl.innerHTML = '';
|
||||
new QRCode(qrEl, invoiceuri);
|
||||
inv.classList.add('show');
|
||||
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
} catch (err) {
|
||||
document.getElementById('iv-status').className = 'err-line';
|
||||
document.getElementById('iv-status').textContent = 'Network error. Please try again.';
|
||||
document.getElementById('invoice').classList.add('show');
|
||||
document.getElementById('qrcode').innerHTML = '';
|
||||
}
|
||||
});
|
||||
|
||||
(function init() {
|
||||
const blob = new Blob([JSON.stringify(allServers, null, 2)], { type: 'application/json' });
|
||||
document.getElementById('download').href = URL.createObjectURL(blob);
|
||||
fetchData();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user