1/** 2 * Nexus Address Session Prefill. 3 * 4 * The site-wide "See if we service you" address lookup (nexus-zip-enrichment.js) 5 * stores whatever address the visitor picked into sessionStorage under 6 * `nexus_selected_address` before redirecting to the matched location's quote 7 * page. This script reads that value on every subsequent page in the same tab 8 * and prefills any Fluent Forms address-shaped fields it finds â so a visitor 9 * who already told us their address on the homepage never has to retype it on 10 * a lead form later in the same visit. 11 * 12 * Never overwrites a field the visitor (or another script) already filled in, 13 * and never touches line2/suite (geocoders don't know the unit). 14 * 15 * Field matching reuses the same name-based classification heuristic as 16 * assets/js/address-autocomplete/autocomplete.js, kept as an independent 17 * (small, load-once) copy here rather than a shared dependency. 18 */ 19( function () { 20 'use strict'; 21 22 // QA-04: a postal code is written into the form the way the post office 23 // writes it ("12345", "12345-6789", "M5H 2N2"), because the /quote/ page 24 // shows the field back as "Quoting for â¦". Mirrors 25 // TMB_Client_Postal_Code::display(); anything else is left as given. 26 function displayPostal( v ) { 27 var raw = String( v == null ? '' : v ).trim(); 28 var c = raw.toUpperCase().replace( /[\s-]+/g, '' ); 29 var m = /^(\d{5})(\d{4})?$/.exec( c ); 30 if ( m ) { return m[ 2 ] ? m[ 1 ] + '-' + m[ 2 ] : m[ 1 ]; } 31 m = /^([ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ])(\d[ABCEGHJKLMNPRSTVWXYZ]\d)$/.exec( c ); 32 return m ? m[ 1 ] + ' ' + m[ 2 ] : raw; 33 } 34 35 if ( window.__tmbAddressSessionPrefillLoaded ) { return; } 36 window.__tmbAddressSessionPrefillLoaded = true; 37 38 function readStoredAddress() { 39 try { 40 var raw = window.sessionStorage.getItem( 'nexus_selected_address' ); 41 if ( ! raw ) { return null; } 42 var data = JSON.parse( raw ); 43 return ( data && typeof data === 'object' ) ? data : null; 44 } catch ( e ) { 45 return null; 46 } 47 } 48 49 function leaf( name ) { 50 if ( ! name ) { return ''; } 51 var m = name.match( /\[([^\]]+)\]\s*$/ ); 52 var l = m ? m[ 1 ] : name; 53 return l.toLowerCase().replace( /[^a-z0-9]+/g, '_' ).replace( /^_+|_+$/g, '' ); 54 } 55 56 function classify( el ) { 57 if ( ! el || ( el.tagName !== 'INPUT' && el.tagName !== 'SELECT' && el.tagName !== 'TEXTAREA' ) ) { return null; } 58 var type = ( el.getAttribute( 'type' ) || 'text' ).toLowerCase(); 59 if ( [ 'hidden', 'checkbox', 'radio', 'file', 'submit', 'button', 'password' ].indexOf( type ) !== -1 ) { return null; } 60 var key = ( el.getAttribute( 'data-key_name' ) || '' ).toLowerCase(); 61 var k = key || leaf( el.getAttribute( 'name' ) || '' ); 62 if ( ! k ) { return null; } 63 if ( /email/.test( k ) ) { return null; } 64 if ( /address_line_2|address_2|^line_?2$|\bsuite\b|^ste$|_ste$|\bapt\b|apartment|\bunit\b/.test( k ) ) { return null; } // never prefill line2 65 if ( /^city$|_city$|\btown\b|municipal/.test( k ) ) { return 'city'; } 66 if ( /^state$|_state$|province|region/.test( k ) ) { return 'state'; } 67 if ( /zip|postal|postcode/.test( k ) ) { return 'zip'; } 68 if ( /address_line_1|address_1|street_address|^street$|_street$|^address$|_address$|\baddress\b/.test( k ) ) { return 'line1'; } 69 return null; 70 } 71 72 function matchOption( select, val ) { 73 var want = String( val || '' ).toLowerCase(); 74 for ( var i = 0; i < select.options.length; i++ ) { 75 var o = select.options[ i ]; 76 if ( ( o.value || '' ).toLowerCase() === want || ( o.text || '' ).trim().toLowerCase() === want ) { return o; } 77 } 78 return null; 79 } 80 81 function setVal( el, val ) { 82 if ( el == null || val == null || val === '' ) { return; } 83 // Never overwrite a value the visitor (or another script) already put there. 84 if ( ( el.value || '' ).trim() !== '' ) { return; } 85 if ( el.tagName === 'SELECT' ) { 86 var opt = matchOption( el, val ); 87 if ( ! opt ) { return; } 88 el.value = opt.value; 89 } else {
90 el.value = String( val ); 91 } 92 el.dispatchEvent( new Event( 'input', { bubbles: true } ) ); 93 el.dispatchEvent( new Event( 'change', { bubbles: true } ) ); 94 } 95 96 function prefillForm( form, addr ) { 97 var nodes = form.querySelectorAll( 'input, select, textarea' ); 98 for ( var i = 0; i < nodes.length; i++ ) { 99 var role = classify( nodes[ i ] ); 100 if ( role === 'line1' ) { setVal( nodes[ i ], addr.line1 || addr.label ); } 101 else if ( role === 'city' ) { setVal( nodes[ i ], addr.city ); } 102 else if ( role === 'state' ) { 103 if ( nodes[ i ].tagName === 'SELECT' ) { 104 setVal( nodes[ i ], addr.state_code && matchOption( nodes[ i ], addr.state_code ) ? addr.state_code : addr.state ); 105 } else { 106 setVal( nodes[ i ], addr.state_code || addr.state ); 107 } 108 } 109 else if ( role === 'zip' ) { setVal( nodes[ i ], displayPostal( addr.zip ) ); } 110 } 111 } 112 113 function run() { 114 var addr = readStoredAddress(); 115 if ( ! addr || ( ! addr.zip && ! addr.line1 ) ) { return; } 116 var forms = document.querySelectorAll( 'form.frm-fluent-form, form.fluentform, form[data-form_id]' ); 117 for ( var i = 0; i < forms.length; i++ ) { prefillForm( forms[ i ], addr ); } 118 } 119 120 if ( document.readyState === 'loading' ) { 121 document.addEventListener( 'DOMContentLoaded', run ); 122 } else { 123 run(); 124 } 125 126 // Fluent Forms can render/re-render after load (AJAX step forms, conversational forms). 127 if ( window.MutationObserver ) { 128 var timer = null; 129 new MutationObserver( function () { 130 if ( timer ) { clearTimeout( timer ); } 131 timer = setTimeout( run, 250 ); 132 } ).observe( document.body, { childList: true, subtree: true } ); 133 } 134} )();
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.