1function generateRequestId() { 2 return Math.random().toString(36).substr(2, 9) + Math.random().toString(36).substr(2, 9); 3} 4 5jQuery(document).ready(function($) { 6 var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; 7 8 // OEC-1086: the one client-side ZIP pattern. 5 digits only -- every MDA zip input is 9 // maxlength=5, so ZIP+4 (which the server accepts) can never be entered here. 10 var ZIP_PATTERN = /^[0-9]{5}$/; 11 function isValidZip(value) { 12 return ZIP_PATTERN.test(value || ''); 13 } 14 15 $('body').on('click', '.mda-submit', function(e) { 16 var form = $(this).closest('form'); 17 // OEC-1086: prevent rapid repeat submissions while a request is already in flight. 18 if (form.attr('data-mda-submitting') === 'true') { 19 return; 20 } 21 form.attr('btnClicked', true); 22 form.submit(); 23 }); 24 25 $('body').on('focus', '[name=zipcode]', function(e) { 26 quote_hide_tooltips(); 27 }); 28 29 $('body').on('keydown', '[name=zipcode]', function(e) { 30 quote_hide_tooltips(); 31 }); 32 33 $('body').on('blur', 'form', function(e) { 34 quote_hide_tooltips(); 35 }); 36 37 $(window).on('resize', function(e) { 38 quote_hide_tooltips(); 39 }); 40 41 function quote_hide_tooltips() { 42 $('.field-holder').each(function(i,z) { 43 tlite.hide(z); 44 }) 45 } 46 47 $('body').on('submit', 'form', function(e) { 48 var form = $(this); 49 50 // OEC-1086: authoritative duplicate-submission lock. Every submission path funnels 51 // through this event -- button click, Enter key in the zip field (the MDA has no 52 // submit button, so the zip input implicitly submits), and programmatic .submit(). 53 // Checking only in the click handler let those other paths re-enter submitForm() and 54 // fire extra zip submit events / window.open calls. 55 if (form.attr('data-mda-submitting') === 'true') { 56 e.preventDefault(); 57 return false; 58 } 59 60 var zip = $(this).find("[name=zipcode]"); 61 var typeSelect = $(this).find("select[name=type]"); 62 var fieldType = form.find('[name="type"]').prop('tagName'); 63 var isMultiVertical = fieldType === 'SELECT' ? true : false; 64 var popper = null; 65 var request = form.find("[name=requestId]"); 66 var is_quote_life_rates_page = form.find('[name="is_quote_life_rates_page"]').val() === '1'; 67 68 /* 69 Preventing the form from being submitted for life insurance modal only when enabled. 70 See insurance-quotes.php, phoenix_config method 71 */ 72 if (window.phoenix.config.lifeModalFormEnabled === 'true' && // life modal enabled in quote settings 73 form[0].id !== 'life' && // making sure its not when the modal is submitted 74 getInsuranceType(form) === 'Life' && // Only when you click on the life drop down or type === 'Life' 75 !isMultiVertical && // Only opening modal if its not a multi-vertical 76 zip.is(':hidden') === true 77 ) { 78 openLifeModal(false) 79 e.preventDefault(); 80 return false; 81 } 82 83 /** 84 * If Life rates page is enabled validate the age and income fields 85 */ 86 if ( 87 window.phoenix.config.quoteLifeRatesPageEnabled === "true" && 88 is_quote_life_rates_page 89 ) { 90 var age = form.find('[name="age"]'); 91 var income = form.find('[name="income"]'); 92 var ageHasValue = age.is(":checked"); 93 var incomeHasValue = income.is(":checked"); 94 95 if (!ageHasValue) { 96 e.preventDefault(); 97 setTimeout(function () { 98 var ageTooltipTarget = age.first().closest(".field-holder")[0]; 99 if (ageTooltipTarget) { 100 tlite.show(ageTooltipTarget, { grav: "s" }); 101 } 102 }, 100); 103 return false; 104 } 105 if (!incomeHasValue) { 106 e.preventDefault(); 107 setTimeout(function () { 108 var incomeTooltipTarget = income.first().closest(".field-holder")[0]; 109 if (incomeTooltipTarget) { 110 tlite.show(incomeTooltipTarget, { grav: "s" }); 111 } 112 }, 100); 113 return false; 114 } 115 } 116 117 if (getVisitorId()) { 118 request.val(getVisitorId() + "|" + generateSubmitId()); 119 } 120 121 if (zip.length > 0) { 122 // OEC-1086: Only skip validation for a genuinely hidden zip *input* (single-field 123 // flows where the zip is an <input type="hidden">). A real, interactive zip field 124 // that merely has zero layout because an ancestor is display:none (e.g. the sticky 125 // header before scroll or on mobile) must STILL be validated. Using jQuery ":hidden" 126 // here previously let the sticky header submit empty/invalid ZIPs and fire the zip 127 // submit event without any validation. 128 var zipIsHiddenInput = (zip.attr("type") || "").toLowerCase() === "hidden"; 129 if (zipIsHiddenInput) { 130 submitForm(form, e); 131 return true; 132 } 133 134 if (typeSelect.length > 0 && !typeSelect.val()) { 135 e.preventDefault(); 136 setTimeout(function() { 137 tlite.show(typeSelect.closest('.field-holder')[0], { grav: 's' }) 138 }, 100); 139 return false;
140 } 141 if(!zip.val() || zip.val().length < 1) { 142 e.preventDefault(); 143 setTimeout(function() { 144 tlite.show(zip.closest('.field-holder')[0], { grav: 's' }) 145 }, 100); 146 return false; 147 } else if (!isValidZip(zip.val())) { 148 e.preventDefault(); 149 setTimeout(function() { 150 zip_error = zip.closest('.field-holder'); 151 zip_error.attr("title","Please enter a valid Zip Code!"); 152 tlite.show(zip_error[0], { grav: 's' }); 153 //reset filed holder value 154 zip_error.attr("title","ZIP Code must be filled out!"); 155 }, 100); 156 return false; 157 } else { 158 if (!iOS || form.attr('btnClicked')) { 159 if (window.phoenix.config.lifeModalFormEnabled === 'true' && // life modal enabled in quote settings 160 form[0].id !== 'life' && // making sure its not when the modal is submitted 161 getInsuranceType(form) === 'Life' && // Only when you click on the life drop down or type === 'Life' 162 zip.is(':hidden') === true // Only when zipcode is hidden in the MDA 163 ) { 164 openLifeModal(true, zip.val()); 165 e.preventDefault(); 166 return false; 167 } else { 168 submitForm(form, e); 169 } 170 } else { 171 e.preventDefault(); 172 var button = form.find('.mda-submit').closest('.field-holder')[0]; 173 zip.blur(); 174 tlite.show(button, { grav: 's' }) 175 form.find('.mda-submit').addClass('needs-submit'); 176 setTimeout(function() { 177 form.find('.mda-submit').removeClass('needs-submit'); 178 }, 1000); 179 return false; 180 } 181 } 182 } else if (zip.is(":hidden")) { 183 submitForm(form, e); 184 } 185 }); 186 187 function openLifeModal(prepop = false, zip = '') { 188 //Hidding MDA background to prevent double black backgrounds 189 jQuery('.mda-background').fadeOut(200); 190 jQuery('#lifeModal').modal(); 191 if (prepop) { /* Prepopping zip field from previous input */ 192 var lifeForm = $('form.life'); 193 var lifeFormZip = $(lifeForm).find("[name=zipcode]"); 194 lifeFormZip.val(zip); 195 } 196 } 197 198 // OEC-1086: lock a form once a valid submission is underway so duplicate rapid 199 // submissions from the same interaction cannot fire multiple zip submit events. 200 function lockMdaSubmission(form) { 201 form.attr('data-mda-submitting', 'true'); 202 var btn = form.find('.mda-submit'); 203 btn.prop('disabled', true).addClass('mda-submit-processing'); 204 // Safety valve: re-enable so the user isn't permanently locked out when the 205 // request stays on-page (popup flow) or fails to navigate away. 206 setTimeout(function() { 207 form.removeAttr('data-mda-submitting'); 208 btn.prop('disabled', false).removeClass('mda-submit-processing'); 209 }, 3000); 210 } 211 212 function submitForm(form, e) { 213 if (form.has('[name="zipcode"],[name="zip"]').length) { 214 var zipField = form.find('[name="zipcode"],[name="zip"]').first(); 215 216 // OEC-1086: last-line defense â never open a tab or allow native POST without a valid ZIP. 217 var zipVal = (zipField.val() || "").trim(); 218 if (!isValidZip(zipVal)) { 219 e.preventDefault(); 220 return false; 221 } 222 223 lockMdaSubmission(form); 224 var type = getInsuranceType(form); 225 var is_quote_life_rates_page = form.find('[name="is_quote_life_rates_page"]').val() === '1'; 226 227 var _showQuoteLifeRatesPage = 228 type === "Life" && 229 window.phoenix.config.quoteLifeRatesPageEnabled === "true" && 230 !is_quote_life_rates_page; 231 232 var _showPopup = showPopup(form) && !_showQuoteLifeRatesPage; 233 var _showNative = showNative(form) || _showQuoteLifeRatesPage; 234 235 if(_showQuoteLifeRatesPage){ 236 form.attr('action', window.phoenix.config.quoteLifeRatesPageUrl || "/quote-life-rates"); 237 } 238 239 if (_showPopup) { 240 /* Trigger GA tracking if only a newtab */ 241 if (!_showNative) { 242 window.dataLayer = window.dataLayer || []; 243 window.dataLayer.push({ 244 event: 'zip_submit_event' 245 }); 246 } 247 248 var zip = form.find('[name="zipcode"],[name="zip"]').val(); 249 // var type = getInsuranceType(form); 250 var referrer = form.find('[name="referrer"]').val(); 251 var requestId = form.find('[name="requestId"]').val(); 252 var tag = form.find('[name="tag"]').val(); 253 var insured = form.find('[name="insured"]:checked').val(); 254 var maPlacement = form.find('[name="ma_placement"]').val(); 255 var qwPlacement = form.find('[name="qw_placement"]').val(); 256 var paidTraffic = form.find('[name="paid_traffic"]').val(); 257 var paidTrafficParams = ""; 258 var utm_source = form.find('[name="utm_source"]').val(); 259 var utm_medium = form.find('[name="utm_medium"]').val(); 260 var utm_campaign = form.find('[name="utm_campaign"]').val(); 261 var utm_term = form.find('[name="utm_term"]').val(); 262 var utm_content = form.find('[name="utm_content"]').val(); 263 var utm_adgroup = form.find('[name="utm_adgroup"]').val(); 264 var test_mode = form.find('[name="test_mode"]').val(); 265 var gclid = form.find('[name="gclid"]').val(); 266 var maToken = form.find('[name="token"]').val(); 267 var publisherId = form.find('[name="publisher_id"]').val(); 268 var mvaParams = getMVAParams(form); 269 var experimentId = form.find('[name="experimentId"]').val(); 270 var category = window.phoenix.config.pageCategory; 271 var userAgent = form.find('[name="useragent"]').val(); 272 273 // adding breadcrumb text and replacing arrow symbol with comma 274 var breadCrumbText; 275 if (document.getElementsByClassName("aioseo-breadcrumbs").length === 0) { 276 breadCrumbText = undefined; 277 } else { 278 breadCrumbText = document.getElementsByClassName("aioseo-breadcrumbs")[0].innerText.replaceAll("(", "").replaceAll(")", ""); 279 } 280 281 // For custom life insurance modal form 282 var coverage_amount = form.find('[name="coverage_amount"]:checked').val(); 283 var smoker = form.find('[name="smoker"]:checked').val(); 284 var health = form.find('[name="health"]:checked').val(); 285 var age = form.find('[name="age"]:checked').val(); 286 var income = form.find('[name="income"]:checked').val(); 287 288 paidTrafficParams = "&paid_traffic=" + paidTraffic + 289 "&utm_source=" + utm_source + "&utm_medium=" + utm_medium + 290 "&utm_campaign=" + utm_campaign + "&utm_term=" + utm_term + 291 "&utm_content=" + utm_content + "&utm_adgroup=" + 292 utm_adgroup + "&publisher_id=" + publisherId 293 + (test_mode ? "&test_mode=" + test_mode : ""); 294 295 window.open(encodeURI( 296 '/apps?requestId=' + requestId + '&zipcode=' + 297 zip + '&type=' + type + "&referrer=" + referrer + 298 (isKrakenPost() ? "&content_type=kraken" : "") + 299 "&tag=" + tag + (insured ? "&insured=" + insured : "") + 300 paidTrafficParams + 301 (maPlacement ? "&ma_placement=" + maPlacement : "") + 302 (qwPlacement ? "&qw_placement=" + qwPlacement : "") + 303 "&gclid=" + gclid + 304 "&token=" + maToken + '&' + mvaParams +
305 "&coverage_amount=" + coverage_amount + 306 "&smoker=" + smoker + 307 "&health=" + health + 308 "&age=" + age + 309 "&income=" + income + 310 "&category=" + category + 311 "&breadcrumb=" + breadCrumbText + 312 "&experimentId=" + experimentId + 313 "&useragent=" + userAgent 314 )); 315 } 316 317 if (!_showNative) { 318 e.preventDefault(); 319 return false; 320 } 321 } 322 } 323 324 function getInsuranceType(form) { 325 var type = form.find('[name="type"]').val(), alt_type = form.find('[name="alt_type"]').val(); 326 if ( type == "Other" && alt_type) 327 return alt_type; 328 else 329 return type; 330 } 331 332 function standardizeType(rawType) { 333 switch(rawType) { 334 case "Home": return "home"; 335 case "Renter": return "renter"; 336 case "Long Term Care": return "long term care"; 337 case "Health": return "health"; 338 case "Dental": return "dental"; 339 case "Travel": return "travel"; 340 case "Life": return "life"; 341 case "Business": return "business"; 342 case "Motorcycle": return "motorcycle"; 343 case "Electronics": return "electronics"; 344 case "Pet": return "pet"; 345 case "Medicare": return "medicare"; 346 case "Burial": return "burial"; 347 case "Group Health": return "group health"; 348 case "Commercial": return "commercial"; 349 case "Child Life": return "child life"; 350 case "Rental": return "rental"; 351 case "Condo": return "condo"; 352 case "RV": return "rv"; 353 case "Boat": return "boat"; 354 case "Disability": return "disability"; 355 case "Personal Injury": return "personal injury"; 356 case "Divorce": return "divorce"; 357 case "Auto Accident Injury": return "auto accident injury"; 358 case "DUI": return "dui"; 359 case "Real Estate": return "real estate"; 360 case "Criminal Defense": return "criminal defense"; 361 case "Prenuptial": return "prenuptial"; 362 case "Bankruptcy": return "bankruptcy"; 363 case "Child Support": return "child support"; 364 case "Immigration": return "immigration"; 365 case "Family Law": return "family law"; 366 case "Employment": return "employment"; 367 case "Copyright": return "copyright"; 368 case "Traffic": return "traffic"; 369 case "Wills": return "wills"; 370 case "Civil Law": return "civil law"; 371 case "Elder Law": return "elder law"; 372 case "Workers Comp": return "workers comp"; 373 case "Social Security Disability": return "social security disability"; 374 case "Auto Loan": return "auto loan"; 375 case "Business Loan": return "business loan"; 376 case "Mortgage Loan": return "mortgage loan"; 377 case "Student Loan": return "student loan"; 378 case "Payday Loan": return "payday loan"; 379 case "Personal Loan": return "personal loan"; 380 case "Debt": return "debt"; 381 } 382 return "auto"; 383 } 384 385 function showPopup(form) { 386 var type = getInsuranceType(form); 387 type = standardizeType(type); 388 return QUOTE_POPUP_TYPES[type]; 389 } 390 391 function showNative(form) { 392 var type = getInsuranceType(form); 393 type = standardizeType(type); 394 return QUOTE_NATIVE_TYPES[type]; 395 } 396 var paidParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_adgroup', 'token', 'gclid', 'publisher_id']; 397 398 var legalMVAParams = [ 399 'email', 400 'first_name', 401 'last_name', 402 'phone', 403 'dob', 404 'have_attorney', 405 'comments', 406 'degree_of_interest', 407 'type_of_legal_problem', 408 'tcpa', 409 'is_incident_date', 410 'incident_date', 411 'incident_date_option_b', 412 'injured', 413 'mva_type', 414 'doctor_treatment', 415 'were_you_at_fault', 416 'police_report', 417 'completed', 418 'original_referrer', 419 'submission_id', 420 ]; 421 422 function getMVAParams(form) { 423 var params = []; 424 legalMVAParams.forEach(function(fieldName) { 425 var fieldValue = form.find('[name="' + fieldName + '"]').val(); 426 if(fieldValue && fieldValue != ''){ 427 params.push(fieldName + '=' + fieldValue); 428 } 429 }); 430 return params.join('&'); 431 } 432 433 function checkPaidUrlParams() { 434 for (i = 0; i < paidParams.length; i++) { 435 var param = paidParams[i]; 436 var cur = getUrlParam(param); 437 if(cur) { Cookies ? Cookies.set(param, cur, { expires: 1/24 }) : null } 438 } 439 } 440 441 function getVisitorId() { 442 if(!Cookies.get('visitorId')) Cookies.set('visitorId', generateRequestId(), { expires: 1/24 }); 443 return Cookies.get('visitorId'); 444 } 445 446 function loadPaidUrlParams() { 447 $('.mda-submit').each(function() { 448 var form = $(this).closest('form'); 449 for (i = 0; i < paidParams.length; i++) { 450 var param = paidParams[i]; 451 cookie = Cookies.get(param); 452 input = form.find('[name="' + param + '"]'); 453 if(cookie && !input.val()) { input.val(cookie); } 454 } 455 456 $("<input />").attr("type", "hidden") 457 .attr("name", "requestId") 458 .attr("value", Cookies.get('visitorId')) 459 .appendTo(form); 460 if( isKrakenPost() ) { 461 $("<input />").attr("type", "hidden") 462 .attr("name", "content_type") 463 .attr("value", 'kraken') 464 .appendTo(form); 465 } 466 467 }); 468 } 469 470 // Get zip value from cookies 471 function getSavedZipCode() { 472 var zipVal; 473 474 var contactCookie = Cookies.get('contact'); 475 if(contactCookie){ 476 contactCookie = JSON.parse(contactCookie); 477 zipVal = contactCookie["contact"]["zip"]; 478 } 479 return zipVal; 480 } 481 482 function preFillForms() { 483 $('.mda-submit').each(function() { 484 var form = $(this).closest('form'); 485 var zipInput = form.find('[name="zipcode"]'); 486
487 if(zipInput && !zipInput.val()){ 488 var zipVal = getSavedZipCode(); 489 zipInput.val(zipVal); 490 } 491 492 }); 493 } 494 495 function getUrlVars() { 496 var vars = {}; 497 var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { 498 vars[key] = value; 499 }); 500 return vars; 501 } 502 503 function getUrlParam(parameter, defaultvalue) { 504 var urlparameter = defaultvalue; 505 if(window.location.href.indexOf(parameter) > -1){ 506 urlparameter = getUrlVars()[parameter]; 507 } 508 return urlparameter; 509 } 510 511 512 function generateSubmitId() { 513 return Math.random().toString(36).substr(2, 3); 514 } 515 516 // Handles setting hidden fields for paid traffic data from a cookie or 517 // saving the data in a cookie 518 function loadPaidTrafficParams() { 519 $('.mda-submit').each(function() { 520 var categories = ["internal", "tracking", "contact", "current_insurance", "requested_coverage", "vehicles", "incidents", "individuals", "claims"]; 521 var arrayCategories = ["vehicles", "incidents", "individuals", "claims"]; 522 var form = $(this).closest('form'); 523 524 var inputs = form.find('input[type="hidden"]'); 525 526 var paramObject = {}; 527 528 // Converts hidden inputs into a js object 529 for (var i = 0; i < inputs.length; i++) { 530 // ex: contact[email] 531 // ex: vehicles[0][submodel] 532 var input = inputs[i]; 533 var field = input.name.replace(/\]/g, ""); 534 var fieldCat = field.split("[")[0]; 535 if(input.value && categories.includes(fieldCat)){ 536 if(arrayCategories.includes(fieldCat)){ 537 // ex: [ "vehicles", 0, "year" ] 538 var [category, index, param] = field.split("["); 539 if(!paramObject.hasOwnProperty(category)){ 540 paramObject[category] = []; 541 } 542 if(!paramObject[category][index]){ 543 paramObject[category][index] = {}; 544 } 545 paramObject[category][index][param] = input.value; 546 } else { 547 // ex: ["contact", "email"] 548 var [category, param] = field.split("["); 549 if(!paramObject.hasOwnProperty(fieldCat)){ 550 paramObject[fieldCat] = {}; 551 } 552 paramObject[fieldCat][param] = input.value; 553 } 554 } 555 } 556 557 for (var catIndex = 0; catIndex < categories.length; catIndex++) { 558 var cat = categories[catIndex]; 559 try{ 560 if(paramObject[cat]) { 561 // Set new cookie for params that are set 562 var newCookie = {}; 563 newCookie[cat] = paramObject[cat]; 564 var jsonCookie = JSON.stringify(newCookie); 565 Cookies.remove(cat); 566 Cookies.set(cat, jsonCookie, { expires: 1/24 }); 567 } else { 568 var cookie = Cookies.get(cat); 569 // if the cookie is set for the category and the fields for the 570 // category aren't filled then set them to data in the cookie 571 // NOTE: we are filling based on all of the params inside a 572 // category otherwise we may add incorrect data. 573 if(cookie){ 574 // Convert the params to url parameters & use that for input name 575 var urlParams = decodeURI($.param(JSON.parse(cookie))); 576 var cookieParams = urlParams.split("&"); 577 // console.log(cookieParams); 578 // add a hidden field for each parameter 579 for (var i = 0; i < cookieParams.length; i++) { 580 var nameVal = cookieParams[i].split("="); 581 if(nameVal.length == 2){ 582 var [name, value] = nameVal; 583 $("<input />").attr("type", "hidden") 584 .attr("name", name) 585 .attr("value", value) 586 .appendTo(form); 587 } 588 } 589 } 590 } 591 } catch (e) { 592 console.error(e); 593 } 594 } 595 }); 596 } 597 598 599 checkPaidUrlParams(); 600 getVisitorId(); 601 loadPaidUrlParams(); 602 loadPaidTrafficParams(); 603 preFillForms(); 604});
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.