var locationSet = 0; var addressLocationInit = false; var placeSearch, autocompleteAddress; var componentForm = { street_number: 'short_name', route: 'long_name', locality: 'long_name', administrative_area_level_1: 'short_name', country: 'long_name', postal_code: 'short_name' }; var autoCompleteAddressListener = null; var autocompleteAddressInputTimeout = null; var pacElementAddress = null; var pendingGeoData = null; function createOrAttachPlaceAutocomplete(inputId, onPlaceChange) { var input = document.getElementById(inputId); if (!input || !window.google || !google.maps || !google.maps.places || !google.maps.places.PlaceAutocompleteElement) { return null; } // Always use the new PlaceAutocompleteElement var existing = document.getElementById(inputId + '-pac'); if (existing) { existing.remove(); } var pac = new google.maps.places.PlaceAutocompleteElement(); pac.id = inputId + '-pac'; pac.setAttribute('for', inputId); try { pac.types = ['address']; } catch (e) {} try { pac.fields = ['addressComponents', 'location']; } catch (e) {} // Hide the original input to prevent duplicate UI and copy sizing styles try { input.setAttribute('aria-hidden', 'true'); input.style.display = 'none'; var cs = window.getComputedStyle(input); pac.style.display = 'block'; pac.style.width = '100%'; pac.style.boxSizing = 'border-box'; if (cs && cs.height) { pac.style.height = cs.height; } if (cs && cs.fontSize) { pac.style.fontSize = cs.fontSize; } if (cs && cs.padding) { pac.style.padding = cs.padding; } if (cs && cs.borderRadius) { pac.style.borderRadius = cs.borderRadius; } if (cs && cs.border) { pac.style.border = cs.border; } if (input.className) { pac.className = input.className; } // Suppress gaudy blue outline/glow on focus pac.style.outline = 'none'; pac.style.boxShadow = 'none'; pac.addEventListener('focus', function(){ this.style.outline='none'; this.style.boxShadow='none'; }); pac.addEventListener('focusin', function(){ this.style.outline='none'; this.style.boxShadow='none'; }); pac.addEventListener('blur', function(){ this.style.outline='none'; this.style.boxShadow='none'; }); } catch (e) {} // Insert right after the input so it can attach input.insertAdjacentElement('afterend', pac); pac.addEventListener('gmp-select', async ({ placePrediction }) => { const place = placePrediction.toPlace(); await place.fetchFields({ fields: ['addressComponents','formattedAddress','location'] }); if (typeof onPlaceChange === 'function') { onPlaceChange(place); } }); return { mode: 'element', control: pac }; } async function checkPlacesEnabled({ apiKey }) { const url = 'https://places.googleapis.com/v1/places:autocomplete'; const body = { input: "a" }; // minimal probe const headers = { 'Content-Type': 'application/json', 'X-Goog-Api-Key': apiKey, // minimal field mask to keep payload tiny 'X-Goog-FieldMask': 'suggestions.placePrediction.placeId' }; const resp = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body) }); if (!resp.ok) { const data = await resp.json().catch(() => ({})); const msg = data?.error?.message || `HTTP ${resp.status}`; throw new Error(msg); } } function initAutocomplete() { var setup = createOrAttachPlaceAutocomplete('autocompleteAddress', fillInAddress); if (setup && setup.mode === 'element') { pacElementAddress = setup.control; // Apply pending geolocation bias if available and Google Maps is ready if (pendingGeoData && window.google && google.maps && google.maps.Circle) { try { var circle = new google.maps.Circle({ center: { lat: pendingGeoData.lat, lng: pendingGeoData.lng }, radius: pendingGeoData.accuracy }); pacElementAddress.locationBias = circle.getBounds(); } catch (e) {} } } } // [START region_fillform] function fillInAddress(data) { // Prefer provided place (from onPlaceChange) when available; fallback to current controls var place = (data && (data.address_components || data.addressComponents)) ? data : null; if (!place) { if (pacElementAddress && pacElementAddress.value) { place = pacElementAddress.value; } else if (autocompleteAddress && typeof autocompleteAddress.getPlace === 'function') { place = autocompleteAddress.getPlace(); } } $('#contactFormFieldset #address').show(); $('#shippingForm #address').show(); $('#shippingAddressForm #address').show(); $('#newShippingAddressForm #address').show(); $('.editShippingTable').show(); combineAddress(); setTimeout(function(){ combineAddress(); }, 100); for (var component in componentForm) { document.getElementById(component).value = ''; document.getElementById(component).disabled = false; } if (!place || !(place.address_components || place.addressComponents)) { return; } // Get each component of the address from the place details // and fill the corresponding field on the form. var addrComps = place && (place.address_components || place.addressComponents) || []; for (var i = 0; i < addrComps.length; i++) { var comp = addrComps[i]; var addressType = comp.types && comp.types[0]; if (componentForm[addressType]) { var val = ''; if (comp.hasOwnProperty(componentForm[addressType])) { val = comp[componentForm[addressType]]; // legacy short_name/long_name } else if (componentForm[addressType] === 'short_name' && (comp.short_name || comp.shortText)) { val = comp.short_name || comp.shortText; } else if (componentForm[addressType] === 'long_name' && (comp.long_name || comp.longText)) { val = comp.long_name || comp.longText; } else if (comp.shortText || comp.longText) { val = comp.shortText || comp.longText; } var targetEl = document.getElementById(addressType); if (targetEl) { targetEl.value = val; } if (addressType === 'administrative_area_level_1') { // Support either a select with id=administrative_area_level_1 or a legacy select#state (contact form) var stateSelect = document.getElementById('administrative_area_level_1'); if (!stateSelect || stateSelect.tagName !== 'SELECT') { stateSelect = document.getElementById('state'); } if (stateSelect && stateSelect.tagName === 'SELECT') { var $sel = $(stateSelect); if ($sel.find('option[value="' + val + '"]').length === 0) { $sel.append(''); } $sel.val(val); } } } } $('#country').change(); } // [END region_fillform] // [START region_geolocation] // Bias the autocompleteAddress object to the user's geographical location, // as supplied by the browser's 'navigator.geolocation' object. function geolocate() { if (typeof isSecureContext == 'undefined' || !isSecureContext) { return false; } if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(success,error,{enableHighAccuracy:true, timeout:10000}); // This loads the location once. locationSet = 1; } } function success(position) { var lat = position.coords.latitude; var lng = position.coords.longitude; var accuracy = position.coords.accuracy; pendingGeoData = { lat: lat, lng: lng, accuracy: accuracy }; // Attach autocomplete if not already attached initAutocomplete(); // If Google Maps JS isn't loaded yet, defer applying location bias if (!window.google || !google.maps || !google.maps.Circle) { return; } try { var circle = new google.maps.Circle({ center: { lat: lat, lng: lng }, radius: accuracy }); if (pacElementAddress) { try { pacElementAddress.locationBias = circle.getBounds(); } catch (e) {} } } catch (e) {} } function error() { geolocation = null; } // [END region_geolocation] function combineAddress(){ $('#contactFormFieldset #address1').val( $('#street_number').val() + " " + $('#route').val()); $('#shippingForm #address1').val( $('#street_number').val() + " " + $('#route').val()); $('#shippingAddressForm #address1').val( $('#street_number').val() + " " + $('#route').val()); $('#newShippingAddressForm #address1').val( $('#street_number').val() + " " + $('#route').val()); // $('#address1').val( $('#street_number').val() + " " + $('#route').val()); } // Safe DOM ready helper that works with or without jQuery function domReady(fn) { if (window.jQuery && jQuery.fn && (jQuery.fn.ready || jQuery.fn.on)) { // Prefer jQuery's shorthand if available jQuery(fn); return; } if (document.readyState === 'complete' || document.readyState === 'interactive') { // DOM is already ready setTimeout(fn, 0); } else { document.addEventListener('DOMContentLoaded', fn, { once: true }); } } domReady(function() { if(locationSet === 0){ geolocate(); } $('#autocompleteAddress').on('keyup paste click', function(){ if(locationSet === 0){ geolocate(); } }); // This example displays an address form, using the autocompleteAddress feature // of the Google Places API to help users fill in the information. $('#street_number, #route').on('change keyup paste click', function(){ combineAddress(); }); // Ensure PAC is attached once on load try { initAutocomplete(); } catch (e) {} $('#autocompleteAddress').keyup(function() { if (autocompleteAddressInputTimeout !== null) { clearTimeout(autocompleteAddressInputTimeout); } autocompleteAddressInputTimeout = setTimeout('initAutocomplete()', 300); }); }); // Below is a hack for styling the Google Places input element. // Guard against double execution when this script is included multiple times. (function(){ if (Element.prototype.__placesAttachShadowPatched) { return; } Element.prototype.__placesAttachShadowPatched = true; const originalAttachShadow = Element.prototype.attachShadow; Element.prototype.attachShadow = function(init){ // Check if we are the new Google places autocomplete element... if (this.localName === 'gmp-place-autocomplete' || this.localName === 'gmpx-place-autocomplete') { // Force mode:'open' so we can inject styles into the shadow DOM. const shadow = originalAttachShadow.call(this, { ...init, mode: 'open' }); const style = document.createElement('style'); // Apply our own styles to the shadow DOM. style.textContent = `[part=\"leading-icon\"],[part=\"trailing-icon\"],[part=\"clear-button\"],.clear-button,.autocomplete-icon, button.clear-button,.focus-ring{display:none!important;} .clear-button{width:24px!important;height:24px!important;min-width:24px!important;min-height:24px!important;padding:0!important;line-height:24px!important;margin:2px!important;box-shadow:none!important;} .clear-button svg{width:16px!important;height:16px!important;}`; shadow.appendChild(style); return shadow; } // For other elements, proceed with the original behaviour of attachShadow(). return originalAttachShadow.call(this, init); }; // If a Places element was already created before we patched attachShadow, // inject the same style into its existing shadow root. try { var nodes = document.querySelectorAll('gmp-place-autocomplete, gmpx-place-autocomplete'); nodes.forEach(function(el){ if (el.shadowRoot && !el.shadowRoot.__placesStylePatched) { var style = document.createElement('style'); style.textContent = `[part=\"leading-icon\"],[part=\"trailing-icon\"],[part=\"clear-button\"],.clear-button,.focus-ring{display:none!important;} .clear-button{width:24px!important;height:24px!important;min-width:24px!important;min-height:24px!important;padding:0!important;line-height:24px!important;margin:2px!important;box-shadow:none!important;} .clear-button svg{width:16px!important;height:16px!important;}`; el.shadowRoot.appendChild(style); el.shadowRoot.__placesStylePatched = true; } }); } catch(e) {} })();