PageSourceSearch

https://kylopeptides.com/wp-content/themes/koi-child/assets/js/koi-coa.js?ver=1788166960

js kylopeptides.com collected 2026-09-28 09:34:01 UTC 5,963 bytes, 183 lines download raw bytes

1/**
2 * koi-coa.js — global COA modal (PDF only).
3 *
4 * Works with any element that has a [data-coa] attribute holding JSON:
5 *   { name, pdf, pending }
6 *
7 * The modal no longer renders a transcribed summary of the certificate
8 * (product / lot / laboratory ID / report date / lab / headline purity /
9 * assay table / assay-count footer). It loads the PDF itself, so anything
10 * beyond `name`, `pdf` and `pending` in the payload is ignored.
11 *
12 * Capture-phase click listener so it runs BEFORE a card's inline
13 * onclick="location.href=…" and can block that navigation.
14 *
15 * The PDF URL is VERIFIED (same-origin HEAD) before it's loaded, so a
16 * missing/404 PDF never renders the site's 404 page inside the iframe —
17 * it hides the frame and shows a short note instead.
18 */
19(function () {
20	'use strict';
21
22	function $(id) { return document.getElementById(id); }
23
24	function setText(id, val, fallback) {
25		var el = $(id);
26		if (el) { el.textContent = val || fallback || '—'; }
27	}
28
29	/* ---------------------------------------------------------------- *
30	 * PDF frame
31	 * ---------------------------------------------------------------- */
32
33	/* Ensure a small "PDF not available" note exists inside the data area. */
34	function pdfNote() {
35		var note = $('coaPdfMissing');
36		if (!note) {
37			note = document.createElement('div');
38			note.id = 'coaPdfMissing';
39			note.className = 'mpdfhint';
40			note.style.display = 'none';
41			note.style.padding = '18px 4px';
42			note.style.textAlign = 'center';
43			note.textContent = 'The certificate PDF for this lot is being finalized and will appear here shortly.';
44			var wrap = $('coaPdfWrap');
45			if (wrap && wrap.parentNode) { wrap.parentNode.insertBefore(note, wrap.nextSibling); }
46		}
47		return note;
48	}
49
50	/* Show/hide the PDF frame + action buttons together. */
51	function setPdfVisible(show, url, name) {
52		var frame = $('coaPdfFrame'), wrap = $('coaPdfWrap'), btns = document.querySelector('#coaModal .mcoabtns');
53		var view = $('coaBtnView'), dl = $('coaBtnDl'), note = pdfNote();
54
55		if (show) {
56			if (frame) { frame.src = url; }
57			if (wrap)  { wrap.style.display = ''; }
58			if (btns)  { btns.style.display = ''; }
59			if (view)  { view.href = url; }
60			if (dl)    { dl.href = url; dl.dataset.coaName = name || 'koi'; }
61			note.style.display = 'none';
62		} else {
63			if (frame) { frame.src = 'about:blank'; }
64			if (wrap)  { wrap.style.display = 'none'; }
65			if (btns)  { btns.style.display = 'none'; }
66			note.style.display = '';
67		}
68	}
69
70	/* Verify a same-origin URL is really a PDF before we trust it. */
71	function verifyPdf(url, name) {
72		fetch(url, { method: 'HEAD' })
73			.then(function (res) {
74				var ct = (res.headers.get('content-type') || '').toLowerCase();
75				var okPdf = res.ok && ct.indexOf('pdf') !== -1;
76				setPdfVisible(okPdf, url, name);
77			})
78			.catch(function () { setPdfVisible(false, url, name); });
79	}
80
81	/* ---------------------------------------------------------------- *
82	 * Open / close
83	 * ---------------------------------------------------------------- */
84
85	function openCOA(data) {
86		var modal = $('coaModal');
87		if (!modal) { return; }
88
89		var pending = !!data.pending;
90		var has  = $('coaHasData');
91		var pend = $('coaPending');
92		if (has)  { has.style.display  = pending ? 'none' : ''; }
93		if (pend) { pend.style.display = pending ? '' : 'none'; }
94
95		if (pending) {
96			setText('coaPendName', data.name, 'this compound');
97		} else {
98			/* Reset frame first, then verify the URL before loading it. */
99			setPdfVisible(false, '', data.name);
100			if (data.pdf) {
101				verifyPdf(data.pdf, data.name);
102			}
103		}
104
105		modal.classList.add('open');
106		document.body.style.overflow = 'hidden';
107	}
108
109	function closeCOA() {
110		var modal = $('coaModal');
111		if (!modal) { return; }
112		modal.classList.remove('open');
113		document.body.style.overflow = '';
114		var frame = $('coaPdfFrame');
115		if (frame) { frame.src = 'about:blank'; } // stop the PDF loading in the background
116	}
117
118	function downloadCOA(a) {
119		var nm = (a.dataset.coaName || 'koi')
120			.replace(/[^a-z0-9]+/gi, '-')
121			.toLowerCase()
122			.replace(/^-|-$/g, '');
123		fetch(a.href)
124			.then(function (r) { if (!r.ok) { throw 0; } return r.blob(); })
125			.then(function (b) {
126				var u = URL.createObjectURL(b);
127				var x = document.createElement('a');
128				x.href = u;
129				x.download = nm + '-COA.pdf';
130				document.body.appendChild(x);
131				x.click();
132				x.remove();
133				setTimeout(function () { URL.revokeObjectURL(u); }, 1500);
134			})
135			.catch(function () { window.open(a.href, '_blank', 'noopener'); });
136	}
137
138	/* ONE delegated listener, CAPTURE phase (the `true` at the end). */
139	document.addEventListener('click', function (e) {
140
141		/* 1. Open — any element with [data-coa] */
142		var btn = e.target.closest('[data-coa]');
143		if (btn) {
144			e.preventDefault();
145			e.stopPropagation();
146			var data = {};
147			try {
148				var raw = btn.getAttribute('data-coa');
149				try { data = JSON.parse(raw); } catch (err2) { data = JSON.parse(decodeURIComponent(raw)); }
150			} catch (err) { return; }
151			openCOA(data);
152			return;
153		}
154
155		/* 2. Close — the ✕ button */
156		if (e.target.closest('[data-coa-close]')) {
157			e.stopPropagation();
158			closeCOA();
159			return;
160		}
161
162		/* 3. Close — click on the dark backdrop */
163		var m = $('coaModal');
164		if (m && m.classList.contains('open') && e.target === m) {
165			e.stopPropagation();
166			closeCOA();
167			return;
168		}
169
170		/* 4. Download button — blob download with a friendly filename */
171		var d = e.target.closest('#coaBtnDl');
172		if (d && d.href && d.getAttribute('href') !== '#') {
173			e.preventDefault();
174			e.stopPropagation();
175			downloadCOA(d);
176		}
177	}, true); // ← capture phase: critical, do not remove
178
179	/* Esc closes the modal */
180	document.addEventListener('keydown', function (e) {
181		if (e.key === 'Escape') { closeCOA(); }
182	});
183})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.