'); paymentWindow.document.close(); } } catch (e) { console.warn('Failed to open payment window:', e); }bbShowLoading('Reserving slots with ranger...');const tourTypeMap = { 'private-town': 'private_town_tour', 'living-lake': 'living_at_the_lake', 'twilight': 'twilight_tour', 'large-group': 'large_group_tour', 'mines': 'mines_mills_tour' };const payload = { date: bbState.selectedDate, time: bbState.selectedTime, party_size: partySize, tour_type: tourTypeMap[tourKey] || 'large_group_tour', vehicle_acknowledgment: tourKey === 'mines' ? (document.getElementById('mines-vehicle-ack') ? document.getElementById('mines-vehicle-ack').checked : false) : false, guest: { name: name, email: email, phone: phone } };try { let csrfToken = ''; let recaptchaSiteKey = '';try { const csrfResponse = await fetchWithTimeout(BOOKING_API, { method: 'GET', credentials: 'include', timeout: 10000 });if (csrfResponse.ok) { const csrfData = await csrfResponse.json(); csrfToken = csrfData.csrf_token; recaptchaSiteKey = csrfData.recaptcha_site_key; } } catch (csrfErr) { console.warn('Could not retrieve CSRF token or site key:', csrfErr); }let recaptchaToken = '';if (recaptchaSiteKey) { try { await Promise.race([ loadRecaptchaScript(recaptchaSiteKey), new Promise(function(_, reject) { setTimeout(function() { reject(new Error('reCAPTCHA script load timeout')); }, 5000); }) ]);if (typeof grecaptcha !== 'undefined') { recaptchaToken = await Promise.race([ grecaptcha.execute(recaptchaSiteKey, { action: 'submit_booking' }), new Promise(function(_, reject) { setTimeout(function() { reject(new Error('reCAPTCHA execution timeout')); }, 4000); }) ]); } } catch (err) { console.warn('reCAPTCHA execution failed, proceeding without token:', err); } }payload.recaptcha_token = recaptchaToken;const response = await fetchWithTimeout(BOOKING_API, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, credentials: 'include', body: JSON.stringify(payload), timeout: 10000 });let result = {};try { result = await response.json(); } catch (jsonErr) { console.warn('Could not parse response JSON:', jsonErr); }if (response.ok && result.status === 'success') { const totalPrice = tour ? tour.calcPrice(partySize) : partySize * 25.00;document.getElementById('bb-amount-display').textContent = '$' + totalPrice.toFixed(2) + ' DUE'; document.getElementById('bb-confirm-id').textContent = result.booking_id || '--'; document.getElementById('bb-confirm-party').textContent = partySize + ' Guest(s)';const displayDate = new Date(bbState.selectedDate + 'T12:00:00').toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });document.getElementById('bb-confirm-datetime').textContent = tourName + ' on ' + displayDate + ' @ ' + bbState.selectedTime;const invoiceLink = document.getElementById('bb-invoice-link'); const countdownEl = document.getElementById('bb-redirect-countdown');if (invoiceLink && result.payment_link && isTrustedRedirectUrl(result.payment_link)) { invoiceLink.href = result.payment_link; invoiceLink.style.display = 'inline-flex'; } else if (invoiceLink) { invoiceLink.style.display = 'none'; }const cancelLink = document.getElementById('bb-cancel-link');if (cancelLink && result.booking_id && result.token) { cancelLink.href = 'https://us-west2-bodie-tours-prod.cloudfunctions.net/cancel-tour?booking_id=' + encodeURIComponent(result.booking_id) + '&token=' + encodeURIComponent(result.token); }let openedInNewTab = false;if (paymentWindow) { try { if (!paymentWindow.closed) { if (result.payment_link && isTrustedRedirectUrl(result.payment_link)) { paymentWindow.location.href = result.payment_link; openedInNewTab = true; } else { paymentWindow.close(); } } } catch (e) { console.warn('Failed to set payment window location:', e); } }if (countdownEl) { if (openedInNewTab) { countdownEl.innerHTML = 'Your invoice has been opened in a new tab. If it did not open, click Pay Your Invoice Now above.'; } else { countdownEl.innerHTML = 'Please click the Pay Your Invoice Now button above to complete your secure payment.'; } }bbGoTo(4); } else { if (paymentWindow) { try { paymentWindow.close(); } catch (e) {} }if (response.status === 400) { errMsg.textContent = result.message || 'Validation error. Please check your inputs.'; } else if (response.status === 409) { errMsg.textContent = result.message || 'The requested slot is already booked. Please choose another slot.'; } else if (response.status === 429) { errMsg.textContent = 'Too many booking attempts. Please wait a moment and try again.'; } else if (response.status === 500) { errMsg.textContent = 'A temporary server error occurred. Please try again shortly.'; } else { errMsg.textContent = result.message || 'The slot is no longer available. Please select another time.'; }errBox.style.display = 'flex'; } } catch (err) { if (paymentWindow) { try { paymentWindow.close(); } catch (e) {} }console.error('Booking failed:', err); errMsg.textContent = 'Network or connection error. Please try again.'; errBox.style.display = 'flex'; } finally { bbState.isSubmitting = false;if (confirmBtn) { confirmBtn.disabled = false; }bbHideLoading(); } }function bbAnnounce(message) { const announcer = document.getElementById('bb-announcer');if (announcer) { announcer.textContent = '';setTimeout(function() { announcer.textContent = message; }, 50); } }function bbGoTo(step) { if (step < 4) { bbClearTimers(); }document.querySelectorAll('.bb-step').forEach(function(pane) { pane.classList.remove('active'); });document.getElementById('step-pane-' + step).classList.add('active');document.getElementById('bb-progress-bar').style.width = (step / 4) * 100 + '%';const progressContainer = document.getElementById('bb-progress-container');if (progressContainer) { progressContainer.setAttribute('aria-valuenow', step + 1); }document.querySelectorAll('.bb-progress-step').forEach(function(dot, index) { if (index < step) { dot.className = 'bb-progress-step completed'; dot.removeAttribute('aria-current'); } else if (index === step) { dot.className = 'bb-progress-step active'; dot.setAttribute('aria-current', 'step'); } else { dot.className = 'bb-progress-step'; dot.removeAttribute('aria-current'); } });if (step === 1) { bbFetchMonthAvailability(bbState.currentYear, bbState.currentMonth).then(function() { bbRenderCalendar(); }); }if (step === 2) { bbPopulateSlots(); }if (step === 3) { bbUpdateStep3Fields(); }const heading = document.getElementById('bb-step-heading-' + step);if (heading) { heading.focus({ preventScroll: true }); bbAnnounce('Step ' + (step + 1) + ' of 5: ' + heading.textContent.trim()); }const widget = document.getElementById('bodie-booking-widget');if (widget) { window.scrollTo({ top: widget.offsetTop - 30, behavior: 'smooth' }); } }function bbSelectTour(tourKey) { bbState.selectedTour = tourKey;document.querySelectorAll('.bb-tour-card').forEach(function(card) { const isSelected = card.getAttribute('data-tour') === tourKey;if (isSelected) { card.classList.add('selected'); card.setAttribute('aria-checked', 'true'); card.setAttribute('aria-pressed', 'true'); card.setAttribute('tabindex', '0'); } else { card.classList.remove('selected'); card.setAttribute('aria-checked', 'false'); card.setAttribute('aria-pressed', 'false'); card.setAttribute('tabindex', '-1'); } });const tourName = TOURS[tourKey] ? TOURS[tourKey].name : 'Tour';bbAnnounce('Selected tour: ' + tourName);document.getElementById('bb-to-step-1').disabled = false;bbState.selectedDate = null; bbState.selectedTime = null;document.getElementById('bb-to-step-2').disabled = true; document.getElementById('bb-to-step-3').disabled = true; }function bbUpdateStep3Fields() { const tourKey = bbState.selectedTour; const tour = TOURS[tourKey];const ackGroup = document.getElementById('mines-vehicle-ack-group'); const ackInput = document.getElementById('mines-vehicle-ack'); const partyInput = document.getElementById('guest-party'); const partyLabel = document.querySelector('label[for="guest-party"]');if (tourKey === 'mines') { ackGroup.style.display = 'block'; ackInput.required = true; } else { ackGroup.style.display = 'none'; ackInput.required = false; ackInput.checked = false; }const maxParty = tour ? tour.maxParty : 20;partyInput.max = maxParty; partyInput.placeholder = 'e.g., 4 (maximum ' + maxParty + ')'; partyLabel.textContent = 'Number of Guests (Required, max ' + maxParty + ')'; }async function bbRetryCalendarFetch() { await bbFetchMonthAvailability(bbState.currentYear, bbState.currentMonth); bbRenderCalendar(); }function bbStartOver() { bbClearTimers();bbState = { selectedTour: null, currentYear: new Date().getFullYear(), currentMonth: new Date().getMonth(), selectedDate: null, selectedTime: null, availableDates: {}, redirectTimeout: null, redirectInterval: null, calendarFetchFailed: false, isSubmitting: false };const form = document.getElementById('bb-booking-form');if (form) { form.reset(); }const errBox = document.getElementById('bb-error-box');if (errBox) { errBox.style.display = 'none'; }document.getElementById('bb-to-step-1').disabled = true; document.getElementById('bb-to-step-2').disabled = true; document.getElementById('bb-to-step-3').disabled = true;document.querySelectorAll('.bb-tour-card').forEach(function(card) { card.classList.remove('selected'); card.setAttribute('aria-checked', 'false'); card.setAttribute('aria-pressed', 'false'); });bbGoTo(0); }function bbShowLoading(text) { const loading = document.getElementById('bb-loading'); const loadingText = document.getElementById('bb-loading-text');if (loadingText) { loadingText.textContent = text; }if (loading) { loading.setAttribute('aria-hidden', 'false'); loading.classList.add('active'); } }function bbHideLoading() { const loading = document.getElementById('bb-loading');if (loading) { loading.setAttribute('aria-hidden', 'true'); loading.classList.remove('active'); } }window.bbSelectTour = bbSelectTour; window.bbRetryCalendarFetch = bbRetryCalendarFetch; window.bbStartOver = bbStartOver; window.bbChangeMonth = bbChangeMonth; window.bbGoTo = bbGoTo; window.bbSubmitBooking = bbSubmitBooking; window.bbInit = bbInit;function bbSafeInit() { if (!document.getElementById('bodie-booking-widget')) { return; }bbInit(); }if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', bbSafeInit); } else { bbSafeInit(); } })();

