1import { 2 submitPeopleSearch, 3 submitPropertySearch, 4 submitPhoneSearch, 5} from "./frontEndRoutes.js"; 6 7export const relocate = (newPage, relocateDelay = 0) => { 8 const query = ["property", "p", "phone", "r", "people"]; 9 10 const hreffArr = window.location.href.split("/"); 11 const check = query.some((el) => hreffArr.includes(el)); 12 13 hreffArr.pop(); 14 if (check) { 15 hreffArr.pop(); 16 } 17 18 hreffArr.push(newPage); 19 20 const newHref = hreffArr.join("/"); 21 22 setTimeout(function () { 23 window.location.href = newHref; 24 }, relocateDelay); 25}; 26 27// Google Map Event Listener 28const addGoogleEventListener = (element, addressObj) => { 29 element.addListener("place_changed", () => { 30 const place = element.getPlace(); 31 if (place.address_components) { 32 addressObj.method = "google"; 33 addressObj.address = placeToAddress(place); 34 //move this to the backend to save $$ 35 //addressObj.lat = place.geometry.location.lat(); 36 //addressObj.lng = place.geometry.location.lng(); 37 } else { 38 //Entry was typed or pasted 39 addressObj.method = "manual"; 40 } 41 }); 42}; 43 44const placeToAddress = (place) => { 45 let address = {}; 46 place.address_components.forEach(function (c) { 47 switch (c.types[0]) { 48 case "street_number": 49 address.StreetNumber = c; 50 break; 51 case "route": 52 address.StreetName = c; 53 break; 54 case "neighborhood": 55 case "locality": 56 address.City = c; 57 break; 58 case "administrative_area_level_1": 59 address.State = c; 60 break; 61 case "postal_code": 62 address.Zip = c; 63 break; 64 case "country": 65 address.Country = c; 66 break; 67 case 'subpremise': 68 address.SubPremise = c; 69 break; 70 } 71 }); 72 73 return address; 74}; 75 76export const addGoogleMapsInstance = (element, addressObj, propertyContainer, searching) => { 77 if (!element) return; 78 const fields = ["formatted_address", "address_components"]; 79 80 const googleMapsElement = new google.maps.places.Autocomplete(element, { 81 fields: fields, 82 types: ['street_address', 'premise'], 83 }); 84 85 googleMapsElement.setComponentRestrictions({ country: ["us"] }); 86 87 addGoogleEventListener(googleMapsElement, addressObj); 88 89 element.addEventListener("input", () => { 90 addressObj.method = "manual"; 91 }); 92 93 element.addEventListener('keydown', function(event) { 94 if (event.keyCode === 13) { 95 event.preventDefault(); 96 } 97 }); 98 99 google.maps.event.addListener(googleMapsElement, 'place_changed', function() { 100 if(addressObj.resultPage) { 101 ga("prop", "result", "new search"); 102 } else if (addressObj.secondInput) { 103 ga("prop", "index", "search map"); 104 } else { 105 ga("prop", "index", "search top"); 106 } 107 submitPropertySearch(addressObj, propertyContainer, searching); 108 }); 109 110 return googleMapsElement; 111}; 112 113// error handling 114export const addErrorHandling = (element, elementContainer) => { 115 if (!element) return; 116 element.addEventListener("focus", () => { 117 elementContainer.classList.remove("has-error"); 118 }); 119 element.addEventListener("input", () => { 120 elementContainer.classList.remove("has-error"); 121 }); 122}; 123 124export const propertyMultiSearchInit = (propertyObj, searching, neighborhoodInputObj) => { 125 // property Inputs 126 const propertyInputs = $(".search__field__property"); 127 const propertyContainer = document.getElementById("search-container1"); 128 const propertyButton = document.getElementById("btn-search"); 129 130 const propertyInput = propertyInputs[0]; 131 addGoogleMapsInstance(propertyInput, propertyObj, propertyContainer, searching); 132 addErrorHandling(propertyInput, propertyContainer); 133 134 if (propertyButton) { 135 propertyButton.addEventListener("click", (event) => { 136 if(propertyObj.resultPage) { 137 ga("prop", "result", "new search"); 138 } else { 139 ga("prop", "index", "search top"); 140 } 141 event.preventDefault(); 142 143 submitPropertySearch(propertyObj, propertyContainer, searching); 144 }); 145 } 146 // Neighborhood Input 147 if (neighborhoodInputObj) { 148 // Google Maps Input and Error Handling for SECOND INPUT 149 const searchFields = $(".search__field__property"); 150 const searchContainer2 = document.getElementById("search-container2"); 151 152 // Neighborhood Input 153 const neighborhoodInput = searchFields[1]; 154 addGoogleMapsInstance(neighborhoodInput, neighborhoodInputObj, searchContainer2, searching) 155 addErrorHandling(neighborhoodInput, searchContainer2); 156 157 // click event for submit button of neighborhood input 158 const buttonSearchMap = document.getElementById("btn-search-map"); 159 buttonSearchMap.addEventListener("click", (event) => { 160 ga("prop", "index", "search map"); 161 event.preventDefault(); 162 163 submitPropertySearch(neighborhoodInputObj, searchContainer2, searching); 164 }); 165 } 166} 167 168// use this if you are using the multi-search partial 169export const multiSearchInit = (mainInputObj, searching, neighborhoodInputObj) => { 170 // tab changing 171 172 window.propertyMultiSearchInit = propertyMultiSearchInit.bind(this, mainInputObj, searching, neighborhoodInputObj); 173 174 $(".hero__body").on("click", ".tabs__nav a", function (event) { 175 const $this = $(this); 176 const href = $this.attr("data-tab"); 177 const $currentTab = $(`#${href}`); 178 const $currentHeader = $(`#header-${href}`); 179 180 $this 181 .closest("li") 182 .addClass("is-current") 183 .siblings() 184 .removeClass("is-current"); 185 186 $currentTab.addClass("is-current").siblings().removeClass("is-current"); 187 $currentHeader 188 .addClass("is-current") 189 .siblings() 190 .removeClass("is-current"); 191 192 event.preventDefault(); 193 }); 194 195 196 // people Inputs 197 const peopleInput = document.getElementById("search-name"); 198 const peopleFirstInput = document.getElementById("search-first-name") 199 const peopleLastInput = document.getElementById("search-last-name") 200 const peopleContainer = document.getElementById("ppl-search"); 201 202 submitPeopleSearch(); 203 204 if (peopleInput) { 205 peopleInput.addEventListener("focus", () => { 206 peopleContainer.classList.remove("has-error"); 207 }); 208 peopleInput.addEventListener("input", () => { 209 peopleContainer.classList.remove("has-error"); 210 }); 211 } else if (peopleFirstInput) { 212 peopleFirstInput.addEventListener("focus", () => { 213 peopleContainer.classList.remove("has-error"); 214 }); 215 peopleFirstInput.addEventListener("input", () => { 216 peopleContainer.classList.remove("has-error"); 217 }); 218 peopleLastInput.addEventListener("focus", () => { 219 peopleContainer.classList.remove("has-error"); 220 }); 221 peopleLastInput.addEventListener("input", () => { 222 peopleContainer.classList.remove("has-error"); 223 }); 224 } 225 226 227 // phone Inputs 228 const phoneInput = document.getElementById("search-number"); 229 if (phoneInput) { 230 const phoneContainer = document.getElementById("phone-search"); 231 const phoneButton = document.getElementById("phone-search-btn"); 232 let phoneNumber = phoneInput.value.replace(/[^\d]/g, "") || ""; 233 234 phoneButton.addEventListener("click", (event) => { 235 ga("phone", "index", "search"); 236 event.preventDefault(); 237 238 submitPhoneSearch(searching, phoneNumber); 239 }); 240 241 phoneInput.addEventListener("focus", () => { 242 phoneContainer.classList.remove("has-error"); 243 }); 244 245 phoneInput.addEventListener("keyup", (event) => { 246 event.target.value = formatPhoneNumber(event.target.value) 247 248 phoneNumber = event.target.value.replace(/[^\d]/g, "") 249 }); 250 }; 251}; 252 253//showPopup 254export const showPopup = (modal) => { 255 const selectedModal = document.getElementById(modal); 256 const body = document.getElementsByTagName("body")[0]; 257 const contentBody = document.getElementsByClassName("popup__body"); 258 259 const iframeElement = selectedModal.querySelector("iframe"); 260 let iFrameSrc; 261 switch (modal) { 262 case "terms-of-use": 263 iFrameSrc = "/help-center/termsContent" 264 break; 265 case "privacy-policy": 266 iFrameSrc = "/help-center/privacyContent" 267 break; 268 case "fcra": 269 iFrameSrc = "/help-center/fcraContent" 270 break; 271 default: 272 console.log("Invalid case"); 273 } 274 iframeElement.src = iFrameSrc; 275 selectedModal.classList.add("is-visible"); 276 selectedModal.style.opacity = null 277 body.classList.add("is-fixed"); 278 279 const closeButtons = document.getElementsByClassName("js-close"); 280
281 Array.from(closeButtons).forEach(function (element) { 282 element.addEventListener("click", (event) => { 283 event.preventDefault(); 284 contentBody.scrollTop = 0; 285 selectedModal.classList.remove("is-visible"); 286 body.classList.remove("is-fixed"); 287 }); 288 }); 289}; 290 291// format phoneNumber 292export const formatPhoneNumber = (inputValue) => { 293 // logic if you want to use it onChange 294 if (!inputValue) { 295 return inputValue; 296 } 297 298 const phoneNumber = inputValue.replace(/[^\d]/g, ""); 299 if (phoneNumber.length < 4) { 300 return phoneNumber; 301 } else if (phoneNumber.length < 7) { 302 return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(3)}`; 303 } else { 304 // logic required if not using onChange 305 return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice( 306 3, 307 6 308 )}-${phoneNumber.slice(6, 10)}`; 309 } 310}; 311 312export const verifyEmail = (email) => { 313 const re = 314 /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 315 316 return re.test(String(email).toLowerCase()); 317}; 318 319export const verifyPhone = (phoneNumber) => { 320 // make sure to import the library into the metaPartial 321 return libphonenumber.parsePhoneNumberFromString(phoneNumber, "US").isValid() 322} 323 324export const setupCreateAccount = () => { 325 // grab elements 326 const nameInput = document.getElementById("name"); 327 const nameInputError = document.getElementById("name-error"); 328 const emailInput = document.getElementById("email"); 329 const emailInputError = document.getElementById("email-error"); 330 331 // apply listeners 332 nameInput.addEventListener("focus", () => { 333 nameInput.classList.remove("has-error"); 334 nameInputError.innerText = ""; 335 }); 336 337 emailInput.addEventListener("focus", () => { 338 emailInput.classList.remove("has-error"); 339 emailInputError.innerText = ""; 340 }); 341 342 nameInput.addEventListener("input", () => { 343 nameInput.classList.remove("has-error"); 344 nameInputError.innerText = ""; 345 }); 346 347 emailInput.addEventListener("input", () => { 348 emailInput.classList.remove("has-error"); 349 emailInputError.innerText = ""; 350 }); 351}; 352 353export const setupPassword = () => { 354 // input elements 355 const passwordInputOne = document.getElementById("password"); 356 const passwordInputTwo = document.getElementById("verify"); 357 const phoneNumberOne = document.getElementById("phoneOne"); 358 const phoneNumberTwo = document.getElementById("phoneTwo"); 359 const phoneNumberThree = document.getElementById("phoneThree"); 360 361 // error elements 362 const passwordOneError = document.getElementById("password-error"); 363 const passwordTwoError = document.getElementById("verify-error"); 364 const phoneNumberError = document.getElementById("phone-error"); 365 366 // tooltip 367 const toolTipOne = document.querySelector("#tooltip-1"); 368 const toolTipTwo = document.querySelector("#tooltip-2"); 369 370 // displaying and hiding tooltip methods for first and second password input 371 const displayToolTip1 = () => { 372 toolTipOne.style = "display:block"; 373 }; 374 const hideToolTip1 = () => { 375 toolTipOne.style = "display:none"; 376 }; 377 378 const displayToolTip2 = () => { 379 toolTipTwo.style = "display:block"; 380 }; 381 382 const hideToolTip2 = () => { 383 toolTipTwo.style = "display:none"; 384 }; 385 386 // clear error event listeners, and call to method to display tool tip. 387 passwordInputOne.addEventListener("focus", () => { 388 passwordInputOne.classList.remove("has-error"); 389 passwordInputTwo.classList.remove("has-error"); 390 passwordOneError.innerText = ""; 391 passwordTwoError.innerText = ""; 392 displayToolTip1(); 393 }); 394 395 // event listener on first input, listening for a when a user clicks out of the first input box. 396 passwordInputOne.addEventListener("blur", () => { 397 hideToolTip1(); 398 }); 399 400 // clear error event listeners, and call to method to display tool tip. 401 passwordInputTwo.addEventListener("focus", () => { 402 passwordInputOne.classList.remove("has-error"); 403 passwordInputTwo.classList.remove("has-error"); 404 passwordOneError.innerText = ""; 405 passwordTwoError.innerText = ""; 406 displayToolTip2(); 407 }); 408 409 // event listener on second input, listening for a when a user clicks out of the second input box. 410 passwordInputTwo.addEventListener("blur", () => { 411 hideToolTip2(); 412 }); 413 414 phoneNumberOne.addEventListener("focus", () => { 415 phoneNumberOne.classList.remove("has-error"); 416 phoneNumberTwo.classList.remove("has-error"); 417 phoneNumberThree.classList.remove("has-error"); 418 phoneNumberError.innerText = ""; 419 }); 420 421 phoneNumberTwo.addEventListener("focus", () => { 422 phoneNumberOne.classList.remove("has-error"); 423 phoneNumberTwo.classList.remove("has-error"); 424 phoneNumberThree.classList.remove("has-error"); 425 phoneNumberError.innerText = ""; 426 }); 427 428 phoneNumberThree.addEventListener("focus", () => { 429 phoneNumberOne.classList.remove("has-error"); 430 phoneNumberTwo.classList.remove("has-error"); 431 phoneNumberThree.classList.remove("has-error"); 432 phoneNumberError.innerText = ""; 433 }); 434 435 /** 436 * Input phone restrict to 3 digits 437 **/ 438 439 const $phoneInput = $(".js-phone-input"); 440 441 if ($phoneInput.length > 0) { 442 $phoneInput.on("input", function (event) { 443 const thisHtmlEl = this; 444 445 if (thisHtmlEl.value.length > thisHtmlEl.maxLength) { 446 thisHtmlEl.value = thisHtmlEl.value.slice( 447 0, 448 thisHtmlEl.maxLength 449 ); 450 $(".js-phone-input").nextAll(".js-phone-input").focus(); 451 } 452 }); 453 } 454 455 function moveOnMax(field, nextField) { 456 if (field.value.length >= field.maxLength) { 457 nextField.focus(); 458 } 459 } 460
461 phoneNumberOne.addEventListener("keyup", () => { 462 moveOnMax(phoneNumberOne, phoneNumberTwo); 463 }); 464 465 phoneNumberTwo.addEventListener("keyup", () => { 466 moveOnMax(phoneNumberTwo, phoneNumberThree); 467 }); 468}; 469 470export const setupCaptcha = () => { 471 let recaptchaScript = document.createElement("script"); 472 recaptchaScript.setAttribute( 473 "src", 474 "https://www.google.com/recaptcha/api.js" 475 ); 476 document.head.appendChild(recaptchaScript); 477}; 478 479export const titleCase = (string) => { 480 const wordArray = string.toLowerCase().split(" ") 481 for (let i = 0; i < wordArray.length; i++) { 482 let word = wordArray[i].charAt(0).toUpperCase() + wordArray[i].slice(1) 483 484 wordArray[i] = word 485 } 486 return wordArray.join(" ") 487} 488 489export const decodeHtml = (string) => { 490 const element = document.createElement("textarea"); 491 element.innerHTML = string; 492 493 return element.value; 494}; 495 496export const setupCollapsableNav = () => { 497 const $window = $(window); 498 const $body = $("body"); 499 500 /** 501 * Handle mobile menu. 502 * 503 * @param {Object} 504 * @return {Void} 505 */ 506 507 $(".js-nav-trigger").on("click", function (event) { 508 $(this).toggleClass("is-active"); 509 $body.toggleClass("has-open-menu"); 510 $(".js-mobile-menu").toggleClass("is-active"); 511 512 event.preventDefault(); 513 }); 514}; 515 516export const phoneNumberInput = () => { 517 const $inputPhone = $(".js-input-phone"); 518 519 if ($inputPhone.length > 0) { 520 $inputPhone.on("input", function (event) { 521 const thisHtmlEl = this; 522 const hasMaxLength = thisHtmlEl.value.length > thisHtmlEl.maxLength; 523 524 if (hasMaxLength) 525 thisHtmlEl.value = thisHtmlEl.value.slice( 526 0, 527 thisHtmlEl.maxLength 528 ); 529 }); 530 } 531}; 532 533 534export const buttonLoadingToggle = (element, text) => { 535 if(element.innerHTML.trim() === `<span>${text}</span>`) { 536 element.innerHTML = "<span><img src='/assets/globalAssets/images/loading-spinner.gif' alt='processing-animation' width='38'></span>" 537 } else { 538 element.innerHTML = `<span>${text}</span>` 539 } 540}
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.