{"version":3,"file":"farmalytics.min.js","sources":["../ClientApp/src/CONFIG.ts","../ClientApp/src/modules/hyperstorage/hyperstorage.ts","../ClientApp/src/modules/referrer-to-source/referrer-to-source.ts","../ClientApp/node_modules/js-cookie/dist/js.cookie.mjs","../ClientApp/src/Farmalytics.ts","../ClientApp/src/modules/hash/hash.ts","../ClientApp/src/modules/ga/ga.ts"],"sourcesContent":["// API end point for tracking source\r\nexport const API_ENDPOINT = \"https://farmalytics.infarmbureau.com\";\r\n// How long should a source last in Days\r\nexport const EXPIRE_SOURCE_DAYS = 30;\r\n// How long should a source last for in MS\r\nexport const EXPIRE_SOURCE_MS = EXPIRE_SOURCE_DAYS * 24 * 60 * 60 * 1000;\r\n// Which URL Params should we capture and process\r\nexport const ALLOWED_PARAMS = [\r\n \"utm_source\",\r\n \"utm_content\",\r\n \"utm_campaign\",\r\n \"utm_medium\",\r\n \"utm_id\",\r\n \"utm_term\",\r\n];\r\n// Known Sources - so we can tag them appropriately\r\nexport const WEBSITE_SOURCES = {\r\n \"(facebook).*(ad_id)\": {\r\n source: \"Facebook\",\r\n medium: \"social-paid\",\r\n },\r\n facebook: {\r\n source: \"Facebook\",\r\n medium: \"social\",\r\n },\r\n tiktok: {\r\n source: \"TikTok\",\r\n medium: \"social\",\r\n },\r\n twitter: {\r\n source: \"Twitter\",\r\n medium: \"social\",\r\n },\r\n linkedin: {\r\n source: \"LinkedIn\",\r\n medium: \"social\",\r\n },\r\n youtube: {\r\n source: \"Youtube\",\r\n medium: \"social\",\r\n },\r\n gmail: {\r\n source: \"Gmail\",\r\n medium: \"email\",\r\n },\r\n google: {\r\n source: \"Google\",\r\n medium: \"organic\",\r\n },\r\n wallethub: {\r\n source: \"WalletHub\",\r\n medium: \"social\",\r\n },\r\n yelp: {\r\n source: \"Yelp\",\r\n medium: \"social\",\r\n },\r\n clearsurance: {\r\n source: \"Clearsurance\",\r\n medium: \"social\",\r\n },\r\n};\r\n","const STORE_KEY = \"FAV2\";\r\n// A fast way to pull structured \r\n// data from LocalStorage \r\nconst getFromLocalStorage = () => {\r\n let data = {};\r\n try {\r\n data = JSON.parse((localStorage).getItem(STORE_KEY) || \"{}\");\r\n }\r\n catch (e) {\r\n console.error(e.message);\r\n }\r\n return data || {};\r\n};\r\n/**\r\n * HyperStorage\r\n * An object friendly version of LocalStorage using a single data store.\r\n * @returns hyperStorage\r\n */\r\nconst HyperstorageInit = () => {\r\n // Get Current Data from LS\r\n let data = getFromLocalStorage();\r\n /**\r\n * Save\r\n * Save data to Local Storage\r\n */\r\n const save = () => {\r\n localStorage.setItem(STORE_KEY, JSON.stringify(data));\r\n };\r\n return {\r\n getItem(key) {\r\n return data[key];\r\n },\r\n update(newData = {}) {\r\n data = Object.assign(Object.assign({}, data), newData);\r\n save();\r\n },\r\n setItem(key, value) {\r\n data[key] = value;\r\n save();\r\n },\r\n deleteItem(key) {\r\n delete data[key];\r\n save();\r\n },\r\n clear() {\r\n data = {};\r\n save();\r\n },\r\n };\r\n};\r\nexport default HyperstorageInit();\r\n","import { WEBSITE_SOURCES } from \"../../CONFIG\";\r\n/**\r\n * Converts a Referrer URL to a SourceType\r\n * See the WEBSITE_SOURCES for the patterns to identify\r\n * each of the known urls\r\n * @param url\r\n * @returns\r\n */\r\nexport const urlToSource = (url) => {\r\n // Get array of keys from our source map (all known sources we track)\r\n const sourceKeys = Object.keys(WEBSITE_SOURCES);\r\n // Match to the first source key\r\n const match = sourceKeys.find((searchKey) => {\r\n return url.search(new RegExp(searchKey, 'gi')) > -1;\r\n });\r\n // Return accordingly\r\n if (match) {\r\n const source = WEBSITE_SOURCES[match];\r\n // Set now as the created date\r\n source.created = new Date();\r\n return source;\r\n }\r\n return undefined;\r\n};\r\n/**\r\n * Referrer to Source\r\n * Converts a known referral URL to a source Type\r\n * @param url\r\n * @returns\r\n */\r\nexport const ReferrerToSource = (url) => {\r\n // Get the document - for jest testing \r\n const document = (typeof window !== \"undefined\") ? window.document : undefined;\r\n // If url is provided, use that as our referral\r\n // other wise look at document.referrer \r\n const referrer = url || (document === null || document === void 0 ? void 0 : document.referrer);\r\n // Referrer empty is a string --- \"\"\r\n if (referrer == \"\" || !referrer)\r\n return undefined;\r\n // Parse to Source \r\n return urlToSource(referrer);\r\n};\r\n","/*! js-cookie v3.0.5 | MIT */\n/* eslint-disable no-var */\nfunction assign (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n target[key] = source[key];\n }\n }\n return target\n}\n/* eslint-enable no-var */\n\n/* eslint-disable no-var */\nvar defaultConverter = {\n read: function (value) {\n if (value[0] === '\"') {\n value = value.slice(1, -1);\n }\n return value.replace(/(%[\\dA-F]{2})+/gi, decodeURIComponent)\n },\n write: function (value) {\n return encodeURIComponent(value).replace(\n /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,\n decodeURIComponent\n )\n }\n};\n/* eslint-enable no-var */\n\n/* eslint-disable no-var */\n\nfunction init (converter, defaultAttributes) {\n function set (name, value, attributes) {\n if (typeof document === 'undefined') {\n return\n }\n\n attributes = assign({}, defaultAttributes, attributes);\n\n if (typeof attributes.expires === 'number') {\n attributes.expires = new Date(Date.now() + attributes.expires * 864e5);\n }\n if (attributes.expires) {\n attributes.expires = attributes.expires.toUTCString();\n }\n\n name = encodeURIComponent(name)\n .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)\n .replace(/[()]/g, escape);\n\n var stringifiedAttributes = '';\n for (var attributeName in attributes) {\n if (!attributes[attributeName]) {\n continue\n }\n\n stringifiedAttributes += '; ' + attributeName;\n\n if (attributes[attributeName] === true) {\n continue\n }\n\n // Considers RFC 6265 section 5.2:\n // ...\n // 3. If the remaining unparsed-attributes contains a %x3B (\";\")\n // character:\n // Consume the characters of the unparsed-attributes up to,\n // not including, the first %x3B (\";\") character.\n // ...\n stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];\n }\n\n return (document.cookie =\n name + '=' + converter.write(value, name) + stringifiedAttributes)\n }\n\n function get (name) {\n if (typeof document === 'undefined' || (arguments.length && !name)) {\n return\n }\n\n // To prevent the for loop in the first place assign an empty array\n // in case there are no cookies at all.\n var cookies = document.cookie ? document.cookie.split('; ') : [];\n var jar = {};\n for (var i = 0; i < cookies.length; i++) {\n var parts = cookies[i].split('=');\n var value = parts.slice(1).join('=');\n\n try {\n var found = decodeURIComponent(parts[0]);\n jar[found] = converter.read(value, found);\n\n if (name === found) {\n break\n }\n } catch (e) {}\n }\n\n return name ? jar[name] : jar\n }\n\n return Object.create(\n {\n set,\n get,\n remove: function (name, attributes) {\n set(\n name,\n '',\n assign({}, attributes, {\n expires: -1\n })\n );\n },\n withAttributes: function (attributes) {\n return init(this.converter, assign({}, this.attributes, attributes))\n },\n withConverter: function (converter) {\n return init(assign({}, this.converter, converter), this.attributes)\n }\n },\n {\n attributes: { value: Object.freeze(defaultAttributes) },\n converter: { value: Object.freeze(converter) }\n }\n )\n}\n\nvar api = init(defaultConverter, { path: '/' });\n/* eslint-enable no-var */\n\nexport { api as default };\n","/**\r\n * Farmalytics Main Library\r\n * Author: Brandon Corbin \r\n * Team: Jarvis\r\n * Date: Nov 8 2021\r\n */\r\nimport { API_ENDPOINT, EXPIRE_SOURCE_MS } from \"./CONFIG\";\r\nimport { isGA3, isGA4, getUTMParams, } from \"./modules/ga/ga\";\r\nimport toHash from \"./modules/hash/hash\";\r\nimport hyperstorage from \"./modules/hyperstorage/hyperstorage\";\r\nimport { ReferrerToSource } from \"./modules/referrer-to-source/referrer-to-source\";\r\nimport Cookies from 'js-cookie';\r\nclass Hit {\r\n}\r\nclass FaCookieManager {\r\n}\r\nFaCookieManager.cookieName = 'farmalytics';\r\nFaCookieManager.proxyFarmalyticsCookieName = '!Proxy!apiProxyfarmalyticsClientId';\r\nFaCookieManager.farmalyticsCookieName = 'farmalyticsClientId';\r\nFaCookieManager.save = (hit) => {\r\n Cookies.set(FaCookieManager.cookieName, JSON.stringify(hit));\r\n};\r\nFaCookieManager.saveCookies = (hit) => {\r\n if (hit.clientId !== null && hit.clientId !== undefined) {\r\n Cookies.set(FaCookieManager.farmalyticsCookieName, hit.clientId, {\r\n domain: '.infarmbureau.com',\r\n secure: true\r\n });\r\n Cookies.set(FaCookieManager.proxyFarmalyticsCookieName, hit.clientId, {\r\n domain: '.infarmbureau.com',\r\n secure: true\r\n });\r\n }\r\n};\r\nFaCookieManager.get = () => {\r\n const value = Cookies.get(FaCookieManager.cookieName);\r\n if (value) {\r\n var parsed = JSON.parse(value);\r\n if (parsed) {\r\n return parsed;\r\n }\r\n }\r\n return null;\r\n};\r\n/**\r\n * Generates a unique ID from a Source\r\n * @param source\r\n * @returns\r\n */\r\nconst getSourceHash = (source) => {\r\n return toHash(`${source.campaign}${source.medium}${source.source}`);\r\n};\r\n/**\r\n * Farmalytics Initializer\r\n * Setups Farmatics\r\n */\r\nconst FarmalyticsInitializer = () => {\r\n /**\r\n * Farmalytics State\r\n * Holds sources, property info and client id\r\n */\r\n const state = {\r\n sources: [],\r\n propertyId: null,\r\n clientId: null,\r\n shouldTrack: false,\r\n };\r\n /**\r\n * Retrieve GA4 Client ID from local storage or cookies\r\n * @returns {string | null} The retrieved Client ID or null if not found\r\n */\r\n const retrieveGA4ClientId = () => {\r\n let clientId = null;\r\n // Try to get the client ID from local storage\r\n const gaLocalStorage = Object.keys(localStorage).find(key => key.includes('_ga'));\r\n if (gaLocalStorage) {\r\n const storedValue = localStorage.getItem(gaLocalStorage);\r\n if (storedValue) {\r\n const parts = storedValue.split('.');\r\n if (parts.length >= 2) {\r\n clientId = parts.slice(-2).join(\".\");\r\n }\r\n }\r\n }\r\n // If not found in local storage, try cookies\r\n if (!clientId) {\r\n const gaCookie = document.cookie.split('; ').find(row => row.startsWith('_ga'));\r\n if (gaCookie) {\r\n const cookieValue = gaCookie.split('=')[1];\r\n if (cookieValue) {\r\n const parts = cookieValue.split('.');\r\n if (parts.length >= 2) {\r\n clientId = parts.slice(-2).join(\".\");\r\n }\r\n }\r\n }\r\n }\r\n // Ensure clientId is not '0.0' and return\r\n return clientId && clientId !== '0.0' ? clientId : null;\r\n };\r\n /**\r\n * Initialize Farmalytics\r\n * @param config\r\n * @returns Promise void\r\n */\r\n const init = async (config) => {\r\n state.propertyId = config.propertyId;\r\n state.sources = (hyperstorage.getItem(\"sources\") || []).filter((source) => {\r\n const expiresOn = new Date(source.created).getTime() + EXPIRE_SOURCE_MS;\r\n return new Date().getTime() < expiresOn;\r\n });\r\n /**\r\n * Handle Active Srouce\r\n * If a source is detected (any utm_* param)\r\n * it will be saved to the users hyperstorage\r\n */\r\n let activeSource = getUTMParams();\r\n /**\r\n * If we have no sources for this user, lets check the referrer\r\n * For any knowns sources\r\n */\r\n if (state.sources.length === 0 && !activeSource) {\r\n activeSource = ReferrerToSource();\r\n }\r\n if (activeSource) {\r\n // We have an active Source\r\n if (config.debug)\r\n console.log(`🔥 Active Source`, activeSource);\r\n // Does this source hash already exist?\r\n const activeHash = getSourceHash(activeSource);\r\n const exists = state.sources.find((s) => getSourceHash(s) === activeHash);\r\n // If a source doesn't exxist we'll add it\r\n if (!exists) {\r\n // This is a new Source for this user\r\n state.sources.unshift(activeSource); // Add source to first position\r\n state.sources = state.sources.filter((s, index) => index < 6);\r\n // Save to hyperstorage\r\n hyperstorage.setItem(\"sources\", state.sources);\r\n // Track when we're loaded since it's a new\r\n state.shouldTrack = true;\r\n if (config.debug)\r\n console.log(\"📁 Saved new source\", activeSource);\r\n }\r\n }\r\n // Return a promise since GA might not be fully loaded yet.\r\n return new Promise(async (resolve) => {\r\n // Wait till we can get Client ID from Google Analytics\r\n state.clientId = retrieveGA4ClientId() || \"Blocked\";\r\n // Log out if logging\r\n if (config.debug) {\r\n console.log(`🐐 Property: ${state.propertyId}, Client: ${state.clientId}, GA: ${isGA3() ? \"V3\" : isGA4() ? \"V4\" : \"⚠️ Unknown\"}`);\r\n }\r\n // Return promise\r\n if (state.shouldTrack) {\r\n setTimeout(() => {\r\n track()\r\n .then((res) => {\r\n if (config.debug)\r\n console.log(res);\r\n })\r\n .catch((e) => {\r\n console.error(e);\r\n });\r\n state.shouldTrack = false;\r\n }, 500);\r\n }\r\n resolve();\r\n });\r\n };\r\n /**\r\n * Get Client ID\r\n * This function will be publicly exposed and will return the GA4 client ID\r\n * @returns {string | null} The retrieved Client ID or null if not found\r\n */\r\n const getClientId = () => {\r\n return retrieveGA4ClientId();\r\n };\r\n const getPropertyId = () => {\r\n return state.propertyId;\r\n };\r\n /**\r\n * Get Sources\r\n * Returns an Array of the users last 5 sources\r\n * @returns Array Sources\r\n */\r\n const getSources = () => {\r\n return state.sources;\r\n };\r\n /**\r\n * Get Source\r\n * Returns the users last known source\r\n * @returns UTM Source\r\n */\r\n const getSource = () => {\r\n return state.sources.length ? state.sources[0] : undefined;\r\n };\r\n /**\r\n * Get the URL for the API endpoint to hit\r\n * @returns URL one level above executing script\r\n */\r\n const getUrl = () => {\r\n try {\r\n if ((document === null || document === void 0 ? void 0 : document.currentScript) && typeof document.currentScript !== 'undefined') {\r\n const url = new URL(`${document.currentScript.src}/..`);\r\n return url.href;\r\n }\r\n return API_ENDPOINT;\r\n }\r\n catch (e) {\r\n return API_ENDPOINT;\r\n }\r\n };\r\n const originUrl = getUrl();\r\n /**\r\n * Track Source to API\r\n */\r\n const track = async () => {\r\n const source = getSource();\r\n const payload = {\r\n clientId: state.clientId,\r\n referrer: document.referrer,\r\n propertyId: state.propertyId,\r\n source: (source || {}).source || \"na\",\r\n campaign: (source || {}).campaign || \"na\",\r\n medium: (source || {}).medium || \"na\",\r\n host: window.location.origin,\r\n };\r\n FaCookieManager.save(payload);\r\n const push = await fetch((Farmalytics === null || Farmalytics === void 0 ? void 0 : Farmalytics.originUrl) || API_ENDPOINT, {\r\n method: \"POST\",\r\n headers: {\r\n \"content-type\": \"application/json\",\r\n },\r\n body: JSON.stringify(payload),\r\n mode: 'cors'\r\n });\r\n const resp = await push.json();\r\n FaCookieManager.saveCookies(resp);\r\n return resp;\r\n };\r\n const getHistory = () => FaCookieManager.get();\r\n // Return functions\r\n return {\r\n init,\r\n getClientId,\r\n getPropertyId,\r\n getSources,\r\n getSource,\r\n track,\r\n originUrl,\r\n getHistory\r\n };\r\n};\r\nconst Farmalytics = FarmalyticsInitializer();\r\nif (window)\r\n window[\"Farmalytics\"] = Farmalytics;\r\nexport default Farmalytics;\r\n","// Simple Hashing Algo\r\n// No need to worry about collisions above 10,000\r\n// https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/52171480\r\nconst toHash = (obj) => {\r\n let hash = 0;\r\n const keyString = JSON.stringify(obj);\r\n for (let charIndex = 0; charIndex < keyString.length; ++charIndex) {\r\n hash += keyString.charCodeAt(charIndex);\r\n hash += hash << 10;\r\n hash ^= hash >> 6;\r\n }\r\n hash += hash << 3;\r\n hash ^= hash >> 11;\r\n return (((hash + (hash << 15)) & 4294967295) >>> 0).toString(16);\r\n};\r\nexport default toHash;\r\n","import { ALLOWED_PARAMS } from \"../../CONFIG\";\r\n/**\r\n * Is this Google Analytics 3\r\n * @returns boolean\r\n */\r\nexport const isGA3 = () => {\r\n return window[\"ga\"] ? true : false;\r\n};\r\n/**\r\n * Is this Google Analytics 4\r\n * @returns boolean\r\n */\r\nexport const isGA4 = () => {\r\n return window[\"gtag\"] ? true : false;\r\n};\r\n/**\r\n * Checks to see when Google V3 is Loaded\r\n * @returns void\r\n */\r\nconst isV3Loaded = () => {\r\n return Promise.race([\r\n new Promise((resolve) => setTimeout(() => resolve(true), 10000)),\r\n new Promise((resolve) => {\r\n const isLoaded = () => window[\"ga\"] && window[\"ga\"].loaded\r\n ? resolve(true)\r\n : setTimeout(isLoaded, 200);\r\n isLoaded();\r\n }),\r\n ]);\r\n};\r\n/**\r\n * Get a ClientId for Version 3\r\n * @returns Promise\r\n */\r\nconst getGA3ClientId = async () => {\r\n await isV3Loaded();\r\n const _ga = window[\"ga\"];\r\n const ids = _ga === null || _ga === void 0 ? void 0 : _ga.getAll().map((tracker) => {\r\n return tracker.get(\"clientId\");\r\n });\r\n return ids.length ? ids[0] : undefined;\r\n};\r\n/**\r\n * Get a ClientId for Version 4\r\n * @returns Promise\r\n */\r\nconst getGA4ClientId = (propertyId) => {\r\n return new Promise((resolve) => {\r\n const _gtag = window[\"gtag\"];\r\n _gtag(\"get\", propertyId, \"client_id\", (clientId) => {\r\n resolve(clientId);\r\n });\r\n });\r\n};\r\n/**\r\n * Get a Client ID\r\n * @param propertyId\r\n * @returns\r\n */\r\nexport const getClientId = async (propertyId) => {\r\n if (isGA3()) {\r\n return getGA3ClientId();\r\n }\r\n else if (isGA4()) {\r\n return getGA4ClientId(propertyId);\r\n }\r\n else {\r\n return undefined;\r\n }\r\n};\r\n/**\r\n * Get the Active Source\r\n * Parses the url params for any utm_params as listed below\r\n * @returns UTM or Null\r\n */\r\nexport const getUTMParams = () => {\r\n const utm = {};\r\n const params = new URLSearchParams(window === null || window === void 0 ? void 0 : window.location.search);\r\n ALLOWED_PARAMS.forEach((tag) => {\r\n const utmlessTag = tag.replace(\"utm_\", \"\");\r\n const value = params.get(tag);\r\n if (value)\r\n utm[utmlessTag] = value.toString().substr(0, 100); // if it exists capture it in the utm object\r\n });\r\n if (Object.keys(utm).length) {\r\n // Lets timestamp it\r\n utm.created = new Date();\r\n utm.source = utm.source || \"Google UTM\";\r\n return utm;\r\n }\r\n else {\r\n return undefined;\r\n }\r\n};\r\n"],"names":["API_ENDPOINT","ALLOWED_PARAMS","WEBSITE_SOURCES","source","medium","facebook","tiktok","twitter","linkedin","youtube","gmail","google","wallethub","yelp","clearsurance","STORE_KEY","data","JSON","parse","getItem","e","console","error","message","getFromLocalStorage","save","localStorage","setItem","stringify","key","update","newData","Object","assign","value","deleteItem","clear","HyperstorageInit","ReferrerToSource","url","document","window","undefined","referrer","match","keys","find","searchKey","search","RegExp","created","Date","urlToSource","target","i","arguments","length","api","init","converter","defaultAttributes","set","name","attributes","expires","now","toUTCString","encodeURIComponent","replace","decodeURIComponent","escape","stringifiedAttributes","attributeName","split","cookie","write","create","get","cookies","jar","parts","slice","join","found","read","remove","withAttributes","this","withConverter","freeze","path","FaCookieManager","cookieName","proxyFarmalyticsCookieName","farmalyticsCookieName","hit","Cookies","saveCookies","clientId","domain","secure","parsed","getSourceHash","obj","hash","keyString","charIndex","charCodeAt","toString","toHash","campaign","Farmalytics","state","sources","propertyId","shouldTrack","retrieveGA4ClientId","gaLocalStorage","includes","storedValue","gaCookie","row","startsWith","cookieValue","getSource","originUrl","currentScript","URL","src","href","getUrl","track","async","payload","host","location","origin","push","fetch","method","headers","body","mode","resp","json","config","hyperstorage","filter","expiresOn","getTime","EXPIRE_SOURCE_DAYS","activeSource","utm","params","URLSearchParams","forEach","tag","utmlessTag","substr","getUTMParams","debug","log","activeHash","s","unshift","index","Promise","resolve","setTimeout","then","res","catch","getClientId","getPropertyId","getSources","getHistory","FarmalyticsInitializer"],"mappings":"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"}