chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { // NEW: Extract structure for AI mapping if (message.action === "extract_form_structure") { const inputs = document.querySelectorAll('input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled])'); const fields = Array.from(inputs).map(el => ({ id: el.id, name: el.name, placeholder: el.placeholder, label: findLabelFor(el) })).filter(x => x.id || x.name || x.label); sendResponse({ fields }); return true; } // NEW: Show cute boxes when smart map exists if (message.action === "show_cute_boxes") { injectCuteBoxes(message.smartMap); sendResponse({ ok: true }); } if (message.action === "fill_form") { // Look up both active client and smart map const hostname = window.location.hostname; chrome.storage.local.get(['activeClient', `smartMap_${hostname}`], (res) => { if (res.activeClient) { fillInputs(res.activeClient, res[`smartMap_${hostname}`]); } else { alert("Please select a client in the Carrier Bridge side panel first."); } }); } // Step-walk: fill visible fields, then report whether a next button exists if (message.action === "fill_form_walk") { const hostname = window.location.hostname; chrome.storage.local.get(['activeClient', `smartMap_${hostname}`], (res) => { if (!res.activeClient) { sendResponse({ filled: 0, hasNext: false, error: 'No active client' }); return; } const filled = fillInputs(res.activeClient, res[`smartMap_${hostname}`]); const nextBtn = findNextButton(message.customNextSelector || null); sendResponse({ filled: filled || 0, hasNext: !!nextBtn, nextText: nextBtn ? (nextBtn.textContent || nextBtn.value || '').trim() : '' }); }); return true; } // Click whatever "next/continue" button is visible if (message.action === "click_next_button") { const btn = findNextButton(message.customSelector || null); if (btn) { btn.click(); sendResponse({ clicked: true, btnText: (btn.textContent || btn.value || '').trim() }); } else { sendResponse({ clicked: false }); } return true; } if (message.action === "fill_acord_form") { const hostname = window.location.hostname; chrome.storage.local.get(['activeClient', `smartMap_${hostname}`], (res) => { if (res.activeClient) { fillInputs(res.activeClient, res[`smartMap_${hostname}`], { acordOnly: true }); } else { alert("Please select a client in the Carrier Bridge side panel first."); } }); } if (message.action === "preview_fill") { const hostname = window.location.hostname; chrome.storage.local.get(['activeClient', `smartMap_${hostname}`], (res) => { if (res.activeClient) { fillInputs(res.activeClient, res[`smartMap_${hostname}`], { previewOnly: true }); sendResponse({ ok: true }); } else { alert("Please select a client in the Carrier Bridge side panel first."); sendResponse({ ok: false }); } }); return true; } if (message.action === "paste_to_focused") { const activeEl = document.activeElement; if (!activeEl) { alert("No field focused. Click into a form field first!"); return; } const isInput = activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA'; const isEditable = activeEl.isContentEditable || activeEl.getAttribute('contenteditable') === 'true'; if (isInput || isEditable) { const value = message.value || ''; if (isInput) { activeEl.value = value; } else if (isEditable) { document.execCommand('insertText', false, value); } ['input', 'change', 'keyup', 'blur', 'focus'].forEach(ev => { activeEl.dispatchEvent(new Event(ev, { bubbles: true })); }); const originalBg = activeEl.style.backgroundColor; activeEl.style.backgroundColor = '#a7f3d0'; activeEl.style.transition = 'background-color 0.7s'; setTimeout(() => { activeEl.style.backgroundColor = originalBg; }, 700); console.log(`Super Paste: "${value}" → ${activeEl.name || activeEl.id || activeEl.tagName || 'focused element'}`); } else { alert("Please focus an input, textarea, or editable area first."); } } }); function injectCuteBoxes(smartMap) { if (!smartMap) return; // Remove old boxes if they exist document.querySelectorAll('.supa-cute-box').forEach(e => e.remove()); const inputs = document.querySelectorAll('input, select, textarea'); inputs.forEach(input => { let matchedKey = null; for (const [key, selector] of Object.entries(smartMap)) { if (selector && (input.id === selector || input.name === selector)) { matchedKey = key; break; } } if (matchedKey) { const badge = document.createElement('span'); badge.className = 'supa-cute-box'; badge.textContent = `✨ ${matchedKey}`; badge.style.cssText = 'background: #10b981; color: white; font-size: 10px; padding: 2px 6px; border-radius: 4px; margin-left: 6px; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.2); vertical-align: middle; display: inline-block; pointer-events: none; z-index: 9999;'; // Insert the cute badge right after the input if (input.parentNode) { input.parentNode.insertBefore(badge, input.nextSibling); } } }); } function clearPreviewBadges() { document.querySelectorAll('.supa-preview-box').forEach((e) => e.remove()); } function injectPreviewBadge(input, matchedKey, value) { const badge = document.createElement('span'); badge.className = 'supa-preview-box'; badge.textContent = `Preview: ${matchedKey}`; badge.title = String(value || ''); badge.style.cssText = 'background: #f59e0b; color: white; font-size: 10px; padding: 2px 6px; border-radius: 4px; margin-left: 6px; font-weight: bold; box-shadow: 0 1px 3px rgba(0,0,0,0.2); vertical-align: middle; display: inline-block; pointer-events: none; z-index: 9999;'; if (input.parentNode) { input.parentNode.insertBefore(badge, input.nextSibling); } } function getFieldContext(input) { const name = (input.name || '').toLowerCase().replace(/[^a-z0-9]/g, ''); const id = (input.id || '').toLowerCase().replace(/[^a-z0-9]/g, ''); const ph = (input.placeholder || '').toLowerCase().replace(/[^a-z0-9]/g, ''); const labelText = findLabelFor(input).toLowerCase().replace(/[^a-z0-9]/g, ''); const aria = (input.getAttribute('aria-label') || '').toLowerCase().replace(/[^a-z0-9]/g, ''); const analytics = (input.getAttribute('analytics-id') || input.getAttribute('data-analytics-id') || '').toLowerCase().replace(/[^a-z0-9]/g, ''); const classes = (input.className || '').toLowerCase().replace(/[^a-z0-9]/g, ''); const matInput = (input.getAttribute('matinput') !== null ? 'matinput' : ''); const combined = name + id + ph + labelText + aria + analytics + classes + matInput; return { name, id, ph, labelText, aria, analytics, classes, combined }; } function isRequiredField(input) { return input.required || input.getAttribute('aria-required') === 'true' || input.getAttribute('data-val-required') !== null || /\brequired\b/i.test(input.outerHTML || ''); } function isMiddleNameField(context) { return /middlename|middleinitial|middle|^mi$|namemi/.test(context.combined); } function isWestfieldStructuredField(context) { return /^(dataobjects|addresses)/.test(context.name || '') || /^(dataobjects|addresses)/.test(context.id || ''); } function isWestfieldGenericSafeField(context) { return /maritalstatus|occupation|suffixcode|driverstatuscode|nondriverreasoncode|relationshipcode|applicanttype|namednonownedpolicy|ppqchoice/.test(context.combined); } function isAcordField(context) { return /acord|applicant|insured|producer|agency|mailing|residence|premises|driver|vehicle|losspayee|contact|location|garage|operator|business|individual|entity|fein|taxid|license|birth|dob|address|city|state|zip|postal|phone|email|coinsured|coapplicant|dwelling|coveragea|coveragec|contents|bpp|vin|make|model/.test(context.combined); } function isCommonFillField(context) { return /first|last|middle|name|dob|birth|date|email|phone|cell|mobile|address|street|city|state|zip|postal|ssn|tax|fein|vin|make|model|year|driver|license|insured|applicant|residence|mailing|vehicle|producer|agency|coinsured|coapplicant|dwelling|coveragea|coveragec|contents|bpp|deductible/.test(context.combined); } function isSsnField(context) { return /socialsecuritynumber|ssn|taxid|fein|sin/.test(context.combined); } function isPhoneTypeField(context) { return /phonetypecode|primaryphonetype|secondaryphonetype/.test(context.combined); } function isPhoneExtensionField(context) { return /phoneextension|extension|[^a-z]ext[^a-z]?/.test(context.combined) && /phone/.test(context.combined); } function isPhoneSegmentField(context) { return /phonenumberareacode|phonenumberexchange|phonenumberlocal|areacode|exchange|telephone/.test(context.combined); } function isPhoneField(context) { return /phone|mobile|cell|telephone/.test(context.combined); } function isDateField(context) { return /dateofbirth|birthdate|dob|effectivedate|expirationdate|licensedate|monthincremented|dateyear|datedate/.test(context.combined); } function isLicenseField(context) { return /license/.test(context.combined); } function isAddressField(context) { return /address|street|city|state|zip|postal|county/.test(context.combined); } function shouldSkipGenericField(context, options = {}) { if (options.previewOnly) return false; if (!window.location.hostname.includes('westfield')) return false; if (!isWestfieldStructuredField(context)) return false; return !isWestfieldGenericSafeField(context); } function getMiddleNameValue(client) { return client.MiddleName || client.middleName || client.MI || client.mi || ''; } function getValue(obj, ...keys) { for (const key of keys) { const value = obj?.[key]; if (value !== undefined && value !== null && value !== '') return value; } return ''; } function digitsOnly(value) { return String(value || '').replace(/\D/g, ''); } function normalizeString(value) { return String(value || '').toLowerCase().replace(/[^a-z0-9]/g, ''); } function buildPersonName(firstName, lastName) { return `${String(firstName || '').trim()} ${String(lastName || '').trim()}`.trim(); } function cleanPersonFirstName(value) { return String(value || '') .replace(/^\s*(mr|mrs|ms|miss|dr)\.?\s+/i, '') .trim(); } function extractHonorific(value) { const match = String(value || '').trim().match(/^\s*(mr|mrs|ms|miss|dr)\.?\b/i); return match ? match[1].toLowerCase() : ''; } function getPersonFirstName(person) { return cleanPersonFirstName(getValue(person, 'FirstName', 'firstName')); } function getPersonLastName(person) { return getValue(person, 'LastName', 'lastName'); } function getDateParts(dateStr) { if (!dateStr) return { month: '', day: '', year: '' }; const isoMatch = String(dateStr).match(/^(\d{4})-(\d{2})-(\d{2})/); if (isoMatch) { return { month: isoMatch[2], day: isoMatch[3], year: isoMatch[1] }; } const d = new Date(dateStr); if (!isNaN(d.getTime())) { return { month: String(d.getMonth() + 1).padStart(2, '0'), day: String(d.getDate()).padStart(2, '0'), year: String(d.getFullYear()) }; } const parts = String(dateStr).match(/(\d{1,2})\/(\d{1,2})\/(\d{2,4})/); if (parts) { return { month: String(parts[1]).padStart(2, '0'), day: String(parts[2]).padStart(2, '0'), year: String(parts[3]).padStart(4, '20') }; } return { month: '', day: '', year: '' }; } function addYearsToDateParts(dateStr, yearsToAdd) { if (!dateStr && dateStr !== 0) return { month: '', day: '', year: '' }; const base = new Date(dateStr); if (isNaN(base.getTime())) return { month: '', day: '', year: '' }; const derived = new Date(base); derived.setFullYear(derived.getFullYear() + Number(yearsToAdd || 0)); return { month: String(derived.getMonth() + 1).padStart(2, '0'), day: String(derived.getDate()).padStart(2, '0'), year: String(derived.getFullYear()) }; } function getPhoneParts(phone) { const digits = digitsOnly(phone); if (digits.length < 10) return { area: '', exchange: '', local: '', extension: '' }; return { area: digits.slice(0, 3), exchange: digits.slice(3, 6), local: digits.slice(6, 10), extension: digits.slice(10, 15) }; } function getSsnParts(ssn) { const digits = digitsOnly(ssn); if (digits.length < 9) return { first3: '', middle2: '', last4: '' }; return { first3: digits.slice(0, 3), middle2: digits.slice(3, 5), last4: digits.slice(5, 9) }; } function getSecondPerson(client) { return getSecondDriver(client) || client?.coApplicant || client?.CoApplicant || null; } function getLicenseNumber(person) { return getValue(person, 'DriverLicenseNumber', 'driverLicenseNumber', 'LicenseNumber', 'licenseNumber', 'DLNumber', 'dlNumber', 'DlNumber'); } function getLicenseState(person, fallbackState = '') { return getValue(person, 'DriverLicenseState', 'driverLicenseState', 'LicenseState', 'licenseState', 'DLState', 'dlState', 'dlStateName', 'DlState', 'DlStateName') || fallbackState; } function getBirthDate(person) { return getValue(person, 'BirthDate', 'birthDate', 'birthday', 'Birthday', 'DateOfBirth', 'dateOfBirth', 'DOB', 'dob'); } function getGenderCode(person) { const raw = String(getValue(person, 'Gender', 'gender', 'Sex', 'sex') || '').trim().toUpperCase(); if (!raw) { const honorific = extractHonorific(getValue(person, 'FirstName', 'firstName') || getValue(person, 'Prefix', 'prefix')); if (honorific === 'mr') return 'M'; if (honorific === 'mrs' || honorific === 'ms' || honorific === 'miss') return 'F'; return ''; } if (raw === '0') return 'M'; if (raw === '1') return 'F'; if (raw.startsWith('F')) return 'F'; if (raw.startsWith('M')) return 'M'; return ''; } function getEmailValue(person) { return getValue(person, 'Email', 'email', 'PrimaryEmail', 'personalEMail', 'businessEMail', 'eMail'); } function getPrimaryPhoneValue(person) { return getValue(person, 'CellPhone', 'cellPhone', 'MobilePhone', 'mobilePhone', 'Phone', 'phone', 'PrimaryPhone', 'homePhone', 'officePhone'); } function getSecondaryPhoneValue(person) { const primary = digitsOnly(getPrimaryPhoneValue(person)); const candidates = [ getValue(person, 'homePhone'), getValue(person, 'officePhone'), getValue(person, 'Phone', 'phone'), getValue(person, 'CellPhone', 'cellPhone') ]; for (const candidate of candidates) { const digits = digitsOnly(candidate); if (digits && digits !== primary) return candidate; } return ''; } function getPhoneTypeCode(person, phoneValue) { const digits = digitsOnly(phoneValue); if (!digits) return ''; if (digits === digitsOnly(getValue(person, 'CellPhone', 'cellPhone', 'MobilePhone', 'mobilePhone'))) return 'MOBILE'; if (digits === digitsOnly(getValue(person, 'officePhone'))) return 'WORK'; return 'HOME'; } function getRelationshipCode(person) { const raw = String(getValue(person, 'type', 'relationship', 'Relationship', 'relation') || '').trim().toLowerCase(); if (!raw) return ''; if (raw.includes('spouse')) return 'SPSE'; if (raw.includes('child')) return 'RCHD'; if (raw.includes('relative')) return 'RREL'; if (raw.includes('other')) return 'OTHR'; return ''; } function getMaritalStatusCode(person) { const raw = String(getValue(person, 'MaritalStatus', 'maritalStatus', 'MaritalStatusCode', 'maritalStatusCode') || '').trim().toLowerCase(); if (!raw) return ''; if (raw.startsWith('mar') || raw.includes('married')) return 'MAR'; if (raw.startsWith('sin') || raw.includes('single')) return 'SNG'; if (raw.startsWith('div') || raw.includes('divorc')) return 'DIV'; if (raw.startsWith('sep') || raw.includes('separat')) return 'SEP'; if (raw.startsWith('wid') || raw.includes('widow')) return 'WID'; return ''; } function normalizeStateCode(value) { const raw = String(value || '').trim(); if (!raw) return ''; if (raw.length === 2) return raw.toUpperCase(); const states = { ALABAMA: 'AL', ALASKA: 'AK', ARIZONA: 'AZ', ARKANSAS: 'AR', CALIFORNIA: 'CA', COLORADO: 'CO', CONNECTICUT: 'CT', DELAWARE: 'DE', FLORIDA: 'FL', GEORGIA: 'GA', HAWAII: 'HI', IDAHO: 'ID', ILLINOIS: 'IL', INDIANA: 'IN', IOWA: 'IA', KANSAS: 'KS', KENTUCKY: 'KY', LOUISIANA: 'LA', MAINE: 'ME', MARYLAND: 'MD', MASSACHUSETTS: 'MA', MICHIGAN: 'MI', MINNESOTA: 'MN', MISSISSIPPI: 'MS', MISSOURI: 'MO', MONTANA: 'MT', NEBRASKA: 'NE', NEVADA: 'NV', 'NEW HAMPSHIRE': 'NH', 'NEW JERSEY': 'NJ', 'NEW MEXICO': 'NM', 'NEW YORK': 'NY', 'NORTH CAROLINA': 'NC', 'NORTH DAKOTA': 'ND', OHIO: 'OH', OKLAHOMA: 'OK', OREGON: 'OR', PENNSYLVANIA: 'PA', 'RHODE ISLAND': 'RI', 'SOUTH CAROLINA': 'SC', 'SOUTH DAKOTA': 'SD', TENNESSEE: 'TN', TEXAS: 'TX', UTAH: 'UT', VERMONT: 'VT', VIRGINIA: 'VA', WASHINGTON: 'WA', 'WEST VIRGINIA': 'WV', WISCONSIN: 'WI', WYOMING: 'WY', 'DISTRICT OF COLUMBIA': 'DC' }; return states[raw.toUpperCase()] || ''; } function getCurrentProperty(client) { return Array.isArray(client?.properties) && client.properties.length ? client.properties[0] : null; } function formatCoverageValue(value) { if (value === null || value === undefined) return ''; if (typeof value === 'number') { if (!Number.isFinite(value)) return ''; if (Number.isInteger(value)) return value.toLocaleString(); return value.toFixed(2); } return String(value || '').trim(); } function collectScalarEntries(value, path = '', entries = []) { if (value === null || value === undefined) return entries; if (Array.isArray(value)) { value.forEach((item, index) => collectScalarEntries(item, `${path}[${index}]`, entries)); return entries; } if (typeof value === 'object') { Object.entries(value).forEach(([key, child]) => { collectScalarEntries(child, path ? `${path}.${key}` : key, entries); }); return entries; } const str = formatCoverageValue(value); if (!str) return entries; entries.push({ path, normalizedPath: String(path || '').toLowerCase().replace(/[^a-z0-9]/g, ''), value: str }); return entries; } function findCoverageValue(source, keywordGroups) { const entries = collectScalarEntries(source); let best = null; entries.forEach((entry) => { if (!entry.normalizedPath || !entry.value) return; const matchedAll = keywordGroups.every((group) => group.some((pattern) => pattern.test(entry.normalizedPath))); if (!matchedAll) return; const score = keywordGroups.reduce((total, group) => { return total + group.filter((pattern) => pattern.test(entry.normalizedPath)).length; }, 0) - entry.normalizedPath.length / 1000; if (!best || score > best.score) { best = { value: entry.value, score }; } }); return best?.value || ''; } function extractVehicleDeductibles(client) { const vehicle = Array.isArray(client?.vehicles) && client.vehicles.length ? client.vehicles[0] : null; if (!vehicle) return { comp: '', collision: '' }; return { comp: findCoverageValue(vehicle, [[/comp/, /comprehensive/, /otherthancollision/, /otc/], [/ded/, /deduct/]]), collision: findCoverageValue(vehicle, [[/collision/, /coll/], [/ded/, /deduct/]]) }; } function extractPropertyLimits(client) { const property = getCurrentProperty(client); const policy = Array.isArray(client?.policies) && client.policies.length ? client.policies[0] : null; if (!property && !policy) { return { dwelling: '', personalProperty: '', businessPersonalProperty: '' }; } const source = { property, policy }; return { dwelling: findCoverageValue(source, [[/dwelling/, /coveragea/, /building/, /structure/], [/limit/, /amount/, /coverage/, /value/]]), personalProperty: findCoverageValue(source, [[/personalproperty/, /contents/, /coveragec/], [/limit/, /amount/, /coverage/, /value/]]), businessPersonalProperty: findCoverageValue(source, [[/businesspersonalproperty/, /businesscontents/, /coveragebpp/, /bpp/], [/limit/, /amount/, /coverage/, /value/]]) }; } function mergeObjects(base, extra) { const result = { ...(base || {}) }; Object.entries(extra || {}).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '' && (result[key] === undefined || result[key] === null || result[key] === '')) { result[key] = value; } }); return result; } function buildPersonProfile(...sources) { let profile = {}; sources.forEach((source) => { profile = mergeObjects(profile, source); }); return profile; } function findMatchingRecord(records, firstName, lastName) { const target = normalizeString(buildPersonName(cleanPersonFirstName(firstName), lastName)); if (!target || !Array.isArray(records)) return null; return records.find((record) => { const recordName = normalizeString(buildPersonName( getPersonFirstName(record), getPersonLastName(record) )); return recordName === target; }) || null; } function getAllPersonRecords(client) { return [ ...(Array.isArray(client?.insuredContacts) ? client.insuredContacts : []), ...(Array.isArray(client?.contacts) ? client.contacts : []), ...(Array.isArray(client?.drivers) ? client.drivers : []) ]; } function getPrimaryInsuredProfile(client) { const records = getAllPersonRecords(client); const topLevel = { FirstName: cleanPersonFirstName(getValue(client, 'FirstName', 'firstName')), LastName: getValue(client, 'LastName', 'lastName'), MiddleName: getValue(client, 'MiddleName', 'middleName'), BirthDate: getValue(client, 'BirthDate', 'dateOfBirth', 'DateOfBirth'), Email: getValue(client, 'Email', 'email', 'eMail'), Phone: getValue(client, 'Phone', 'phone'), CellPhone: getValue(client, 'CellPhone', 'cellPhone', 'MobilePhone'), SSN: getValue(client, 'SSN', 'SocialSecurityNumber', 'socialSecurityNumber'), Address1: getValue(client, 'Address1', 'address1', 'addressLine1'), Address2: getValue(client, 'Address2', 'address2', 'addressLine2'), City: getValue(client, 'City', 'city'), State: getValue(client, 'State', 'state'), Zip: getValue(client, 'Zip', 'zip', 'zipCode', 'ZipCode'), Gender: getValue(client, 'Gender', 'gender', 'Sex', 'sex'), MaritalStatus: getValue(client, 'MaritalStatus', 'maritalStatus'), DriverLicenseNumber: getValue(client, 'DriverLicenseNumber', 'driverLicenseNumber', 'LicenseNumber', 'licenseNumber'), DriverLicenseState: getValue(client, 'DriverLicenseState', 'driverLicenseState', 'LicenseState', 'licenseState') }; const matched = findMatchingRecord(records, topLevel.FirstName, topLevel.LastName); const fallbackOwner = records.find((record) => String(getValue(record, 'type')).toLowerCase() === 'owner') || null; return buildPersonProfile(topLevel, matched, fallbackOwner); } function getCoApplicantProfile(client, primaryProfile) { const records = getAllPersonRecords(client); const coFirst = cleanPersonFirstName(getValue(client, 'coInsured_FirstName')); const coLast = getValue(client, 'coInsured_LastName'); const primaryName = normalizeString(buildPersonName(getValue(primaryProfile, 'FirstName', 'firstName'), getValue(primaryProfile, 'LastName', 'lastName'))); const driverCandidates = Array.isArray(client?.drivers) ? client.drivers : []; const contactCandidates = [ ...(Array.isArray(client?.insuredContacts) ? client.insuredContacts : []), ...(Array.isArray(client?.contacts) ? client.contacts : []) ]; const matchedNonPrimaryContact = contactCandidates.find((record) => { const name = normalizeString(buildPersonName(getPersonFirstName(record), getPersonLastName(record))); const sameLast = normalizeString(getPersonLastName(record)) && normalizeString(getPersonLastName(record)) === normalizeString(getValue(primaryProfile, 'LastName', 'lastName')); const isPrimary = record?.primaryContact === true || record?.PrimaryContact === true; return name && name !== primaryName && !isPrimary && sameLast; }) || null; const matchedDriverByRelationship = driverCandidates.find((record) => { const relationship = String(getValue(record, 'type', 'relationship', 'Relationship', 'relation') || '').toLowerCase(); const name = normalizeString(buildPersonName(getValue(record, 'FirstName', 'firstName'), getValue(record, 'LastName', 'lastName'))); return name && name !== primaryName && relationship.includes('spouse'); }) || null; const matchedDriverByName = findMatchingRecord(driverCandidates, coFirst, coLast); const matched = findMatchingRecord(records, coFirst, coLast) || matchedDriverByName || matchedNonPrimaryContact || records.find((record) => String(getValue(record, 'type')).toLowerCase().includes('spouse')) || matchedDriverByRelationship || records.find((record) => { const name = normalizeString(buildPersonName(getPersonFirstName(record), getPersonLastName(record))); return !!name && name !== primaryName; }) || driverCandidates.find((record) => { const name = normalizeString(buildPersonName(getPersonFirstName(record), getPersonLastName(record))); return !!name && name !== primaryName; }) || null; const seed = { FirstName: coFirst, LastName: coLast, MiddleName: getValue(client, 'coInsured_MiddleName'), BirthDate: getValue(client, 'coInsured_DateOfBirth') }; const profile = buildPersonProfile(seed, matched); if (profile) { profile.FirstName = cleanPersonFirstName(getValue(profile, 'FirstName', 'firstName')); } return profile; } function getAdditionalDriverProfiles(client, primaryProfile, coApplicantProfile) { const records = [ ...(Array.isArray(client?.drivers) ? client.drivers : []), ...(Array.isArray(client?.insuredContacts) ? client.insuredContacts.filter((x) => x?.isDriver) : []), ...(Array.isArray(client?.contacts) ? client.contacts.filter((x) => x?.isDriver) : []) ]; const seen = new Set([ normalizeString(buildPersonName(getValue(primaryProfile, 'FirstName', 'firstName'), getValue(primaryProfile, 'LastName', 'lastName'))), normalizeString(buildPersonName(getValue(coApplicantProfile, 'FirstName', 'firstName'), getValue(coApplicantProfile, 'LastName', 'lastName'))) ]); return records.filter((record) => { const key = normalizeString(buildPersonName(getValue(record, 'FirstName', 'firstName'), getValue(record, 'LastName', 'lastName'))); if (!key || seen.has(key)) return false; seen.add(key); return true; }); } function fillByName(name, value) { if (!name || value === undefined || value === null || value === '') return false; const nodes = document.getElementsByName(name); if (!nodes || !nodes.length) return false; let filled = false; Array.from(nodes).forEach((node) => { if (node.tagName === 'SELECT') { filled = setSelectValue(node, value) || filled; } else { filled = setInputValue(node, value) || filled; } }); return filled; } function normalizeCountyValue(value) { return String(value || '').toLowerCase().replace(/county/g, '').replace(/[^a-z0-9]/g, ''); } function selectCountyWithRetry(selector, countyValue, attempts = 8, delay = 250) { if (!selector || !countyValue) return; const normalizedCounty = normalizeCountyValue(countyValue); let remaining = attempts; const trySelect = () => { const el = typeof selector === 'string' ? document.getElementById(selector) || document.querySelector(selector) : selector; if (!el || !el.options || !el.options.length) { if (remaining-- > 0) setTimeout(trySelect, delay); return; } const options = Array.from(el.options); const match = options.find((option) => { const normalizedOption = normalizeCountyValue(option.text || option.value); return normalizedOption && normalizedOption === normalizedCounty; }); if (match) { setSelectValue(el, match.value); return; } const hasRealOptions = options.some((option) => normalizeCountyValue(option.text || option.value)); if (!hasRealOptions && remaining-- > 0) { setTimeout(trySelect, delay); } }; setTimeout(trySelect, delay); } function selectByNameWithRetry(name, value, attempts = 8, delay = 250) { if (!name || value === undefined || value === null || value === '') return; let remaining = attempts; const trySelect = () => { const nodes = document.getElementsByName(name); const el = nodes && nodes.length ? nodes[0] : null; if (!el || el.disabled) { if (remaining-- > 0) setTimeout(trySelect, delay); return; } const didSet = el.tagName === 'SELECT' ? setSelectValue(el, value) : setInputValue(el, value); if (!didSet && remaining-- > 0) { setTimeout(trySelect, delay); } }; setTimeout(trySelect, delay); } function isEmptyFieldValue(input) { const currentVal = String(input?.value || '').trim(); if (!currentVal) return true; return currentVal.replace(/[_/\-\s]/g, '') === '' || currentVal.toLowerCase() === 'mm/dd/yyyy'; } function fillAcordFieldByAliases(inputs, fieldAliases, value, roleAliases = [], excludeAliases = []) { if (value === undefined || value === null || value === '') return false; const normalizedFieldAliases = fieldAliases.map((alias) => normalizeString(alias)); const normalizedRoleAliases = roleAliases.map((alias) => normalizeString(alias)); const normalizedExcludeAliases = excludeAliases.map((alias) => normalizeString(alias)); let filled = false; inputs.forEach((input) => { if (filled || input.type === 'hidden' || input.disabled || input.readOnly) return; if (!isEmptyFieldValue(input)) return; const context = getFieldContext(input); const combined = context.combined; if (!normalizedFieldAliases.some((alias) => combined.includes(alias))) return; if (normalizedRoleAliases.length && !normalizedRoleAliases.some((alias) => combined.includes(alias))) return; if (normalizedExcludeAliases.some((alias) => combined.includes(alias))) return; filled = input.tagName === 'SELECT' ? setSelectValue(input, value) : setInputValue(input, value); }); return filled; } function fillAcordDateByAliases(inputs, fieldAliases, dateValue, roleAliases = [], excludeAliases = []) { const parts = getDateParts(dateValue); if (!parts.month && !parts.day && !parts.year) return false; let filled = false; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'month', 'mm'], parts.month, roleAliases, excludeAliases) || filled; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'day', 'dd'], parts.day, roleAliases, excludeAliases) || filled; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'year', 'yyyy'], parts.year, roleAliases, excludeAliases) || filled; const formatted = formatDate(dateValue); filled = fillAcordFieldByAliases(inputs, fieldAliases, formatted, roleAliases, excludeAliases) || filled; return filled; } function fillAcordPhoneByAliases(inputs, fieldAliases, phoneValue, roleAliases = [], excludeAliases = []) { if (!phoneValue) return false; const parts = getPhoneParts(phoneValue); let filled = false; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'areacode'], parts.area, roleAliases, excludeAliases) || filled; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'exchange'], parts.exchange, roleAliases, excludeAliases) || filled; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'local'], parts.local, roleAliases, excludeAliases) || filled; filled = fillAcordFieldByAliases(inputs, [...fieldAliases, 'extension', 'ext'], parts.extension, roleAliases, excludeAliases) || filled; filled = fillAcordFieldByAliases(inputs, fieldAliases, phoneValue, roleAliases, excludeAliases) || filled; return filled; } function getPrimaryVehicle(client) { return Array.isArray(client?.vehicles) && client.vehicles.length ? client.vehicles[0] : null; } function prefillAcordStructuredFields(client) { const inputs = Array.from(document.querySelectorAll('input, select, textarea')); const primary = getPrimaryInsuredProfile(client); const secondary = getCoApplicantProfile(client, primary); const extraDrivers = getAdditionalDriverProfiles(client, primary, secondary); const firstDriver = extraDrivers[0] || null; const vehicle = getPrimaryVehicle(client); const property = getCurrentProperty(client); const policyEffectiveDate = getFirstPolicyValue(client, ['EffectiveDate', 'effectiveDate', 'StartDate']); const propertyLimits = extractPropertyLimits(client); const vehicleDeductibles = extractVehicleDeductibles(client); const applicantRoles = ['applicant', 'insured', 'namedinsured', 'applicant1', 'insured1']; const coApplicantRoles = ['coapplicant', 'coinsured', 'spouse', 'applicant2', 'insured2', 'additionalinsured']; const driverRoles = ['driver', 'operator']; const vehicleRoles = ['vehicle', 'auto']; const propertyRoles = ['premises', 'location', 'residence', 'property', 'building']; fillAcordFieldByAliases(inputs, ['firstname', 'first', 'givenname'], getValue(primary, 'FirstName', 'firstName'), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordFieldByAliases(inputs, ['middlename', 'middleinitial', 'middle', 'mi'], getMiddleNameValue(primary), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordFieldByAliases(inputs, ['lastname', 'last', 'surname'], getValue(primary, 'LastName', 'lastName'), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordDateByAliases(inputs, ['dateofbirth', 'birthdate', 'dob'], getBirthDate(primary), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordFieldByAliases(inputs, ['ssn', 'socialsecuritynumber', 'socialsecurity'], getValue(primary, 'SSN', 'SocialSecurityNumber', 'socialSecurityNumber'), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordFieldByAliases(inputs, ['email', 'emailaddress'], getEmailValue(primary), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordPhoneByAliases(inputs, ['phone', 'phonenumber', 'telephone'], getPrimaryPhoneValue(primary), applicantRoles, ['coapplicant', 'coinsured', 'driver', 'operator']); fillAcordFieldByAliases(inputs, ['address', 'street', 'addressline1'], getValue(primary, 'Address1', 'address1', 'addressLine1'), applicantRoles, ['premises', 'location', 'property']); fillAcordFieldByAliases(inputs, ['address2', 'street2', 'suite', 'apt'], getValue(primary, 'Address2', 'address2', 'addressLine2'), applicantRoles, ['premises', 'location', 'property']); fillAcordFieldByAliases(inputs, ['city'], getValue(primary, 'City', 'city'), applicantRoles, ['premises', 'location', 'property']); fillAcordFieldByAliases(inputs, ['state'], normalizeStateCode(getValue(primary, 'State', 'state')), applicantRoles, ['premises', 'location', 'property']); fillAcordFieldByAliases(inputs, ['zip', 'zipcode', 'postalcode'], getValue(primary, 'Zip', 'zip', 'zipCode', 'ZipCode'), applicantRoles, ['premises', 'location', 'property']); if (getValue(secondary, 'FirstName', 'firstName') || getValue(secondary, 'LastName', 'lastName')) { fillAcordFieldByAliases(inputs, ['firstname', 'first', 'givenname'], getValue(secondary, 'FirstName', 'firstName'), coApplicantRoles); fillAcordFieldByAliases(inputs, ['middlename', 'middleinitial', 'middle', 'mi'], getMiddleNameValue(secondary), coApplicantRoles); fillAcordFieldByAliases(inputs, ['lastname', 'last', 'surname'], getValue(secondary, 'LastName', 'lastName'), coApplicantRoles); fillAcordDateByAliases(inputs, ['dateofbirth', 'birthdate', 'dob'], getBirthDate(secondary), coApplicantRoles); fillAcordFieldByAliases(inputs, ['ssn', 'socialsecuritynumber', 'socialsecurity'], getValue(secondary, 'SSN', 'SocialSecurityNumber', 'socialSecurityNumber'), coApplicantRoles); fillAcordFieldByAliases(inputs, ['email', 'emailaddress'], getEmailValue(secondary), coApplicantRoles); fillAcordPhoneByAliases(inputs, ['phone', 'phonenumber', 'telephone'], getPrimaryPhoneValue(secondary), coApplicantRoles); } if (firstDriver) { fillAcordFieldByAliases(inputs, ['firstname', 'first', 'givenname'], getValue(firstDriver, 'FirstName', 'firstName'), driverRoles, ['coapplicant', 'coinsured']); fillAcordFieldByAliases(inputs, ['middlename', 'middleinitial', 'middle', 'mi'], getMiddleNameValue(firstDriver), driverRoles, ['coapplicant', 'coinsured']); fillAcordFieldByAliases(inputs, ['lastname', 'last', 'surname'], getValue(firstDriver, 'LastName', 'lastName'), driverRoles, ['coapplicant', 'coinsured']); fillAcordDateByAliases(inputs, ['dateofbirth', 'birthdate', 'dob'], getBirthDate(firstDriver), driverRoles, ['coapplicant', 'coinsured']); fillAcordFieldByAliases(inputs, ['license', 'licensenumber', 'driverslicense'], getLicenseNumber(firstDriver), driverRoles); fillAcordFieldByAliases(inputs, ['licensestate', 'dlstate'], normalizeStateCode(getLicenseState(firstDriver)), driverRoles); fillAcordFieldByAliases(inputs, ['relationship'], getRelationshipCode(firstDriver), driverRoles); } if (vehicle) { fillAcordFieldByAliases(inputs, ['vin'], getValue(vehicle, 'VIN', 'vin'), vehicleRoles); fillAcordFieldByAliases(inputs, ['year', 'modelyear'], getValue(vehicle, 'Year', 'year', 'ModelYear'), vehicleRoles, ['birth', 'dob']); fillAcordFieldByAliases(inputs, ['make'], getValue(vehicle, 'Make', 'make'), vehicleRoles); fillAcordFieldByAliases(inputs, ['model'], getValue(vehicle, 'Model', 'model'), vehicleRoles); fillAcordFieldByAliases(inputs, ['collisiondeductible', 'colldeductible', 'collisionded'], vehicleDeductibles.collision, vehicleRoles); fillAcordFieldByAliases(inputs, ['comprehensivedeductible', 'compdeductible', 'otherthancollisiondeductible', 'otcdeductible'], vehicleDeductibles.comp, vehicleRoles); } if (property) { fillAcordFieldByAliases(inputs, ['address', 'street', 'premisesaddress', 'locationaddress'], getValue(property, 'Address1', 'address1', 'addressLine1'), propertyRoles); fillAcordFieldByAliases(inputs, ['address2', 'suite', 'apt'], getValue(property, 'Address2', 'address2', 'addressLine2'), propertyRoles); fillAcordFieldByAliases(inputs, ['city'], getValue(property, 'City', 'city'), propertyRoles); fillAcordFieldByAliases(inputs, ['state'], normalizeStateCode(getValue(property, 'State', 'state', 'stateName')), propertyRoles); fillAcordFieldByAliases(inputs, ['zip', 'zipcode', 'postalcode'], getValue(property, 'Zip', 'zip', 'zipCode', 'ZipCode'), propertyRoles); fillAcordFieldByAliases(inputs, ['county'], getValue(property, 'County', 'county', 'CountyName', 'countyName'), propertyRoles); } fillAcordDateByAliases(inputs, ['effectivedate', 'policyeffectivedate', 'inceptiondate'], policyEffectiveDate, [], ['birth', 'dob']); fillAcordFieldByAliases(inputs, ['dwelling', 'building', 'coveragea', 'buildinglimit'], propertyLimits.dwelling, propertyRoles); fillAcordFieldByAliases(inputs, ['personalproperty', 'contents', 'coveragec'], propertyLimits.personalProperty, propertyRoles); fillAcordFieldByAliases(inputs, ['businesspersonalproperty', 'bpp', 'businesscontents'], propertyLimits.businessPersonalProperty, propertyRoles); } function prefillWestfieldStructuredFields(client) { const primary = getPrimaryInsuredProfile(client); const secondary = getCoApplicantProfile(client, primary); const extraDrivers = getAdditionalDriverProfiles(client, primary, secondary); const mailingState = normalizeStateCode(getValue(primary, 'State', 'state')); const primaryDob = getDateParts(getBirthDate(primary)); const secondaryDob = getDateParts(getBirthDate(secondary)); const primaryLicensed = addYearsToDateParts(getBirthDate(primary), 16); const secondaryLicensed = addYearsToDateParts(getBirthDate(secondary), 16); const effectiveDate = getDateParts(getFirstPolicyValue(client, ['EffectiveDate', 'effectiveDate', 'StartDate']) || getValue(client, 'EffectiveDate', 'effectiveDate')); const primarySsn = getSsnParts(getValue(primary, 'SSN', 'SocialSecurityNumber', 'socialSecurityNumber')); const secondarySsn = getSsnParts(getValue(secondary, 'SSN', 'SocialSecurityNumber', 'socialSecurityNumber')); const primaryPhoneValue = getPrimaryPhoneValue(primary); const secondaryPhoneValue = getPrimaryPhoneValue(secondary); const primarySecondaryPhoneValue = getSecondaryPhoneValue(primary); const secondarySecondaryPhoneValue = getSecondaryPhoneValue(secondary); const primaryPhone = getPhoneParts(primaryPhoneValue); const secondaryPhone = getPhoneParts(secondaryPhoneValue); const primarySecondaryPhone = getPhoneParts(primarySecondaryPhoneValue); const secondarySecondaryPhone = getPhoneParts(secondarySecondaryPhoneValue); const mailingZipDigits = digitsOnly(getValue(primary, 'Zip', 'zip', 'ZipCode', 'zipCode')); const mailingAddress1 = getValue(primary, 'Address1', 'address1', 'addressLine1'); const mailingAddress2 = getValue(primary, 'Address2', 'address2', 'addressLine2'); const mailingCity = getValue(primary, 'City', 'city'); const mailingCounty = getValue(primary, 'County', 'county', 'CountyName', 'countyName'); const priorAddress1 = getValue(primary, 'PreviousAddress1', 'previousAddress1') || mailingAddress1; const priorCity = getValue(primary, 'PreviousCity', 'previousCity') || mailingCity; const priorState = getValue(primary, 'PreviousState', 'previousState') || mailingState; const priorZipDigits = digitsOnly(getValue(primary, 'PreviousZip', 'previousZip') || mailingZipDigits); const priorCounty = getValue(primary, 'PreviousCounty', 'previousCounty', 'PreviousCountyName', 'previousCountyName') || mailingCounty; fillByName('dataObjects.applicant.firstName', getPersonFirstName(primary)); fillByName('dataObjects.applicant.middleInitial', getMiddleNameValue(primary).slice(0, 1)); fillByName('dataObjects.applicant.lastName', getPersonLastName(primary)); fillByName('dataObjects.applicant.dateOfBirthMonthIncremented', primaryDob.month); fillByName('dataObjects.applicant.dateOfBirthDate', primaryDob.day); fillByName('dataObjects.applicant.dateOfBirthYear', primaryDob.year); fillByName('dataObjects.applicant.socialSecurityNumberFirst3', primarySsn.first3); fillByName('dataObjects.applicant.socialSecurityNumberMiddle2', primarySsn.middle2); fillByName('dataObjects.applicant.socialSecurityNumberLast4', primarySsn.last4); fillByName('dataObjects.applicant.maritalStatusCode', getMaritalStatusCode(primary)); selectByNameWithRetry('dataObjects.applicant.genderCode', getGenderCode(primary), 15, 400); fillByName('dataObjects.applicant.licenseDateMonthIncremented', primaryLicensed.month); fillByName('dataObjects.applicant.licenseDateDate', primaryLicensed.day); fillByName('dataObjects.applicant.licenseDateYear', primaryLicensed.year); fillByName('dataObjects.applicant.licenseNumber', getLicenseNumber(primary)); fillByName('dataObjects.applicant.licenseStateCode', normalizeStateCode(getLicenseState(primary, mailingState))); fillByName('dataObjects.applicantContactInfo.primaryEmailAddress', getEmailValue(primary)); fillByName('dataObjects.applicantContactInfo.primaryPhoneNumberAreaCode', primaryPhone.area); fillByName('dataObjects.applicantContactInfo.primaryPhoneNumberExchange', primaryPhone.exchange); fillByName('dataObjects.applicantContactInfo.primaryPhoneNumberLocal', primaryPhone.local); fillByName('dataObjects.applicantContactInfo.primaryPhoneExtension', primaryPhone.extension); fillByName('dataObjects.applicantContactInfo.primaryPhoneTypeCode', getPhoneTypeCode(primary, primaryPhoneValue)); fillByName('dataObjects.applicantContactInfo.secondaryPhoneNumberAreaCode', primarySecondaryPhone.area); fillByName('dataObjects.applicantContactInfo.secondaryPhoneNumberExchange', primarySecondaryPhone.exchange); fillByName('dataObjects.applicantContactInfo.secondaryPhoneNumberLocal', primarySecondaryPhone.local); fillByName('dataObjects.applicantContactInfo.secondaryPhoneExtension', primarySecondaryPhone.extension); fillByName('dataObjects.applicantContactInfo.secondaryPhoneTypeCode', getPhoneTypeCode(primary, primarySecondaryPhoneValue)); fillByName('dataObjects.applicantContactInfo.secondaryEmailAddress', getValue(primary, 'eMail2', 'secondaryEmailAddress')); if (getValue(secondary, 'FirstName', 'firstName') || getValue(secondary, 'LastName', 'lastName')) { fillByName('applicantType', 'COAPPL'); fillByName('dataObjects.applicant.maritalStatusCode', 'MAR'); fillByName('dataObjects.coApplicant.firstName', getPersonFirstName(secondary)); fillByName('dataObjects.coApplicant.middleInitial', getMiddleNameValue(secondary).slice(0, 1)); fillByName('dataObjects.coApplicant.lastName', getPersonLastName(secondary)); fillByName('dataObjects.coApplicant.dateOfBirthMonthIncremented', secondaryDob.month); fillByName('dataObjects.coApplicant.dateOfBirthDate', secondaryDob.day); fillByName('dataObjects.coApplicant.dateOfBirthYear', secondaryDob.year); fillByName('dataObjects.coApplicant.socialSecurityNumberFirst3', secondarySsn.first3); fillByName('dataObjects.coApplicant.socialSecurityNumberMiddle2', secondarySsn.middle2); fillByName('dataObjects.coApplicant.socialSecurityNumberLast4', secondarySsn.last4); fillByName('dataObjects.coApplicant.maritalStatusCode', 'MAR'); fillByName('dataObjects.coApplicant.genderCode', getGenderCode(secondary)); selectByNameWithRetry('dataObjects.coApplicant.genderCode', getGenderCode(secondary), 10, 300); fillByName('dataObjects.coApplicant.licenseDateMonthIncremented', secondaryLicensed.month); fillByName('dataObjects.coApplicant.licenseDateDate', secondaryLicensed.day); fillByName('dataObjects.coApplicant.licenseDateYear', secondaryLicensed.year); fillByName('dataObjects.coApplicant.licenseNumber', getLicenseNumber(secondary)); fillByName('dataObjects.coApplicant.licenseStateCode', normalizeStateCode(getLicenseState(secondary, mailingState))); fillByName('dataObjects.coApplicant.relationshipCode', 'SPSE'); fillByName('dataObjects.coapplicantContactInfo.primaryEmailAddress', getEmailValue(secondary)); fillByName('dataObjects.coapplicantContactInfo.primaryPhoneNumberAreaCode', secondaryPhone.area); fillByName('dataObjects.coapplicantContactInfo.primaryPhoneNumberExchange', secondaryPhone.exchange); fillByName('dataObjects.coapplicantContactInfo.primaryPhoneNumberLocal', secondaryPhone.local); fillByName('dataObjects.coapplicantContactInfo.primaryPhoneExtension', secondaryPhone.extension); fillByName('dataObjects.coapplicantContactInfo.primaryPhoneTypeCode', getPhoneTypeCode(secondary, secondaryPhoneValue)); fillByName('dataObjects.coapplicantContactInfo.secondaryPhoneNumberAreaCode', secondarySecondaryPhone.area); fillByName('dataObjects.coapplicantContactInfo.secondaryPhoneNumberExchange', secondarySecondaryPhone.exchange); fillByName('dataObjects.coapplicantContactInfo.secondaryPhoneNumberLocal', secondarySecondaryPhone.local); fillByName('dataObjects.coapplicantContactInfo.secondaryPhoneExtension', secondarySecondaryPhone.extension); fillByName('dataObjects.coapplicantContactInfo.secondaryPhoneTypeCode', getPhoneTypeCode(secondary, secondarySecondaryPhoneValue)); fillByName('dataObjects.coapplicantContactInfo.secondaryEmailAddress', getValue(secondary, 'eMail2', 'secondaryEmailAddress')); } fillByName('effectiveDateMonthIncremented', effectiveDate.month); fillByName('effectiveDateDate', effectiveDate.day); fillByName('effectiveDateYear', effectiveDate.year); fillByName('addresses.currentDisplayAddress.addressLine1', mailingAddress1); fillByName('addresses.currentDisplayAddress.addressLine2', mailingAddress2); fillByName('addresses.currentDisplayAddress.cityName', mailingCity); fillByName('addresses.currentDisplayAddress.stateCode', mailingState); fillByName('addresses.currentDisplayAddress.zipCode', mailingZipDigits.slice(0, 5)); fillByName('addresses.currentDisplayAddress.zipPlusFour', mailingZipDigits.slice(5, 9)); selectCountyWithRetry('mailingCountyId', mailingCounty); fillByName('addresses.priorDisplayAddress.addressLine1', priorAddress1); fillByName('addresses.priorDisplayAddress.addressLine2', mailingAddress2); fillByName('addresses.priorDisplayAddress.cityName', priorCity); fillByName('addresses.priorDisplayAddress.stateCode', priorState); fillByName('addresses.priorDisplayAddress.zipCode', priorZipDigits.slice(0, 5)); fillByName('addresses.priorDisplayAddress.zipPlusFour', priorZipDigits.slice(5, 9)); selectCountyWithRetry('prevCountyId', priorCounty); fillByName('addresses.garageDisplayAddress.addressLine1', mailingAddress1); fillByName('addresses.garageDisplayAddress.addressLine2', mailingAddress2); fillByName('addresses.garageDisplayAddress.cityName', mailingCity); fillByName('addresses.garageDisplayAddress.stateCode', mailingState); fillByName('addresses.garageDisplayAddress.zipCode', mailingZipDigits.slice(0, 5)); fillByName('addresses.garageDisplayAddress.zipPlusFour', mailingZipDigits.slice(5, 9)); selectCountyWithRetry('garagingCountyId', mailingCounty); fillByName('addresses.homeInfoDisplayAddress.addressLine1', mailingAddress1); fillByName('addresses.homeInfoDisplayAddress.addressLine2', mailingAddress2); fillByName('addresses.homeInfoDisplayAddress.cityName', mailingCity); fillByName('addresses.homeInfoDisplayAddress.stateCode', mailingState); fillByName('addresses.homeInfoDisplayAddress.zipCode', mailingZipDigits.slice(0, 5)); fillByName('addresses.homeInfoDisplayAddress.zipPlusFour', mailingZipDigits.slice(5, 9)); selectCountyWithRetry('locationCountyId', mailingCounty); const driver = extraDrivers[0]; if (driver) { const driverDob = getDateParts(getBirthDate(driver)); const driverLicensed = addYearsToDateParts(getBirthDate(driver), 16); const driverSsn = getSsnParts(getValue(driver, 'SSN', 'SocialSecurityNumber', 'socialSecurityNumber')); fillByName('dataObjects.driverInfo.firstName', getPersonFirstName(driver)); fillByName('dataObjects.driverInfo.middleInitial', getMiddleNameValue(driver).slice(0, 1)); fillByName('dataObjects.driverInfo.lastName', getPersonLastName(driver)); fillByName('dataObjects.driverInfo.dateOfBirthMonthIncremented', driverDob.month); fillByName('dataObjects.driverInfo.dateOfBirthDate', driverDob.day); fillByName('dataObjects.driverInfo.dateOfBirthYear', driverDob.year); fillByName('dataObjects.driverInfo.socialSecurityNumberFirst3', driverSsn.first3); fillByName('dataObjects.driverInfo.socialSecurityNumberMiddle2', driverSsn.middle2); fillByName('dataObjects.driverInfo.socialSecurityNumberLast4', driverSsn.last4); fillByName('dataObjects.driverInfo.maritalStatusCode', getMaritalStatusCode(driver)); fillByName('dataObjects.driverInfo.genderCode', getGenderCode(driver)); selectByNameWithRetry('dataObjects.driverInfo.genderCode', getGenderCode(driver), 10, 300); fillByName('dataObjects.driverInfo.licenseDateMonthIncremented', driverLicensed.month); fillByName('dataObjects.driverInfo.licenseDateDate', driverLicensed.day); fillByName('dataObjects.driverInfo.licenseDateYear', driverLicensed.year); fillByName('dataObjects.driverInfo.licenseNumber', getLicenseNumber(driver)); fillByName('dataObjects.driverInfo.licenseStateCode', normalizeStateCode(getLicenseState(driver, mailingState))); fillByName('dataObjects.driverInfo.relationshipCode', getRelationshipCode(driver)); } } function fillInputs(client, smartMap = null, options = {}) { const inputs = document.querySelectorAll('input, select, textarea'); let filledCount = 0; const middleNameValue = getMiddleNameValue(client); const vehicleDeductibles = extractVehicleDeductibles(client); const propertyLimits = extractPropertyLimits(client); if (!options.previewOnly && window.location.hostname.includes('westfield')) { prefillWestfieldStructuredFields(client); } if (!options.previewOnly && options.acordOnly) { prefillAcordStructuredFields(client); } if (options.previewOnly) { clearPreviewBadges(); } let dobM = '', dobD = '', dobY = ''; const rawDob = client.BirthDate || client.DateOfBirth || client.dateOfBirth || client.DOB || ''; if (rawDob) { const d = new Date(rawDob); if (!isNaN(d.getTime())) { dobM = (d.getMonth() + 1).toString().padStart(2, '0'); dobD = d.getDate().toString().padStart(2, '0'); dobY = d.getFullYear().toString(); } } const map = { // Specific fields first to avoid generic 'name' matching 'NamedInsured' IDs 'emailaddress': client.Email || client.email || client.PrimaryEmail || '', 'email': client.Email || client.email || client.PrimaryEmail || '', 'phone': client.Phone || client.phone || client.CellPhone || client.cellPhone || client.PrimaryPhone || '', 'cell': client.CellPhone || client.cellPhone || client.MobilePhone || '', 'mobile': client.CellPhone || client.cellPhone || client.MobilePhone || '', 'homephone': client.HomePhone || client.phone || '', 'city': client.City || client.city || '', 'zip': client.Zip || client.zip || client.ZipCode || client.zipCode || client.PostalCode || '', 'postal': client.Zip || client.zip || client.ZipCode || client.zipCode || client.PostalCode || '', 'state': client.State || client.state || '', 'address': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'addr': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'street': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'inputdate': formatDate(rawDob), 'dateofbirth': formatDate(rawDob), 'birthdate': formatDate(rawDob), 'birth': formatDate(rawDob), 'dob': formatDate(rawDob), 'mmddyyyy': formatDate(rawDob), 'dobmonth': dobM, 'birthmonth': dobM, 'monthofbirth': dobM, 'datemonth': dobM, 'mm': (c) => (c.includes('dob') || c.includes('birth') || (c.includes('date') && !c.includes('dateofbirth'))) ? dobM : '', 'dobday': dobD, 'birthday': dobD, 'dayofbirth': dobD, 'dateday': dobD, 'dd': (c) => (c.includes('dob') || c.includes('birth') || (c.includes('date') && !c.includes('dateofbirth'))) ? dobD : '', 'dobyear': dobY, 'birthyear': dobY, 'yearofbirth': dobY, 'dateyear': dobY, 'yyyy': (c) => (c.includes('dob') || c.includes('birth') || (c.includes('date') && !c.includes('dateofbirth'))) ? dobY : '', 'ssn': client.SSN || client.ssn || client.TaxId || client.taxId || '', 'taxid': client.SSN || client.ssn || client.TaxId || client.taxId || '', 'sin': client.SSN || client.ssn || '', 'license': client.DriverLicenseNumber || client.driverLicenseNumber || client.LicenseNumber || '', 'dlnumber': client.DriverLicenseNumber || client.driverLicenseNumber || client.LicenseNumber || '', 'dln': client.DriverLicenseNumber || client.driverLicenseNumber || client.LicenseNumber || '', 'gender': client.Gender || client.gender || client.Sex || client.sex || '', 'sex': client.Gender || client.gender || client.Sex || client.sex || '', // Generic name fields last 'first': client.FirstName || client.firstName || '', 'fname': client.FirstName || client.firstName || '', 'givenname': client.FirstName || client.firstName || '', 'given': client.FirstName || client.firstName || '', 'firstname': client.FirstName || client.firstName || '', 'applicantfirstname': client.FirstName || client.firstName || '', 'insuredfirstname': client.FirstName || client.firstName || '', 'operatorfirstname': client.FirstName || client.firstName || '', 'last': client.LastName || client.lastName || '', 'lname': client.LastName || client.lastName || '', 'surname': client.LastName || client.lastName || '', 'familyname': client.LastName || client.lastName || '', 'lastname': client.LastName || client.lastName || '', 'applicantlastname': client.LastName || client.lastName || '', 'insuredlastname': client.LastName || client.lastName || '', 'operatorlastname': client.LastName || client.lastName || '', 'middle': middleNameValue, 'middlename': middleNameValue, 'middleinitial': middleNameValue, 'mi': middleNameValue, 'applicantmiddlename': middleNameValue, 'insuredmiddlename': middleNameValue, 'fullname': client.FullName || '', // FIX: Ensure name field doesn't clash with NamedInsured 'name': (c) => c.includes('named') ? '' : (client.FullName || `${client.FirstName || ''} ${client.LastName || ''}`.trim()), 'applicantname': client.FullName || `${client.FirstName || ''} ${client.LastName || ''}`.trim(), 'namedinsured': client.CommercialName || client.commercialName || client.FullName || `${client.FirstName || ''} ${client.LastName || ''}`.trim(), 'applicant': client.CommercialName || client.commercialName || client.FullName || `${client.FirstName || ''} ${client.LastName || ''}`.trim(), 'insured': client.CommercialName || client.commercialName || client.FullName || `${client.FirstName || ''} ${client.LastName || ''}`.trim(), 'mailingaddress': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'residenceaddress': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'mailingstreet': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'applicantaddress': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'insuredaddress': client.Address1 || client.Address || client.address || client.addressLine1 || '', 'fein': client.TaxId || client.taxId || client.TaxID || '', 'businessname': client.CommercialName || client.commercialName || client.FullName || '', 'commercialname': client.CommercialName || client.commercialName || client.FullName || '', 'effectivedate': formatDate(getFirstPolicyValue(client, ['EffectiveDate', 'effectiveDate', 'StartDate'])), 'startdate': formatDate(getFirstPolicyValue(client, ['EffectiveDate', 'effectiveDate', 'StartDate'])), 'expirationdate': formatDate(getFirstPolicyValue(client, ['ExpirationDate', 'expirationDate', 'ExpDate'])), 'expdate': formatDate(getFirstPolicyValue(client, ['ExpirationDate', 'expirationDate', 'ExpDate'])), 'carrier': getFirstPolicyValue(client, ['CarrierName', 'carrierName', 'CompanyName']), 'company': getFirstPolicyValue(client, ['CarrierName', 'carrierName', 'CompanyName']), 'vin': getFirstVehicleValue(client, ['VIN', 'vin']), 'vehicleyear': getFirstVehicleValue(client, ['Year', 'year', 'ModelYear']), 'vehiclemake': getFirstVehicleValue(client, ['Make', 'make']), 'vehiclemodel': getFirstVehicleValue(client, ['Model', 'model']), 'compdeductible': vehicleDeductibles.comp, 'comprehensivedeductible': vehicleDeductibles.comp, 'otherthancollisiondeductible': vehicleDeductibles.comp, 'otcdeductible': vehicleDeductibles.comp, 'collisiondeductible': vehicleDeductibles.collision, 'colldeductible': vehicleDeductibles.collision, 'dwelling': propertyLimits.dwelling, 'building': propertyLimits.dwelling, 'coveragea': propertyLimits.dwelling, 'dwellinglimit': propertyLimits.dwelling, 'buildinglimit': propertyLimits.dwelling, 'personalproperty': propertyLimits.personalProperty, 'contents': propertyLimits.personalProperty, 'coveragec': propertyLimits.personalProperty, 'personalpropertylimit': propertyLimits.personalProperty, 'businesspersonalproperty': propertyLimits.businessPersonalProperty, 'bpp': propertyLimits.businessPersonalProperty, 'businesscontents': propertyLimits.businessPersonalProperty, 'businesspersonalpropertylimit': propertyLimits.businessPersonalProperty, 'year': (c) => (c.includes('dob') || c.includes('birth')) ? '' : getFirstVehicleValue(client, ['Year', 'year', 'ModelYear']), 'make': getFirstVehicleValue(client, ['Make', 'make']), 'model': getFirstVehicleValue(client, ['Model', 'model']), }; inputs.forEach(input => { if (input.type === 'hidden' || input.readOnly || input.disabled) return; const context = getFieldContext(input); if (options.acordOnly && !isAcordField(context) && !isCommonFillField(context)) return; if (shouldSkipGenericField(context, options)) return; let smartMatchKey = null; if (smartMap) { for (const [key, selector] of Object.entries(smartMap)) { if (selector && (input.id === selector || input.name === selector)) { smartMatchKey = key.toLowerCase(); break; } } } let finalValue = null; let matchedKeyword = null; // --- 1. Try Smart Map First --- if (smartMatchKey && map[smartMatchKey]) { const val = map[smartMatchKey]; finalValue = typeof val === 'function' ? val('', input) : val; matchedKeyword = smartMatchKey; } // --- 2. Fallback to classic combined string check --- else { for (const [keyword, value] of Object.entries(map)) { if (isMiddleNameField(context) && !['middle', 'middlename', 'middleinitial', 'mi'].includes(keyword)) { continue; } if (isSsnField(context) && !['ssn', 'taxid', 'sin', 'fein'].includes(keyword)) { continue; } if (isPhoneTypeField(context) && !['phone', 'cell', 'mobile', 'homephone'].includes(keyword)) { continue; } if (isPhoneExtensionField(context)) { continue; } if (isPhoneSegmentField(context) && !['phone', 'cell', 'mobile', 'homephone'].includes(keyword)) { continue; } if (isPhoneField(context) && !isPhoneSegmentField(context) && !isPhoneTypeField(context) && !['phone', 'cell', 'mobile', 'homephone'].includes(keyword)) { continue; } if (isDateField(context) && !['inputdate', 'dateofbirth', 'birthdate', 'birth', 'dob', 'mmddyyyy', 'dobmonth', 'birthmonth', 'monthofbirth', 'datemonth', 'mm', 'dobday', 'birthday', 'dayofbirth', 'dateday', 'dd', 'dobyear', 'birthyear', 'yearofbirth', 'dateyear', 'yyyy', 'effectivedate', 'startdate', 'expirationdate', 'expdate'].includes(keyword)) { continue; } if (isLicenseField(context) && !['license', 'dlnumber', 'dln', 'state'].includes(keyword)) { continue; } if (isAddressField(context) && ['first', 'fname', 'givenname', 'given', 'firstname', 'applicantfirstname', 'insuredfirstname', 'operatorfirstname', 'last', 'lname', 'surname', 'familyname', 'lastname', 'applicantlastname', 'insuredlastname', 'operatorlastname', 'fullname', 'name', 'applicantname', 'namedinsured', 'applicant', 'insured', 'businessname', 'commercialname'].includes(keyword)) { continue; } if (context.combined.includes(keyword)) { finalValue = typeof value === 'function' ? value(context.combined, input) : value; if (finalValue) { matchedKeyword = keyword; break; } } } } if (isMiddleNameField(context)) { if (!middleNameValue) return; if (!isRequiredField(input) && String(input.value || '').trim()) return; finalValue = middleNameValue; matchedKeyword = matchedKeyword || 'middle'; } if (finalValue) { const currentVal = input.value || ''; const isSelect = input.tagName === 'SELECT'; const isEffectivelyEmpty = isSelect ? !String(currentVal || '').trim() : currentVal.trim() === '' || currentVal.replace(/[_/\-\s]/g, '') === '' || currentVal.toLowerCase() === 'mm/dd/yyyy'; if (isEffectivelyEmpty) { if (options.previewOnly) { injectPreviewBadge(input, matchedKeyword || 'field', finalValue); filledCount++; return; } input.focus(); if (isSelect) { setSelectValue(input, finalValue); } else { const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value') || Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value'); if (nativeSetter && nativeSetter.set) { nativeSetter.set.call(input, finalValue); } else { input.value = finalValue; } } ['input', 'change', 'keyup', 'blur'].forEach(ev => { input.dispatchEvent(new Event(ev, { bubbles: true })); }); input.style.backgroundColor = '#d1fae5'; input.style.transition = 'background-color 0.6s'; setTimeout(() => { input.style.backgroundColor = ''; }, 1200); filledCount++; console.log(`Auto-filled: ${matchedKeyword} → "${finalValue}"`); } } }); // ─── Agent Exchange: Address Info + Insurance Coverage page ────────── if (isErieAddressInfoPage()) { fillErieAddressInfoPage(client); } // ─── Agent Exchange: Second Named Insured ──────────────────────────── if (isAgentExchangeCustomerPage()) { fillAgentExchangeSecondNamedInsured(client); } console.log(`Carrier Bridge: Auto-filled ${filledCount} fields.`); return filledCount; } // ──────────────────────────────────────────────────────────────────────────── // Step-walk helpers // ──────────────────────────────────────────────────────────────────────────── function isVisibleEl(el) { if (!el) return false; if (el.disabled) return false; const style = window.getComputedStyle(el); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false; const rect = el.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; } function findNextButton(customSelector) { // 1. Custom selector takes priority if (customSelector) { const el = document.querySelector(customSelector); if (el && isVisibleEl(el)) return el; } // 2. Generic text match across all clickable elements const NEXT_KEYWORDS = ['next', 'continue', 'proceed', 'save & continue', 'save and continue', 'go to next', 'move on']; const candidates = Array.from(document.querySelectorAll( 'button:not([disabled]), input[type="submit"]:not([disabled]), input[type="button"]:not([disabled]), a[role="button"]' )); for (const el of candidates) { if (!isVisibleEl(el)) continue; const text = (el.textContent || el.value || el.getAttribute('aria-label') || '').toLowerCase().trim(); if (NEXT_KEYWORDS.some(kw => text.includes(kw))) return el; } return null; } // ──────────────────────────────────────────────────────────────────────────── // Agent Exchange — Second Named Insured // ──────────────────────────────────────────────────────────────────────────── function isAgentExchangeCustomerPage() { return ( window.location.href.includes('agentexchange.com') && window.location.pathname.includes('/Customer') ); } function detectRiskState() { try { const contentArea = document.getElementById('contentarea'); if (contentArea && typeof ko !== 'undefined') { const vm = ko.dataFor(contentArea); if (vm) { const state = vm.AutoRiskState || vm.HomeRiskState || vm.ResidenceRiskState || ''; if (state) return state; } } } catch (e) { /* ko not available */ } const stateEl = document.getElementById('selMailingState'); if (stateEl && stateEl.value) return stateEl.value; return ''; } function getSecondDriver(client) { if (client.drivers && client.drivers.length > 1) return client.drivers[1]; if (client.contacts && client.contacts.length > 1) return client.contacts[1]; if (client.householdMembers && client.householdMembers.length > 1) return client.householdMembers[1]; return null; } function setSelectValue(el, value) { if (!el || value === undefined || value === null) return false; const target = String(value).trim(); const normalizedTarget = normalizeString(target); let matchedValue = target; let matchedIndex = -1; if (el.options && el.options.length) { const options = Array.from(el.options); const exact = options.find((option) => option.value === target || option.text.trim() === target); const normalized = exact || options.find((option) => { return normalizeString(option.value) === normalizedTarget || normalizeString(option.text) === normalizedTarget; }); if (normalized) { matchedValue = normalized.value; matchedIndex = options.indexOf(normalized); } } if (matchedIndex >= 0) { el.selectedIndex = matchedIndex; } el.value = matchedValue; ['input', 'change', 'click', 'blur'].forEach(ev => el.dispatchEvent(new Event(ev, { bubbles: true }))); if (typeof el.onchange === 'function') { try { el.onchange(); } catch (e) {} } if (typeof ko !== 'undefined') { try { ko.utils.triggerEvent(el, 'change'); } catch (e) {} } el.style.backgroundColor = '#d1fae5'; el.style.transition = 'background-color 0.6s'; setTimeout(() => { el.style.backgroundColor = ''; }, 1200); return true; } function setInputValue(el, value) { if (!el || value === undefined || value === null || value === '') return false; const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value'); if (nativeSetter && nativeSetter.set) { nativeSetter.set.call(el, value); } else { el.value = value; } ['input', 'change', 'blur'].forEach(ev => el.dispatchEvent(new Event(ev, { bubbles: true }))); if (typeof ko !== 'undefined') { try { ko.utils.triggerEvent(el, 'change'); } catch (e) {} } el.style.backgroundColor = '#d1fae5'; el.style.transition = 'background-color 0.6s'; setTimeout(() => { el.style.backgroundColor = ''; }, 1200); return true; } function fillAgentExchangeSecondNamedInsured(client) { const sniDropdown = document.getElementById('ddlSecondNamedInsured'); if (!sniDropdown) { console.log('Carrier Bridge: SNI dropdown not found — skipping SNI fill.'); return; } if (sniDropdown.value !== '0') { console.log(`Carrier Bridge: SNI already set ("${sniDropdown.value}") — skipping.`); return; } const secondDriver = getSecondDriver(client); if (!secondDriver) { console.log('Carrier Bridge: No second driver in client data — skipping SNI fill.'); return; } const riskState = detectRiskState(); console.log(`Carrier Bridge: Risk state = "${riskState}". Filling SNI with second driver...`); setSelectValue(sniDropdown, '-1'); setTimeout(() => fillSniFields(secondDriver, riskState, client), 450); } function fillSniFields(driver, riskState, primaryClient) { console.log('Carrier Bridge: Populating SNI fields...'); const get = (obj, ...keys) => { for (const k of keys) { const v = obj[k]; if (v !== undefined && v !== null && v !== '') return v; } return ''; }; let filled = []; // First name intentionally left blank per Erie workflow — agent fills manually const middleName = get(driver, 'MiddleName', 'middleName', 'middle_name', 'middle', 'MI'); const lastName = get(driver, 'LastName', 'lastName', 'last_name', 'last'); const suffix = get(driver, 'Suffix', 'suffix'); // Leave FirstName blank if (setInputValue(document.getElementById('SecondNamedInsured_MiddleName'), middleName)) filled.push('MiddleName'); if (setInputValue(document.getElementById('SecondNamedInsured_LastName'), lastName)) filled.push('LastName'); const suffixEl = document.getElementById('SecondNamedInsured_Suffix'); if (suffixEl && suffix && setSelectValue(suffixEl, suffix)) filled.push('Suffix'); // Gender: use OPPOSITE of primary insured's gender const genderEl = document.getElementById('selGender2'); if (genderEl) { const primaryGender = primaryClient ? getGenderCode(primaryClient) : ''; let sniGender = ''; if (primaryGender === 'M') sniGender = 'F'; else if (primaryGender === 'F') sniGender = 'M'; else { // Fall back to driver's own gender if primary is unknown const driverGender = get(driver, 'Gender', 'gender', 'Sex', 'sex'); sniGender = driverGender; } if (sniGender && setSelectValue(genderEl, sniGender)) filled.push('Gender'); } // DOB — try multiple ID patterns Erie uses const rawDob = get(driver, 'BirthDate', 'birthDate', 'DateOfBirth', 'dateOfBirth', 'DOB', 'dob'); if (rawDob) { const dobFormatted = formatDate(rawDob); const dobEl = document.getElementById('txtDateOfBirth_2') || document.getElementById('SecondNamedInsured_DateOfBirth') || document.querySelector('[id*="DateOfBirth"][id*="2"], [id*="DOB"][id*="2"], [name*="DateOfBirth2"], [name*="DOB2"]'); if (dobEl && setInputValue(dobEl, dobFormatted)) filled.push('DOB'); } const licenseState = get(driver, 'DriverLicenseState', 'driverLicenseState', 'LicenseState', 'licenseState', 'DLState'); const effectiveLicenseState = licenseState || (riskState === 'NC' ? 'NC' : ''); const licenseStateEl = document.getElementById('selLicenseState2'); if (licenseStateEl && effectiveLicenseState && setSelectValue(licenseStateEl, effectiveLicenseState)) { filled.push('LicenseState'); } const licenseNum = get(driver, 'DriverLicenseNumber', 'driverLicenseNumber', 'LicenseNumber', 'licenseNumber', 'DLNumber', 'dlNumber'); const licenseNumEl = document.getElementById('licenseNumber2'); if (licenseNumEl && licenseNum && setInputValue(licenseNumEl, licenseNum)) filled.push('LicenseNumber'); if (filled.length > 0) { console.log(`Carrier Bridge: SNI filled — ${filled.join(', ')}`); } else { console.warn('Carrier Bridge: SNI dropdown set to "Other" but no second driver fields could be filled. Check driver data property names.'); } } // ──────────────────────────────────────────────────────────────────────────── // Agent Exchange — Address Information + Current Insurance Coverage page // ──────────────────────────────────────────────────────────────────────────── function isErieAddressInfoPage() { return window.location.hostname.includes('agentexchange.com') && window.location.pathname.toLowerCase().includes('/customer'); } function clickRadioYes(questionTextFragment) { // Find a label/td/span containing the question text, then find Yes radio nearby const allEls = Array.from(document.querySelectorAll('td, th, label, span, div, p')); const questionEl = allEls.find(el => !el.querySelector('input') && el.textContent.toLowerCase().includes(questionTextFragment.toLowerCase()) ); if (!questionEl) return false; const container = questionEl.closest('tr') || questionEl.closest('div') || questionEl.parentElement; if (!container) return false; // Look for radio buttons in the container or its siblings let radios = Array.from(container.querySelectorAll('input[type="radio"]')); if (radios.length === 0) { const parent = container.parentElement; radios = parent ? Array.from(parent.querySelectorAll('input[type="radio"]')) : []; } const yesRadio = radios.find(r => { const lbl = document.querySelector(`label[for="${r.id}"]`); const labelText = (lbl?.textContent || r.value || r.nextSibling?.textContent || '').toLowerCase(); return labelText.includes('yes') || r.value === 'true' || r.value === '1' || r.value.toLowerCase() === 'yes'; }); if (yesRadio && !yesRadio.checked) { yesRadio.checked = true; ['click', 'change', 'input'].forEach(ev => yesRadio.dispatchEvent(new Event(ev, { bubbles: true }))); if (typeof ko !== 'undefined') { try { ko.utils.triggerEvent(yesRadio, 'change'); } catch(e) {} } console.log(`Carrier Bridge: Clicked Yes for "${questionTextFragment}"`); return true; } return false; } // Maps a raw carrier name string to the value Erie's dropdown expects function mapToErieCarrierValue(rawName, type) { const n = (rawName || '').toLowerCase(); if (!n) return ''; if (n.includes('erie')) return type === 'home' ? 'ErieSecure' : 'ErieInsurance'; if (n.includes('state farm')) return 'StateFarm'; if (n.includes('allstate')) return 'Allstate'; if (n.includes('progressive')) return 'Progressive'; if (n.includes('geico')) return 'Geico'; if (n.includes('nationwide')) return 'Nationwide'; if (n.includes('liberty')) return 'LibertyMutual'; if (n.includes('farmers')) return 'Farmers'; if (n.includes('travelers')) return 'Travelers'; if (n.includes('usaa')) return 'USAA'; if (n.includes('westfield')) return 'Westfield'; if (n.includes('grange')) return 'Grange'; if (n.includes('auto-owners') || n.includes('auto owners')) return 'AutoOwners'; if (n.includes('american family')) return 'AmericanFamily'; if (n.includes('hartford')) return 'Hartford'; if (n.includes('chubb')) return 'Chubb'; if (n.includes('cincinnati')) return 'Cincinnati'; if (n.includes('motorists')) return 'Motorists'; if (n.includes('donegal')) return 'Donegal'; if (n.includes('safeco')) return 'Safeco'; if (n.includes('amica')) return 'Amica'; if (n.includes('acuity')) return 'Acuity'; if (n.includes('no carrier') || n.includes('no prior') || n.includes('none')) return 'NoPriorCarrier'; return 'Other'; } function fillErieAddressInfoPage(client) { console.log('Carrier Bridge: Filling Erie Customer page (address + insurance + SNI)...'); // Grab the KO viewmodel — most reliable way to set values on a KO app const vm = (() => { try { return (typeof plw !== 'undefined') ? plw.customer.viewmodel : null; } catch(e) { return null; } })(); const setObs = (obs, val) => { if (typeof obs === 'function') { try { obs(val); return true; } catch(e) {} } return false; }; // ── Address Information — Yes/No radio questions ────────────────────── // Q1: Has this been the mailing address for 3+ years? id="rdMailingAddressThreeYearsYes" if (vm) setObs(vm.MailingAddressThreeYears, true); const q1 = document.getElementById('rdMailingAddressThreeYearsYes'); if (q1 && !q1.checked) q1.click(); // Q2: Is the current mailing address where the named insured lives? id="rdResidentialSameAsMailingYes" if (vm) setObs(vm.ResidentialSameAsMailing, true); const q2 = document.getElementById('rdResidentialSameAsMailingYes'); if (q2 && !q2.checked) q2.click(); // Q3: Is the current mailing address the location of the property? name="locationSameAsMailing" (no ID) if (vm) setObs(vm.LocationSameAsMailing, true); const q3 = document.querySelector('input[name="locationSameAsMailing"][value="true"]'); if (q3 && !q3.checked) q3.click(); // ── County — id="selMailingCountyList" (appears after Q2 = Yes) ────── const county = client.County || client.county || client.CountyName || client.countyName || ''; if (county) { setTimeout(() => { // Try via KO viewmodel first (TerritoryAddress.County observable) if (vm && typeof vm.TerritoryAddress === 'function') { const territory = vm.TerritoryAddress(); if (territory && setObs(territory.County, county)) { console.log(`Carrier Bridge: County set via KO: "${county}"`); } } // Also set via DOM (handles both cases) selectCountyWithRetry('selMailingCountyList', county, 12, 250); }, 700); } // ── Current Insurance Coverage ──────────────────────────────────────── const policies = Array.isArray(client.policies) ? client.policies : []; const autoPolicy = policies.find(p => { const line = (p.LineOfBusiness || p.lineOfBusiness || p.PolicyType || p.policyType || '').toLowerCase(); return line.includes('auto') || line.includes('vehicle') || line.includes('car'); }); const homePolicy = policies.find(p => { const line = (p.LineOfBusiness || p.lineOfBusiness || p.PolicyType || p.policyType || '').toLowerCase(); return line.includes('home') || line.includes('homeowner') || line.includes('dwelling'); }); const erieAutoPolicy = policies.find(p => { const carrier = (p.CarrierName || p.carrierName || p.Carrier || p.carrier || '').toLowerCase(); const line = (p.LineOfBusiness || p.lineOfBusiness || p.PolicyType || p.policyType || '').toLowerCase(); return carrier.includes('erie') && (line.includes('auto') || line.includes('vehicle')); }); // Current Auto Insurer — id="CurrentAutoInsurer", values like "ErieInsurance", "StateFarm", etc. if (autoPolicy) { const raw = autoPolicy.CarrierName || autoPolicy.carrierName || autoPolicy.Carrier || autoPolicy.carrier || ''; const val = mapToErieCarrierValue(raw, 'auto'); if (val) { setObs(vm && vm.CurrentAutoInsurer, val); const el = document.getElementById('CurrentAutoInsurer'); if (el) { setSelectValue(el, val); console.log(`Carrier Bridge: Auto Insurer = "${val}"`); } } } // ERIE Policy Number — id="txtPriorAutoEriePolicyNumber" (only shown when CurrentAutoInsurer = ErieInsurance) // Rewrite/Spinoff — id="ErieAutoRewriteSpinoff" if (erieAutoPolicy) { const pnum = erieAutoPolicy.PolicyNumber || erieAutoPolicy.policyNumber || erieAutoPolicy.Number || erieAutoPolicy.number || ''; if (pnum) { setObs(vm && vm.PriorAutoEriePolicyNumber, pnum); const el = document.getElementById('txtPriorAutoEriePolicyNumber'); if (el) { setInputValue(el, pnum); console.log(`Carrier Bridge: Erie policy # = "${pnum}"`); } } setObs(vm && vm.ErieAutoRewriteSpinoff, 'Rewrite'); const rwEl = document.getElementById('ErieAutoRewriteSpinoff'); if (rwEl) setSelectValue(rwEl, 'Rewrite'); } // Current Home Insurer — id="CurrentHomeInsurer", values like "ErieSecure", "StateFarm", etc. if (homePolicy) { const raw = homePolicy.CarrierName || homePolicy.carrierName || homePolicy.Carrier || homePolicy.carrier || ''; const val = mapToErieCarrierValue(raw, 'home'); if (val) { setObs(vm && vm.CurrentHomeInsurer, val); const el = document.getElementById('CurrentHomeInsurer'); if (el) { setSelectValue(el, val); console.log(`Carrier Bridge: Home Insurer = "${val}"`); } } } // ── Second Named Insured — Gender (opposite of FNI) + DOB ───────────── // Gender: select id="selGender1" (FNI), id="selGender2" (SNI) // These are inside visible:EditModeForLockDown divs but still in DOM const fniGender = getGenderCode(client); const sniGender = fniGender === 'M' ? 'F' : fniGender === 'F' ? 'M' : ''; if (sniGender) { if (vm && vm.SecondNamedInsured && typeof vm.SecondNamedInsured.Gender === 'function') { vm.SecondNamedInsured.Gender(sniGender); console.log(`Carrier Bridge: SNI Gender = "${sniGender}" via KO`); } const gEl = document.getElementById('selGender2'); if (gEl) setSelectValue(gEl, sniGender); } // DOB: txtDateOfBirth_2 — click "Edit Date of Birth" link first to enter edit mode, then set const secondDriver = getSecondDriver(client); if (secondDriver) { const rawDob = secondDriver.BirthDate || secondDriver.birthDate || secondDriver.DateOfBirth || secondDriver.dateOfBirth || secondDriver.DOB || secondDriver.dob || ''; if (rawDob) { const dobFormatted = formatDate(rawDob); // Try KO viewmodel — DateOfBirthForm is a sub-observable if (vm && vm.SecondNamedInsured) { const sni = vm.SecondNamedInsured; try { const dobForm = typeof sni.DateOfBirthForm === 'function' ? sni.DateOfBirthForm() : sni.DateOfBirthForm; if (dobForm && typeof dobForm.DateOfBirth === 'function') { dobForm.DateOfBirth(dobFormatted); if (typeof sni.onNewUserEnteredDateOfBirth === 'function') sni.onNewUserEnteredDateOfBirth(dobFormatted); console.log(`Carrier Bridge: SNI DOB = "${dobFormatted}" via KO`); } } catch(e) { console.warn('Carrier Bridge: SNI DOB KO set failed', e); } } // Also click "Edit Date of Birth" link to surface the input, then fill DOM const editDobLink = document.getElementById('lnkEditDateOfBirth_2'); if (editDobLink) { editDobLink.click(); setTimeout(() => { const dobEl = document.getElementById('txtDateOfBirth_2'); if (dobEl) { setInputValue(dobEl, dobFormatted); dobEl.dispatchEvent(new Event('blur', { bubbles: true })); } }, 350); } } } } // ──────────────────────────────────────────────────────────────── // Helpers // ──────────────────────────────────────────────────────────────── function formatDate(dateStr) { if (!dateStr) return ''; try { const d = new Date(dateStr); if (isNaN(d.getTime())) return dateStr; const mm = (d.getMonth() + 1).toString().padStart(2, '0'); const dd = d.getDate().toString().padStart(2, '0'); const yyyy = d.getFullYear(); return `${mm}/${dd}/${yyyy}`; } catch { return dateStr; } } function findLabelFor(input) { let text = ""; if (input.id) { const label = document.querySelector(`label[for="${input.id}"]`); if (label) text += label.innerText + " "; } const parentLabel = input.closest('label'); if (parentLabel) text += parentLabel.innerText + " "; try { const matLabel = input.closest('mat-form-field')?.querySelector('mat-label'); if (matLabel) text += matLabel.innerText + " "; } catch(e) {} if (input.getAttribute('aria-label')) { text += input.getAttribute('aria-label') + " "; } const prev = input.previousElementSibling; if (prev && (prev.tagName === 'LABEL' || prev.tagName === 'SPAN' || prev.tagName === 'MAT-LABEL')) { text += prev.innerText + " "; } return text.trim(); } function getFirstPolicyValue(client, possibleKeys) { if (!client?.policies?.length) return ''; for (const p of client.policies) { for (const k of possibleKeys) { if (p[k] || p[k.toLowerCase()] || p[k.charAt(0).toUpperCase() + k.slice(1)]) { return p[k] || p[k.toLowerCase()] || p[k.charAt(0).toUpperCase() + k.slice(1)]; } } } return ''; } function getFirstVehicleValue(client, possibleKeys) { if (!client?.vehicles?.length) return ''; for (const v of client.vehicles) { for (const k of possibleKeys) { if (v[k] || v[k.toLowerCase()] || v[k.charAt(0).toUpperCase() + k.slice(1)]) { return v[k] || v[k.toLowerCase()] || v[k.charAt(0).toUpperCase() + k.slice(1)]; } } } return ''; }