{"version":3,"file":"js/65-daa083bb32af8ac0f633.js","mappings":"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","sources":["webpack://realm_app/./app/javascript/utils/analyticsConfig.ts","webpack://realm_app/./app/javascript/utils/analytics.ts","webpack://realm_app/./app/javascript/utils/formatting.ts"],"sourcesContent":["export interface AnalyticsConfig {\n toSegment: boolean\n toConsole: boolean\n consoleStyle: string\n}\n\nconst analyticsConfigHelp = {\n toSegment: 'If true, send all tracking events to Segment.',\n toConsole: 'If true, send all tracking events to console.',\n consoleStyle:\n 'A limited CSS string, which a defines how the console output should look. See https://developer.mozilla.org/en-US/docs/Web/API/console#styling_console_output.',\n}\n\nexport let analyticsConfig: AnalyticsConfig = {\n toSegment: false,\n toConsole: false,\n consoleStyle: 'font-weight: bold; color: #4a957a',\n}\n\nexport const getAnalyticsConfig = function (): AnalyticsConfig {\n return { ...analyticsConfig }\n}\n\nfunction setAnalyticsConfig(newAnalyticsConfig: Partial) {\n if (typeof window === 'undefined') return\n\n // Poor man's deep copy\n const lastState = JSON.parse(JSON.stringify(analyticsConfig))\n\n // Maintain previous options if none are specified\n analyticsConfig = { ...analyticsConfig, ...newAnalyticsConfig }\n\n // Store our new state\n if (localStorage) {\n localStorage.setItem('analyticsConfig', JSON.stringify(analyticsConfig))\n }\n\n // Emit output\n if (analyticsConfig.toConsole && !lastState.toConsole) {\n emitHeader()\n }\n emitProperties()\n}\n\nfunction emitHeader() {\n const groups = [\n {\n text: '%c✨ Realm Analytics Config ✨',\n styles: [\n \"font-family:'Moderat',sans-serif;color:white;border-radius:6px;padding:8px 16px;margin-bottom:4px;font-size:16px;background:#4a957a;\",\n ],\n },\n {\n text: '%cTo configure, enter %csetAnalyticsConfig(options)%c current options are:',\n styles: [\n \"font-family:'Moderat',sans-serif;font-size:12px;color:#4a957a;padding-left:8px\",\n 'font-family:monospace;font-size: 12px;color:#6a6c6e;',\n \"font-family:'Moderat',sans-serif;font-size:12px;color:#4a957a;\",\n ],\n },\n ]\n console.log(\n groups.map((g) => g.text).join('%c\\r\\n'),\n ...groups.reduce((memo, g) => memo.concat([...g.styles, '']), [] as Array)\n )\n}\n\nfunction emitProperty(property: string) {\n const msg = analyticsConfigHelp[property]\n const val = analyticsConfig[property]\n console.groupCollapsed(\n `%c${property}%c: ${val}`,\n 'font-family:monospace;font-size:12px;color:#777;padding-left:16px;cursor:pointer;',\n 'font-family:monospace;font-size:12px;color:#6a6c6e;cursor:pointer;'\n )\n console.log(`%c${msg}`, \"font-family:'Moderat';font-size:12px;padding-left:8px;color:#4a957a\")\n console.groupEnd()\n}\n\nfunction emitProperties() {\n Object.keys(analyticsConfigHelp).forEach((prop) => emitProperty(prop))\n}\n\nfunction deserializeAnalyticsConfig() {\n try {\n const analyticsConfigString = localStorage.getItem('analyticsConfig')\n if (!analyticsConfigString) return null\n return JSON.parse(analyticsConfigString)\n } catch {\n /* failing to parse is ok. */\n return null\n }\n}\n\nfunction initializeAnalyticsConfig() {\n // This means nothing if we're not in the browser.\n if (typeof window === 'undefined') return\n\n // Recover config from localStorage, if any\n if (localStorage) {\n const newAnalyticsConfig = deserializeAnalyticsConfig()\n if (newAnalyticsConfig) {\n analyticsConfig = newAnalyticsConfig\n } else {\n analyticsConfig = {\n ...analyticsConfig,\n toSegment: !!document.body.dataset.segmentKey && !document.body.dataset.ml,\n toConsole: !document.body.dataset.segmentKey,\n }\n }\n }\n\n // log instrutions to console if appropriate\n if (analyticsConfig.toConsole) {\n emitHeader()\n emitProperties()\n }\n\n // mount method to set analytics config onto the global window.\n ;(window as any).setAnalyticsConfig = setAnalyticsConfig\n}\n\ninitializeAnalyticsConfig()\n","import { useState, useEffect } from 'react'\nimport { getAnalyticsConfig } from './analyticsConfig'\nimport { NonAppSignupType } from 'recoil/user'\n\nexport const UTM_PARAMS_SESSION_KEY = 'utm-params'\nexport const SEGMENT_UTM_PARAMS_SESSION_KEY = 'segment-utm-params'\nexport const STICKY_PROPS_KEY = 'sticky-props'\n\nexport interface UtmParams {\n utm_medium?: string\n utm_source?: string\n utm_campaign?: string\n utm_content?: string\n irclickid?: string // Honorary utm parameter. This is for our partnership with Impact.com\n}\n\n// We can't use EventTarget, since that is browser only (and we need to SSR)\n// We can't use EventEmitter since that is node only\n// So make a simple one ourselves.\n\nconst utmParamEventTarget = (function () {\n const listeners: Array<() => void> = []\n return {\n fire: () => listeners.forEach((listener) => listener()),\n addListener: (listener: () => void) => listeners.push(listener),\n removeListener: (listener: () => void) => {\n const index = listeners.indexOf(listener)\n listeners.splice(index, 1)\n },\n }\n})()\n\nexport function useUtmParams(): UtmParams {\n const [utmParams, setUtmParams] = useState({})\n\n useEffect(() => {\n const handleSetUtmParams = () => {\n const params = (window?.sessionStorage && window.sessionStorage.getItem(UTM_PARAMS_SESSION_KEY)) || '{}'\n const jParams = JSON.parse(params)\n setUtmParams(jParams)\n }\n utmParamEventTarget.addListener(handleSetUtmParams)\n\n // Set initial\n handleSetUtmParams()\n return () => {\n utmParamEventTarget.removeListener(handleSetUtmParams)\n }\n }, [])\n\n return utmParams\n}\n\nasync function waitForObjInWindow(obj): Promise {\n let attempts = 0\n const promise = new Promise((resolve) => {\n const check = () => {\n const w = window as any\n if (w[obj]) {\n resolve(w[obj])\n } else if (attempts < 5) {\n attempts += 1\n window.setTimeout(check, 300)\n } else {\n resolve(undefined)\n }\n }\n check()\n })\n return promise\n}\n\n// We can't make some calls like 'user' on analytics.js until it is\n// actually loaded in (calls like track, and identify get cached before\n// it is fully loaded, and emitted afterwards).\n// For those spots, wait for the lib to be ready before continuing.\nasync function waitForAnalytics(): Promise {\n const analytics = await waitForObjInWindow('analytics')\n if (analytics) {\n const success = await new Promise((resolve) => {\n let finished = false\n const complete = (success: boolean) => {\n if (finished) return\n finished = true\n clearTimeout(timer)\n resolve(success)\n }\n analytics.ready(() => complete(true))\n const timer = setTimeout(() => complete(false), 10000)\n })\n if (success) {\n // Now that it is loaded, grab the new full version and return that.\n return await waitForObjInWindow('analytics')\n } else console.warn('Failed to load analytics.js in time')\n }\n\n // This is the old, not ready (or undefined) analytics.\n return analytics\n}\n\nasync function impactTrackConversion(conversionId: number, conversionData: Record): Promise {\n const ire = await waitForObjInWindow('ire')\n if (ire) {\n ire('trackConversion', conversionId, conversionData)\n } else {\n console.info('ire trackConversion', conversionId, conversionData)\n }\n}\n\nexport interface IdentifyStub {\n id: number | string\n full_name: string\n email: string\n phone_number: string\n created_at: string\n\n // only users have this (not vendor users)\n entered_address?: string | null\n sha1_email?: string\n referred_by_user?: number | null\n non_app_signup_type?: NonAppSignupType\n\n // only vendor users have this:\n vendor_name?: string\n}\n\nexport async function identify(user: IdentifyStub): Promise {\n const analytics = await waitForAnalytics()\n const analyticsConfig = getAnalyticsConfig()\n\n // vendor portal and advisor hub are using the new \"Mixpanel (Actions)\" destination type\n // https://segment.com/docs/connections/destinations/catalog/actions-mixpanel/\n // In those cases, we send the attributes without a dollar sign, as segment will add it\n const traits: Record =\n user.id.toString().startsWith('vendor-contact-') || user.id.toString().startsWith('operations-admin-')\n ? {\n created: user.created_at,\n email: user.email,\n name: user.full_name,\n phone: user.phone_number,\n }\n : {\n $created: user.created_at,\n $email: user.email,\n $name: user.full_name,\n $phone: user.phone_number,\n }\n\n if (user.entered_address) {\n traits.address = user.entered_address\n }\n if (user.sha1_email) {\n traits.sha1Email = user.sha1_email\n }\n if (user.referred_by_user) {\n traits.referredByUser = user.referred_by_user\n }\n if (user.non_app_signup_type) {\n traits.unserviceable = user.non_app_signup_type == 'unserviceable'\n }\n if (user.vendor_name) {\n traits.vendorName = user.vendor_name\n }\n\n const oldAnonymousId = analytics?.user()?.anonymousId()\n\n if (analytics && analyticsConfig.toSegment) {\n await new Promise((resolve) => analytics.identify(user.id, traits, resolve))\n }\n if (analyticsConfig.toConsole) {\n console.info('%cidentify %s %o', analyticsConfig.consoleStyle, user.id, traits)\n }\n\n // If we're identified with a userId, and we re-identify with another\n // userId, it will reset our anonymousId as well, and we won't be able\n // to link the events of the two accounts together logically.\n // Therefore, remember the old anonymous id, and send a custom event\n // to link the two anonymous ids together.\n if (analytics && analyticsConfig.toSegment) {\n if (oldAnonymousId && oldAnonymousId != analytics.user().anonymousId()) {\n // Send our linking event\n const options = { old_id: oldAnonymousId, new_id: analytics.user().anonymousId() }\n await new Promise((resolve) => analytics.track('anonymous_id link', options, resolve))\n // In this case, only bother sending to console it if we sent it to segment.\n if (analyticsConfig.toConsole) {\n console.info('%c%s %o', analyticsConfig.consoleStyle, 'anonymous_id link', options)\n }\n }\n }\n\n // gtm:\n const dataLayer = await waitForObjInWindow('dataLayer')\n dataLayer?.push({\n userId: user.id,\n userSha1Email: user.sha1_email,\n userName: user.full_name,\n userEmail: user.email,\n userPhone: user.phone_number,\n userAddress: user.entered_address,\n })\n}\n\nexport async function addTraits(traits: Record): Promise {\n const analytics = await waitForObjInWindow('analytics')\n const analyticsConfig = getAnalyticsConfig()\n if (analytics && analyticsConfig.toSegment) {\n analytics.identify(traits)\n }\n if (analyticsConfig.toConsole) {\n console.info('%cidentify %o', analyticsConfig.consoleStyle, traits)\n }\n}\n\nexport function addStickyProps(props: Record, sessionOnly = false): void {\n const storage = sessionOnly ? 'sessionStorage' : 'localStorage'\n if (window?.[storage]) {\n window[storage].setItem(STICKY_PROPS_KEY, JSON.stringify({ ...getStickyProps(sessionOnly), ...props }))\n }\n}\n\nfunction getStickyProps(sessionOnly = false): Record {\n const storage = sessionOnly ? 'sessionStorage' : 'localStorage'\n if (!window?.[storage]) return {}\n const sAllProps = window[storage].getItem(STICKY_PROPS_KEY)\n return (sAllProps?.length || 0) > 0 ? JSON.parse(sAllProps || '') : {}\n}\n\nconst campaignKeywords = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'irclickid']\n// Returns an object map of the raw utm key to the raw utm value.\nfunction utmParams(): Record {\n const params = new URLSearchParams(location.search)\n return campaignKeywords.reduce((a, param) => {\n const value = params.get(param)\n if (value && value.length > 0) {\n a[param] = value\n }\n return a\n }, {})\n}\n// Returns an object map of the utm key, denoted with a \"[last touch]\" string,\n// to the utm value with any non-word characters replaced by spaces.\nfunction campaignParams(rawParams: Record): Record {\n return campaignKeywords.reduce((a, param) => {\n const value = rawParams[param]\n if (value && value.length > 0) {\n a[`${param} [last touch]`] = value\n }\n return a\n }, {})\n}\n\nexport function captureUTMParams(): {\n rawParams: Record\n params: Record\n} {\n const rawParams = utmParams()\n const params = campaignParams(rawParams)\n if (Object.keys(params).length > 0 && window?.sessionStorage) {\n window.sessionStorage.setItem(SEGMENT_UTM_PARAMS_SESSION_KEY, JSON.stringify(params))\n // Also store for general reference\n window.sessionStorage.setItem(UTM_PARAMS_SESSION_KEY, JSON.stringify(rawParams))\n utmParamEventTarget.fire()\n }\n return { rawParams, params }\n}\n\nexport async function trackPage(name?: string, customParams?: Record): Promise {\n const analytics = await waitForObjInWindow('analytics')\n const analyticsConfig = getAnalyticsConfig()\n // Extract UTM params and store in session storage.\n const { params: utmParams } = captureUTMParams()\n\n if (Object.keys(utmParams).length > 0) {\n // Update our identity for any *fresh* utm params.\n if (analytics && analyticsConfig.toSegment) {\n analytics.identify(utmParams)\n }\n if (analyticsConfig.toConsole) {\n console.info('%cidentify %o', analyticsConfig.consoleStyle, utmParams)\n }\n }\n\n // Grab any UTM params from session storage\n const sPageParams = (window?.sessionStorage && window.sessionStorage.getItem(SEGMENT_UTM_PARAMS_SESSION_KEY)) || null\n const pageParams =\n (sPageParams?.length || 0) > 0\n ? JSON.parse(sPageParams || '')\n : // Fallback to fresh UTM params if session storage is empty\n // this may happen if we don't have access to session storage.\n Object.keys(utmParams).length > 0\n ? utmParams\n : undefined\n\n const stickyProps = getStickyProps()\n const tackyProps = getStickyProps(true)\n // Fire trackPage event.\n if (analytics && analyticsConfig.toSegment) {\n analytics.page(name || document.body.dataset.pageName, {\n ...pageParams,\n ...stickyProps,\n ...tackyProps,\n ...customParams,\n })\n }\n if (analyticsConfig.toConsole) {\n console.info('%ctrackPage %s', analyticsConfig.consoleStyle, name, {\n ...pageParams,\n ...stickyProps,\n ...tackyProps,\n ...customParams,\n })\n }\n}\n\ninterface ImpactArgs {\n id: number\n args?: Record\n}\n// name and args are sent to segment as-is. impactArgs useful for impact tracking\nexport async function track(name: string, args?: Record, impactArgs?: ImpactArgs): Promise {\n // segment:\n const analytics = await waitForObjInWindow('analytics')\n const analyticsConfig = getAnalyticsConfig()\n const stickyProps = getStickyProps()\n const tackyProps = getStickyProps(true)\n if (analytics && analyticsConfig.toSegment) {\n await new Promise((resolve) => analytics.track(name, { ...stickyProps, ...tackyProps, ...args }, resolve))\n }\n if (analyticsConfig.toConsole) {\n console.info('%c%s %o', analyticsConfig.consoleStyle, name, { ...stickyProps, ...tackyProps, ...args })\n }\n // impact: https://app.impact.com/secure/advertiser/tracking-settings/actiontracker/view-actiontracker-flow.ihtml?execution=e6s1\n if (impactArgs) {\n await impactTrackConversion(impactArgs.id, impactArgs.args || {})\n }\n}\nexport function trackSync(name: string, args?: Record): void {\n const analytics = window['analytics']\n const analyticsConfig = getAnalyticsConfig()\n const stickyProps = getStickyProps()\n const tackyProps = getStickyProps(true)\n if (analytics && analyticsConfig.toSegment) {\n analytics.track(name, args)\n }\n if (analyticsConfig.toConsole) {\n console.info('%c%s %o', analyticsConfig.consoleStyle, name, { ...stickyProps, ...tackyProps, ...args })\n }\n}\n","import { PropertyHead } from 'recoil/properties'\n\nconst currencyFormatter = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n})\n\nconst numberFormatter = new Intl.NumberFormat('en-US')\n\n// Money should never round. We use it for exact amounts (report prices)\nexport const formatMoney = (val: number | string | null): string => {\n if (val === null) return '$??.??'\n\n return currencyFormatter.format(Number(val))\n}\n\nexport const formatMoneyNoDecimals = (val: number | string | null): string => {\n return formatMoney(val).replace(/.[\\d]{2}$/, '')\n}\n\nexport function formatCurrency(input: string | number, long?: boolean): string {\n const num = Number(input)\n\n // 995,000 and up, round to 10,000's place.\n let rNum = round(num, 10000)\n if (Math.abs(rNum) >= 1000000) {\n if (long) return formatMoneyNoDecimals(rNum)\n return `$${(rNum / 1000000).toFixed(2)}M`\n }\n // 4,950 and up, round to 1000's place\n rNum = round(num, 100) // Round to 100 to match boundary on next condition\n if (Math.abs(rNum) >= 5000) {\n rNum = round(num, 1000) // But round to 1000 for display\n if (long) return formatMoneyNoDecimals(rNum)\n return `$${rNum / 1000}k`\n }\n // 495 and up, round to 100's place.\n rNum = round(num, 10) // Round to 10 to match boundary on next condition\n if (Math.abs(rNum) >= 500) {\n rNum = round(num, 100) // But round to 100 for display\n if (long) return formatMoneyNoDecimals(rNum)\n return `$${rNum}`\n }\n // Round to 10s place.\n rNum = round(num, 10)\n return `$${rNum}`\n}\n\n// deprecated\nexport function formatCurrencyShort(input: string | number): string {\n return formatCurrency(input)\n}\n\n// deprecated\nexport function formatCurrencyLong(input: string | number): string {\n return formatCurrency(input)\n}\n\nexport function getInitials(fullName: string): string {\n const parts = fullName.split(' ').filter((x) => Boolean(x))\n\n if (parts.length == 2) {\n return parts[0].charAt(0) + parts[1].charAt(0)\n }\n\n return parts.length == 1 ? parts[0].charAt(0) : ''\n}\n\nexport function round(num, roundTo) {\n return Math.round(num / roundTo) * roundTo\n}\n\nexport function formatNumber(val: number): string {\n return numberFormatter.format(val)\n}\n\nexport function formatPercentage(input: string | number, decimals = 2): string {\n return `${Number(input).toFixed(decimals)}%`\n}\n\nexport function numberWithCommas(input: number): string {\n return input.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n}\n\nexport function formatPropertyAddress(property: PropertyHead): string {\n return [\n property.delivery_line_1,\n property.delivery_line_2,\n property.city,\n property.state,\n property.zip9 ?? property.zip5,\n ]\n .filter((el) => el != null)\n .join(', ')\n}\n\nexport function formatPublicPropertyAddress(publicProperty: AttomTaxAssessor): string {\n return [\n formatAddressCasing(publicProperty.propertyaddressfull),\n titleCase(publicProperty.propertyaddresscity),\n publicProperty.propertyaddressstate.toUpperCase(),\n publicProperty.propertyaddresszip,\n ]\n .filter((el) => el && el.length > 0)\n .join(', ')\n}\n\nexport function formatPublicPropertyAddress2nd(publicProperty: AttomTaxAssessor): string {\n return [\n titleCase(publicProperty.propertyaddresscity),\n publicProperty.propertyaddressstate.toUpperCase(),\n publicProperty.propertyaddresszip,\n ]\n .filter((el) => el != null)\n .join(', ')\n}\n\nexport function formatPublicPropertyAddressPrediction(publicProperty: AttomTaxAssessor): string {\n const addr = formatAddressCasing(publicProperty.propertyaddressfull)\n const city = titleCase(publicProperty.propertyaddresscity)\n const state = publicProperty.propertyaddressstate.toUpperCase()\n return `${addr}, ${city}, ${state}, USA`\n}\n\nexport function formatOrdinal(value: string | number): string {\n let suffix = 'th'\n\n if (Number(value) < 4 || Number(value) > 13) {\n const string = String(value)\n const char = string.charAt(string.length - 1)\n suffix = char === '1' ? 'st' : char === '2' ? 'nd' : char === '3' ? 'rd' : 'th'\n }\n\n return `${value}${suffix}`\n}\n\nexport function formatAddressCasing(address: string): string {\n return titleCase(address)\n}\n\nexport function squareFeetInAcres(squareFeet: number): string {\n return (squareFeet / 43560).toFixed(2)\n}\n\nexport function formatLotSize(lotSizeInFeet: number): string {\n if (lotSizeInFeet / 43560 < 0.2) {\n return `${numberWithCommas(lotSizeInFeet)} sqft`\n } else {\n return `${squareFeetInAcres(lotSizeInFeet)} acres`\n }\n}\n\nexport function titleCase(str: string): string {\n if (!str) return ''\n\n return str.replace(/\\w\\S*/g, capitalize)\n}\n\nexport function capitalize(txt: string): string {\n return txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase()\n}\n\nexport function capitalizeFirst(txt: string): string {\n return txt.charAt(0).toUpperCase() + txt.slice(1)\n}\n\nexport function formatPhoneNumber(phone: string): string {\n if (!phone) return ''\n\n const match = /([+]?1)?\\s*[(]?(?\\d{3})?[)]?\\s*(?\\d{3})\\s*[-]?\\s*(?\\d{4})/.exec(phone)\n\n if (match && match.groups) {\n const area: undefined | string = match.groups['area']\n const prefix: string = match.groups['prefix']\n const line: string = match.groups['line']\n\n const formatted = `${prefix}-${line}`\n\n if (area) return `(${area}) ${formatted}`\n return formatted\n }\n\n return phone\n}\n\nexport function humanize(str: string): string {\n return str.replace(/_/g, ' ').replace(/^./, (match) => match.toUpperCase())\n}\n\nexport function truncate(text, length = 12, separator = '…', frontChars = 6, backChars = 5) {\n if (text.length <= length) return text\n\n return text.substr(0, frontChars) + separator + text.substr(text.length - backChars)\n}\n\nexport function toSentence(array: string[], separator: string = ', ', lastSeparator: string = ', and ') {\n if (array.length === 0) return ''\n if (array.length === 1) return array[0]\n return array.slice(0, -1).join(separator) + lastSeparator + array[array.length - 1]\n}\n"],"names":["analyticsConfigHelp","toSegment","toConsole","consoleStyle","analyticsConfig","getAnalyticsConfig","setAnalyticsConfig","newAnalyticsConfig","window","lastState","JSON","parse","stringify","localStorage","setItem","emitHeader","emitProperties","groups","text","styles","console","log","map","g","join","reduce","memo","concat","Object","keys","forEach","prop","property","msg","val","groupCollapsed","groupEnd","emitProperty","analyticsConfigString","getItem","deserializeAnalyticsConfig","document","body","dataset","segmentKey","ml","initializeAnalyticsConfig","UTM_PARAMS_SESSION_KEY","SEGMENT_UTM_PARAMS_SESSION_KEY","STICKY_PROPS_KEY","utmParamEventTarget","listeners","fire","listener","addListener","push","removeListener","index","indexOf","splice","useUtmParams","utmParams","setUtmParams","useState","useEffect","handleSetUtmParams","params","sessionStorage","jParams","waitForObjInWindow","obj","attempts","Promise","resolve","check","w","setTimeout","identify","user","analytics","finished","complete","success","clearTimeout","timer","ready","warn","waitForAnalytics","traits","id","toString","startsWith","created","created_at","email","name","full_name","phone","phone_number","$created","$email","$name","$phone","entered_address","address","sha1_email","sha1Email","referred_by_user","referredByUser","non_app_signup_type","unserviceable","vendor_name","vendorName","oldAnonymousId","anonymousId","info","options","old_id","new_id","track","dataLayer","userId","userSha1Email","userName","userEmail","userPhone","userAddress","addTraits","addStickyProps","props","sessionOnly","storage","getStickyProps","sAllProps","length","campaignKeywords","captureUTMParams","rawParams","URLSearchParams","location","search","a","param","value","get","campaignParams","trackPage","customParams","sPageParams","pageParams","stickyProps","tackyProps","page","pageName","args","impactArgs","conversionId","conversionData","ire","impactTrackConversion","trackSync","currencyFormatter","Intl","NumberFormat","style","currency","numberFormatter","formatMoney","format","Number","formatMoneyNoDecimals","replace","formatCurrency","input","long","num","rNum","round","Math","abs","toFixed","getInitials","fullName","parts","split","filter","x","Boolean","charAt","roundTo","formatNumber","formatPercentage","decimals","numberWithCommas","formatPropertyAddress","delivery_line_1","delivery_line_2","city","state","zip9","zip5","el","formatPublicPropertyAddress","publicProperty","formatAddressCasing","propertyaddressfull","titleCase","propertyaddresscity","propertyaddressstate","toUpperCase","propertyaddresszip","formatPublicPropertyAddress2nd","formatPublicPropertyAddressPrediction","formatOrdinal","suffix","string","String","char","formatLotSize","lotSizeInFeet","squareFeet","str","capitalize","txt","slice","toLowerCase","capitalizeFirst","formatPhoneNumber","match","exec","area","formatted","humanize","truncate","separator","frontChars","backChars","substr","toSentence","array","lastSeparator"],"sourceRoot":""}