// --- API KEYS AND CONFIG --- const TRAVELPAYOUTS_MARKER = '634425'; const AMADEUS_API_KEY = 'wnxdD8f2YGFwGBNqA7hXafKbIL4gsojf'; // 🔒 IMPORTANT: The Amadeus API Secret should NOT be stored here in production. // See the security note at the end of this guide. const AMADEUS_API_SECRET = 'NafcUGE85nx0RPVO'; let amadeusAccessToken = null; // --- 1. GOOGLE PLACES AUTOCOMPLETE --- function initAutocomplete() { const autocompleteOptions = { types: ['(cities)', 'airport'] }; // Hero Search new google.maps.places.Autocomplete(document.getElementById('from-input'), autocompleteOptions); new google.maps.places.Autocomplete(document.getElementById('to-input'), autocompleteOptions); // Tracker Search new google.maps.places.Autocomplete(document.getElementById('tracker-from-input'), autocompleteOptions); new google.maps.places.Autocomplete(document.getElementById('tracker-to-input'), autocompleteOptions); // Predictor Search new google.maps.places.Autocomplete(document.getElementById('predictor-from-input'), autocompleteOptions); new google.maps.places.Autocomplete(document.getElementById('predictor-to-input'), autocompleteOptions); // Other Searches new google.maps.places.Autocomplete(document.getElementById('hotel-location-input'), { types: ['(cities)'] }); new google.maps.places.Autocomplete(document.getElementById('hostel-location-input'), { types: ['(cities)'] }); new google.maps.places.Autocomplete(document.getElementById('car-pickup-location-input'), { types: ['(cities)', 'airport'] }); } // --- 2. AMADEUS API FLIGHT LOGIC --- async function getAmadeusToken() { // Note: This is a client-side token request for demonstration. // In production, this should be handled server-side to protect your API Secret. if (amadeusAccessToken) return amadeusAccessToken; try { const response = await fetch('https://test.api.amadeus.com/v1/security/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `grant_type=client_credentials&client_id=${AMADEUS_API_KEY}&client_secret=${AMADEUS_API_SECRET}` }); const data = await response.json(); amadeusAccessToken = data.access_token; setTimeout(() => { amadeusAccessToken = null; }, (data.expires_in - 300) * 1000); return amadeusAccessToken; } catch (error) { console.error("Amadeus Auth Error:", error); return null; } } async function getIataCode(place) { // This is a simplified function. A real implementation would use Google's Geocoding API // or a dedicated IATA code lookup service to convert the place name to a code. // For now, we will return a placeholder or extract from input if possible. console.warn("getIataCode is a placeholder. You need to implement a real IATA lookup."); return "LHR"; // Placeholder } // --- 3. DYNAMIC SEARCH FUNCTIONS --- async function searchDeals() { // This function will now power the main hero search showAudienceTab('all-travel', document.getElementById('all-travel-btn')); const fromInput = document.getElementById('from-input').value; const toInput = document.getElementById('to-input').value; const dateInput = document.getElementById('date-input').value; const dealsGrid = document.getElementById('deals-grid'); if (!fromInput || !toInput || !dateInput) { alert("Please provide an origin, destination, and departure date."); return; } dealsGrid.innerHTML = '

✈️ Searching for flights...

'; // In a real application, you would convert user-friendly names to IATA codes here. const originIata = await getIataCode(fromInput); const destinationIata = await getIataCode(toInput); const token = await getAmadeusToken(); if (!token) { dealsGrid.innerHTML = '

Could not connect to flight service. Please try again later.

'; return; } try { const url = `https://test.api.amadeus.com/v2/shopping/flight-offers?originLocationCode=${originIata}&destinationLocationCode=${destinationIata}&departureDate=${dateInput}&adults=1&nonStop=false&max=6`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` }}); if (!response.ok) throw new Error(`API Error: ${response.statusText}`); const data = await response.json(); if (data.data.length === 0) { dealsGrid.innerHTML = '

No flights found for this route. Please try another search.

'; return; } // Generate dynamic Travelpayout affiliate links const aviasalesLink = `https://www.travelpayouts.com/click?shmarker=${TRAVELPAYOUTS_MARKER}&promo_id=4047&origin_iata=${originIata}&destination_iata=${destinationIata}&trip_class=0&adults=1&with_request=true`; grid.innerHTML = data.data.map(deal => `

${deal.itineraries[0].segments[0].departure.iataCode} → ${deal.itineraries[0].segments.slice(-1)[0].arrival.iataCode}

${deal.itineraries[0].segments.length - 1} stop(s) via ${deal.validatingAirlineCodes[0]}

$${deal.price.total}

