// Run immediately when script loads, not waiting for DOM
(function() {
console.log('Form Enhancement Script Loaded - Running immediately...');
// If DOM is already ready, run now
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
initializeScript();
initializeTabListeners();
});
} else {
// DOM is already loaded, run immediately
initializeScript();
initializeTabListeners();
}
// Function to initialize tab listeners for Elementor tabs
function initializeTabListeners() {
// console.log('Initializing tab listeners...');
// Wait a bit for Elementor to be ready
setTimeout(function() {
// Select all Elementor tab titles
const tabTitles = document.querySelectorAll('.elementor-tab-title');
if (tabTitles.length > 0) {
console.log('Found', tabTitles.length, 'Elementor tabs');
tabTitles.forEach(function(tab) {
// Remove existing listeners to prevent duplicates
tab.removeEventListener('click', handleTabClick);
// Add click listener
tab.addEventListener('click', handleTabClick);
});
} else {
// console.log('No Elementor tabs found, retrying in 1 second...');
// Retry after 1 second if tabs not found
setTimeout(initializeTabListeners, 1000);
}
}, 500);
}
// Function to handle tab clicks
function handleTabClick(event) {
console.log('Tab clicked:', event.target.textContent.trim());
// Wait for the tab content to be visible
setTimeout(function() {
console.log('Re-initializing script for new tab...');
initializeScript();
}, 300);
}
function initializeScript() {
console.log('Initializing Form Enhancement Script...');
// Vehicle data with images, passengers, and suitcases
const vehicleData = {
'Luxury Sedan (3 passengers)': { // Updated to match CF7 option values
passengers: 3,
suitcases: 3,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/sedan-1.jpg'
},
'Mercedes S Class Sedan (3 passengers)': {
passengers: 3,
suitcases: 3,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/S-class-2.png'
},
'Luxury Suburban (6 passengers)': {
passengers: 6,
suitcases: 6,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/09/2025-suburban-ck10906-1lt-gba-trimselector.jpg'
},
'Luxury Escalade (6 passengers)': {
passengers: 6,
suitcases: 6,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/escalade-5.png'
},
'Stretch Limousine (10 passengers)': {
passengers: 10,
suitcases: 4,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/stretch-white-limousine-6.jpg'
},
'Passenger Van (10 passengers)': {
passengers: 10,
suitcases: 10,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/passenger-vans-9.jpg'
},
'Stretch Hummer Limousine (18 passengers)': {
passengers: 18,
suitcases: 6,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/stretch-hummer-limousine-7.jpg'
},
'Stretch Escalade Limousine (18 passengers)': {
passengers: 18,
suitcases: 8,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/stretch-escalade-limousine-8.jpeg'
},
'Limo Bus (20 passengers)': {
passengers: 20,
suitcases: 10,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/20-passenger-limo-bus-11.jpg'
},
'Mini Bus (25 - 30 passengers)': {
passengers: 30,
suitcases: 30,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/shuttle-buses-12.jpg'
},
'Limo Bus (30 passengers)': {
passengers: 30,
suitcases: 20,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/30-Pass-Limo-Bus.png'
},
'Luxury Mercedes Sprinter Van (14 passengers)': {
passengers: 14,
suitcases: 14,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/mercedes-sprinter-10.jpg'
},
'Luxury Executive Shuttle Bus (40 passengers)': {
passengers: 40,
suitcases: 40,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/09/Luxury-Executive-Shuttle-Bus-Royal-Carriages.png'
},
'Charter Bus / Motor Coach (55 passengers)': {
passengers: 55,
suitcases: 55,
image: 'https://www.royalcarriages.com/wp-content/uploads/2025/08/charter-bus-motor-coach-16.jpeg'
}
};
// Wait for Contact Form 7 to fully render the form
function waitForCF7Elements(callback, maxAttempts = 50) {
let attempts = 0;
function checkElements() {
attempts++;
// Check for CF7-generated elements
const typeOfVehicleSelect = document.querySelector('select[name="type-of-vehicle"]');
const passengerSelect = document.querySelector('select[name="number-of-passengers"]');
const suitcaseSelect = document.querySelector('select[name="number-of-suitcases"]');
const pickupDateInput = document.querySelector('input[name="pickup-date"]');
if (typeOfVehicleSelect && passengerSelect && suitcaseSelect && pickupDateInput) {
console.log('CF7 elements found, initializing scripts...');
callback();
} else if (attempts < maxAttempts) {
setTimeout(checkElements, 100);
} else {
console.warn('CF7 elements not found after maximum attempts');
// Try to run anyway in case some elements exist
callback();
}
}
checkElements();
}
// Initialize all functionality after CF7 elements are ready
waitForCF7Elements(function() {
initializeAllFeatures();
// Run placeholder initialization again after a short delay to catch late CF7 rendering
setTimeout(() => {
console.log('Running delayed placeholder initialization...');
initializeSelectPlaceholders();
}, 500);
// Also run when CF7 form events fire
if (typeof jQuery !== 'undefined') {
jQuery(document).on('wpcf7mailsent wpcf7mailfailed wpcf7submit wpcf7invalid wpcf7spam', function() {
setTimeout(() => {
console.log('Re-running placeholders after CF7 event...');
initializeSelectPlaceholders();
}, 100);
});
}
});
function initializeAllFeatures() {
preloadVehicleImages();
initializeGooglePlaces();
initializeVehicleSelection();
initializeDatePicker();
initializeSelectPlaceholders();
initializeValidation();
}
// Preload all vehicle images for instant display
function preloadVehicleImages() {
console.log('Preloading vehicle images...');
const imagePromises = [];
Object.keys(vehicleData).forEach(vehicleName => {
const img = new Image();
const promise = new Promise((resolve, reject) => {
img.onload = () => {
console.log(`Loaded: ${vehicleName}`);
resolve(vehicleName);
};
img.onerror = () => {
console.warn(`Failed to load: ${vehicleName}`);
reject(vehicleName);
};
});
img.src = vehicleData[vehicleName].image;
imagePromises.push(promise);
});
// Log when all images are loaded
Promise.allSettled(imagePromises).then(results => {
const loaded = results.filter(r => r.status === 'fulfilled').length;
const failed = results.filter(r => r.status === 'rejected').length;
console.log(`Vehicle images preloaded: ${loaded} loaded, ${failed} failed`);
});
}
// Google Places Autocomplete functionality (unchanged - uses preserved IDs)
let pickupValidSelection = false;
let dropoffValidSelection = false;
function initializeGooglePlaces() {
// Wait for Google Places API to be available
function waitForGoogleAPI(callback, maxAttempts = 50) {
let attempts = 0;
function checkAPI() {
attempts++;
if (typeof google !== 'undefined' && google.maps && google.maps.places) {
console.log('Google Places API is ready!');
callback();
} else if (attempts < maxAttempts) {
console.log(`Waiting for Google Places API... attempt ${attempts}`);
setTimeout(checkAPI, 100);
} else {
console.error('Google Places API failed to load after maximum attempts');
}
}
checkAPI();
}
waitForGoogleAPI(function() {
setupGooglePlaces();
});
}
function setupGooglePlaces() {
console.log('Setting up Google Places autocomplete...');
const service = new google.maps.places.AutocompleteService();
const placesService = new google.maps.places.PlacesService(document.createElement('div'));
function setupCustomAutocomplete(inputElement, fieldType) {
if (!inputElement) return;
let selectedPrediction = null;
// Create dropdown container
const dropdown = document.createElement('div');
dropdown.className = 'custom-pac-container';
dropdown.style.cssText = `
position: absolute;
z-index: 9999;
background: white;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
max-height: 300px;
overflow-y: auto;
display: none;
width: 100%;
font-family: Arial, sans-serif;
`;
inputElement.parentNode.style.position = 'relative';
inputElement.parentNode.appendChild(dropdown);
// Input event handler (rest of Google Places logic - unchanged)
let debounceTimer;
inputElement.addEventListener('input', function() {
clearTimeout(debounceTimer);
const query = this.value.trim();
// Reset selection flag when user types - this means manual typing
if (fieldType === 'pickup') {
pickupValidSelection = false;
} else {
dropoffValidSelection = false;
}
// Clear any validation styling while typing
inputElement.classList.remove('field-required-highlight', 'field-valid');
hideAddressError(inputElement);
if (query.length < 1) {
dropdown.style.display = 'none';
return;
}
debounceTimer = setTimeout(() => {
service.getPlacePredictions({
input: query,
types: ['geocode'] // Includes addresses, airports, and establishments
}, (predictions, status) => {
console.log('Google Places API Response:', { status, predictions, query });
dropdown.innerHTML = '';
if (status === google.maps.places.PlacesServiceStatus.OK && predictions) {
dropdown.style.display = 'block';
dropdown.innerHTML = '
Loading addresses...
';
let processedCount = 0;
const totalPredictions = predictions.slice(0, 5).length;
const items = [];
predictions.slice(0, 5).forEach((prediction, index) => {
placesService.getDetails({
placeId: prediction.place_id,
fields: ['address_components', 'formatted_address']
}, (place, detailStatus) => {
processedCount++;
if (detailStatus === google.maps.places.PlacesServiceStatus.OK) {
// Create address display item
const item = document.createElement('div');
item.className = 'custom-pac-item';
item.style.cssText = `
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s ease;
`;
item.innerHTML = `${place.formatted_address}
`;
// Click handler
item.addEventListener('click', () => {
inputElement.value = place.formatted_address;
dropdown.style.display = 'none';
// Validate that address has house number and street
const addressValidation = validateAddressComponents(place.address_components, place.formatted_address);
if (fieldType === 'pickup') {
pickupValidSelection = addressValidation.isValid;
} else {
dropoffValidSelection = addressValidation.isValid;
}
// Show validation result
if (!addressValidation.isValid) {
showAddressError(inputElement, addressValidation.message);
} else {
showAddressValid(inputElement);
}
});
// Hover effects
item.addEventListener('mouseenter', () => {
item.style.backgroundColor = '#f8f9fa';
});
item.addEventListener('mouseleave', () => {
item.style.backgroundColor = 'white';
});
items.push({ index, item });
}
// Update dropdown when all predictions are processed
if (processedCount === totalPredictions) {
dropdown.innerHTML = '';
if (items.length === 0) {
dropdown.innerHTML = 'No addresses found
';
} else {
items.sort((a, b) => a.index - b.index);
items.forEach(({ item }) => {
dropdown.appendChild(item);
});
}
}
});
});
} else {
console.error('Google Places API Error:', { status, query });
dropdown.style.display = 'block';
dropdown.innerHTML = `API Error: ${status}
`;
setTimeout(() => {
dropdown.style.display = 'none';
}, 3000);
}
});
}, 100);
});
// Hide dropdown on blur
inputElement.addEventListener('blur', function() {
setTimeout(() => {
dropdown.style.display = 'none';
}, 150);
});
}
// Setup autocomplete for address fields - find ALL forms including roundtrip
const pickupInputs = document.querySelectorAll('#search_input, input[name="address"], [id*="search_input"], [id*="pickup"], #roundtrippick_input, #roundtripreturnpick_input');
const dropoffInputs = document.querySelectorAll('#drop_input, input[name="destination"], [id*="drop_input"], [id*="dropoff"], #roundtripdrop_input, #roundtripreturndrop_input');
console.log('Found', pickupInputs.length, 'pickup address inputs');
console.log('Found', dropoffInputs.length, 'dropoff address inputs');
pickupInputs.forEach(function(input) {
if (input) {
setupCustomAutocomplete(input, 'pickup');
}
});
dropoffInputs.forEach(function(input) {
if (input) {
setupCustomAutocomplete(input, 'dropoff');
}
});
console.log('Google Places Autocomplete initialized for CF7 form');
}
// Address validation functions
function validateAddressComponents(addressComponents, formattedAddress = '') {
if (!addressComponents || addressComponents.length === 0) {
return {
isValid: false,
message: 'Please select a valid address from the dropdown.'
};
}
let hasStreetNumber = false;
let hasRoute = false;
let isSpecialPlace = false;
// Debug: Log all component types to console
console.log('Address components:', addressComponents.map(c => ({ name: c.long_name, types: c.types })));
console.log('Formatted address:', formattedAddress);
// Check formatted address for airport-related keywords
const formattedLower = formattedAddress.toLowerCase();
if (formattedLower.includes('airport') ||
formattedLower.includes('terminal') ||
formattedLower.includes('intl') ||
formattedLower.includes('international') ||
formattedLower.includes('regional') ||
formattedLower.includes('airfield') ||
formattedLower.includes('airway') ||
formattedLower.includes('aviation')) {
console.log('Airport detected in formatted address');
isSpecialPlace = true;
}
// Check for all types that should be considered valid
addressComponents.forEach(component => {
const types = component.types;
const name = component.long_name.toLowerCase();
// Regular address components
if (types.includes('street_number')) {
hasStreetNumber = true;
}
if (types.includes('route')) {
hasRoute = true;
// Check if route name indicates airport/special location
if (name.includes('airport') ||
name.includes('terminal') ||
name.includes('concourse') ||
name.includes('gate') ||
name.includes('departure') ||
name.includes('arrival')) {
isSpecialPlace = true;
}
}
// Special places that should always be valid
if (types.includes('airport') ||
types.includes('establishment') ||
types.includes('point_of_interest') ||
types.includes('transit_station') ||
types.includes('bus_station') ||
types.includes('train_station') ||
types.includes('subway_station') ||
types.includes('hospital') ||
types.includes('university') ||
types.includes('school') ||
types.includes('shopping_mall') ||
types.includes('park') ||
types.includes('tourist_attraction') ||
types.includes('lodging')) {
isSpecialPlace = true;
}
});
// Special places (airports, hotels, etc.) are always valid
if (isSpecialPlace) {
console.log('Valid special place detected');
return {
isValid: true,
message: ''
};
}
// For regular addresses, require street number and route
if (!hasStreetNumber && !hasRoute) {
return {
isValid: false,
message: 'Address must include both house number and street name.'
};
} else if (!hasStreetNumber) {
return {
isValid: false,
message: 'Address must include a house number, street name or Special place like Airport address.'
};
} else if (!hasRoute) {
return {
isValid: false,
message: 'Address must include a street name.'
};
}
console.log('Valid regular address detected');
return {
isValid: true,
message: ''
};
}
function showAddressError(inputElement, message) {
// Remove any existing error
hideAddressError(inputElement);
// Add error styling
inputElement.classList.add('field-required-highlight');
inputElement.classList.remove('field-valid');
// Create and show error message
const errorDiv = document.createElement('div');
errorDiv.className = 'field-error-message address-error-message';
errorDiv.style.display = 'block';
errorDiv.textContent = message;
inputElement.parentNode.appendChild(errorDiv);
}
function hideAddressError(inputElement) {
// Remove error styling and messages
inputElement.classList.remove('field-required-highlight');
// Remove error message
const errorMessage = inputElement.parentNode.querySelector('.address-error-message');
if (errorMessage) {
errorMessage.remove();
}
}
function showAddressValid(inputElement) {
// Only add valid styling when explicitly called (after dropdown selection)
inputElement.classList.remove('field-required-highlight');
inputElement.classList.add('field-valid');
hideAddressError(inputElement);
}
// Vehicle selection with CF7-compatible selectors
function initializeVehicleSelection() {
// Use CF7-generated select elements - find ALL forms
const vehicleSelects = document.querySelectorAll('select[name="type-of-vehicle"]');
if (vehicleSelects.length === 0) {
console.warn('No vehicle select elements found');
return;
}
console.log('Found', vehicleSelects.length, 'vehicle select elements');
vehicleSelects.forEach(function(vehicleSelect, index) {
// Remove existing listeners to prevent duplicates
vehicleSelect.removeEventListener('change', vehicleSelect._changeHandler);
// Create a change handler for this specific select
vehicleSelect._changeHandler = function() {
const selectedVehicle = this.value;
// Find the vehicle image elements within the same form/container
const formContainer = this.closest('form') || this.closest('.wpcf7') || this.closest('.elementor-tab-content');
let vehicleImageDiv, vehicleImg, vehiclePassengers;
if (formContainer) {
vehicleImageDiv = formContainer.querySelector('#vehicleImage, .vehicleImage, [id*="vehicleImage"]');
vehicleImg = formContainer.querySelector('#vehicleImg, .vehicleImg, [id*="vehicleImg"]');
vehiclePassengers = formContainer.querySelector('#vehiclePassengers, .vehiclePassengers, [id*="vehiclePassengers"]');
} else {
// Fallback to document-wide search with index
const allVehicleImageDivs = document.querySelectorAll('#vehicleImage, .vehicleImage, [id*="vehicleImage"]');
const allVehicleImgs = document.querySelectorAll('#vehicleImg, .vehicleImg, [id*="vehicleImg"]');
const allVehiclePassengers = document.querySelectorAll('#vehiclePassengers, .vehiclePassengers, [id*="vehiclePassengers"]');
vehicleImageDiv = allVehicleImageDivs[index];
vehicleImg = allVehicleImgs[index];
vehiclePassengers = allVehiclePassengers[index];
}
if (selectedVehicle && vehicleData[selectedVehicle]) {
const vehicle = vehicleData[selectedVehicle];
// Show vehicle image and details
if (vehicleImg) vehicleImg.src = vehicle.image;
if (vehiclePassengers) vehiclePassengers.textContent = vehicle.passengers;
if (vehicleImageDiv) vehicleImageDiv.style.display = 'block';
// Update passenger and suitcase dropdowns for this specific form
updatePassengerOptions(vehicle.passengers, formContainer);
updateSuitcaseOptions(vehicle.suitcases, formContainer);
// For roundtrip forms, also update return trip fields
updateReturnTripFields(vehicle.passengers, vehicle.suitcases, formContainer);
} else {
// Hide vehicle display
if (vehicleImageDiv) vehicleImageDiv.style.display = 'none';
// Reset dropdowns to full range for this specific form
updatePassengerOptions(55, formContainer);
updateSuitcaseOptions(100, formContainer);
// For roundtrip forms, also reset return trip fields
updateReturnTripFields(55, 100, formContainer);
}
};
// Add the event listener
vehicleSelect.addEventListener('change', vehicleSelect._changeHandler);
});
console.log('Vehicle selection system initialized for CF7 form');
}
// Initialize select field placeholders
function initializeSelectPlaceholders() {
// Define all select fields and their placeholder texts
const selectFields = [
{ selector: 'select[name="pickup-time"]', placeholder: 'Select Time' },
{ selector: 'select[name="drop-time"]', placeholder: 'Select Time' },
{ selector: 'select[name="type-of-service"]', placeholder: 'Select Service' },
{ selector: 'select[name="type-of-vehicle"]', placeholder: 'Select Vehicle' },
{ selector: 'select[name="number-of-passengers"]', placeholder: 'Select Passengers' },
{ selector: 'select[name="number-of-suitcases"]', placeholder: 'Select Suitcases' }
];
// Set placeholders for all select fields - find ALL forms
selectFields.forEach(field => {
const selectElements = document.querySelectorAll(field.selector);
if (selectElements.length > 0) {
console.log(`Found ${selectElements.length} elements for ${field.selector}`);
selectElements.forEach((selectElement, index) => {
console.log(`Processing ${field.selector} #${index + 1}:`, selectElement);
console.log('Current options:', Array.from(selectElement.options).map(opt => ({ value: opt.value, text: opt.textContent })));
// Find the blank option (should be first option with empty value)
let blankOption = selectElement.querySelector('option[value=""]');
if (!blankOption) {
// If no blank option exists, create one at the beginning
blankOption = document.createElement('option');
blankOption.value = '';
blankOption.textContent = field.placeholder;
selectElement.insertBefore(blankOption, selectElement.firstChild);
console.log(`Created placeholder option for ${field.selector} #${index + 1}`);
} else {
// Update existing blank option
blankOption.textContent = field.placeholder;
console.log(`Updated placeholder option for ${field.selector} #${index + 1}`);
}
// Force the select to show placeholder by resetting selectedIndex
selectElement.selectedIndex = 0;
// Also trigger a change event to update any dependencies
const changeEvent = new Event('change', { bubbles: true });
selectElement.dispatchEvent(changeEvent);
});
} else {
console.warn(`Select element not found: ${field.selector}`);
}
});
console.log('Select field placeholders initialized for CF7 form');
}
// Update passenger options with CF7-compatible selector
function updatePassengerOptions(maxPassengers, formContainer = null) {
let passengerSelect;
if (formContainer) {
passengerSelect = formContainer.querySelector('select[name="number-of-passengers"]');
} else {
// Fallback to all passenger selects
const passengerSelects = document.querySelectorAll('select[name="number-of-passengers"]');
if (passengerSelects.length > 0) {
// Update all passenger selects if no specific container
passengerSelects.forEach(select => updateSinglePassengerSelect(select, maxPassengers));
return;
}
}
if (!passengerSelect) return;
updateSinglePassengerSelect(passengerSelect, maxPassengers);
}
function updateSinglePassengerSelect(passengerSelect, maxPassengers) {
const currentValue = passengerSelect.value;
// Clear existing options and add placeholder
passengerSelect.innerHTML = '';
// Add options up to max passengers
for (let i = 1; i <= maxPassengers; i++) {
const option = document.createElement('option');
option.value = i.toString().padStart(2, '0');
option.textContent = i.toString().padStart(2, '0');
passengerSelect.appendChild(option);
}
// Restore previous value if still valid
if (currentValue && parseInt(currentValue) <= maxPassengers) {
passengerSelect.value = currentValue;
}
}
// Update suitcase options with CF7-compatible selector
function updateSuitcaseOptions(maxSuitcases, formContainer = null) {
let suitcaseSelect;
if (formContainer) {
suitcaseSelect = formContainer.querySelector('select[name="number-of-suitcases"]');
} else {
// Fallback to all suitcase selects
const suitcaseSelects = document.querySelectorAll('select[name="number-of-suitcases"]');
if (suitcaseSelects.length > 0) {
// Update all suitcase selects if no specific container
suitcaseSelects.forEach(select => updateSingleSuitcaseSelect(select, maxSuitcases));
return;
}
}
if (!suitcaseSelect) return;
updateSingleSuitcaseSelect(suitcaseSelect, maxSuitcases);
}
function updateSingleSuitcaseSelect(suitcaseSelect, maxSuitcases) {
const currentValue = suitcaseSelect.value;
// Clear existing options and add placeholder
suitcaseSelect.innerHTML = '';
// Add options up to max suitcases
for (let i = 1; i <= maxSuitcases; i++) {
const option = document.createElement('option');
option.value = i.toString().padStart(2, '0');
option.textContent = i.toString().padStart(2, '0');
suitcaseSelect.appendChild(option);
}
// Restore previous value if still valid
if (currentValue && parseInt(currentValue) <= maxSuitcases) {
suitcaseSelect.value = currentValue;
}
}
// Update return trip fields for roundtrip forms
function updateReturnTripFields(maxPassengers, maxSuitcases, formContainer = null) {
let returnPassengerSelect, returnSuitcaseSelect;
if (formContainer) {
returnPassengerSelect = formContainer.querySelector('select[name="return-number-of-passengers"]');
returnSuitcaseSelect = formContainer.querySelector('select[name="return-number-of-suitcases"]');
} else {
// Fallback to all return trip selects
const returnPassengerSelects = document.querySelectorAll('select[name="return-number-of-passengers"]');
const returnSuitcaseSelects = document.querySelectorAll('select[name="return-number-of-suitcases"]');
returnPassengerSelects.forEach(select => updateSinglePassengerSelect(select, maxPassengers));
returnSuitcaseSelects.forEach(select => updateSingleSuitcaseSelect(select, maxSuitcases));
return;
}
// Update return trip passenger select if found
if (returnPassengerSelect) {
updateSinglePassengerSelect(returnPassengerSelect, maxPassengers);
}
// Update return trip suitcase select if found
if (returnSuitcaseSelect) {
updateSingleSuitcaseSelect(returnSuitcaseSelect, maxSuitcases);
}
}
// Date picker initialization with CF7-compatible selector
function initializeDatePicker() {
const pickupDateInputs = document.querySelectorAll('input[name="pickup-date"], input[name="return-pickup-date"]');
if (pickupDateInputs.length === 0 || typeof AirDatepicker === 'undefined') {
console.warn('Date picker elements or AirDatepicker not available');
return;
}
console.log('Found', pickupDateInputs.length, 'pickup date inputs');
const today = new Date();
pickupDateInputs.forEach(function(pickupDateInput) {
// Skip if already initialized
if (pickupDateInput._airDatepicker) {
return;
}
new AirDatepicker(pickupDateInput, {
minDate: today,
dateFormat: 'MM-dd-yyyy',
autoClose: true,
isMobile: false,
toggleSelected: false,
onSelect: function({date, formattedDate, datepicker}) {
// Trigger validation when a date is selected
if (typeof jQuery !== 'undefined') {
const $input = jQuery(pickupDateInput);
// Manually trigger the validation function
$input.removeClass('field-required-highlight').addClass('field-valid');
$input.siblings('.field-error-message').hide();
// Also trigger a blur event to ensure validation runs
setTimeout(() => {
$input.trigger('blur');
}, 10);
}
},
locale: {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
months: ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'],
monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
today: 'Today',
clear: 'Clear',
dateFormat: 'MM-dd-yyyy',
timeFormat: 'HH:mm',
firstDay: 0
}
});
});
console.log('Date picker initialized for CF7 form');
}
// Validate that address was selected from dropdown (not manually typed)
function validateManualAddress(inputElement, fieldName) {
const address = inputElement.value.trim();
if (!address) return; // Empty is handled by required field validation
// Check if this was a valid selection from dropdown
if ((fieldName === 'address' && pickupValidSelection) ||
(fieldName === 'destination' && dropoffValidSelection)) {
hideAddressError(inputElement); // Valid selection
return;
}
// If there's text but it wasn't selected from dropdown, show error
if (address) {
showAddressError(inputElement, 'Please select an address from the dropdown suggestions.');
}
}
// Form validation for CF7 forms
function initializeValidation() {
// Wait for jQuery if using CF7
if (typeof jQuery !== 'undefined') {
jQuery(document).ready(function($) {
// CF7 form validation logic
function validateRequiredField(field) {
var isRequired = field.hasClass('wpcf7-validates-as-required') || field.attr('aria-required') === 'true' || field.prop('required');
var isEmpty = false;
var errorMessage = 'This field is required.';
var isValid = true;
if (field.is('select')) {
var value = field.val();
isEmpty = value === '' || value === null;
// Check if a disabled placeholder option is selected
if (!isEmpty) {
var selectedOption = field.find('option:selected');
if (selectedOption.length && selectedOption.prop('disabled')) {
isEmpty = true;
errorMessage = 'Please select a valid option.';
}
}
} else {
isEmpty = field.val().trim() === '';
}
// Check if field is required and empty
if (isRequired && isEmpty) {
field.addClass('field-required-highlight').removeClass('field-valid');
showErrorMessage(field, errorMessage);
return false;
}
// If not empty, validate format for specific field types
if (!isEmpty) {
// Email validation
if (field.hasClass('wpcf7-validates-as-email') || field.attr('type') === 'email') {
var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(field.val().trim())) {
field.addClass('field-required-highlight').removeClass('field-valid');
showErrorMessage(field, 'Please enter an email address.');
return false;
}
}
}
// Field is valid
field.removeClass('field-required-highlight');
hideErrorMessage(field);
if (!isEmpty) {
field.addClass('field-valid');
}
return true;
}
function showErrorMessage(field, message) {
var errorDiv = field.siblings('.field-error-message');
if (errorDiv.length === 0) {
errorDiv = $('' + message + '
');
field.parent().append(errorDiv);
} else {
errorDiv.text(message);
}
errorDiv.show();
}
function hideErrorMessage(field) {
field.siblings('.field-error-message').hide();
}
// Event listeners for CF7 form elements
$('.wpcf7-form input, .wpcf7-form select, .wpcf7-form textarea').on('blur', function() {
validateRequiredField($(this));
// Additional validation for address fields
const fieldName = $(this).attr('name');
if (fieldName === 'address' || fieldName === 'destination') {
validateManualAddress(this, fieldName);
}
});
$('.wpcf7-form input, .wpcf7-form select, .wpcf7-form textarea').on('focus', function() {
$(this).removeClass('field-required-highlight field-valid');
hideErrorMessage($(this));
});
console.log('CF7 form validation initialized');
});
}
}
} // End initializeScript function
})(); // End immediate execution
// Load Air Datepicker if not already loaded
if (typeof AirDatepicker === 'undefined') {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/air-datepicker@3.5.3/air-datepicker.js';
document.head.appendChild(script);
}