Real travel experts
Hassle-free booking
Flexible changes
Ref #D3FSM8Quote this when you call
We book these airlines
UNITED△ DELTAAMERICANBRITISH AIRWAYS
50K+
Flights booked
4.9★
Customer rating
<60s
Avg. wait time
24/7
Agents available
// Set default depart date to today + 7 days
const defaultDepart = new Date();
defaultDepart.setDate(defaultDepart.getDate() + 7);
departInput.value = defaultDepart.toISOString().split('T')[0];
// Set default return date to depart + 7 days
const defaultReturn = new Date(defaultDepart);
defaultReturn.setDate(defaultReturn.getDate() + 7);
returnInput.value = defaultReturn.toISOString().split('T')[0];
returnInput.min = departInput.value;
departInput.addEventListener('change', () => {
if (returnInput.value && returnInput.value < departInput.value) {
returnInput.value = departInput.value;
}
returnInput.min = departInput.value;
syncToMoja();
});
// ── Trip type toggle ──
let tripType = 'One Way';
function setTripType(type) {
tripType = type;
if (type === 'Round Trip') {
roundBtn.classList.add('active');
oneBtn.classList.remove('active');
datesRow.classList.add('round-trip');
} else {
oneBtn.classList.add('active');
roundBtn.classList.remove('active');
datesRow.classList.remove('round-trip');
}
syncToMoja();
}
roundBtn.addEventListener('click', () => setTripType('Round Trip'));
oneBtn.addEventListener('click', () => setTripType('One Way'));
// ── Moja sync ──────────────────────────────────────────────────────────────
// Three-layer approach to guarantee tags land on the session before the call:
//
// Layer 1 — _moja_tags queue: safe pre-init, replayed when snippet loads.
// Layer 2 — direct mergeCustomTags(): bypasses queue timing entirely.
// Layer 3 — immediate sendHeartbeat(): pushes tags to backend right now.
//
// On CTA click we run all three so the agent sees the data when the call
// comes in, even if the visitor calls within seconds of selecting airports.
// ──────────────────────────────────────────────────────────────────────────
function buildTags() {
return {
trip_type: tripType,
from_airport: fromSel.value || '(not selected)',
to_airport: toSel.value || '(not selected)',
depart_date: departInput.value || '',
return_date: tripType === 'Round Trip' ? (returnInput.value || '') : '',
passengers: passengersSel.value || '1',
};
}
function syncToMoja(forceSend) {
const tags = buildTags();
// Layer 1: _moja_tags queue (works pre-init and post-init)
window._moja_tags = window._moja_tags || [];
window._moja_tags.push(tags);
// Layer 2 & 3: direct API — only available after Moja has initialised
try {
const T = window.MojaTracker;
if (T && T.initialized && T.dataCollector && T.apiClient) {
// Merge immediately into the visitor data object
T.dataCollector.mergeCustomTags(tags);
// Fire heartbeat right now (not waiting for the 500 ms debounce)
// This writes the tags to the Moja backend session before the call lands.
if (forceSend) {
const sid = T.dataCollector.getSnippetId();
const vid = T.dataCollector.getVisitorId();
if (sid && vid) {
T.apiClient.sendHeartbeat(sid, vid).catch(() => {});
}
}
}
} catch (e) {}
}
// Sync on every form change
[fromSel, toSel, returnInput, passengersSel].forEach(el => {
el.addEventListener('change', () => syncToMoja(false));
});
// On CTA click: force an immediate heartbeat so the agent sees the data
ctaBtn.addEventListener('click', () => syncToMoja(true));
// Poll until MojaTracker is initialised, then push the current form state.
// This handles the race where Moja loads and runs BEFORE our inline script,
// leaving the pre-init queue empty.
let mojaPollCount = 0;
const mojaPoller = setInterval(() => {
mojaPollCount++;
if (mojaPollCount > 50) { clearInterval(mojaPoller); return; } // give up after 10s
if (window.MojaTracker && window.MojaTracker.initialized) {
clearInterval(mojaPoller);
syncToMoja(false); // push current state now that Moja is ready
}
}, 200);