const RECEIPT_SELECTOR = '.receipt'; function textOf(node) { return (node?.textContent || '').replace(/\s+/g, ' ').trim(); } function escapeHtml(value) { return String(value || '').replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); } function getReference(receipt) { const meta = [...receipt.querySelectorAll('.receipt-meta > div')]; const ref = meta.find((item) => /reference/i.test(textOf(item))); return textOf(ref).replace(/^reference\s*/i, '').trim(); } function getAssetTag(receipt) { const match = textOf(receipt).match(/\bAST-[A-Z0-9-]+\b/i); return match ? match[0].toUpperCase() : ''; } function makeQrValue(assetTag, reference) { if (assetTag) { return location.origin + location.pathname + '?asset=' + encodeURIComponent(assetTag); } return 'AssetDesk receipt ' + (reference || 'document'); } function qrFallback(img, value) { img.src = 'https://api.qrserver.com/v1/create-qr-code/?size=180x180&margin=8&data=' + encodeURIComponent(value); img.dataset.qrSource = 'qrserver-fallback'; img.addEventListener('error', () => { img.hidden = true; img.closest('.receipt-qr-block')?.classList.add('qr-unavailable'); }, { once: true }); } async function renderQr(img, value) { try { if (globalThis.AssetDeskQRCode?.toDataURL) { img.src = await globalThis.AssetDeskQRCode.toDataURL(value, { width: 180, margin: 1 }); img.dataset.qrSource = 'local'; return; } } catch { // The online fallback still gives the user a printable QR when available. } qrFallback(img, value); } function enhanceReceipt(receipt) { if (receipt.dataset.receiptEnhanced === 'true') return; receipt.dataset.receiptEnhanced = 'true'; receipt.classList.add('receipt-modernized'); const heading = receipt.querySelector('.receipt-header > strong'); const kind = textOf(heading) || 'RECEIPT'; const reference = getReference(receipt); const assetTag = getAssetTag(receipt); const qrValue = makeQrValue(assetTag, reference); const meta = receipt.querySelector('.receipt-meta'); if (meta) { const bar = document.createElement('div'); bar.className = 'receipt-document-bar'; bar.innerHTML = 'DOCUMENT TYPE' + escapeHtml(kind) + '' + (assetTag ? 'ASSET' : 'REFERENCE') + '' + escapeHtml(assetTag || reference || '—') + ''; meta.insertAdjacentElement('afterend', bar); } const qr = document.createElement('aside'); qr.className = 'receipt-qr-block'; qr.innerHTML = '
SCAN FOR DETAILS' + escapeHtml(assetTag || reference || 'DOCUMENT') + '' + (assetTag ? 'Open asset history and current location' : 'Document reference for this receipt') + '
QR code for ' + escapeHtml(assetTag || reference || 'receipt') + '
QR code unavailable until network access is restored.
'; const firstTable = receipt.querySelector('table'); if (firstTable) firstTable.insertAdjacentElement('beforebegin', qr); else receipt.append(qr); renderQr(qr.querySelector('img'), qrValue); const printButton = receipt.querySelector('button.no-print'); if (printButton) printButton.setAttribute('aria-label', 'Print ' + kind.toLowerCase()); } function scanReceipts() { document.querySelectorAll(RECEIPT_SELECTOR).forEach(enhanceReceipt); } const observer = new MutationObserver(scanReceipts); observer.observe(document.documentElement, { childList: true, subtree: true }); scanReceipts();