View Deal →
`).join(''); } catch (error) { console.error("Flight Search Error:", error); dealsGrid.innerHTML = '

An error occurred while searching for flights.

'; } } function searchHotels() { const location = document.getElementById('hotel-location-input').value; if (!location) { alert("Please enter a city or hotel name."); return; } const url = `https://search.hotellook.com/?marker=${TRAVELPAYOUTS_MARKER}&destination=${encodeURIComponent(location)}&language=en`; window.open(url, '_blank'); } function searchHostels() { const location = document.getElementById('hostel-location-input').value; if (!location) { alert("Please enter a city."); return; } // Hotellook is also used for hostels const url = `https://search.hotellook.com/?marker=${TRAVELPAYOUTS_MARKER}&destination=${encodeURIComponent(location)}&language=en`; window.open(url, '_blank'); } function searchCars() { const location = document.getElementById('car-pickup-location-input').value; if (!location) { alert("Please enter a pick-up location."); return; } // Using EconomyBookings.com link from Travelpayout const url = `https://www.travelpayouts.com/click?shmarker=${TRAVELPAYOUTS_MARKER}&promo_id=1415&source_type=link&type=click&trs=201555`; window.open(url, '_blank'); // Note: For a better user experience, you'd find a way to pass the 'location' to the partner site. } function setPriceAlert() { const from = document.getElementById('tracker-from-input').value; const to = document.getElementById('tracker-to-input').value; if (!from || !to) { alert("Please enter an origin and destination for the price alert."); return; } alert(`Price alert for ${from} to ${to} has been set! (This is a demo).`); // Here you would integrate with Amadeus's Flight Price Analysis API or a similar service. } function getPricePrediction() { const from = document.getElementById('predictor-from-input').value; const to = document.getElementById('predictor-to-input').value; const date = document.getElementById('predictor-date-input').value; const container = document.getElementById('prediction-result-container'); if (!from || !to || !date) { alert("Please enter all fields for a prediction."); return; } container.innerHTML = `

🔮 Analyzing historical data for ${from} to ${to}...

`; // This is where you would call your historical data API (e.g., Tefaa or Amadeus). // For this demo, we'll simulate a result. setTimeout(() => { const shouldBuy = Math.random() > 0.5; const confidence = Math.floor(Math.random() * (95 - 75 + 1) + 75); if (shouldBuy) { container.innerHTML = `

Good time to Buy!

Our analysis of historical data suggests prices are lower than average. We are ${confidence}% confident in this prediction.

`; } else { container.innerHTML = `

Consider Waiting.

Prices are currently higher than usual for this route. It may be better to track prices. We are ${confidence}% confident in this prediction.

`; } }, 2000); }

Alex’s Immersion Kit

Alex’s Recommended Cultural Immersion Gear – Endless Travel Plans
Portrait of Alex Rivera

Alex’s Cultural Immersion Kit

True cultural immersion isn’t about the gear, but the right tools can open doors. This is my curated kit of discreet, durable, and practical items that help you blend in, connect more deeply, and focus on what truly matters: the experience.

The Immersion Pack

Anti-Theft Backpack

Anti-Theft Daypack

A discreet, slash-proof bag with hidden zippers is key for peace of mind in crowded markets and public transport, letting you focus on the culture, not your valuables.

Check Price on Amazon
Travel Journal

Durable Travel Journal

The most important tool for reflection. A sturdy, pocket-sized journal (like a Moleskine) is perfect for capturing thoughts, sketches, and contact info from new friends.

Check Price on Amazon
Water Filter Bottle

Water Filter Bottle

Essential for off-the-beaten-path travel. A reliable bottle with a built-in filter allows you to drink safely from taps and streams, reducing plastic waste.

Check Price on Amazon
Scarf or Sarong

Versatile Scarf/Sarong

Incredibly useful. Use it to cover your shoulders when entering temples, as a towel, a blanket on a cold bus, or a privacy screen in a hostel.

Check Price on Amazon

Apparel for the Thoughtful Traveler

Merino Wool Shirt

Merino Wool T-Shirt

The perfect travel fabric. It’s odor-resistant, temperature-regulating, and comfortable. You can wear it for days without washing, allowing you to pack less.

Check Price on Amazon
Travel Pants

Versatile Travel Pants

A single pair of durable, quick-drying pants in a neutral color that can be dressed up for a nice dinner or worn on a light hike is essential.

Check Price on Amazon
Comfortable Walking Shoes

All-Day Walking Shoes

Your most important item. A pair of comfortable, broken-in shoes in a discreet style will let you explore for hours without pain.

Check Price on Amazon
Packable Rain Jacket

Packable Rain Jacket

Weather is unpredictable. A lightweight, waterproof, and breathable jacket that packs into its own pocket is a must-have for any destination.

Check Price on Amazon

Connection & Documentation

Compact Camera

High-Quality Compact Camera

A phone is good, but a dedicated camera is better for respectfully capturing high-quality images of your experiences without being intrusive.

Check Price on Amazon
Language Phrasebook

Pocket Phrasebook

Even if you use translation apps, a physical phrasebook shows effort and is a great icebreaker. Learning a few key phrases is a sign of respect.

Check Price on Amazon
Portable Charger

Portable Solar Charger

For multi-day treks or staying in rural areas with unreliable electricity, a small solar panel can keep your essential devices charged.

Check Price on Amazon
Gifts for hosts

Small Gifts for Hosts

Bringing small, thoughtful gifts from your home country (postcards, local candies) is a wonderful way to show appreciation to new friends or homestay hosts.

Check Price on Amazon

Get the Best Travel Deals & Tips

Join our free newsletter for expert advice, exclusive deals, and travel inspiration delivered straight to your inbox.

EndlessTravel

Your expert guides to smarter travel.

Newsletter

Get the best deals and tips. Subscribe now.

Affiliate Disclosure: This site contains affiliate links, which means we may receive a commission if you click a link and purchase something that we have recommended. While clicking these links won’t cost you any extra money, they will help us keep this site up and running! Thank you for your support!

© 2024 Endless Travel Plans. All Rights Reserved.