Volunteering at Bodie

Bodie State Historic Park is now accepting volunteer applications for the 2019 season!

Contact usnowto obtain an application form. You’ll learn how to develop and present interpretive programs, how to interact with visitors and all about the wonderful history of Bodie, California’s official gold mining ghost town.

Purpose:

The purpose of Bodie’s volunteer program is to augment and enhance our interpretive program. Volunteers can accomplish this by presenting interpretive programs, providing information to visitors while roving the park, assisting at the visitor center and helping with maintenance projects.

Volunteer Benefits:

  • You will gain valuable public speaking skills and learn about the roll Bodie played in California’s history. You will gain an appreciation of the natural and cultural history of the area and enjoy the satisfaction of serving visitors from all over the world.
  • After donating 50 hours a year, volunteers recieve a one-year pass for day-use to all Sierra Disctrict State Parks. After donating 200 hours, a pass is issued for for free day-use to all California State Parks for the following year.
  • Through your contribution of time and energy, the public will have a better understanding, awareness and appreciation of Bodie, the California State Park System and the Bodie Foundation, our non-profit cooperating association.

Qualifications:

In order to be a California State Park Volunteers at Bodie:

  • You must be 18 years of age or older, able to move around easily and present programs at an altitude of 8,375 feet.
  • You must be reliable and committed.
  • You are requiried to log a minimum of 24 hours between Memorial Day Weekend and the end of September.
  • You must be fingerprinted and disclose any past criminal convictions.

Please call 760-932-7574 for more information.

Join our mailing list

Love Bodie State Historic Park? Stay connected for information on Bodie and the Foundation’s work.