(function injectQAApp() {
// Prevent duplicate injections if re-rendered
if (window.__QA_APP_INITIALIZED__) return;
window.__QA_APP_INITIALIZED__ = true;

// 1. Inject Styles dynamically to avoid Gutenberg stripping
const css = `
#qa-app-root {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999999;
background: #121212;
font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, sans-serif;
box-sizing: border-box;
}
#qa-app-root * { box-sizing: border-box; }
#reviewer-toolbar {
height: 48px; background: #1f1f2e; color: #e0e0e0; display: flex; align-items: center;
justify-content: space-between; padding: 0 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.4); border-bottom: 1px solid #333;
}
.reviewer-btn-group { display: flex; align-items: center; gap: 8px; }
.reviewer-label { font-size: 13px; font-weight: 600; color: #a1a1aa; margin-right: 4px; }
#reviewer-toolbar button, .history-nav-btn {
background: #3b82f6; color: #ffffff; border: none; padding: 6px 12px; border-radius: 4px;
font-weight: 600; cursor: pointer; transition: background 0.2s; font-size: 13px;
}
#reviewer-toolbar button:hover:not(:disabled), .history-nav-btn:hover:not(:disabled) { background: #2563eb; }
#reviewer-toolbar button:disabled, .history-nav-btn:disabled { opacity: 0.4; cursor: not-allowed; }
#btn-log-comment { background: #10b981 !important; }
#btn-log-comment:hover:not(:disabled) { background: #059669 !important; }
.counter-wrapper { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 600; color: #a1a1aa; margin-left: 8px; }
.counter-input, .name-input {
background: #27272a; color: #ffffff; border: 1px solid #3f3f46;
padding: 4px 8px; border-radius: 4px; font-size: 13px; font-weight: 600;
outline: none; transition: border-color 0.2s, background-color 0.2s;
}
.counter-input { width: 56px; text-align: center; }
.name-input { width: 180px; }
.counter-input:focus, .name-input:focus { border-color: #3b82f6; }
.counter-input.invalid { background-color: #7f1d1d !important; border-color: #ef4444 !important; color: #ffffff !important; }
.counter-input::-webkit-outer-spin-button, .counter-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.counter-input[type=number] { -moz-appearance: textfield; }
.frame-container { display: flex; width: 100vw; height: calc(100vh – 48px); }
.frame-wrapper { flex: 1; display: flex; flex-direction: column; border-right: 1px solid #333; overflow: hidden; min-width: 0; }
.frame-wrapper:last-child { border-right: none; }
.frame-header {
background: #27272a; color: #a1a1aa; padding: 6px 12px; font-size: 12px; font-family: monospace;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.frame-header-left { display: flex; align-items: center; gap: 8px; overflow: hidden; min-width: 0; }
.frame-header a { color: #60a5fa; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.frame-header a:hover { text-decoration: underline; }
.history-btn-group { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.history-nav-btn { padding: 2px 8px; font-size: 11px; background: #3f3f46; color: #d4d4d8; }
.history-nav-btn:hover:not(:disabled) { background: #52525b; }
#qa-app-root iframe { width: 100%; height: 100%; border: none; background: #ffffff; }
`;
const styleEl = document.createElement(‘style’);
styleEl.textContent = css;
document.head.appendChild(styleEl);

// 2. Build DOM markup dynamically
const root = document.getElementById(‘qa-app-root’);
if (!root) return;

root.innerHTML = `

Path Pairs:



/ 0

Name:

Old URL (Live):

New URL (Staging):

`;

// 3. Execution logic injected via dynamic script blob bypass
const scriptBody = () => {
const CSV_URL = ‘https://docs.google.com/spreadsheets/d/e/2PACX-1vSRZlHfzgF50BUNYyMbVdCSWlG7QmqiRRfFJORXpxzgJ2EhqCrjhxFDfP5JL7pVek8wUvOQN_G1yPRl/pub?gid=1701992001&single=true&output=csv’;
const FORM_BASE_URL = ‘https://docs.google.com/forms/d/e/1FAIpQLSesoBXmBxVq1lyiiD6eR9in9u6BPpRwwQ5R3u8hPVZMLJvTYA/viewform?usp=pp_url&entry.1759474761=CouncilWare&entry.31705431=Specific+URL’;
const STORAGE_KEY_NAME = ‘reviewer_user_name’;

const DOMAIN_MAPPING = {
‘www.wccscouting.org’: ‘777.www.cwwpdev.org’,
‘www.ockanickon.org’: ‘777.ockanickon.cwwpdev.org’
};

let urlPairs = [];
let currentIndex = 0;

const computeStagingUrl = (liveUrl) => {
try {
const parsed = new URL(liveUrl);
const targetHost = DOMAIN_MAPPING[parsed.hostname];
if (targetHost) parsed.hostname = targetHost;
return parsed.toString();
} catch {
return liveUrl;
}
};

const parseCSV = (text) => {
const lines = text.trim().split(/\r?\n/);
if (lines.length h.trim().replace(/^”|”$/g, ”));
const urlHeaderIndex = headers.findIndex(h => /^url$/i.test(h) || /^live_url$/i.test(h) || h === headers[0]);

return lines.slice(1).reduce((acc, line) => {
const trimmed = line.trim();
if (!trimmed) return acc;
const values = trimmed.split(‘,’).map(v => v.trim().replace(/^”|”$/g, ”));
const rawUrl = values[urlHeaderIndex];

if (rawUrl && /^https?:\/\//i.test(rawUrl)) {
acc.push({ URL: rawUrl, Staging_URL: computeStagingUrl(rawUrl) });
}
return acc;
}, []);
};

const fetchCSV = async (url) => {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
return await response.text();
};

const initApp = () => {
const iframeOld = document.getElementById(‘iframe-old’);
const iframeNew = document.getElementById(‘iframe-new’);
const linkOld = document.getElementById(‘link-old’);
const linkNew = document.getElementById(‘link-new’);

const counterInput = document.getElementById(‘counter-input’);
const counterTotal = document.getElementById(‘counter-total’);
const inputName = document.getElementById(‘input-reviewer-name’);

const btnFirst = document.getElementById(‘btn-first’);
const btnPrev = document.getElementById(‘btn-prev’);
const btnNext = document.getElementById(‘btn-next’);
const btnLast = document.getElementById(‘btn-last’);
const btnLogComment = document.getElementById(‘btn-log-comment’);

const navigateFrameHistory = (iframe, delta) => {
try { iframe.contentWindow?.history.go(delta); }
catch (err) { console.warn(‘[Reviewer] History navigation blocked cross-origin:’, err); }
};

document.getElementById(‘hist-old-back’)?.addEventListener(‘click’, () => navigateFrameHistory(iframeOld, -1));
document.getElementById(‘hist-old-forward’)?.addEventListener(‘click’, () => navigateFrameHistory(iframeOld, 1));
document.getElementById(‘hist-new-back’)?.addEventListener(‘click’, () => navigateFrameHistory(iframeNew, -1));
document.getElementById(‘hist-new-forward’)?.addEventListener(‘click’, () => navigateFrameHistory(iframeNew, 1));

const syncNameState = () => {
const nameVal = inputName.value.trim();
btnLogComment.disabled = !nameVal;
nameVal ? localStorage.setItem(STORAGE_KEY_NAME, nameVal) : localStorage.removeItem(STORAGE_KEY_NAME);
};

inputName.value = localStorage.getItem(STORAGE_KEY_NAME) || ”;
syncNameState();
inputName.addEventListener(‘input’, syncNameState);

const loadPair = (index) => {
if (index = urlPairs.length) return;
currentIndex = index;

const pair = urlPairs[currentIndex];
iframeOld.src = pair.URL;
iframeNew.src = pair.Staging_URL;

linkOld.href = pair.URL;
linkOld.textContent = pair.URL;

linkNew.href = pair.Staging_URL;
linkNew.textContent = pair.Staging_URL;

counterInput.value = currentIndex + 1;
counterInput.classList.remove(‘invalid’);
counterTotal.textContent = `/ ${urlPairs.length}`;

btnFirst.disabled = currentIndex === 0;
btnPrev.disabled = currentIndex === 0;
btnNext.disabled = currentIndex === urlPairs.length – 1;
btnLast.disabled = currentIndex === urlPairs.length – 1;
};

const openLogCommentForm = () => {
if (!urlPairs.length) return;
const reviewerName = inputName.value.trim();
if (!reviewerName) return;

const pair = urlPairs[currentIndex];
const formUrl = `${FORM_BASE_URL}&entry.2136859418=${encodeURIComponent(reviewerName)}&entry.1450637981=${encodeURIComponent(pair.Staging_URL)}&entry.44829319=${encodeURIComponent(pair.URL)}`;
window.open(formUrl, ‘_blank’, ‘noopener’);
};

const handleInputSubmit = () => {
const rawVal = counterInput.value.trim();
const pageNum = parseInt(rawVal, 10);
if (!rawVal || isNaN(pageNum) || pageNum urlPairs.length) {
counterInput.classList.add(‘invalid’);
} else {
counterInput.classList.remove(‘invalid’);
loadPair(pageNum – 1);
}
};

counterInput.addEventListener(‘input’, () => {
const rawVal = counterInput.value.trim();
const pageNum = parseInt(rawVal, 10);
counterInput.classList.toggle(‘invalid’, !rawVal || isNaN(pageNum) || pageNum urlPairs.length);
});

counterInput.addEventListener(‘keydown’, (e) => {
if (e.key === ‘Enter’) {
e.preventDefault();
handleInputSubmit();
counterInput.blur();
}
});

counterInput.addEventListener(‘blur’, handleInputSubmit);

window.addEventListener(‘keydown’, (e) => {
if ([‘INPUT’, ‘TEXTAREA’].includes(e.target.tagName)) return;
if (e.key === ‘ArrowLeft’) loadPair(currentIndex – 1);
if (e.key === ‘ArrowRight’) loadPair(currentIndex + 1);
if (e.key === ‘Home’) loadPair(0);
if (e.key === ‘End’) loadPair(urlPairs.length – 1);
});

btnFirst.addEventListener(‘click’, () => loadPair(0));
btnPrev.addEventListener(‘click’, () => loadPair(currentIndex – 1));
btnNext.addEventListener(‘click’, () => loadPair(currentIndex + 1));
btnLast.addEventListener(‘click’, () => loadPair(urlPairs.length – 1));
btnLogComment.addEventListener(‘click’, openLogCommentForm);

if (urlPairs.length) loadPair(0);
};

(async () => {
try {
const csvText = await fetchCSV(CSV_URL);
urlPairs = parseCSV(csvText);
if (urlPairs.length) initApp();
} catch (err) {
console.error(‘[Reviewer Error]:’, err);
}
})();
};

// Inject JS executable blob to completely bypass Gutenberg block sanitization
const blobNode = document.createElement(‘script’);
blobNode.textContent = `(${scriptBody.toString()})();`;
document.body.appendChild(blobNode);
})();