Save your assessment
Enter your details to continue
We’ll save your progress so you can return anytime, and we can email you the results and next steps.
Not now
Save & Continue
By continuing, you agree to be contacted about your assessment and related services. You can unsubscribe anytime.
`;
}
function downloadReport() {
const htmlDoc = buildReadableReportHtmlDoc();
const blob = new Blob([htmlDoc], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'iso9004_self_assessment_report.html';
document.body.appendChild(a);
a.click();
a.remove();
setTimeout(() => URL.revokeObjectURL(url), 500);
}
function printReport() {
// Print ONLY the report (not the WP header/footer) using a temporary iframe.
// This avoids relying on global print CSS, which can result in blank pages in some browsers.
const htmlDoc = buildReadableReportHtmlDoc();
const iframe = document.createElement('iframe');
iframe.setAttribute('title', 'ISO 9004 Report Print Frame');
iframe.style.position = 'fixed';
iframe.style.right = '0';
iframe.style.bottom = '0';
iframe.style.width = '0';
iframe.style.height = '0';
iframe.style.border = '0';
iframe.style.visibility = 'hidden';
const cleanup = () => {
try { iframe.remove(); } catch (_) {}
};
iframe.onload = () => {
// Give the browser a moment to finish layout before printing.
setTimeout(() => {
try {
iframe.contentWindow?.focus();
iframe.contentWindow?.print();
} catch (_) {
// Fallback: if iframe printing is blocked, print the page.
window.print();
}
// Remove iframe after print starts.
setTimeout(cleanup, 1000);
}, 50);
};
// Use srcdoc when available; fallback to document.write
if ('srcdoc' in iframe) {
iframe.srcdoc = htmlDoc;
}
document.body.appendChild(iframe);
if (!('srcdoc' in iframe)) {
const doc = iframe.contentDocument;
if (doc) {
doc.open();
doc.write(htmlDoc);
doc.close();
}
}
}
function handleSaveClick() {
// Always open modal if email missing; otherwise just autosave
if (!isValidEmail(lead.email)) {
saveActionPending = true;
openLeadCapture(true, false, 'save');
return;
}
autosaveDebounced('in_progress', lastComputedScore);
showToast('Progress saved.');
}
function maybePromptSaveOnLeave() {
if (leaveIntentShown) return;
if (reviewMode) return;
if (Object.keys(responses).length === 0) return;
if (isValidEmail(lead.email)) return;
leaveIntentShown = true;
saveActionPending = true;
openLeadCapture(false, false, 'save');
}
// Detect leave intent (desktop mouse) + back button
function setupLeaveIntent() {
document.addEventListener('mouseout', (e) => {
// Only if leaving upwards (towards tabs/back)
if (!e) return;
const from = e.relatedTarget || e.toElement;
if (from) return;
if (e.clientY <= 0) maybePromptSaveOnLeave();
});
// Back button intent
try {
history.pushState({ iso9004: true }, '', window.location.href);
window.addEventListener('popstate', (e) => {
maybePromptSaveOnLeave();
// keep the user on page
history.pushState({ iso9004: true }, '', window.location.href);
});
} catch (_) {}
}
// Save abandoned state (best-effort)
window.addEventListener('beforeunload', function () {
const hasAnyAnswer = Object.keys(responses).length > 0;
if (!hasAnyAnswer) return;
saveLocalState({ status: 'abandoned' });
if (!isValidEmail(lead.email) || !resumeToken) return;
try {
const payload = {
token: resumeToken,
name: lead.name || '',
email: lead.email || '',
status: 'abandoned',
current_step: currentStep,
responses
};
const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
navigator.sendBeacon(API_BASE + '/save', blob);
} catch (_) {}
});
// Keyboard accessibility
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
// Close help always
const hm = document.getElementById('help-modal');
if (hm && !hm.classList.contains('hidden')) closeHelp();
const lm = document.getElementById('lead-modal');
if (lm && !lm.classList.contains('hidden') && lm.dataset.lockClose !== '1') closeLeadCapture(false);
}
});
async function init() {
const urlToken = getUrlParam('resume');
const local = loadLocalState();
let loadedStep = 0;
if (urlToken) {
const serverData = await apiResume(urlToken);
if (serverData && serverData.ok) {
resumeToken = urlToken;
lead.name = serverData.name || '';
lead.email = serverData.email || '';
currentStep = Number(serverData.current_step || 0) || 0;
loadedStep = currentStep;
const r = serverData.responses || {};
if (r && typeof r === 'object') {
Object.keys(r).forEach(k => { responses[k] = r[k]; });
}
saveLocalState();
}
} else if (local) {
try {
if (local.lead) {
lead.name = local.lead.name || '';
lead.email = local.lead.email || '';
}
if (local.resumeToken) resumeToken = local.resumeToken;
if (typeof local.currentStep === 'number') currentStep = local.currentStep;
loadedStep = currentStep;
if (local.responses && typeof local.responses === 'object') {
Object.keys(local.responses).forEach(k => { responses[k] = local.responses[k]; });
}
} catch (_) {}
}
sanitizeResponsesInPlace();
setupLeaveIntent();
// Always show the intro screen on initial load/reload.
// If there is saved progress, expose it via a Resume button on the intro screen.
const hasResponses = Object.keys(responses).length > 0;
const firstQuestionIdx = Array.isArray(assessmentData)
? assessmentData.findIndex(s => s && s.type === 'question')
: 0;
const fallbackResumeStep = firstQuestionIdx >= 0 ? firstQuestionIdx : 0;
pendingResumeStep = (typeof loadedStep === 'number' && loadedStep > 0)
? loadedStep
: (hasResponses ? fallbackResumeStep : null);
resumeAvailable = hasResponses || (pendingResumeStep !== null && pendingResumeStep > 0);
currentStep = 0;
renderStep();
const shouldAutosave = Object.keys(responses).length > 0 && isValidEmail(lead.email);
if (shouldAutosave) { /* autosave disabled */ } //('in_progress', lastComputedScore);
}
init();