Add Lightning tips (LNURL-pay → bolt11 → QR)

A self-contained tip flow that works for any author whose kind-0 profile
publishes a lud16 Lightning address or lud06 LNURL. Picks an amount,
fetches a bolt11 invoice from the LNURL-pay endpoint, renders it as a
scannable QR + copyable string. No NIP-57 zap request, no NWC, no
WebLN — just the smallest path to actually moving sats.

Phase A — LNURL helpers and tip modal:
- Vendored qrcode-generator (~20 KB, MIT) as src/vendor/qrcode.min.js
  so the QR encoder runs offline with no CDN dependency.
- lud16ToLnurlpUrl / lud06ToHttpUrl / resolveLnurlPayUrl pick the right
  endpoint from a profile.
- fetchLnurlPayMetadata validates the payRequest tag and surfaces the
  min/max range; fetchLnurlInvoice requests an amount-locked bolt11.
- renderQrInto auto-scales the typeNumber so long invoices still fit.
- tipModal: amount-chip picker (21 / 100 / 500 / 1k / 5k / 21k / Custom),
  inline min/max validation, fetching state, then an invoice view with
  QR, lightning: deeplink, copyable bolt11, and a New amount / Done row.
- Brought to front via the modal z-stack; ESC, backdrop, and × close it.

Phase B — Feed card surface:
- Every feed card whose author has a resolvable LN address gets a
  ⚡ Tip button in the actions row. No button when there's no address,
  so feeds stay clean.

Phase C — Design preview surface:
- Same conditional ⚡ Tip button in the design preview action row, next
  to Save JSON and Copy share link.

Profile pages now treat the Lightning chip as the entry point — clicking
the chip opens the tip modal instead of copying the address. Profiles
with an address that fails to resolve fall back to the original copy chip.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.7 committed 2026-05-18 13:44:46 -04:00
1 parent e9b38710d6
commit e33d15f41f
2 files changed
+331 -4

No files matched your search

