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."); } }); } 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: Second Named Insured ──────────────────────────── if (isAgentExchangeCustomerPage()) { fillAgentExchangeSecondNamedInsured(client); } console.log(`Carrier Bridge: Auto-filled ${filledCount} fields.`); } // ──────────────────────────────────────────────────────────────────────────── // 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), 450); } function fillSniFields(driver, riskState) { 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 = []; const firstName = get(driver, 'FirstName', 'firstName', 'first_name', 'first'); 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'); if (setInputValue(document.getElementById('SecondNamedInsured_FirstName'), firstName)) filled.push('FirstName'); 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'); const gender = get(driver, 'Gender', 'gender', 'Sex', 'sex'); const genderEl = document.getElementById('selGender2'); if (genderEl && gender && setSelectValue(genderEl, gender)) filled.push('Gender'); const rawDob = get(driver, 'BirthDate', 'DateOfBirth', 'dateOfBirth', 'DOB', 'dob'); if (rawDob) { const dobFormatted = formatDate(rawDob); const dobEl = document.getElementById('txtDateOfBirth_2') || document.querySelector('[id^="txtDateOfBirth_"][id$="2"]'); 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.'); } } // ──────────────────────────────────────────────────────────────── // 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 ''; }