1document.addEventListener('DOMContentLoaded', function() { 2 const form = document.querySelector('[data-contest-city-form]'); 3 const input = form ? form.querySelector('[data-contest-city-input]') : null; 4 const cityIdInput = form ? form.querySelector('[data-contest-city-id]') : null; 5 const cityLabelInput = form ? form.querySelector('[data-contest-city-label]') : null; 6 const results = form ? form.querySelector('[data-contest-city-results]') : null; 7 const feedback = document.querySelector('[data-contest-city-feedback]'); 8 const findBaseUrl = 'https://find.portalidea.top/'; 9 let debounceTimer = null; 10 let activeRequest = null; 11 let selectedCity = null; 12 13 if (!form || !input || !cityIdInput || !cityLabelInput || !results || !feedback) { 14 return; 15 } 16 17 function buildCityLabel(city) { 18 return city.city + ' â ' + city.state; 19 } 20 21 function setFeedback(message) { 22 feedback.textContent = message; 23 feedback.hidden = !message; 24 } 25 26 function clearSelectedCity() { 27 selectedCity = null; 28 cityIdInput.value = ''; 29 cityLabelInput.value = ''; 30 } 31 32 function closeResults() { 33 results.innerHTML = ''; 34 results.hidden = true; 35 } 36 37 function selectCity(city) { 38 const label = buildCityLabel(city); 39 40 selectedCity = { 41 id: city.id, 42 label: label 43 }; 44 45 input.value = label; 46 cityIdInput.value = city.id; 47 cityLabelInput.value = label; 48 setFeedback(''); 49 closeResults(); 50 } 51 52 function renderResults(cities) { 53 results.innerHTML = ''; 54 55 if (!cities.length) { 56 closeResults(); 57 setFeedback('Nenhuma cidade encontrada.'); 58 return; 59 } 60 61 cities.forEach(function(city) { 62 const item = document.createElement('li'); 63 const button = document.createElement('button'); 64 const cityName = document.createElement('strong'); 65 66 button.type = 'button'; 67 cityName.textContent = city.city; 68 button.appendChild(cityName); 69 button.appendChild(document.createTextNode(' â ' + city.state)); 70 button.addEventListener('click', function() { 71 selectCity(city); 72 }); 73 74 item.appendChild(button); 75 results.appendChild(item); 76 }); 77 78 results.hidden = false; 79 setFeedback(''); 80 } 81 82 function fetchCities(term) { 83 const url = new URL('/api/locations', findBaseUrl); 84 85 url.searchParams.set('q', term); 86 url.searchParams.set('limit', '8'); 87 88 if (activeRequest) { 89 activeRequest.abort(); 90 } 91 92 activeRequest = new AbortController(); 93 setFeedback('Buscando cidades...'); 94 95 fetch(url.toString(), { 96 signal: activeRequest.signal 97 }) 98 .then(function(response) { 99 if (!response.ok) { 100 throw new Error('Falha ao buscar cidades.'); 101 } 102 103 return response.json(); 104 }) 105 .then(function(data) { 106 renderResults(Array.isArray(data.results) ? data.results : []); 107 }) 108 .catch(function(error) { 109 if (error.name === 'AbortError') { 110 return; 111 } 112 113 closeResults(); 114 setFeedback('Não foi possÃvel buscar as cidades agora.'); 115 }); 116 } 117 118 input.addEventListener('input', function() { 119 const term = input.value.trim(); 120 121 clearSelectedCity(); 122 clearTimeout(debounceTimer); 123 124 if (term.length < 2) { 125 closeResults(); 126 setFeedback(''); 127 return; 128 } 129 130 debounceTimer = setTimeout(function() { 131 fetchCities(term); 132 }, 250); 133 }); 134 135 document.addEventListener('click', function(event) { 136 if (!form.contains(event.target)) { 137 closeResults(); 138 } 139 }); 140 141 form.addEventListener('submit', function(event) { 142 event.preventDefault(); 143 144 if (!selectedCity || !selectedCity.id) { 145 setFeedback('Selecione uma cidade da lista para pesquisar.'); 146 input.focus(); 147 return; 148 } 149 150 const url = new URL(findBaseUrl); 151 152 url.searchParams.set('city', selectedCity.id); 153 url.searchParams.set('city_label', selectedCity.label); 154 155 window.open(url.toString(), '_blank', 'noopener'); 156 }); 157});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.