+323 -4
View File
@@ -1582,6 +1582,16 @@
.profileAvatar { margin-top:-44px; }
}
.tipAmounts { display:grid; grid-template-columns:repeat(auto-fit,minmax(72px,1fr)); gap:8px; margin:0 0 14px; }
.tipAmounts button { padding:10px 6px; font-size:.9rem; font-weight:600; cursor:pointer; border:1px solid var(--line); background:var(--bg); color:var(--ink); border-radius:8px; }
.tipAmounts button.active { background:var(--accent); color:#fff; border-color:var(--accent); }
.tipAmounts button:hover { filter:brightness(.97); }
.tipQr { display:flex; justify-content:center; align-items:center; background:#fff; padding:14px; border-radius:8px; margin:0 auto 14px; max-width:280px; }
.tipQr svg { display:block; width:240px; height:240px; }
.tipBolt11 { display:flex; gap:8px; align-items:center; margin-bottom:10px; padding:8px 10px; border:1px solid var(--line); border-radius:6px; background:var(--bg); }
.tipBolt11 code { flex:1; min-width:0; font-family:ui-monospace,SFMono-Regular,monospace; font-size:.72rem; word-break:break-all; max-height:3em; overflow:hidden; line-height:1.3; }
.tipBolt11 button { flex-shrink:0; }
.addIdentifiersDetails { }
.addIdentifiersBtn {
display: inline-flex;
@@ -2737,6 +2747,48 @@
</div>
</div>
<div class="modal" id="tipModal" aria-hidden="true" style="display:none">
<div class="modalInner" style="max-width:420px;position:relative">
<button type="button" id="tipCloseBtn" class="welcomeCloseBtn" aria-label="Close">×</button>
<h2 id="tipTitle" style="margin:0 0 4px;font-size:1.05rem;font-family:inherit">Send a tip</h2>
<p class="hint" id="tipSubtitle" style="margin:0 0 14px"></p>
<div id="tipPickerView">
<label class="hint" style="display:block;font-size:.78rem;margin-bottom:6px">Amount (sats)</label>
<div class="tipAmounts" id="tipAmounts">
<button type="button" data-sats="21">21</button>
<button type="button" data-sats="100">100</button>
<button type="button" data-sats="500">500</button>
<button type="button" data-sats="1000">1k</button>
<button type="button" data-sats="5000">5k</button>
<button type="button" data-sats="21000">21k</button>
<button type="button" data-sats="custom">Custom</button>
</div>
<input type="number" id="tipCustomInput" min="1" placeholder="Custom amount in sats" style="display:none;width:100%;margin-bottom:10px;padding:8px;border:1px solid var(--line);border-radius:6px;font-size:.9rem" />
<p class="hint" id="tipPickerError" style="min-height:1.2em;margin:0 0 10px"></p>
<div class="buttonRow" style="margin:0;justify-content:flex-end">
<button type="button" id="tipGetInvoiceBtn" class="primary">Get invoice</button>
</div>
</div>
<div id="tipInvoiceView" style="display:none">
<p class="hint" id="tipInvoiceAmount" style="margin:0 0 10px;text-align:center;font-size:.88rem"></p>
<a id="tipQrLink" href="#" target="_blank" rel="noopener" style="text-decoration:none;color:inherit;display:block">
<div id="tipQr" class="tipQr"></div>
</a>
<div class="tipBolt11">
<code id="tipBolt11"></code>
<button type="button" id="tipCopyBtn" aria-label="Copy invoice">📋</button>
</div>
<p class="hint" style="margin:0 0 14px;font-size:.78rem">Scan the QR with a Lightning wallet, or copy the invoice and paste it into your wallet to send the tip.</p>
<div class="buttonRow" style="margin:0;justify-content:space-between">
<button type="button" id="tipBackBtn">← New amount</button>
<button type="button" id="tipDoneBtn" class="primary">Done</button>
</div>
</div>
</div>
</div>
<div class="modal" id="closeProjectModal" aria-hidden="true" style="display:none">
<div class="modalInner" style="max-width:440px">
<h2 style="font-size:1.05rem;margin:0 0 8px;font-family:inherit">Close this project?</h2>
@@ -2775,6 +2827,7 @@
</div>
</div>
<script src="vendor/qrcode.min.js"></script>
<script>
const $ = (id) => document.getElementById(id);
const safe$ = (id) => { try { return $(id); } catch { return null; } };
@@ -7915,12 +7968,21 @@
}).catch(() => {});
}
// Lightning chip (read-only copy — payment flow is a separate feature)
// Lightning chip: clicking opens the Tip modal (LNURL-pay → bolt11 → QR)
if (lnRowEl) {
lnRowEl.innerHTML = '';
const lnAddress = (profile?.lud16 || decodeLud06ToAddress(profile?.lud06) || '').trim();
if (lnAddress) {
const chip = makeProfileChip({
const lnurlpUrl = resolveLnurlPayUrl(profile);
if (lnAddress && lnurlpUrl) {
const tipChip = makeProfileChip({
label: `⚡ Tip ${lnAddress}`,
title: 'Send a Lightning tip',
onClick: () => openTipModal({ lnurlpUrl, label: displayName, lud16: lnAddress })
});
lnRowEl.appendChild(tipChip);
} else if (lnAddress) {
// Have an address but couldn't resolve it — fall back to copy.
const copyChip = makeProfileChip({
label: `⚡ ${lnAddress}`,
title: 'Copy Lightning address',
onClick: async (btn) => {
@@ -7928,7 +7990,7 @@
flashChipText(btn, ok ? '⚡ Copied!' : '⚡ Copy failed');
}
});
lnRowEl.appendChild(chip);
lnRowEl.appendChild(copyChip);
}
if (isHttpUrl(profile?.website)) {
const link = document.createElement('a');
@@ -8070,6 +8132,199 @@
}
}
// ---- Lightning Tips (LNURL-pay) ----
function lud16ToLnurlpUrl(ln) {
const parts = String(ln || '').trim().split('@');
if (parts.length !== 2 || !parts[0] || !parts[1]) return '';
const name = parts[0];
const host = parts[1];
if (!/^[a-z0-9.-]+$/i.test(host)) return '';
return `https://${host}/.well-known/lnurlp/${encodeURIComponent(name)}`;
}
function lud06ToHttpUrl(bech32) {
try {
const dec = bech32Decode(String(bech32 || '').trim());
if (!dec || dec.hrp !== 'lnurl') return '';
const bytes = convertBits(dec.data, 5, 8, false);
if (!bytes) return '';
const url = new TextDecoder().decode(new Uint8Array(bytes));
return url.startsWith('http://') || url.startsWith('https://') ? url : '';
} catch { return ''; }
}
function resolveLnurlPayUrl(profile) {
if (!profile) return '';
if (profile.lud16) {
const u = lud16ToLnurlpUrl(profile.lud16);
if (u) return u;
}
if (profile.lud06) {
const u = lud06ToHttpUrl(profile.lud06);
if (u) return u;
}
return '';
}
async function fetchLnurlPayMetadata(url) {
const r = await fetch(url, { headers: { Accept: 'application/json' } });
if (!r.ok) throw new Error(`Lookup failed (${r.status})`);
const j = await r.json();
if (j.status === 'ERROR') throw new Error(j.reason || 'Service returned an error');
if (j.tag !== 'payRequest') throw new Error('Not a Lightning tip endpoint');
return j;
}
async function fetchLnurlInvoice(callback, msats) {
const sep = callback.includes('?') ? '&' : '?';
const url = `${callback}${sep}amount=${msats}`;
const r = await fetch(url, { headers: { Accept: 'application/json' } });
if (!r.ok) throw new Error(`Invoice fetch failed (${r.status})`);
const j = await r.json();
if (j.status === 'ERROR') throw new Error(j.reason || 'Service returned an error');
if (!j.pr) throw new Error('No invoice returned');
return j.pr;
}
function renderQrInto(el, text) {
if (typeof qrcode !== 'function') { el.textContent = 'QR library unavailable'; return; }
el.innerHTML = '';
// Try increasing typeNumber until the data fits. Bolt11 invoices can be long.
let qr = null;
for (let typeNumber = 6; typeNumber <= 40; typeNumber++) {
try {
const candidate = qrcode(typeNumber, 'M');
candidate.addData(text);
candidate.make();
qr = candidate;
break;
} catch { /* keep trying larger type */ }
}
if (!qr) { el.textContent = 'QR too large to render'; return; }
el.innerHTML = qr.createSvgTag({ scalable: true });
}
let _tipTarget = null; // { lnurlpUrl, label, lud16 }
let _tipSelectedSats = 0;
let _tipMetadata = null;
function openTipModal(opts = {}) {
const { lnurlpUrl, label, lud16 } = opts;
if (!lnurlpUrl) return;
_tipTarget = { lnurlpUrl, label: label || 'Recipient', lud16: lud16 || '' };
_tipSelectedSats = 0;
_tipMetadata = null;
const modal = $('tipModal');
if (!modal) return;
$('tipTitle').textContent = `Tip ${label || 'Recipient'}`;
$('tipSubtitle').textContent = lud16 || '';
$('tipPickerError').textContent = '';
$('tipPickerView').style.display = '';
$('tipInvoiceView').style.display = 'none';
$('tipCustomInput').style.display = 'none';
$('tipCustomInput').value = '';
const getBtn = $('tipGetInvoiceBtn');
if (getBtn) { getBtn.disabled = true; getBtn.textContent = 'Get invoice'; }
// Clear any active selection
$('tipAmounts').querySelectorAll('button').forEach(b => b.classList.remove('active'));
modal.style.display = 'flex';
modal.setAttribute('aria-hidden', 'false');
bringModalToFront(modal);
// Fetch metadata in the background; once it arrives we know min/max.
fetchLnurlPayMetadata(lnurlpUrl).then(meta => {
if (_tipTarget?.lnurlpUrl !== lnurlpUrl) return;
_tipMetadata = meta;
}).catch(err => {
if (_tipTarget?.lnurlpUrl !== lnurlpUrl) return;
const errEl = $('tipPickerError');
if (errEl) errEl.textContent = String(err?.message || err);
});
}
function closeTipModal() {
const modal = $('tipModal');
if (modal) { modal.style.display = 'none'; modal.setAttribute('aria-hidden', 'true'); }
_tipTarget = null;
_tipSelectedSats = 0;
_tipMetadata = null;
}
function selectTipAmount(value) {
const errEl = $('tipPickerError');
const customInput = $('tipCustomInput');
const getBtn = $('tipGetInvoiceBtn');
if (errEl) errEl.textContent = '';
$('tipAmounts').querySelectorAll('button').forEach(b => b.classList.toggle('active', b.dataset.sats === String(value)));
if (value === 'custom') {
customInput.style.display = '';
customInput.focus();
_tipSelectedSats = parseInt(customInput.value, 10) || 0;
} else {
customInput.style.display = 'none';
_tipSelectedSats = parseInt(value, 10) || 0;
}
if (getBtn) getBtn.disabled = _tipSelectedSats <= 0;
}
async function getTipInvoiceFlow() {
const errEl = $('tipPickerError');
const getBtn = $('tipGetInvoiceBtn');
if (!_tipTarget) return;
let sats = _tipSelectedSats;
if (sats <= 0) { if (errEl) errEl.textContent = 'Pick an amount first.'; return; }
try {
if (getBtn) { getBtn.disabled = true; getBtn.textContent = 'Fetching…'; }
const meta = _tipMetadata || await fetchLnurlPayMetadata(_tipTarget.lnurlpUrl);
_tipMetadata = meta;
const minSats = Math.ceil((meta.minSendable || 1000) / 1000);
const maxSats = Math.floor((meta.maxSendable || 100000000) / 1000);
if (sats < minSats || sats > maxSats) {
if (errEl) errEl.textContent = `Enter between ${minSats} and ${maxSats.toLocaleString()} sats.`;
if (getBtn) { getBtn.disabled = false; getBtn.textContent = 'Get invoice'; }
return;
}
const msats = sats * 1000;
const bolt11 = await fetchLnurlInvoice(meta.callback, msats);
showTipInvoiceView(bolt11, sats);
} catch (err) {
if (errEl) errEl.textContent = String(err?.message || err);
if (getBtn) { getBtn.disabled = false; getBtn.textContent = 'Get invoice'; }
}
}
function showTipInvoiceView(bolt11, sats) {
$('tipPickerView').style.display = 'none';
$('tipInvoiceView').style.display = '';
$('tipInvoiceAmount').textContent = `${sats.toLocaleString()} sats`;
const qrEl = $('tipQr');
const link = $('tipQrLink');
if (link) link.href = `lightning:${bolt11}`;
renderQrInto(qrEl, bolt11.toUpperCase());
$('tipBolt11').textContent = bolt11;
const copyBtn = $('tipCopyBtn');
if (copyBtn) {
copyBtn.onclick = async () => {
const ok = await copyToClipboard(bolt11);
copyBtn.textContent = ok ? '✓' : '✗';
setTimeout(() => { if (copyBtn) copyBtn.textContent = '📋'; }, 1200);
};
}
}
function resetTipPicker() {
$('tipInvoiceView').style.display = 'none';
$('tipPickerView').style.display = '';
const errEl = $('tipPickerError');
if (errEl) errEl.textContent = '';
const getBtn = $('tipGetInvoiceBtn');
if (getBtn) { getBtn.disabled = _tipSelectedSats <= 0; getBtn.textContent = 'Get invoice'; }
}
let _closeProjectTarget = null;
function openCloseProjectModal({ body, save, reset }) {
@@ -8458,7 +8713,9 @@
e.stopPropagation();
openReportModal(row.e?.id, row.e?.pubkey);
});
const tipBtn = makeFeedCardTipButton(row);
actions.append(makeReactionBtn(row), prevBtn, btn, blockBtn, reportBtn);
if (tipBtn) actions.appendChild(tipBtn);
el.append(thumb, meta, actions);
el.addEventListener('click', () => {
if (el.classList.contains('feedCardBlurred')) return;
@@ -8467,6 +8724,25 @@
return el;
}
function makeFeedCardTipButton(row) {
const pk = row?.e?.pubkey;
if (!pk) return null;
const profile = _feedCache.profiles?.[pk] || state.community?.profiles?.[pk] || null;
const lnurlpUrl = resolveLnurlPayUrl(profile);
if (!lnurlpUrl) return null;
const lnAddress = (profile?.lud16 || '').trim();
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = '⚡ Tip';
btn.title = lnAddress ? `Send a Lightning tip to ${lnAddress}` : 'Send a Lightning tip';
btn.addEventListener('click', e => {
e.stopPropagation();
const displayName = profile?.display_name || profile?.name || shortPubkey(pk);
openTipModal({ lnurlpUrl, label: displayName, lud16: lnAddress });
});
return btn;
}
function updateNostrWidget() {
const pubkey = state.community.myPubkey;
const profile = state.community.myProfile;
@@ -8980,6 +9256,25 @@
actionsDiv.removeChild(actionsDiv.firstChild);
}
// Lightning tip button — only shown when the author has a resolvable LN address.
const previewPk = row?.e?.pubkey;
if (previewPk) {
const previewAuthorProfile = _feedCache.profiles?.[previewPk] || state.community?.profiles?.[previewPk] || null;
const previewLnurlpUrl = resolveLnurlPayUrl(previewAuthorProfile);
if (previewLnurlpUrl) {
const previewLnAddress = (previewAuthorProfile?.lud16 || '').trim();
const previewName = previewAuthorProfile?.display_name || previewAuthorProfile?.name || shortPubkey(previewPk);
const tipBtn = document.createElement('button');
tipBtn.type = 'button';
tipBtn.textContent = '⚡ Tip';
tipBtn.title = previewLnAddress ? `Send a Lightning tip to ${previewLnAddress}` : 'Send a Lightning tip';
tipBtn.addEventListener('click', () => {
openTipModal({ lnurlpUrl: previewLnurlpUrl, label: previewName, lud16: previewLnAddress });
});
actionsDiv.insertBefore(tipBtn, saveJsonBtn);
}
}
const shareBtn = document.createElement('button');
shareBtn.type = 'button';
shareBtn.textContent = 'Copy share link';
@@ -10963,9 +11258,33 @@
if (_profilePagePubkey) renderProfileDesigns(_profilePagePubkey);
});
// Tip modal wiring
const tipModalEl = $('tipModal');
if (tipModalEl) tipModalEl.addEventListener('click', e => { if (e.target === tipModalEl) closeTipModal(); });
on('tipCloseBtn', 'click', closeTipModal);
on('tipGetInvoiceBtn', 'click', getTipInvoiceFlow);
on('tipBackBtn', 'click', resetTipPicker);
on('tipDoneBtn', 'click', closeTipModal);
const tipAmountsEl = $('tipAmounts');
if (tipAmountsEl) tipAmountsEl.addEventListener('click', e => {
const btn = e.target.closest('button[data-sats]');
if (!btn) return;
selectTipAmount(btn.dataset.sats);
});
on('tipCustomInput', 'input', e => {
const sats = parseInt(e.target.value, 10) || 0;
_tipSelectedSats = sats;
const getBtn = $('tipGetInvoiceBtn');
if (getBtn) getBtn.disabled = sats <= 0;
const errEl = $('tipPickerError');
if (errEl) errEl.textContent = '';
});
// ESC closes whichever overlay modal is on top.
document.addEventListener('keydown', (e) => {
if (e.key !== 'Escape') return;
const tipM = $('tipModal');
if (tipM && tipM.getAttribute('aria-hidden') === 'false') { closeTipModal(); return; }
const profileM = $('profileModal');
if (profileM && profileM.getAttribute('aria-hidden') === 'false') { closeProfilePage(); return; }
const reportM = $('reportModal');
+8
View File
@@ -0,0 +1,8 @@
/**
* Minified by jsDelivr using Terser v5.37.0.
* Original file: /npm/[email protected]/qrcode.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
var qrcode=function(){var t=function(t,r){var e=t,n=g[r],o=null,i=0,a=null,u=[],f={},c=function(t,r){o=function(t){for(var r=new Array(t),e=0;e<t;e+=1){r[e]=new Array(t);for(var n=0;n<t;n+=1)r[e][n]=null}return r}(i=4*e+17),l(0,0),l(i-7,0),l(0,i-7),s(),h(),d(t,r),e>=7&&v(t),null==a&&(a=p(e,n,u)),w(a,r)},l=function(t,r){for(var e=-1;e<=7;e+=1)if(!(t+e<=-1||i<=t+e))for(var n=-1;n<=7;n+=1)r+n<=-1||i<=r+n||(o[t+e][r+n]=0<=e&&e<=6&&(0==n||6==n)||0<=n&&n<=6&&(0==e||6==e)||2<=e&&e<=4&&2<=n&&n<=4)},h=function(){for(var t=8;t<i-8;t+=1)null==o[t][6]&&(o[t][6]=t%2==0);for(var r=8;r<i-8;r+=1)null==o[6][r]&&(o[6][r]=r%2==0)},s=function(){for(var t=B.getPatternPosition(e),r=0;r<t.length;r+=1)for(var n=0;n<t.length;n+=1){var i=t[r],a=t[n];if(null==o[i][a])for(var u=-2;u<=2;u+=1)for(var f=-2;f<=2;f+=1)o[i+u][a+f]=-2==u||2==u||-2==f||2==f||0==u&&0==f}},v=function(t){for(var r=B.getBCHTypeNumber(e),n=0;n<18;n+=1){var a=!t&&1==(r>>n&1);o[Math.floor(n/3)][n%3+i-8-3]=a}for(n=0;n<18;n+=1){a=!t&&1==(r>>n&1);o[n%3+i-8-3][Math.floor(n/3)]=a}},d=function(t,r){for(var e=n<<3|r,a=B.getBCHTypeInfo(e),u=0;u<15;u+=1){var f=!t&&1==(a>>u&1);u<6?o[u][8]=f:u<8?o[u+1][8]=f:o[i-15+u][8]=f}for(u=0;u<15;u+=1){f=!t&&1==(a>>u&1);u<8?o[8][i-u-1]=f:u<9?o[8][15-u-1+1]=f:o[8][15-u-1]=f}o[i-8][8]=!t},w=function(t,r){for(var e=-1,n=i-1,a=7,u=0,f=B.getMaskFunction(r),c=i-1;c>0;c-=2)for(6==c&&(c-=1);;){for(var g=0;g<2;g+=1)if(null==o[n][c-g]){var l=!1;u<t.length&&(l=1==(t[u]>>>a&1)),f(n,c-g)&&(l=!l),o[n][c-g]=l,-1==(a-=1)&&(u+=1,a=7)}if((n+=e)<0||i<=n){n-=e,e=-e;break}}},p=function(t,r,e){for(var n=A.getRSBlocks(t,r),o=b(),i=0;i<e.length;i+=1){var a=e[i];o.put(a.getMode(),4),o.put(a.getLength(),B.getLengthInBits(a.getMode(),t)),a.write(o)}var u=0;for(i=0;i<n.length;i+=1)u+=n[i].dataCount;if(o.getLengthInBits()>8*u)throw"code length overflow. ("+o.getLengthInBits()+">"+8*u+")";for(o.getLengthInBits()+4<=8*u&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=8*u||(o.put(236,8),o.getLengthInBits()>=8*u));)o.put(17,8);return function(t,r){for(var e=0,n=0,o=0,i=new Array(r.length),a=new Array(r.length),u=0;u<r.length;u+=1){var f=r[u].dataCount,c=r[u].totalCount-f;n=Math.max(n,f),o=Math.max(o,c),i[u]=new Array(f);for(var g=0;g<i[u].length;g+=1)i[u][g]=255&t.getBuffer()[g+e];e+=f;var l=B.getErrorCorrectPolynomial(c),h=k(i[u],l.getLength()-1).mod(l);for(a[u]=new Array(l.getLength()-1),g=0;g<a[u].length;g+=1){var s=g+h.getLength()-a[u].length;a[u][g]=s>=0?h.getAt(s):0}}var v=0;for(g=0;g<r.length;g+=1)v+=r[g].totalCount;var d=new Array(v),w=0;for(g=0;g<n;g+=1)for(u=0;u<r.length;u+=1)g<i[u].length&&(d[w]=i[u][g],w+=1);for(g=0;g<o;g+=1)for(u=0;u<r.length;u+=1)g<a[u].length&&(d[w]=a[u][g],w+=1);return d}(o,n)};f.addData=function(t,r){var e=null;switch(r=r||"Byte"){case"Numeric":e=M(t);break;case"Alphanumeric":e=x(t);break;case"Byte":e=m(t);break;case"Kanji":e=L(t);break;default:throw"mode:"+r}u.push(e),a=null},f.isDark=function(t,r){if(t<0||i<=t||r<0||i<=r)throw t+","+r;return o[t][r]},f.getModuleCount=function(){return i},f.make=function(){if(e<1){for(var t=1;t<40;t++){for(var r=A.getRSBlocks(t,n),o=b(),i=0;i<u.length;i++){var a=u[i];o.put(a.getMode(),4),o.put(a.getLength(),B.getLengthInBits(a.getMode(),t)),a.write(o)}var g=0;for(i=0;i<r.length;i++)g+=r[i].dataCount;if(o.getLengthInBits()<=8*g)break}e=t}c(!1,function(){for(var t=0,r=0,e=0;e<8;e+=1){c(!0,e);var n=B.getLostPoint(f);(0==e||t>n)&&(t=n,r=e)}return r}())},f.createTableTag=function(t,r){t=t||2;var e="";e+='<table style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: "+(r=void 0===r?4*t:r)+"px;",e+='">',e+="<tbody>";for(var n=0;n<f.getModuleCount();n+=1){e+="<tr>";for(var o=0;o<f.getModuleCount();o+=1)e+='<td style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: 0px;",e+=" width: "+t+"px;",e+=" height: "+t+"px;",e+=" background-color: ",e+=f.isDark(n,o)?"#000000":"#ffffff",e+=";",e+='"/>';e+="</tr>"}return e+="</tbody>",e+="</table>"},f.createSvgTag=function(t,r,e,n){var o={};"object"==typeof arguments[0]&&(t=(o=arguments[0]).cellSize,r=o.margin,e=o.alt,n=o.title),t=t||2,r=void 0===r?4*t:r,(e="string"==typeof e?{text:e}:e||{}).text=e.text||null,e.id=e.text?e.id||"qrcode-description":null,(n="string"==typeof n?{text:n}:n||{}).text=n.text||null,n.id=n.text?n.id||"qrcode-title":null;var i,a,u,c,g=f.getModuleCount()*t+2*r,l="";for(c="l"+t+",0 0,"+t+" -"+t+",0 0,-"+t+"z ",l+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',l+=o.scalable?"":' width="'+g+'px" height="'+g+'px"',l+=' viewBox="0 0 '+g+" "+g+'" ',l+=' preserveAspectRatio="xMinYMin meet"',l+=n.text||e.text?' role="img" aria-labelledby="'+y([n.id,e.id].join(" ").trim())+'"':"",l+=">",l+=n.text?'<title id="'+y(n.id)+'">'+y(n.text)+"</title>":"",l+=e.text?'<description id="'+y(e.id)+'">'+y(e.text)+"</description>":"",l+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',l+='<path d="',a=0;aLine truncated
//# sourceMappingURL=/sm/26b4b0d0b1e283d6b3ec9857ac597d7a60c76ac17be1ef4c965f03086de426bb.map