';
document.body.appendChild(overlay);
}
function setBusy(busy, msg) {
SPAP.isBusy = busy;
var overlay = document.getElementById('spap-spinner-overlay');
var msgEl = document.getElementById('spap-spinner-msg');
if (overlay) overlay.style.display = busy ? 'flex' : 'none';
if (msgEl && msg) msgEl.textContent = msg;
if (msgEl && !busy) msgEl.textContent = 'Please wait\u2026';
document.querySelectorAll('button').forEach(function (btn) {
if (busy) {
btn.setAttribute('data-was-disabled', btn.disabled ? '1' : '0');
btn.disabled = true;
} else {
btn.disabled = btn.getAttribute('data-was-disabled') === '1';
}
});
}
/* ════════════════════════════════════════════════════════════
CSRF TOKEN
════════════════════════════════════════════════════════════ */
function waitForToken() {
return new Promise(function (resolve, reject) {
var pc = window.portalContext || {};
var cached = pc.csrfToken || pc.requestVerificationToken;
if (cached && cached.length > 20) { resolve(cached); return; }
var domEl = document.querySelector('input[name="__RequestVerificationToken"]');
if (domEl && domEl.value) { resolve(domEl.value); return; }
var csrfEl = document.getElementById('csrf-token-value');
if (csrfEl && csrfEl.value && csrfEl.value.length > 20) { resolve(csrfEl.value); return; }
var attempts = 0;
var poll = setInterval(function () {
attempts++;
var pv = (window.portalContext || {}).csrfToken;
if (pv && pv.length > 20) { clearInterval(poll); resolve(pv); return; }
var el = document.getElementById('csrf-token-value');
if (el && el.value && el.value.length > 20) { clearInterval(poll); resolve(el.value); return; }
if (attempts >= 30) {
clearInterval(poll);
fetch('/Account/Login', { credentials: 'same-origin', headers: { 'Accept': 'text/html' } })
.then(function (r) { return r.text(); })
.then(function (html) {
var m = html.match(/name="__RequestVerificationToken"[^>]*value="([^"]+)"/);
if (!m) m = html.match(/value="([^"]{40,})"[^>]*name="__RequestVerificationToken"/);
if (m && m[1]) { resolve(m[1]); } else { reject(new Error('CSRF token not found')); }
})
.catch(function (e) { reject(new Error('CSRF fetch failed: ' + e.message)); });
}
}, 150);
});
}
/* ════════════════════════════════════════════════════════════
DATAVERSE API
════════════════════════════════════════════════════════════ */
function extractGuid(data, xhrOrResponse) {
if (data && typeof data === 'object') {
var fromBody = data.leadid || data.contactid || data.ongc_paymentevidenceid;
if (!fromBody) {
var odataId = (data['@odata.id']) || '';
var mo = odataId.match(/\(([0-9a-f-]{36})\)/i);
if (mo) fromBody = mo[1];
}
if (fromBody) return fromBody;
}
var headerVal = '';
if (xhrOrResponse) {
if (typeof xhrOrResponse.headers === 'object' && typeof xhrOrResponse.headers.get === 'function') {
headerVal = xhrOrResponse.headers.get('OData-EntityId') || '';
}
if (!headerVal && typeof xhrOrResponse.getResponseHeader === 'function') {
headerVal = xhrOrResponse.getResponseHeader('OData-EntityId') || '';
}
}
if (headerVal) {
var m = headerVal.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
if (m) return m[0];
}
return null;
}
function parseApiError(x) {
try { if (x.responseJSON && x.responseJSON.error) return x.responseJSON.error.message; } catch (e) { }
try { var p = JSON.parse(x.responseText); return (p && p.error && p.error.message) || 'Unknown error'; } catch (e) { }
return 'Unknown error';
}
async function dataversePost(entitySet, payload) {
var url = '/_api/' + entitySet;
if (window.webapi && typeof window.webapi.safeAjax === 'function') {
return new Promise(function (resolve, reject) {
window.webapi.safeAjax({
type: 'POST', url: url, contentType: 'application/json',
data: JSON.stringify(payload),
success: function (data, s, xhr) { resolve({ id: extractGuid(data, xhr), data: data || {} }); },
error: function (xhr) { reject(new Error(parseApiError(xhr))); }
});
});
}
var token = await waitForToken();
var response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'__RequestVerificationToken': token,
'OData-MaxVersion': '4.0',
'OData-Version': '4.0',
'Prefer': 'return=representation'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
var errMsg = 'API request failed';
try { var errJson = await response.json(); errMsg = (errJson && errJson.error && errJson.error.message) || errMsg; } catch (e) { }
throw new Error(errMsg);
}
var data = {};
try { data = await response.json(); } catch (e) { }
return { id: extractGuid(data, response), data: data };
}
async function fetchLeadRef(leadId) {
try {
var token = await waitForToken();
var r = await fetch('/_api/leads(' + leadId + ')?$select=ongc_leadrefid,ongc_clientid', {
method: 'GET',
headers: { '__RequestVerificationToken': token, 'OData-MaxVersion': '4.0', 'OData-Version': '4.0', 'Accept': 'application/json' }
});
if (!r.ok) { console.warn('[SPAP] fetchLeadRef HTTP ' + r.status); return null; }
var d = await r.json();
var ref = (d && (d.ongc_leadrefid || d.ongc_clientid)) || null;
console.log('[SPAP] fetchLeadRef:', ref);
return ref;
} catch (e) { console.warn('[SPAP] fetchLeadRef error:', e.message); return null; }
}
/* ════════════════════════════════════════════════════════════
SESSION STORAGE
════════════════════════════════════════════════════════════ */
function clearSession() {
[SPAP.leadIdKey, SPAP.leadRefKey, SPAP.evidenceIdKey, SPAP.paymentInProgressKey].forEach(function (k) {
try { sessionStorage.removeItem(k); } catch (e) { }
});
SPAP.leadId = null; SPAP.leadRef = null; SPAP.paymentEvidenceId = null;
}
function saveSession() {
try {
if (SPAP.leadId) sessionStorage.setItem(SPAP.leadIdKey, SPAP.leadId);
if (SPAP.leadRef) sessionStorage.setItem(SPAP.leadRefKey, SPAP.leadRef);
if (SPAP.paymentEvidenceId) sessionStorage.setItem(SPAP.evidenceIdKey, SPAP.paymentEvidenceId);
} catch (e) { }
}
/* ════════════════════════════════════════════════════════════
HELPERS
════════════════════════════════════════════════════════════ */
function round2(n) { return Math.round(n * 100) / 100; }
function gVal(id) {
var el = document.getElementById(id);
return el ? el.value.trim() : '';
}
function stripEmpty(payload) {
Object.keys(payload).forEach(function (k) {
if (payload[k] === '' || payload[k] === null || payload[k] === undefined) delete payload[k];
});
return payload;
}
/* ════════════════════════════════════════════════════════════
FEE CALCULATION
════════════════════════════════════════════════════════════ */
function getFeeBase() {
return SPAP.config.baseFee
+ (SPAP.additionalSiteCount * SPAP.config.additionalSiteFee)
+ (SPAP.programCount * SPAP.config.programFee);
}
function calcFees() {
var base = getFeeBase();
var surcharge = round2(base * SPAP.config.ccSurchargeRate);
var gst = round2((base + surcharge) * SPAP.config.gstRate);
var total = round2(base + surcharge + gst);
return { base: round2(base), surcharge: surcharge, gst: gst, total: total };
}
function spapCalculateFee() {
var base = getFeeBase();
var fmt = base.toLocaleString('en-AU', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
var el = document.getElementById('spapFeeAmount');
if (el) el.textContent = fmt;
/* Populate surcharge % display from portalContext — avoids Liquid math on string values */
var surEl = document.getElementById('spapSurchargeDisplay');
if (surEl) {
var rate = parseFloat(SPAP.config.ccSurchargeRate) || 0.01927;
var pct = (rate * 100).toFixed(3).replace(/\.?0+$/, ''); /* e.g. "1.927" */
surEl.textContent = pct;
}
}
/* ════════════════════════════════════════════════════════════
PAGE STRUCTURE
════════════════════════════════════════════════════════════ */
function spapRebuildStructure() {
var s = ['1', '2', '3'];
for (var i = 1; i <= SPAP.additionalSiteCount; i++) s.push('site' + i);
for (var j = 1; j <= SPAP.programCount; j++) s.push('prog' + j);
s.push('fee');
s.push('submit');
SPAP.pageStructure = s;
spapBuildProgress();
spapCalculateFee();
}
/* ════════════════════════════════════════════════════════════
PROGRESS BAR
════════════════════════════════════════════════════════════ */
function spapBuildProgress() {
var c = document.getElementById('spapProgressSteps');
if (!c) return;
c.innerHTML = '';
var total = SPAP.pageStructure.length;
var currIdx = SPAP.pageStructure.indexOf(SPAP.currentPageKey);
SPAP.pageStructure.forEach(function (key, idx) {
var isCurr = SPAP.currentPageKey === key;
var isDone = currIdx > idx;
var label = SPAP.LABELS[key] || key;
var div = document.createElement('div');
div.className = 'spap-step-item' + (isCurr ? ' active' : '') + (isDone ? ' done' : '');
div.innerHTML =
'
' + (isDone ? '✓' : (idx + 1)) + '
' +
'
' + label + '
';
c.appendChild(div);
});
var counter = document.getElementById('spapPageCounter');
if (counter) counter.textContent = 'Page ' + (currIdx + 1) + ' of ' + total;
}
/* ════════════════════════════════════════════════════════════
PAGE NAVIGATION
════════════════════════════════════════════════════════════ */
function spapShowByKey(key) {
if (SPAP.isBusy) return;
SPAP.currentPageKey = key;
document.querySelectorAll('.spap-form-page').forEach(function (el) { el.classList.remove('active'); });
var target;
if (key === 'fee') { target = document.getElementById('spapFeePage'); }
else if (key === 'submit') { target = document.getElementById('spapSubmitPage'); }
else if (key.indexOf('site') === 0 || key.indexOf('prog') === 0) {
target = document.querySelector('.spap-form-page[data-dynkey="' + key + '"]');
} else {
target = document.querySelector('.spap-form-page[data-page="' + key + '"]');
}
if (target) { target.classList.add('active'); window.scrollTo({ top: 0, behavior: 'smooth' }); }
spapBuildProgress();
if (key === 'submit') spapInitSig();
if (key === 'fee') spapCalculateFee();
}
function spapNavNext(currentKey) {
var idx = SPAP.pageStructure.indexOf(currentKey);
if (idx >= 0 && idx < SPAP.pageStructure.length - 1) spapShowByKey(SPAP.pageStructure[idx + 1]);
}
function spapNavPrev(currentKey) {
var idx = SPAP.pageStructure.indexOf(currentKey);
if (idx > 0) spapShowByKey(SPAP.pageStructure[idx - 1]);
}
function spapGoToPage(n) { spapShowByKey(String(n)); }
function spapPage3Next() { spapNavNext('3'); }
function spapFeeNext() { spapNavNext('fee'); }
function spapFeeBack() { spapNavPrev('fee'); }
function spapSubmitBack() { spapNavPrev('submit'); }
function spapSaveResume() { alert('Your progress has been saved. You can resume using the link sent to your registered email address.'); }
/* ════════════════════════════════════════════════════════════
CONDITIONAL UI
════════════════════════════════════════════════════════════ */
function spapToggleSector() {
var v = gVal('spapSector');
var fld = document.getElementById('spapSectorOtherField');
if (fld) fld.style.display = (v === 'other') ? 'block' : 'none';
}
function spapTogglePostal(val) {
var yesChk = document.getElementById('spapPostalYes');
var noChk = document.getElementById('spapPostalNo');
var block = document.getElementById('spapPostalBlock');
if (val === 'yes') {
if (yesChk) yesChk.checked = true;
if (noChk) noChk.checked = false;
if (block) block.style.display = 'block';
} else {
if (yesChk) yesChk.checked = false;
if (noChk) noChk.checked = true;
if (block) block.style.display = 'none';
}
}
/* ════════════════════════════════════════════════════════════
ADDITIONAL SITES HANDLER
════════════════════════════════════════════════════════════ */
function spapOnAdditionalSitesChange() {
var v = gVal('spapAdditionalSites');
var hintEl = document.getElementById('spapSiteHint');
var over5El = document.getElementById('spapSiteOver5Msg');
if (hintEl) hintEl.style.display = 'none';
if (over5El) over5El.style.display = 'none';
spapRemoveDynPages('site');
SPAP.additionalSiteCount = 0;
if (v === '0') {
if (hintEl) hintEl.style.display = 'block';
} else if (v === '5plus') {
if (hintEl) hintEl.style.display = 'block';
if (over5El) over5El.style.display = 'block';
} else if (v !== '') {
var count = parseInt(v) || 0;
if (hintEl) hintEl.style.display = 'block';
SPAP.additionalSiteCount = count;
spapBuildSitePages(count);
}
spapRebuildStructure();
}
function spapBuildSitePages(count) {
var formShell = document.getElementById('spapFormShell');
var feePage = document.getElementById('spapFeePage');
var stateOpts = spapStateOptions();
for (var i = 1; i <= count; i++) {
var key = 'site' + i;
if (document.querySelector('.spap-form-page[data-dynkey="' + key + '"]')) continue;
var ord = SPAP.ORDINALS[i - 1] || (i + 'TH');
var siteN = i + 1;
var pg = document.createElement('div');
pg.className = 'spap-form-page';
pg.setAttribute('data-dynkey', key);
pg.innerHTML =
'
' +
'
' + ord + ' ADDITIONAL SITE (SITE ' + siteN + ')
' +
spapFieldWrap('Site ' + siteN + ' name', true,
'') +
spapFieldWrap('Site ' + siteN + ' street address', true,
'' +
'
Address Line 1
' +
'' +
'
' +
'
City
' +
'
State
' +
'
Postcode
' +
'
') +
spapFieldWrap('Management level on site at Site ' + siteN, true,
'') +
spapFieldWrap('Site ' + siteN + ': Preferred Accreditation Contact Name', true,
'