only on pages that render a form AND is deferred
// by Rocket's Delay JS, so it missed both cross-page attribution and
// utm_content / utm_term entirely. Fall back to a direct URL read if
// the module is somehow absent.
function getUtm(k) {
if (window.ujUtm && typeof window.ujUtm.get === 'function') {
return window.ujUtm.get(k) || '';
}
try { return new URLSearchParams(location.search).get(k) || ''; } catch(_) { return ''; }
}
function pushSignupEvent(alreadySubscribed) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'newsletter_signup',
already_subscribed: !!alreadySubscribed
});
}
// Persist a "this visitor signed up" flag so cross-page surfaces
// (currently the exit-intent modal) can skip nagging them again.
// Set on both new-subscriber success AND already-subscribed
// response — both confirm the visitor is on the list.
function markSignedUp() {
try { localStorage.setItem('uj_newsletter_signed_up', '1'); } catch (_) {}
}
// Shared response handler for both JSONP and REST paths. Response
// shape: { result: 'success'|'error', msg: '...' }. JSONP returns
// this directly from Mailchimp; the REST endpoint mirrors the same
// shape so this handler doesn't need to branch.
function handleResponse(form, msg, btn, resp) {
btn.disabled = false;
var redirectUrl = form.getAttribute('data-redirect-url') || '';
if (resp && resp.result === 'success') {
pushSignupEvent(false);
markSignedUp();
if (redirectUrl) { window.location.href = redirectUrl; return; }
form.reset();
msg.textContent = '✓ Thanks! Check your inbox to confirm.';
msg.className = 'uj-mc-form__msg uj-mc-form__msg--success';
} else {
var text = (resp && resp.msg ? String(resp.msg) : 'Something went wrong. Please try again.').replace(/<[^>]*>/g, '').trim();
if (text.toLowerCase().indexOf('already subscribed') !== -1) {
pushSignupEvent(true);
markSignedUp();
if (redirectUrl) { window.location.href = redirectUrl; return; }
msg.textContent = "You're already on the list, thanks!";
msg.className = 'uj-mc-form__msg uj-mc-form__msg--success';
} else {
msg.textContent = text;
msg.className = 'uj-mc-form__msg uj-mc-form__msg--error';
}
}
}
document.addEventListener('submit', function(e){
var form = e.target;
if (!form.classList || !form.classList.contains('uj-mc-form')) return;
e.preventDefault();
var msg = form.querySelector('.uj-mc-form__msg');
var btn = form.querySelector('button[type=submit]');
btn.disabled = true;
msg.textContent = 'Submitting…';
msg.className = 'uj-mc-form__msg uj-mc-form__msg--pending';
// Each is independently optional — internal links routinely carry
// only one or two (the main-menu trip-planner link is bare
// ?utm_content=…). Whatever is present ships; the rest go empty
// and are dropped server-side rather than blanking a merge field.
var utm = {
source: getUtm('utm_source'),
medium: getUtm('utm_medium'),
campaign: getUtm('utm_campaign'),
content: getUtm('utm_content'),
term: getUtm('utm_term')
};
var endpoint = form.dataset.endpoint || '';
var isRest = endpoint.indexOf('/wp-json/') > -1 || endpoint.indexOf('rest_route=') > -1;
if (isRest) {
// POST JSON to our server-side endpoint. Used when the form
// needs a Mailchimp tag applied (mc_tags attribute set on the
// shortcode). The endpoint upserts the subscriber and POSTs
// the tag list in two API calls server-side.
var fd = new FormData(form);
var honeypotInput = form.querySelector('input[type=text][name^="b_"]');
var tagsAttr = form.getAttribute('data-uj-mc-tags') || '';
var tags = tagsAttr
? tagsAttr.split(',').map(function(s){ return s.trim(); }).filter(Boolean)
: [];
var payload = {
email: fd.get('EMAIL') || '',
fname: fd.get('FNAME') || '',
mmerge9: fd.get('MMERGE9') || '',
mmerge10: utm.source,
mmerge11: utm.medium,
mmerge12: utm.campaign,
utm_content: utm.content,
utm_term: utm.term,
tags: tags,
honeypot: honeypotInput ? honeypotInput.value : ''
};
fetch(endpoint, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(function(r){
return r.json().catch(function(){ return { result: 'error', msg: 'Network error. Please try again.' }; });
}).then(function(resp){
handleResponse(form, msg, btn, resp);
}).catch(function(){
handleResponse(form, msg, btn, { result: 'error', msg: 'Network error. Please try again.' });
});
return;
}
// JSONP path (Mailchimp hosted endpoint).
var data = new URLSearchParams(new FormData(form));
if (utm.source) data.set('MMERGE10', utm.source);
if (utm.medium) data.set('MMERGE11', utm.medium);
if (utm.campaign) data.set('MMERGE12', utm.campaign);
// content/term only once the audience actually has merge fields
// for them — see uj_utm_extra_merge_tags(). Posting an unknown
// merge tag makes Mailchimp reject the whole write.
if (utm.content && EXTRA_MERGE_TAGS.utm_content) data.set(EXTRA_MERGE_TAGS.utm_content, utm.content);
if (utm.term && EXTRA_MERGE_TAGS.utm_term) data.set(EXTRA_MERGE_TAGS.utm_term, utm.term);
var cb = 'ujMc' + Date.now() + Math.floor(Math.random() * 10000);
data.set('c', cb);
window[cb] = function(resp){
try { delete window[cb]; } catch(_) { window[cb] = undefined; }
var s = document.getElementById(cb + '_s');
if (s) s.parentNode.removeChild(s);
handleResponse(form, msg, btn, resp);
};
var script = document.createElement('script');
script.id = cb + '_s';
script.src = endpoint + '&' + data.toString();
script.onerror = function(){
if (typeof window[cb] === 'function') {
window[cb]({ result: 'error', msg: 'Network error. Please try again.' });
}
};
document.body.appendChild(script);
});
})();