1var searchResultsLimit = 50; 2 3// i18next ui translations 4appTitle.innerHTML = i18next.t('TITLE') 5datedWarning.innerHTML = i18next.t('DATED_WARNING') 6getHelpLinks.innerHTML = i18next.t('GET_HELP') 7legalNoticeLink.innerHTML = `<b>${i18next.t('LEGAL_NOTICE_TITLE')}</b>` 8affiliatedLabel.innerHTML = i18next.t('AFFILIATED_LABEL') 9ownedLabel.innerHTML = i18next.t('OWNED_LABEL') 10viewOnMap.innerHTML = i18next.t('VIEW_ON_MAP') 11taxpayerLabel.innerHTML = i18next.t('TAXPAYER_LABEL') 12unitsLabel.innerHTML = i18next.t('UNITS_LABEL') 13dataInfoLink.innerHTML = i18next.t('DATA_INFO_TITLE') 14additionalDetailsLink.innerHTML = i18next.t('ADDITIONAL_DETAILS_LABEL') 15switchLangLink.innerHTML = i18next.t('SWITCH_LANG_NAME') 16switchLangLink.href = location.origin + '?lng=' + i18next.t('SWITCH_LANG_CODE') 17 18searchInputContainer.querySelector('input').placeholder = i18next.t('SEARCH_LABEL') 19searchButton.innerHTML = i18next.t('SEARCH_BUTTON') 20 21const netlifyFunctionsBaseUrl = '.netlify/functions' 22 23const netlifyFunction = async (methodName, options = {}) => { 24 const response = await fetch(`/${netlifyFunctionsBaseUrl}/${methodName}`, options) 25 return await response.json() 26} 27 28const matchAddresses = async function (query) { 29 // Load search keys 30 var value = query.trim().toLowerCase(); 31 32 firebase.analytics().logEvent("search-initiated", { 33 property_address: searchInput.value 34 }); 35 36 spinner.style.display = "block"; 37 38 // const { results } = await netlifyFunction('search', { 39 // body: JSON.stringify({query: value}), 40 // method: 'POST' 41 // }) 42 43 const results = await flex.search(value, searchResultsLimit + 1) 44 spinner.style.display = "none"; 45 // Show "x" 46 renderClearButton(value); 47 // Reset rendered objects 48 resetSearchResults(); 49 // Reset UI 50 resetMap(); 51 52 if (value != "") { 53 // Render list 54 renderResults(results) 55 }; 56}; 57 58function renderClearButton(value) { 59 if (checkIE() == false) { 60 // Show if input has content 61 clearButton.style.display = (value.length) ? "block" : "none"; 62 63 // Add listener 64 clearButton.onclick = function() { 65 // Hide button 66 this.style.display = "none"; 67 68 // Reset UI 69 searchInput.value = ""; 70 resetSearchResults(); 71 resetMap(); 72 }; 73 // Accessibility 74 clearButton.addEventListener("keypress", 75 function(e) { 76 // Enter key 77 if (e.keyCode == 13) { 78 e.target.click(); 79 }; 80 } 81 ); 82 }; 83}; 84 85function renderResults(features) { 86 if (features.length) { 87 // Results were found 88 // Show container 89 searchResultsContainer.style.display = "block"; 90 // Show counter 91 searchResultsCounter.style.display = "block"; 92 // Show list 93 searchResultsList.style.display = "block"; 94 95 if (features.length > searchResultsLimit) { 96 // More results than limit 97 searchResultsCounter.innerHTML = `<h4>${searchResultsLimit} ${i18next.t("SEARCH_RESULTS")}`; 98 99 var refineMessage = document.createElement("li"); 100 refineMessage.id = "limit-message"; 101 refineMessage.innerText = i18next.t("SEARCH_HINT", { searchResultsLimit }) 102 searchResultsList.appendChild(refineMessage); 103 } else if (features.length > 1 && features.length <= searchResultsLimit) { 104 // Less results than limit 105 searchResultsCounter.innerHTML = `<h4>${features.length} ${i18next.t("SEARCH_RESULTS")}`; 106 } else if (features.length == 1) { 107 // 1 result 108 searchResultsCounter.innerHTML = `<h4>${features.length} ${i18next.t("SEARCH_RESULT")}`; 109 }; 110 111 // Add ListItems 112 for (var i = 0; i < searchResultsLimit && i < features.length; i++) { 113 // Address at current index 114 createListItem(features[i]); 115 }; 116 } else if (features.length == 0 && searchInput.value != "") { 117 // No results found 118 var title = i18next.t("NO_SEARCH_RESULTS"); 119 var message = i18next.t("NO_SEARCH_RESULTS_MESSAGE"); 120 showSearchMessage(title, message); 121 }; 122}; 123 124function showSearchMessage(title, message) { 125 // Show container 126 searchResultsContainer.style.display = "block"; 127 // Show counter 128 searchResultsCounter.style.display = "block"; 129 130 // Create elements 131 var headline = document.createElement("h4"); 132 var description = document.createElement("p"); 133 134 // Set values 135 headline.innerHTML = title; 136 description.id = "no-results-container"; 137 description.innerHTML = message; 138 139 // Add content to containers 140 searchResultsCounter.appendChild(headline); 141 searchResultsContainer.appendChild(description); 142}; 143 144function createListItem(feature) { 145 var item = document.createElement("li"); 146 var address = feature[propertyAddressColumn]; 147 148 item.className = "search-result"; 149 item.tabIndex = 0; 150 item.innerHTML = address; 151 152 // Highlight part of string that matches input 153 highlightText(searchInput, item); 154 155 if (document.getElementById("limit-message")) { 156 // Insert list items before limit message 157 searchResultsList.insertBefore(item, document.getElementById("limit-message")) 158 } else { 159 // Limit doesn't exist 160 searchResultsList.appendChild(item); 161 }; 162 163 // Add click event 164 item.onclick = function(){ 165 async function render() { 166 try { 167 // Show spinner 168 spinner.style.display = "block"; 169 170 var selected = await searchProperty(feature[propertyIndexColumn]); 171 // Reset UI 172 resetMap(); 173 // Update it 174 renderSelectedUI(selected); 175 // Log event 176 firebase.analytics().logEvent("search-result-clicked", { 177 property_address: selected.properties[propertyAddressColumn], 178 taxpayer: selected.properties[taxpayerColumn], 179 affiliated_with: selected.properties[affiliatedWithColumn], 180 }); 181 182 // Hide spinner 183 spinner.style.display = "none"; 184 } catch { 185 // Show error message 186 resetSearchResults(); 187 var title = i18next.t('ERROR_DB'); 188 var message = i18next.t('ERROR_MESSAGE', { supportEmail }); 189 showSearchMessage(title, message); 190 191 // Hide spinner 192 spinner.style.display = "none"; 193 }; 194 }; 195 render(); 196 }; 197 // Accessibility 198 item.addEventListener("keypress", 199 function(e) { 200 // Enter key 201 if (e.keyCode == 13) { 202 e.target.click(); 203 }; 204 } 205 ); 206}; 207 208function highlightText(input, destination) { 209 var regex = new RegExp(input.value, "gi") 210 var response = destination.innerHTML.replace(regex, function(str) { 211 return "<b>" + str + "</b>" 212 }); 213 destination.innerHTML = response; 214};
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.