posthog-js 1.291.0 → 1.292.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/all-external-dependencies.js.map +1 -1
  2. package/dist/array.full.es5.js +1 -1
  3. package/dist/array.full.es5.js.map +1 -1
  4. package/dist/array.full.js +1 -1
  5. package/dist/array.full.js.map +1 -1
  6. package/dist/array.full.no-external.js +1 -1
  7. package/dist/array.full.no-external.js.map +1 -1
  8. package/dist/array.js +1 -1
  9. package/dist/array.js.map +1 -1
  10. package/dist/array.no-external.js +1 -1
  11. package/dist/array.no-external.js.map +1 -1
  12. package/dist/customizations.full.js +1 -1
  13. package/dist/dead-clicks-autocapture.js.map +1 -1
  14. package/dist/lazy-recorder.js +1 -1
  15. package/dist/main.js +1 -1
  16. package/dist/main.js.map +1 -1
  17. package/dist/module.d.ts +29 -10
  18. package/dist/module.full.d.ts +29 -10
  19. package/dist/module.full.js +1 -1
  20. package/dist/module.full.js.map +1 -1
  21. package/dist/module.full.no-external.d.ts +29 -10
  22. package/dist/module.full.no-external.js +1 -1
  23. package/dist/module.full.no-external.js.map +1 -1
  24. package/dist/module.js +1 -1
  25. package/dist/module.js.map +1 -1
  26. package/dist/module.no-external.d.ts +29 -10
  27. package/dist/module.no-external.js +1 -1
  28. package/dist/module.no-external.js.map +1 -1
  29. package/dist/posthog-recorder.js +1 -1
  30. package/dist/src/extensions/rageclick.d.ts +6 -1
  31. package/dist/src/types.d.ts +15 -0
  32. package/dist/surveys-preview.d.ts +29 -10
  33. package/lib/package.json +1 -1
  34. package/lib/src/autocapture.js +1 -1
  35. package/lib/src/autocapture.js.map +1 -1
  36. package/lib/src/extensions/rageclick.d.ts +6 -1
  37. package/lib/src/extensions/rageclick.js +18 -9
  38. package/lib/src/extensions/rageclick.js.map +1 -1
  39. package/lib/src/heatmaps.js +1 -1
  40. package/lib/src/heatmaps.js.map +1 -1
  41. package/lib/src/types.d.ts +15 -0
  42. package/lib/src/types.js.map +1 -1
  43. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"dead-clicks-autocapture.js","sources":["../src/utils/globals.ts","../../core/dist/utils/string-utils.mjs","../../core/dist/utils/type-utils.mjs","../src/utils/logger.ts","../src/utils/index.ts","../src/utils/element-utils.ts","../src/autocapture-utils.ts","../src/autocapture.ts","../src/utils/request-utils.ts","../src/utils/type-utils.ts","../src/utils/prototype-utils.ts","../src/entrypoints/dead-clicks-autocapture.ts","../src/constants.ts"],"sourcesContent":["import type { PostHog } from '../posthog-core'\nimport { SessionIdManager } from '../sessionid'\nimport {\n DeadClicksAutoCaptureConfig,\n ExternalIntegrationKind,\n Properties,\n RemoteConfig,\n SiteAppLoader,\n SessionStartReason,\n} from '../types'\n// only importing types here, so won't affect the bundle\n// eslint-disable-next-line posthog-js/no-external-replay-imports\nimport type { SessionRecordingStatus, TriggerType } from '../extensions/replay/external/triggerMatching'\nimport { eventWithTime } from '../extensions/replay/types/rrweb-types'\nimport { ErrorTracking } from '@posthog/core'\n\n/*\n * Global helpers to protect access to browser globals in a way that is safer for different targets\n * like DOM, SSR, Web workers etc.\n *\n * NOTE: Typically we want the \"window\" but globalThis works for both the typical browser context as\n * well as other contexts such as the web worker context. Window is still exported for any bits that explicitly require it.\n * If in doubt - export the global you need from this file and use that as an optional value. This way the code path is forced\n * to handle the case where the global is not available.\n */\n\n// eslint-disable-next-line no-restricted-globals\nconst win: (Window & typeof globalThis) | undefined = typeof window !== 'undefined' ? window : undefined\n\nexport type AssignableWindow = Window &\n typeof globalThis & {\n /*\n * Main PostHog instance\n */\n posthog: any\n\n /*\n * This is our contract between (potentially) lazily loaded extensions and the SDK\n */\n __PosthogExtensions__?: PostHogExtensions\n\n /**\n * When loading remote config, we assign it to this global configuration\n * for ease of sharing it with the rest of the SDK\n */\n _POSTHOG_REMOTE_CONFIG?: Record<\n string,\n {\n config: RemoteConfig\n siteApps: SiteAppLoader[]\n }\n >\n\n /**\n * If this is set on the window, our logger will log to the console\n * for ease of debugging. Used for testing purposes only.\n *\n * @see {Config.DEBUG} from config.ts\n */\n POSTHOG_DEBUG: any\n\n // Exposed by the browser\n doNotTrack: any\n\n // See entrypoints/customizations.full.ts\n posthogCustomizations: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/exception-autocapture.ts\n *\n * @deprecated use `__PosthogExtensions__.errorWrappingFunctions` instead\n */\n posthogErrorWrappingFunctions: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/posthog-recorder.ts\n *\n * @deprecated use `__PosthogExtensions__.rrweb` instead\n */\n rrweb: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/posthog-recorder.ts\n *\n * @deprecated use `__PosthogExtensions__.rrwebConsoleRecord` instead\n */\n rrwebConsoleRecord: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/posthog-recorder.ts\n *\n * @deprecated use `__PosthogExtensions__.getRecordNetworkPlugin` instead\n */\n getRecordNetworkPlugin: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/web-vitals.ts\n *\n * @deprecated use `__PosthogExtensions__.postHogWebVitalsCallbacks` instead\n */\n postHogWebVitalsCallbacks: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/tracing-headers.ts\n *\n * @deprecated use `__PosthogExtensions__.postHogTracingHeadersPatchFns` instead\n */\n postHogTracingHeadersPatchFns: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/surveys.ts\n *\n * @deprecated use `__PosthogExtensions__.generateSurveys` instead\n */\n extendPostHogWithSurveys: any\n\n /*\n * These are used to handle our toolbar state.\n * @see {Toolbar} from extensions/toolbar.ts\n */\n ph_load_toolbar: any\n ph_load_editor: any\n ph_toolbar_state: any\n } & Record<`__$$ph_site_app_${string}`, any>\n\n/**\n * This is our contract between (potentially) lazily loaded extensions and the SDK\n * changes to this interface can be breaking changes for users of the SDK\n */\n\nexport type ExternalExtensionKind = 'intercom-integration' | 'crisp-chat-integration'\n\nexport type PostHogExtensionKind =\n | 'toolbar'\n | 'exception-autocapture'\n | 'web-vitals'\n | 'recorder'\n | 'lazy-recorder'\n | 'tracing-headers'\n | 'surveys'\n | 'dead-clicks-autocapture'\n | 'remote-config'\n | ExternalExtensionKind\n\nexport interface LazyLoadedSessionRecordingInterface {\n start: (startReason?: SessionStartReason) => void\n stop: () => void\n sessionId: string\n status: SessionRecordingStatus\n onRRwebEmit: (rawEvent: eventWithTime) => void\n log: (message: string, level: 'log' | 'warn' | 'error') => void\n sdkDebugProperties: Properties\n overrideLinkedFlag: () => void\n overrideSampling: () => void\n overrideTrigger: (triggerType: TriggerType) => void\n isStarted: boolean\n tryAddCustomEvent(tag: string, payload: any): boolean\n}\n\nexport interface LazyLoadedDeadClicksAutocaptureInterface {\n start: (observerTarget: Node) => void\n stop: () => void\n}\n\ninterface PostHogExtensions {\n loadExternalDependency?: (\n posthog: PostHog,\n kind: PostHogExtensionKind,\n callback: (error?: string | Event, event?: Event) => void\n ) => void\n\n loadSiteApp?: (posthog: PostHog, appUrl: string, callback: (error?: string | Event, event?: Event) => void) => void\n\n errorWrappingFunctions?: {\n wrapOnError: (captureFn: (props: ErrorTracking.ErrorProperties) => void) => () => void\n wrapUnhandledRejection: (captureFn: (props: ErrorTracking.ErrorProperties) => void) => () => void\n wrapConsoleError: (captureFn: (props: ErrorTracking.ErrorProperties) => void) => () => void\n }\n rrweb?: { record: any; version: string }\n rrwebPlugins?: { getRecordConsolePlugin: any; getRecordNetworkPlugin?: any }\n generateSurveys?: (posthog: PostHog, isSurveysEnabled: boolean) => any | undefined\n postHogWebVitalsCallbacks?: {\n onLCP: (metric: any) => void\n onCLS: (metric: any) => void\n onFCP: (metric: any) => void\n onINP: (metric: any) => void\n }\n tracingHeadersPatchFns?: {\n _patchFetch: (hostnames: string[], distinctId: string, sessionManager?: SessionIdManager) => () => void\n _patchXHR: (hostnames: string[], distinctId: string, sessionManager?: SessionIdManager) => () => void\n }\n initDeadClicksAutocapture?: (\n ph: PostHog,\n config: DeadClicksAutoCaptureConfig\n ) => LazyLoadedDeadClicksAutocaptureInterface\n integrations?: {\n [K in ExternalIntegrationKind]?: { start: (posthog: PostHog) => void; stop: () => void }\n }\n initSessionRecording?: (ph: PostHog) => LazyLoadedSessionRecordingInterface\n}\n\nconst global: typeof globalThis | undefined = typeof globalThis !== 'undefined' ? globalThis : win\n\nexport const ArrayProto = Array.prototype\nexport const nativeForEach = ArrayProto.forEach\nexport const nativeIndexOf = ArrayProto.indexOf\n\nexport const navigator = global?.navigator\nexport const document = global?.document\nexport const location = global?.location\nexport const fetch = global?.fetch\nexport const XMLHttpRequest =\n global?.XMLHttpRequest && 'withCredentials' in new global.XMLHttpRequest() ? global.XMLHttpRequest : undefined\nexport const AbortController = global?.AbortController\nexport const userAgent = navigator?.userAgent\nexport const assignableWindow: AssignableWindow = win ?? ({} as any)\n\nexport { win as window }\n","function includes(str, needle) {\n return -1 !== str.indexOf(needle);\n}\nconst trim = function(str) {\n return str.trim();\n};\nconst stripLeadingDollar = function(s) {\n return s.replace(/^\\$/, '');\n};\nfunction isDistinctIdStringLike(value) {\n return [\n 'distinct_id',\n 'distinctid'\n ].includes(value.toLowerCase());\n}\nexport { includes, isDistinctIdStringLike, stripLeadingDollar, trim };\n","import { knownUnsafeEditableEvent } from \"../types.mjs\";\nimport { includes } from \"./string-utils.mjs\";\nconst nativeIsArray = Array.isArray;\nconst ObjProto = Object.prototype;\nconst type_utils_hasOwnProperty = ObjProto.hasOwnProperty;\nconst type_utils_toString = ObjProto.toString;\nconst isArray = nativeIsArray || function(obj) {\n return '[object Array]' === type_utils_toString.call(obj);\n};\nconst isFunction = (x)=>'function' == typeof x;\nconst isNativeFunction = (x)=>isFunction(x) && -1 !== x.toString().indexOf('[native code]');\nconst isObject = (x)=>x === Object(x) && !isArray(x);\nconst isEmptyObject = (x)=>{\n if (isObject(x)) {\n for(const key in x)if (type_utils_hasOwnProperty.call(x, key)) return false;\n return true;\n }\n return false;\n};\nconst isUndefined = (x)=>void 0 === x;\nconst isString = (x)=>'[object String]' == type_utils_toString.call(x);\nconst isEmptyString = (x)=>isString(x) && 0 === x.trim().length;\nconst isNull = (x)=>null === x;\nconst isNullish = (x)=>isUndefined(x) || isNull(x);\nconst isNumber = (x)=>'[object Number]' == type_utils_toString.call(x);\nconst isBoolean = (x)=>'[object Boolean]' === type_utils_toString.call(x);\nconst isFormData = (x)=>x instanceof FormData;\nconst isFile = (x)=>x instanceof File;\nconst isPlainError = (x)=>x instanceof Error;\nconst isKnownUnsafeEditableEvent = (x)=>includes(knownUnsafeEditableEvent, x);\nfunction isInstanceOf(candidate, base) {\n try {\n return candidate instanceof base;\n } catch {\n return false;\n }\n}\nfunction isPrimitive(value) {\n return null === value || 'object' != typeof value;\n}\nfunction isBuiltin(candidate, className) {\n return Object.prototype.toString.call(candidate) === `[object ${className}]`;\n}\nfunction isError(candidate) {\n switch(Object.prototype.toString.call(candidate)){\n case '[object Error]':\n case '[object Exception]':\n case '[object DOMException]':\n case '[object DOMError]':\n case '[object WebAssembly.Exception]':\n return true;\n default:\n return isInstanceOf(candidate, Error);\n }\n}\nfunction isErrorEvent(event) {\n return isBuiltin(event, 'ErrorEvent');\n}\nfunction isEvent(candidate) {\n return !isUndefined(Event) && isInstanceOf(candidate, Event);\n}\nfunction isPlainObject(candidate) {\n return isBuiltin(candidate, 'Object');\n}\nconst yesLikeValues = [\n true,\n 'true',\n 1,\n '1',\n 'yes'\n];\nconst isYesLike = (val)=>includes(yesLikeValues, val);\nconst noLikeValues = [\n false,\n 'false',\n 0,\n '0',\n 'no'\n];\nconst isNoLike = (val)=>includes(noLikeValues, val);\nexport { type_utils_hasOwnProperty as hasOwnProperty, isArray, isBoolean, isBuiltin, isEmptyObject, isEmptyString, isError, isErrorEvent, isEvent, isFile, isFormData, isFunction, isInstanceOf, isKnownUnsafeEditableEvent, isNativeFunction, isNoLike, isNull, isNullish, isNumber, isObject, isPlainError, isPlainObject, isPrimitive, isString, isUndefined, isYesLike, noLikeValues, yesLikeValues };\n","import Config from '../config'\nimport { isUndefined } from '@posthog/core'\nimport { assignableWindow, window } from './globals'\nimport type { Logger } from '@posthog/core'\n\ntype PosthogJsLogger = Omit<Logger, 'createLogger'> & {\n _log: (level: 'log' | 'warn' | 'error', ...args: any[]) => void\n uninitializedWarning: (methodName: string) => void\n createLogger: (prefix: string) => PosthogJsLogger\n}\n\nconst _createLogger = (prefix: string): PosthogJsLogger => {\n const logger: PosthogJsLogger = {\n _log: (level: 'log' | 'warn' | 'error', ...args: any[]) => {\n if (\n window &&\n (Config.DEBUG || assignableWindow.POSTHOG_DEBUG) &&\n !isUndefined(window.console) &&\n window.console\n ) {\n const consoleLog =\n '__rrweb_original__' in window.console[level]\n ? (window.console[level] as any)['__rrweb_original__']\n : window.console[level]\n\n // eslint-disable-next-line no-console\n consoleLog(prefix, ...args)\n }\n },\n\n info: (...args: any[]) => {\n logger._log('log', ...args)\n },\n\n warn: (...args: any[]) => {\n logger._log('warn', ...args)\n },\n\n error: (...args: any[]) => {\n logger._log('error', ...args)\n },\n\n critical: (...args: any[]) => {\n // Critical errors are always logged to the console\n // eslint-disable-next-line no-console\n console.error(prefix, ...args)\n },\n\n uninitializedWarning: (methodName: string) => {\n logger.error(`You must initialize PostHog before calling ${methodName}`)\n },\n\n createLogger: (additionalPrefix: string) => _createLogger(`${prefix} ${additionalPrefix}`),\n }\n return logger\n}\n\nexport const logger = _createLogger('[PostHog.js]')\n\nexport const createLogger = logger.createLogger\n","import { Breaker, Properties } from '../types'\nimport { nativeForEach, nativeIndexOf } from './globals'\nimport { logger } from './logger'\nimport { isFormData, isNull, isNullish, isNumber, isString, isUndefined, hasOwnProperty, isArray } from '@posthog/core'\n\nconst breaker: Breaker = {}\n\nexport function eachArray<E = any>(\n obj: E[] | null | undefined,\n iterator: (value: E, key: number) => void | Breaker,\n thisArg?: any\n): void {\n if (isArray(obj)) {\n if (nativeForEach && obj.forEach === nativeForEach) {\n obj.forEach(iterator, thisArg)\n } else if ('length' in obj && obj.length === +obj.length) {\n for (let i = 0, l = obj.length; i < l; i++) {\n if (i in obj && iterator.call(thisArg, obj[i], i) === breaker) {\n return\n }\n }\n }\n }\n}\n\n/**\n * @param {*=} obj\n * @param {function(...*)=} iterator\n * @param {Object=} thisArg\n */\nexport function each(obj: any, iterator: (value: any, key: any) => void | Breaker, thisArg?: any): void {\n if (isNullish(obj)) {\n return\n }\n if (isArray(obj)) {\n return eachArray(obj, iterator, thisArg)\n }\n if (isFormData(obj)) {\n for (const pair of obj.entries()) {\n if (iterator.call(thisArg, pair[1], pair[0]) === breaker) {\n return\n }\n }\n return\n }\n for (const key in obj) {\n if (hasOwnProperty.call(obj, key)) {\n if (iterator.call(thisArg, obj[key], key) === breaker) {\n return\n }\n }\n }\n}\n\nexport const extend = function (obj: Record<string, any>, ...args: Record<string, any>[]): Record<string, any> {\n eachArray(args, function (source) {\n for (const prop in source) {\n if (source[prop] !== void 0) {\n obj[prop] = source[prop]\n }\n }\n })\n return obj\n}\n\nexport const extendArray = function <T>(obj: T[], ...args: T[][]): T[] {\n eachArray(args, function (source) {\n eachArray(source, function (item) {\n obj.push(item)\n })\n })\n return obj\n}\n\nexport const include = function (\n obj: null | string | Array<any> | Record<string, any>,\n target: any\n): boolean | Breaker {\n let found = false\n if (isNull(obj)) {\n return found\n }\n if (nativeIndexOf && obj.indexOf === nativeIndexOf) {\n return obj.indexOf(target) != -1\n }\n each(obj, function (value) {\n if (found || (found = value === target)) {\n return breaker\n }\n return\n })\n return found\n}\n\n/**\n * Object.entries() polyfill\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries\n */\nexport function entries<T = any>(obj: Record<string, T>): [string, T][] {\n const ownProps = Object.keys(obj)\n let i = ownProps.length\n const resArray = new Array(i) // preallocate the Array\n\n while (i--) {\n resArray[i] = [ownProps[i], obj[ownProps[i]]]\n }\n return resArray\n}\n\nexport const trySafe = function <T>(fn: () => T): T | undefined {\n try {\n return fn()\n } catch {\n return undefined\n }\n}\n\nexport const safewrap = function <F extends (...args: any[]) => any = (...args: any[]) => any>(f: F): F {\n return function (...args) {\n try {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return f.apply(this, args)\n } catch (e) {\n logger.critical(\n 'Implementation error. Please turn on debug mode and open a ticket on https://app.posthog.com/home#panel=support%3Asupport%3A.'\n )\n logger.critical(e)\n }\n } as F\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport const safewrapClass = function (klass: Function, functions: string[]): void {\n for (let i = 0; i < functions.length; i++) {\n klass.prototype[functions[i]] = safewrap(klass.prototype[functions[i]])\n }\n}\n\nexport const stripEmptyProperties = function (p: Properties): Properties {\n const ret: Properties = {}\n each(p, function (v, k) {\n if ((isString(v) && v.length > 0) || isNumber(v)) {\n ret[k] = v\n }\n })\n return ret\n}\n\n/**\n * Deep copies an object.\n * It handles cycles by replacing all references to them with `undefined`\n * Also supports customizing native values\n *\n * @param value\n * @param customizer\n * @returns {{}|undefined|*}\n */\nfunction deepCircularCopy<T extends Record<string, any> = Record<string, any>>(\n value: T,\n customizer?: <K extends keyof T = keyof T>(value: T[K], key?: K) => T[K]\n): T | undefined {\n const COPY_IN_PROGRESS_SET = new Set()\n\n function internalDeepCircularCopy(value: T, key?: string): T | undefined {\n if (value !== Object(value)) return customizer ? customizer(value as any, key) : value // primitive value\n\n if (COPY_IN_PROGRESS_SET.has(value)) return undefined\n COPY_IN_PROGRESS_SET.add(value)\n let result: T\n\n if (isArray(value)) {\n result = [] as any as T\n eachArray(value, (it) => {\n result.push(internalDeepCircularCopy(it))\n })\n } else {\n result = {} as T\n each(value, (val, key) => {\n if (!COPY_IN_PROGRESS_SET.has(val)) {\n ;(result as any)[key] = internalDeepCircularCopy(val, key)\n }\n })\n }\n return result\n }\n return internalDeepCircularCopy(value)\n}\n\nexport function _copyAndTruncateStrings<T extends Record<string, any> = Record<string, any>>(\n object: T,\n maxStringLength: number | null\n): T {\n return deepCircularCopy(object, (value: any) => {\n if (isString(value) && !isNull(maxStringLength)) {\n return (value as string).slice(0, maxStringLength)\n }\n return value\n }) as T\n}\n\n// NOTE: Update PostHogConfig docs if you change this list\n// We will not try to catch all bullets here, but we should make an effort to catch the most common ones\n// You should be highly against adding more to this list, because ultimately customers can configure\n// their `cross_subdomain_cookie` setting to anything they want.\nconst EXCLUDED_FROM_CROSS_SUBDOMAIN_COOKIE = ['herokuapp.com', 'vercel.app', 'netlify.app']\nexport function isCrossDomainCookie(documentLocation: Location | undefined) {\n const hostname = documentLocation?.hostname\n\n if (!isString(hostname)) {\n return false\n }\n // split and slice isn't a great way to match arbitrary domains,\n // but it's good enough for ensuring we only match herokuapp.com when it is the TLD\n // for the hostname\n const lastTwoParts = hostname.split('.').slice(-2).join('.')\n\n for (const excluded of EXCLUDED_FROM_CROSS_SUBDOMAIN_COOKIE) {\n if (lastTwoParts === excluded) {\n return false\n }\n }\n\n return true\n}\n\nexport function find<T>(value: T[], predicate: (value: T) => boolean): T | undefined {\n for (let i = 0; i < value.length; i++) {\n if (predicate(value[i])) {\n return value[i]\n }\n }\n return undefined\n}\n\n// Use this instead of element.addEventListener to avoid eslint errors\n// this properly implements the default options for passive event listeners\nexport function addEventListener(\n element: Window | Document | Element | undefined,\n event: string,\n callback: EventListener,\n options?: AddEventListenerOptions\n): void {\n const { capture = false, passive = true } = options ?? {}\n\n // This is the only place where we are allowed to call this function\n // because the whole idea is that we should be calling this instead of the built-in one\n // eslint-disable-next-line posthog-js/no-add-event-listener\n element?.addEventListener(event, callback, { capture, passive })\n}\n\n/**\n * Helper to migrate deprecated config fields to new field names with appropriate warnings\n * @param config - The config object to check\n * @param newField - The new field name to use\n * @param oldField - The deprecated field name to check for\n * @param defaultValue - The default value if neither field is set\n * @param loggerInstance - Optional logger instance for deprecation warnings\n * @returns The value to use (new field takes precedence over old field)\n */\nexport function migrateConfigField<T>(\n config: Record<string, any>,\n newField: string,\n oldField: string,\n defaultValue: T,\n loggerInstance?: { warn: (message: string) => void }\n): T {\n const hasNewField = newField in config && !isUndefined(config[newField])\n const hasOldField = oldField in config && !isUndefined(config[oldField])\n\n if (hasNewField) {\n return config[newField]\n }\n\n if (hasOldField) {\n if (loggerInstance) {\n loggerInstance.warn(\n `Config field '${oldField}' is deprecated. Please use '${newField}' instead. ` +\n `The old field will be removed in a future major version.`\n )\n }\n return config[oldField]\n }\n\n return defaultValue\n}\n","import { TOOLBAR_CONTAINER_CLASS, TOOLBAR_ID } from '../constants'\n\nexport function isElementInToolbar(el: EventTarget | null): boolean {\n if (el instanceof Element) {\n // closest isn't available in IE11, but we'll polyfill when bundling\n return el.id === TOOLBAR_ID || !!el.closest?.('.' + TOOLBAR_CONTAINER_CLASS)\n }\n return false\n}\n\n/*\n * Check whether an element has nodeType Node.ELEMENT_NODE\n * @param {Element} el - element to check\n * @returns {boolean} whether el is of the correct nodeType\n */\nexport function isElementNode(el: Node | Element | undefined | null): el is Element {\n return !!el && el.nodeType === 1 // Node.ELEMENT_NODE - use integer constant for browser portability\n}\n\n/*\n * Check whether an element is of a given tag type.\n * Due to potential reference discrepancies (such as the webcomponents.js polyfill),\n * we want to match tagNames instead of specific references because something like\n * element === document.body won't always work because element might not be a native\n * element.\n * @param {Element} el - element to check\n * @param {string} tag - tag name (e.g., \"div\")\n * @returns {boolean} whether el is of the given tag type\n */\nexport function isTag(el: Element | undefined | null, tag: string): el is HTMLElement {\n return !!el && !!el.tagName && el.tagName.toLowerCase() === tag.toLowerCase()\n}\n\n/*\n * Check whether an element has nodeType Node.TEXT_NODE\n * @param {Element} el - element to check\n * @returns {boolean} whether el is of the correct nodeType\n */\nexport function isTextNode(el: Element | undefined | null): el is HTMLElement {\n return !!el && el.nodeType === 3 // Node.TEXT_NODE - use integer constant for browser portability\n}\n\n/*\n * Check whether an element has nodeType Node.DOCUMENT_FRAGMENT_NODE\n * @param {Element} el - element to check\n * @returns {boolean} whether el is of the correct nodeType\n */\nexport function isDocumentFragment(el: Element | ParentNode | undefined | null): el is DocumentFragment {\n return !!el && el.nodeType === 11 // Node.DOCUMENT_FRAGMENT_NODE - use integer constant for browser portability\n}\n","import { AutocaptureConfig, PostHogConfig, Properties } from './types'\nimport { each, entries } from './utils'\n\nimport { isNullish, isString, isUndefined, isArray, isBoolean } from '@posthog/core'\nimport { logger } from './utils/logger'\nimport { window } from './utils/globals'\nimport { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils'\nimport { includes, trim } from '@posthog/core'\n\nexport function splitClassString(s: string): string[] {\n return s ? trim(s).split(/\\s+/) : []\n}\n\nfunction checkForURLMatches(urlsList: (string | RegExp)[]): boolean {\n const url = window?.location.href\n return !!(url && urlsList && urlsList.some((regex) => url.match(regex)))\n}\n\n/*\n * Get the className of an element, accounting for edge cases where element.className is an object\n *\n * Because this is a string it can contain unexpected characters\n * So, this method safely splits the className and returns that array.\n */\nexport function getClassNames(el: Element): string[] {\n let className = ''\n switch (typeof el.className) {\n case 'string':\n className = el.className\n break\n // TODO: when is this ever used?\n case 'object': // handle cases where className might be SVGAnimatedString or some other type\n className =\n (el.className && 'baseVal' in el.className ? (el.className as any).baseVal : null) ||\n el.getAttribute('class') ||\n ''\n break\n default:\n className = ''\n }\n\n return splitClassString(className)\n}\n\nexport function makeSafeText(s: string | null | undefined): string | null {\n if (isNullish(s)) {\n return null\n }\n\n return (\n trim(s)\n // scrub potentially sensitive values\n .split(/(\\s+)/)\n .filter((s) => shouldCaptureValue(s))\n .join('')\n // normalize whitespace\n .replace(/[\\r\\n]/g, ' ')\n .replace(/[ ]+/g, ' ')\n // truncate\n .substring(0, 255)\n )\n}\n\n/*\n * Get the direct text content of an element, protecting against sensitive data collection.\n * Concats textContent of each of the element's text node children; this avoids potential\n * collection of sensitive data that could happen if we used element.textContent and the\n * element had sensitive child elements, since element.textContent includes child content.\n * Scrubs values that look like they could be sensitive (i.e. cc or ssn number).\n * @param {Element} el - element to get the text of\n * @returns {string} the element's direct text content\n */\nexport function getSafeText(el: Element): string {\n let elText = ''\n\n if (shouldCaptureElement(el) && !isSensitiveElement(el) && el.childNodes && el.childNodes.length) {\n each(el.childNodes, function (child) {\n if (isTextNode(child) && child.textContent) {\n elText += makeSafeText(child.textContent) ?? ''\n }\n })\n }\n\n return trim(elText)\n}\n\nexport function getEventTarget(e: Event): Element | null {\n // https://developer.mozilla.org/en-US/docs/Web/API/Event/target#Compatibility_notes\n if (isUndefined(e.target)) {\n return (e.srcElement as Element) || null\n } else {\n if ((e.target as HTMLElement)?.shadowRoot) {\n return (e.composedPath()[0] as Element) || null\n }\n return (e.target as Element) || null\n }\n}\n\nexport const autocaptureCompatibleElements = ['a', 'button', 'form', 'input', 'select', 'textarea', 'label']\n\n/*\n if there is no config, then all elements are allowed\n if there is a config, and there is an allow list, then only elements in the allow list are allowed\n assumes that some other code is checking this element's parents\n */\nfunction checkIfElementTreePassesElementAllowList(\n elements: Element[],\n autocaptureConfig: AutocaptureConfig | undefined\n): boolean {\n const allowlist = autocaptureConfig?.element_allowlist\n if (isUndefined(allowlist)) {\n // everything is allowed, when there is no allow list\n return true\n }\n\n // check each element in the tree\n // if any of the elements are in the allow list, then the tree is allowed\n for (const el of elements) {\n if (allowlist.some((elementType) => el.tagName.toLowerCase() === elementType)) {\n return true\n }\n }\n\n // otherwise there is an allow list and this element tree didn't match it\n return false\n}\n\n/*\n if there is no selector list (i.e. it is undefined), then any elements matches\n if there is an empty list, then no elements match\n if there is a selector list, then check it against each element provided\n */\nfunction checkIfElementsMatchCSSSelector(elements: Element[], selectorList: string[] | undefined): boolean {\n if (isUndefined(selectorList)) {\n // everything is allowed, when there is no selector list\n return true\n }\n\n for (const el of elements) {\n if (selectorList.some((selector) => el.matches(selector))) {\n return true\n }\n }\n\n return false\n}\n\nexport function getParentElement(curEl: Element): Element | false {\n const parentNode = curEl.parentNode\n if (!parentNode || !isElementNode(parentNode)) return false\n return parentNode\n}\n\n// autocapture check will already filter for ph-no-capture,\n// but we include it here to protect against future changes accidentally removing that check\nconst DEFAULT_RAGE_CLICK_IGNORE_LIST = ['.ph-no-rageclick', '.ph-no-capture']\nexport function shouldCaptureRageclick(el: Element | null, _config: PostHogConfig['rageclick']) {\n if (!window || cannotCheckForAutocapture(el)) {\n return false\n }\n\n let selectorIgnoreList: string[] | boolean\n if (isBoolean(_config)) {\n selectorIgnoreList = _config ? DEFAULT_RAGE_CLICK_IGNORE_LIST : false\n } else {\n selectorIgnoreList = _config?.css_selector_ignorelist ?? DEFAULT_RAGE_CLICK_IGNORE_LIST\n }\n\n if (selectorIgnoreList === false) {\n return false\n }\n\n const { targetElementList } = getElementAndParentsForElement(el, false)\n // we don't capture if we match the ignore list\n return !checkIfElementsMatchCSSSelector(targetElementList, selectorIgnoreList)\n}\n\nconst cannotCheckForAutocapture = (el: Element | null) => {\n return !el || isTag(el, 'html') || !isElementNode(el)\n}\n\nconst getElementAndParentsForElement = (el: Element, captureOnAnyElement: false | true | undefined) => {\n if (!window || cannotCheckForAutocapture(el)) {\n return { parentIsUsefulElement: false, targetElementList: [] }\n }\n\n let parentIsUsefulElement = false\n const targetElementList: Element[] = [el]\n let curEl: Element = el\n while (curEl.parentNode && !isTag(curEl, 'body')) {\n // If element is a shadow root, we skip it\n if (isDocumentFragment(curEl.parentNode)) {\n targetElementList.push((curEl.parentNode as any).host)\n curEl = (curEl.parentNode as any).host\n continue\n }\n const parentNode = getParentElement(curEl)\n if (!parentNode) break\n if (captureOnAnyElement || autocaptureCompatibleElements.indexOf(parentNode.tagName.toLowerCase()) > -1) {\n parentIsUsefulElement = true\n } else {\n const compStyles = window.getComputedStyle(parentNode)\n if (compStyles && compStyles.getPropertyValue('cursor') === 'pointer') {\n parentIsUsefulElement = true\n }\n }\n\n targetElementList.push(parentNode)\n curEl = parentNode\n }\n return { parentIsUsefulElement, targetElementList }\n}\n\n/*\n * Check whether a DOM event should be \"captured\" or if it may contain sensitive data\n * using a variety of heuristics.\n * @param {Element} el - element to check\n * @param {Event} event - event to check\n * @param {Object} autocaptureConfig - autocapture config\n * @param {boolean} captureOnAnyElement - whether to capture on any element, clipboard autocapture doesn't restrict to \"clickable\" elements\n * @param {string[]} allowedEventTypes - event types to capture, normally just 'click', but some autocapture types react to different events, some elements have fixed events (e.g., form has \"submit\")\n * @returns {boolean} whether the event should be captured\n */\nexport function shouldCaptureDomEvent(\n el: Element,\n event: Event,\n autocaptureConfig: AutocaptureConfig | undefined = undefined,\n captureOnAnyElement?: boolean,\n allowedEventTypes?: string[]\n): boolean {\n if (!window || cannotCheckForAutocapture(el)) {\n return false\n }\n\n if (autocaptureConfig?.url_allowlist) {\n // if the current URL is not in the allow list, don't capture\n if (!checkForURLMatches(autocaptureConfig.url_allowlist)) {\n return false\n }\n }\n\n if (autocaptureConfig?.url_ignorelist) {\n // if the current URL is in the ignore list, don't capture\n if (checkForURLMatches(autocaptureConfig.url_ignorelist)) {\n return false\n }\n }\n\n if (autocaptureConfig?.dom_event_allowlist) {\n const allowlist = autocaptureConfig.dom_event_allowlist\n if (allowlist && !allowlist.some((eventType) => event.type === eventType)) {\n return false\n }\n }\n\n const { parentIsUsefulElement, targetElementList } = getElementAndParentsForElement(el, captureOnAnyElement)\n\n if (!checkIfElementTreePassesElementAllowList(targetElementList, autocaptureConfig)) {\n return false\n }\n\n if (!checkIfElementsMatchCSSSelector(targetElementList, autocaptureConfig?.css_selector_allowlist)) {\n return false\n }\n\n const compStyles = window.getComputedStyle(el)\n if (compStyles && compStyles.getPropertyValue('cursor') === 'pointer' && event.type === 'click') {\n return true\n }\n\n const tag = el.tagName.toLowerCase()\n switch (tag) {\n case 'html':\n return false\n case 'form':\n return (allowedEventTypes || ['submit']).indexOf(event.type) >= 0\n case 'input':\n case 'select':\n case 'textarea':\n return (allowedEventTypes || ['change', 'click']).indexOf(event.type) >= 0\n default:\n if (parentIsUsefulElement) return (allowedEventTypes || ['click']).indexOf(event.type) >= 0\n return (\n (allowedEventTypes || ['click']).indexOf(event.type) >= 0 &&\n (autocaptureCompatibleElements.indexOf(tag) > -1 || el.getAttribute('contenteditable') === 'true')\n )\n }\n}\n\n/*\n * Check whether a DOM element should be \"captured\" or if it may contain sensitive data\n * using a variety of heuristics.\n * @param {Element} el - element to check\n * @returns {boolean} whether the element should be captured\n */\nexport function shouldCaptureElement(el: Element): boolean {\n for (let curEl = el; curEl.parentNode && !isTag(curEl, 'body'); curEl = curEl.parentNode as Element) {\n const classes = getClassNames(curEl)\n if (includes(classes, 'ph-sensitive') || includes(classes, 'ph-no-capture')) {\n return false\n }\n }\n\n if (includes(getClassNames(el), 'ph-include')) {\n return true\n }\n\n // don't include hidden or password fields\n const type = (el as HTMLInputElement).type || ''\n if (isString(type)) {\n // it's possible for el.type to be a DOM element if el is a form with a child input[name=\"type\"]\n switch (type.toLowerCase()) {\n case 'hidden':\n return false\n case 'password':\n return false\n }\n }\n\n // filter out data from fields that look like sensitive fields\n const name = (el as HTMLInputElement).name || el.id || ''\n // See https://github.com/posthog/posthog-js/issues/165\n // Under specific circumstances a bug caused .replace to be called on a DOM element\n // instead of a string, removing the element from the page. Ensure this issue is mitigated.\n if (isString(name)) {\n // it's possible for el.name or el.id to be a DOM element if el is a form with a child input[name=\"name\"]\n const sensitiveNameRegex =\n /^cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i\n if (sensitiveNameRegex.test(name.replace(/[^a-zA-Z0-9]/g, ''))) {\n return false\n }\n }\n\n return true\n}\n\n/*\n * Check whether a DOM element is 'sensitive' and we should only capture limited data\n * @param {Element} el - element to check\n * @returns {boolean} whether the element should be captured\n */\nexport function isSensitiveElement(el: Element): boolean {\n // don't send data from inputs or similar elements since there will always be\n // a risk of clientside javascript placing sensitive data in attributes\n const allowedInputTypes = ['button', 'checkbox', 'submit', 'reset']\n if (\n (isTag(el, 'input') && !allowedInputTypes.includes((el as HTMLInputElement).type)) ||\n isTag(el, 'select') ||\n isTag(el, 'textarea') ||\n el.getAttribute('contenteditable') === 'true'\n ) {\n return true\n }\n return false\n}\n\n// Define the core pattern for matching credit card numbers\nconst coreCCPattern = `(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})`\n// Create the Anchored version of the regex by adding '^' at the start and '$' at the end\nconst anchoredCCRegex = new RegExp(`^(?:${coreCCPattern})$`)\n// The Unanchored version is essentially the core pattern, usable as is for partial matches\nconst unanchoredCCRegex = new RegExp(coreCCPattern)\n\n// Define the core pattern for matching SSNs with optional dashes\nconst coreSSNPattern = `\\\\d{3}-?\\\\d{2}-?\\\\d{4}`\n// Create the Anchored version of the regex by adding '^' at the start and '$' at the end\nconst anchoredSSNRegex = new RegExp(`^(${coreSSNPattern})$`)\n// The Unanchored version is essentially the core pattern itself, usable for partial matches\nconst unanchoredSSNRegex = new RegExp(`(${coreSSNPattern})`)\n\n/*\n * Check whether a string value should be \"captured\" or if it may contain sensitive data\n * using a variety of heuristics.\n * @param {string} value - string value to check\n * @param {boolean} anchorRegexes - whether to anchor the regexes to the start and end of the string\n * @returns {boolean} whether the element should be captured\n */\nexport function shouldCaptureValue(value: string, anchorRegexes = true): boolean {\n if (isNullish(value)) {\n return false\n }\n\n if (isString(value)) {\n value = trim(value)\n\n // check to see if input value looks like a credit card number\n // see: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s20.html\n const ccRegex = anchorRegexes ? anchoredCCRegex : unanchoredCCRegex\n if (ccRegex.test((value || '').replace(/[- ]/g, ''))) {\n return false\n }\n\n // check to see if input value looks like a social security number\n const ssnRegex = anchorRegexes ? anchoredSSNRegex : unanchoredSSNRegex\n if (ssnRegex.test(value)) {\n return false\n }\n }\n\n return true\n}\n\n/*\n * Check whether an attribute name is an Angular style attr (either _ngcontent or _nghost)\n * These update on each build and lead to noise in the element chain\n * More details on the attributes here: https://angular.io/guide/view-encapsulation\n * @param {string} attributeName - string value to check\n * @returns {boolean} whether the element is an angular tag\n */\nexport function isAngularStyleAttr(attributeName: string): boolean {\n if (isString(attributeName)) {\n return attributeName.substring(0, 10) === '_ngcontent' || attributeName.substring(0, 7) === '_nghost'\n }\n return false\n}\n\n/*\n * Iterate through children of a target element looking for span tags\n * and return the text content of the span tags, separated by spaces,\n * along with the direct text content of the target element\n * @param {Element} target - element to check\n * @returns {string} text content of the target element and its child span tags\n */\nexport function getDirectAndNestedSpanText(target: Element): string {\n let text = getSafeText(target)\n text = `${text} ${getNestedSpanText(target)}`.trim()\n return shouldCaptureValue(text) ? text : ''\n}\n\n/*\n * Iterate through children of a target element looking for span tags\n * and return the text content of the span tags, separated by spaces\n * @param {Element} target - element to check\n * @returns {string} text content of span tags\n */\nexport function getNestedSpanText(target: Element): string {\n let text = ''\n if (target && target.childNodes && target.childNodes.length) {\n each(target.childNodes, function (child) {\n if (child && child.tagName?.toLowerCase() === 'span') {\n try {\n const spanText = getSafeText(child)\n text = `${text} ${spanText}`.trim()\n\n if (child.childNodes && child.childNodes.length) {\n text = `${text} ${getNestedSpanText(child)}`.trim()\n }\n } catch (e) {\n logger.error('[AutoCapture]', e)\n }\n }\n })\n }\n return text\n}\n\n/*\nBack in the day storing events in Postgres we use Elements for autocapture events.\nNow we're using elements_chain. We used to do this parsing/processing during ingestion.\nThis code is just copied over from ingestion, but we should optimize it\nto create elements_chain string directly.\n*/\nexport function getElementsChainString(elements: Properties[]): string {\n return elementsToString(extractElements(elements))\n}\n\n// This interface is called 'Element' in plugin-scaffold https://github.com/PostHog/plugin-scaffold/blob/b07d3b879796ecc7e22deb71bf627694ba05386b/src/types.ts#L200\n// However 'Element' is a DOM Element when run in the browser, so we have to rename it\ninterface PHElement {\n text?: string\n tag_name?: string\n href?: string\n attr_id?: string\n attr_class?: string[]\n nth_child?: number\n nth_of_type?: number\n attributes?: Record<string, any>\n event_id?: number\n order?: number\n group_id?: number\n}\n\nfunction escapeQuotes(input: string): string {\n return input.replace(/\"|\\\\\"/g, '\\\\\"')\n}\n\nfunction elementsToString(elements: PHElement[]): string {\n const ret = elements.map((element) => {\n let el_string = ''\n if (element.tag_name) {\n el_string += element.tag_name\n }\n if (element.attr_class) {\n element.attr_class.sort()\n for (const single_class of element.attr_class) {\n el_string += `.${single_class.replace(/\"/g, '')}`\n }\n }\n const attributes: Record<string, any> = {\n ...(element.text ? { text: element.text } : {}),\n 'nth-child': element.nth_child ?? 0,\n 'nth-of-type': element.nth_of_type ?? 0,\n ...(element.href ? { href: element.href } : {}),\n ...(element.attr_id ? { attr_id: element.attr_id } : {}),\n ...element.attributes,\n }\n const sortedAttributes: Record<string, any> = {}\n entries(attributes)\n .sort(([a], [b]) => a.localeCompare(b))\n .forEach(\n ([key, value]) => (sortedAttributes[escapeQuotes(key.toString())] = escapeQuotes(value.toString()))\n )\n el_string += ':'\n el_string += entries(sortedAttributes)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join('')\n return el_string\n })\n return ret.join(';')\n}\n\nfunction extractElements(elements: Properties[]): PHElement[] {\n return elements.map((el) => {\n const response = {\n text: el['$el_text']?.slice(0, 400),\n tag_name: el['tag_name'],\n href: el['attr__href']?.slice(0, 2048),\n attr_class: extractAttrClass(el),\n attr_id: el['attr__id'],\n nth_child: el['nth_child'],\n nth_of_type: el['nth_of_type'],\n attributes: {} as { [id: string]: any },\n }\n\n entries(el)\n .filter(([key]) => key.indexOf('attr__') === 0)\n .forEach(([key, value]) => (response.attributes[key] = value))\n return response\n })\n}\n\nfunction extractAttrClass(el: Properties): PHElement['attr_class'] {\n const attr_class = el['attr__class']\n if (!attr_class) {\n return undefined\n } else if (isArray(attr_class)) {\n return attr_class\n } else {\n return splitClassString(attr_class)\n }\n}\n","import { addEventListener, each, extend } from './utils'\nimport {\n autocaptureCompatibleElements,\n getClassNames,\n getDirectAndNestedSpanText,\n getElementsChainString,\n getEventTarget,\n getSafeText,\n isAngularStyleAttr,\n isSensitiveElement,\n makeSafeText,\n shouldCaptureDomEvent,\n shouldCaptureElement,\n shouldCaptureRageclick,\n shouldCaptureValue,\n splitClassString,\n} from './autocapture-utils'\n\nimport RageClick from './extensions/rageclick'\nimport { AutocaptureConfig, COPY_AUTOCAPTURE_EVENT, EventName, Properties, RemoteConfig } from './types'\nimport { PostHog } from './posthog-core'\nimport { AUTOCAPTURE_DISABLED_SERVER_SIDE } from './constants'\n\nimport { isBoolean, isFunction, isNull, isObject } from '@posthog/core'\nimport { createLogger } from './utils/logger'\nimport { document, window } from './utils/globals'\nimport { convertToURL } from './utils/request-utils'\nimport { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils'\nimport { includes } from '@posthog/core'\n\nconst logger = createLogger('[AutoCapture]')\n\nfunction limitText(length: number, text: string): string {\n if (text.length > length) {\n return text.slice(0, length) + '...'\n }\n return text\n}\n\nexport function getAugmentPropertiesFromElement(elem: Element): Properties {\n const shouldCaptureEl = shouldCaptureElement(elem)\n if (!shouldCaptureEl) {\n return {}\n }\n\n const props: Properties = {}\n\n each(elem.attributes, function (attr: Attr) {\n if (attr.name && attr.name.indexOf('data-ph-capture-attribute') === 0) {\n const propertyKey = attr.name.replace('data-ph-capture-attribute-', '')\n const propertyValue = attr.value\n if (propertyKey && propertyValue && shouldCaptureValue(propertyValue)) {\n props[propertyKey] = propertyValue\n }\n }\n })\n\n return props\n}\n\nexport function previousElementSibling(el: Element): Element | null {\n if (el.previousElementSibling) {\n return el.previousElementSibling\n }\n let _el: Element | null = el\n do {\n _el = _el.previousSibling as Element | null // resolves to ChildNode->Node, which is Element's parent class\n } while (_el && !isElementNode(_el))\n return _el\n}\n\nexport function getDefaultProperties(eventType: string): Properties {\n return {\n $event_type: eventType,\n $ce_version: 1,\n }\n}\n\nexport function getPropertiesFromElement(\n elem: Element,\n maskAllAttributes: boolean,\n maskText: boolean,\n elementAttributeIgnorelist: string[] | undefined\n): Properties {\n const tag_name = elem.tagName.toLowerCase()\n const props: Properties = {\n tag_name: tag_name,\n }\n if (autocaptureCompatibleElements.indexOf(tag_name) > -1 && !maskText) {\n if (tag_name.toLowerCase() === 'a' || tag_name.toLowerCase() === 'button') {\n props['$el_text'] = limitText(1024, getDirectAndNestedSpanText(elem))\n } else {\n props['$el_text'] = limitText(1024, getSafeText(elem))\n }\n }\n\n const classes = getClassNames(elem)\n if (classes.length > 0)\n props['classes'] = classes.filter(function (c) {\n return c !== ''\n })\n\n // capture the deny list here because this not-a-class class makes it tricky to use this.config in the function below\n each(elem.attributes, function (attr: Attr) {\n // Only capture attributes we know are safe\n if (isSensitiveElement(elem) && ['name', 'id', 'class', 'aria-label'].indexOf(attr.name) === -1) return\n\n if (elementAttributeIgnorelist?.includes(attr.name)) return\n\n if (!maskAllAttributes && shouldCaptureValue(attr.value) && !isAngularStyleAttr(attr.name)) {\n let value = attr.value\n if (attr.name === 'class') {\n // html attributes can _technically_ contain linebreaks,\n // but we're very intolerant of them in the class string,\n // so we strip them.\n value = splitClassString(value).join(' ')\n }\n props['attr__' + attr.name] = limitText(1024, value)\n }\n })\n\n let nthChild = 1\n let nthOfType = 1\n let currentElem: Element | null = elem\n while ((currentElem = previousElementSibling(currentElem))) {\n // eslint-disable-line no-cond-assign\n nthChild++\n if (currentElem.tagName === elem.tagName) {\n nthOfType++\n }\n }\n props['nth_child'] = nthChild\n props['nth_of_type'] = nthOfType\n\n return props\n}\n\nexport function autocapturePropertiesForElement(\n target: Element,\n {\n e,\n maskAllElementAttributes,\n maskAllText,\n elementAttributeIgnoreList,\n elementsChainAsString,\n }: {\n e: Event\n maskAllElementAttributes: boolean\n maskAllText: boolean\n elementAttributeIgnoreList?: string[] | undefined\n elementsChainAsString: boolean\n }\n): { props: Properties; explicitNoCapture?: boolean } {\n const targetElementList = [target]\n let curEl = target\n while (curEl.parentNode && !isTag(curEl, 'body')) {\n if (isDocumentFragment(curEl.parentNode)) {\n targetElementList.push((curEl.parentNode as any).host)\n curEl = (curEl.parentNode as any).host\n continue\n }\n targetElementList.push(curEl.parentNode as Element)\n curEl = curEl.parentNode as Element\n }\n\n const elementsJson: Properties[] = []\n const autocaptureAugmentProperties: Properties = {}\n let href: string | false = false\n let explicitNoCapture = false\n\n each(targetElementList, (el) => {\n const shouldCaptureEl = shouldCaptureElement(el)\n\n // if the element or a parent element is an anchor tag\n // include the href as a property\n if (el.tagName.toLowerCase() === 'a') {\n href = el.getAttribute('href')\n href = shouldCaptureEl && href && shouldCaptureValue(href) && href\n }\n\n // allow users to programmatically prevent capturing of elements by adding class 'ph-no-capture'\n const classes = getClassNames(el)\n if (includes(classes, 'ph-no-capture')) {\n explicitNoCapture = true\n }\n\n elementsJson.push(\n getPropertiesFromElement(el, maskAllElementAttributes, maskAllText, elementAttributeIgnoreList)\n )\n\n const augmentProperties = getAugmentPropertiesFromElement(el)\n extend(autocaptureAugmentProperties, augmentProperties)\n })\n\n if (explicitNoCapture) {\n return { props: {}, explicitNoCapture }\n }\n\n if (!maskAllText) {\n // if the element is a button or anchor tag get the span text from any\n // children and include it as/with the text property on the parent element\n if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'button') {\n elementsJson[0]['$el_text'] = getDirectAndNestedSpanText(target)\n } else {\n elementsJson[0]['$el_text'] = getSafeText(target)\n }\n }\n\n let externalHref: string | undefined\n if (href) {\n elementsJson[0]['attr__href'] = href\n const hrefHost = convertToURL(href)?.host\n const locationHost = window?.location?.host\n if (hrefHost && locationHost && hrefHost !== locationHost) {\n externalHref = href\n }\n }\n\n const props = extend(\n getDefaultProperties(e.type),\n // Sending \"$elements\" is deprecated. Only one client on US cloud uses this.\n !elementsChainAsString ? { $elements: elementsJson } : {},\n // Always send $elements_chain, as it's needed downstream in site app filtering\n { $elements_chain: getElementsChainString(elementsJson) },\n elementsJson[0]?.['$el_text'] ? { $el_text: elementsJson[0]?.['$el_text'] } : {},\n externalHref && e.type === 'click' ? { $external_click_url: externalHref } : {},\n autocaptureAugmentProperties\n )\n\n return { props }\n}\n\nexport class Autocapture {\n instance: PostHog\n _initialized: boolean = false\n _isDisabledServerSide: boolean | null = null\n _elementSelectors: Set<string> | null\n rageclicks = new RageClick()\n _elementsChainAsString = false\n\n constructor(instance: PostHog) {\n this.instance = instance\n this._elementSelectors = null\n }\n\n private get _config(): AutocaptureConfig {\n const config = isObject(this.instance.config.autocapture) ? this.instance.config.autocapture : {}\n // precompile the regex\n config.url_allowlist = config.url_allowlist?.map((url) => new RegExp(url))\n config.url_ignorelist = config.url_ignorelist?.map((url) => new RegExp(url))\n return config\n }\n\n _addDomEventHandlers(): void {\n if (!this.isBrowserSupported()) {\n logger.info('Disabling Automatic Event Collection because this browser is not supported')\n return\n }\n\n if (!window || !document) {\n return\n }\n\n const handler = (e: Event) => {\n e = e || window?.event\n try {\n this._captureEvent(e)\n } catch (error) {\n logger.error('Failed to capture event', error)\n }\n }\n\n addEventListener(document, 'submit', handler, { capture: true })\n addEventListener(document, 'change', handler, { capture: true })\n addEventListener(document, 'click', handler, { capture: true })\n\n if (this._config.capture_copied_text) {\n const copiedTextHandler = (e: Event) => {\n e = e || window?.event\n this._captureEvent(e, COPY_AUTOCAPTURE_EVENT)\n }\n\n addEventListener(document, 'copy', copiedTextHandler, { capture: true })\n addEventListener(document, 'cut', copiedTextHandler, { capture: true })\n }\n }\n\n public startIfEnabled() {\n if (this.isEnabled && !this._initialized) {\n this._addDomEventHandlers()\n this._initialized = true\n }\n }\n\n public onRemoteConfig(response: RemoteConfig) {\n if (response.elementsChainAsString) {\n this._elementsChainAsString = response.elementsChainAsString\n }\n\n if (this.instance.persistence) {\n this.instance.persistence.register({\n [AUTOCAPTURE_DISABLED_SERVER_SIDE]: !!response['autocapture_opt_out'],\n })\n }\n // store this in-memory in case persistence is disabled\n this._isDisabledServerSide = !!response['autocapture_opt_out']\n this.startIfEnabled()\n }\n\n public setElementSelectors(selectors: Set<string>): void {\n this._elementSelectors = selectors\n }\n\n public getElementSelectors(element: Element | null): string[] | null {\n const elementSelectors: string[] = []\n\n this._elementSelectors?.forEach((selector) => {\n const matchedElements = document?.querySelectorAll(selector)\n matchedElements?.forEach((matchedElement: Element) => {\n if (element === matchedElement) {\n elementSelectors.push(selector)\n }\n })\n })\n\n return elementSelectors\n }\n\n public get isEnabled(): boolean {\n const persistedServerDisabled = this.instance.persistence?.props[AUTOCAPTURE_DISABLED_SERVER_SIDE]\n const memoryDisabled = this._isDisabledServerSide\n\n if (isNull(memoryDisabled) && !isBoolean(persistedServerDisabled) && !this.instance._shouldDisableFlags()) {\n // We only enable if we know that the server has not disabled it (unless /flags is disabled)\n return false\n }\n\n const disabledServer = this._isDisabledServerSide ?? !!persistedServerDisabled\n const disabledClient = !this.instance.config.autocapture\n return !disabledClient && !disabledServer\n }\n\n private _captureEvent(e: Event, eventName: EventName = '$autocapture'): boolean | void {\n if (!this.isEnabled) {\n return\n }\n\n /*** Don't mess with this code without running IE8 tests on it ***/\n let target = getEventTarget(e)\n if (isTextNode(target)) {\n // defeat Safari bug (see: http://www.quirksmode.org/js/events_properties.html)\n target = (target.parentNode || null) as Element | null\n }\n\n if (eventName === '$autocapture' && e.type === 'click' && e instanceof MouseEvent) {\n if (\n !!this.instance.config.rageclick &&\n this.rageclicks?.isRageClick(e.clientX, e.clientY, new Date().getTime())\n ) {\n if (shouldCaptureRageclick(target, this.instance.config.rageclick)) {\n this._captureEvent(e, '$rageclick')\n }\n }\n }\n\n const isCopyAutocapture = eventName === COPY_AUTOCAPTURE_EVENT\n if (\n target &&\n shouldCaptureDomEvent(\n target,\n e,\n this._config,\n // mostly this method cares about the target element, but in the case of copy events,\n // we want some of the work this check does without insisting on the target element's type\n isCopyAutocapture,\n // we also don't want to restrict copy checks to clicks,\n // so we pass that knowledge in here, rather than add the logic inside the check\n isCopyAutocapture ? ['copy', 'cut'] : undefined\n )\n ) {\n const { props, explicitNoCapture } = autocapturePropertiesForElement(target, {\n e,\n maskAllElementAttributes: this.instance.config.mask_all_element_attributes,\n maskAllText: this.instance.config.mask_all_text,\n elementAttributeIgnoreList: this._config.element_attribute_ignorelist,\n elementsChainAsString: this._elementsChainAsString,\n })\n\n if (explicitNoCapture) {\n return false\n }\n\n const elementSelectors = this.getElementSelectors(target)\n if (elementSelectors && elementSelectors.length > 0) {\n props['$element_selectors'] = elementSelectors\n }\n\n if (eventName === COPY_AUTOCAPTURE_EVENT) {\n // you can't read the data from the clipboard event,\n // but you can guess that you can read it from the window's current selection\n const selectedContent = makeSafeText(window?.getSelection()?.toString())\n const clipType = (e as ClipboardEvent).type || 'clipboard'\n if (!selectedContent) {\n return false\n }\n props['$selected_content'] = selectedContent\n props['$copy_type'] = clipType\n }\n\n this.instance.capture(eventName, props)\n return true\n }\n }\n\n isBrowserSupported(): boolean {\n return isFunction(document?.querySelectorAll)\n }\n}\n","import { each } from './'\n\nimport { isArray, isFile, isUndefined } from '@posthog/core'\nimport { logger } from './logger'\nimport { document } from './globals'\n\nconst localDomains = ['localhost', '127.0.0.1']\n\n/**\n * IE11 doesn't support `new URL`\n * so we can create an anchor element and use that to parse the URL\n * there's a lot of overlap between HTMLHyperlinkElementUtils and URL\n * meaning useful properties like `pathname` are available on both\n */\nexport const convertToURL = (url: string): HTMLAnchorElement | null => {\n const location = document?.createElement('a')\n if (isUndefined(location)) {\n return null\n }\n\n location.href = url\n return location\n}\n\nexport const formDataToQuery = function (formdata: Record<string, any> | FormData, arg_separator = '&'): string {\n let use_val: string\n let use_key: string\n const tph_arr: string[] = []\n\n each(formdata, function (val: File | string | undefined, key: string | undefined) {\n // the key might be literally the string undefined for e.g. if {undefined: 'something'}\n if (isUndefined(val) || isUndefined(key) || key === 'undefined') {\n return\n }\n\n use_val = encodeURIComponent(isFile(val) ? val.name : val.toString())\n use_key = encodeURIComponent(key)\n tph_arr[tph_arr.length] = use_key + '=' + use_val\n })\n\n return tph_arr.join(arg_separator)\n}\n\n// NOTE: Once we get rid of IE11/op_mini we can start using URLSearchParams\nexport const getQueryParam = function (url: string, param: string): string {\n const withoutHash: string = url.split('#')[0] || ''\n\n // Split only on the first ? to sort problem out for those with multiple ?s\n // and then remove them\n const queryParams: string = withoutHash.split(/\\?(.*)/)[1] || ''\n const cleanedQueryParams = queryParams.replace(/^\\?+/g, '')\n\n const queryParts = cleanedQueryParams.split('&')\n let keyValuePair\n\n for (let i = 0; i < queryParts.length; i++) {\n const parts = queryParts[i].split('=')\n if (parts[0] === param) {\n keyValuePair = parts\n break\n }\n }\n\n if (!isArray(keyValuePair) || keyValuePair.length < 2) {\n return ''\n } else {\n let result = keyValuePair[1]\n try {\n result = decodeURIComponent(result)\n } catch {\n logger.error('Skipping decoding for malformed query param: ' + result)\n }\n return result.replace(/\\+/g, ' ')\n }\n}\n\n// replace any query params in the url with the provided mask value. Tries to keep the URL as instant as possible,\n// including preserving malformed text in most cases\nexport const maskQueryParams = function <T extends string | undefined>(\n url: T,\n maskedParams: string[] | undefined,\n mask: string\n): T extends string ? string : undefined {\n if (!url || !maskedParams || !maskedParams.length) {\n return url as any\n }\n\n const splitHash = url.split('#')\n const withoutHash: string = splitHash[0] || ''\n const hash = splitHash[1]\n\n const splitQuery: string[] = withoutHash.split('?')\n const queryString: string = splitQuery[1]\n const urlWithoutQueryAndHash: string = splitQuery[0]\n const queryParts = (queryString || '').split('&')\n\n // use an array of strings rather than an object to preserve ordering and duplicates\n const paramStrings: string[] = []\n\n for (let i = 0; i < queryParts.length; i++) {\n const keyValuePair = queryParts[i].split('=')\n if (!isArray(keyValuePair)) {\n continue\n } else if (maskedParams.includes(keyValuePair[0])) {\n paramStrings.push(keyValuePair[0] + '=' + mask)\n } else {\n paramStrings.push(queryParts[i])\n }\n }\n\n let result = urlWithoutQueryAndHash\n if (queryString != null) {\n result += '?' + paramStrings.join('&')\n }\n if (hash != null) {\n result += '#' + hash\n }\n\n return result as any\n}\n\nexport const _getHashParam = function (hash: string, param: string): string | null {\n const matches = hash.match(new RegExp(param + '=([^&]*)'))\n return matches ? matches[1] : null\n}\n\nexport const isLocalhost = (): boolean => {\n return localDomains.includes(location.hostname)\n}\n","import { window } from './globals'\n\n// When angular patches functions they pass the above `isNativeFunction` check (at least the MutationObserver)\nexport const isAngularZonePresent = (): boolean => {\n return !!(window as any).Zone\n}\n\nexport const isDocument = (x: unknown): x is Document => {\n // eslint-disable-next-line posthog-js/no-direct-document-check\n return x instanceof Document\n}\n","/**\n * adapted from https://github.com/getsentry/sentry-javascript/blob/72751dacb88c5b970d8bac15052ee8e09b28fd5d/packages/browser-utils/src/getNativeImplementation.ts#L27\n * and https://github.com/PostHog/rrweb/blob/804380afbb1b9bed70b8792cb5a25d827f5c0cb5/packages/utils/src/index.ts#L31\n * after a number of performance reports from Angular users\n */\n\nimport { AssignableWindow } from './globals'\nimport { isAngularZonePresent } from './type-utils'\nimport { isFunction, isNativeFunction } from '@posthog/core'\nimport { logger } from './logger'\n\ninterface NativeImplementationsCache {\n MutationObserver: typeof MutationObserver\n}\n\nconst cachedImplementations: Partial<NativeImplementationsCache> = {}\n\nexport function getNativeImplementation<T extends keyof NativeImplementationsCache>(\n name: T,\n assignableWindow: AssignableWindow\n): NativeImplementationsCache[T] {\n const cached = cachedImplementations[name]\n if (cached) {\n return cached\n }\n\n let impl = assignableWindow[name] as NativeImplementationsCache[T]\n\n if (isNativeFunction(impl) && !isAngularZonePresent()) {\n return (cachedImplementations[name] = impl.bind(assignableWindow) as NativeImplementationsCache[T])\n }\n\n const document = assignableWindow.document\n if (document && isFunction(document.createElement)) {\n try {\n const sandbox = document.createElement('iframe')\n sandbox.hidden = true\n document.head.appendChild(sandbox)\n const contentWindow = sandbox.contentWindow\n if (contentWindow && (contentWindow as any)[name]) {\n impl = (contentWindow as any)[name] as NativeImplementationsCache[T]\n }\n document.head.removeChild(sandbox)\n } catch (e) {\n // Could not create sandbox iframe, just use assignableWindow.xxx\n logger.warn(`Could not create sandbox iframe for ${name} check, bailing to assignableWindow.${name}: `, e)\n }\n }\n\n // Sanity check: This _should_ not happen, but if it does, we just skip caching...\n // This can happen e.g. in tests where fetch may not be available in the env, or similar.\n if (!impl || !isFunction(impl)) {\n return impl\n }\n\n return (cachedImplementations[name] = impl.bind(assignableWindow) as NativeImplementationsCache[T])\n}\n\nexport function getNativeMutationObserverImplementation(assignableWindow: AssignableWindow): typeof MutationObserver {\n return getNativeImplementation('MutationObserver', assignableWindow)\n}\n","import { assignableWindow, LazyLoadedDeadClicksAutocaptureInterface } from '../utils/globals'\nimport { PostHog } from '../posthog-core'\nimport { isNull, isNumber, isUndefined } from '@posthog/core'\nimport { autocaptureCompatibleElements, getEventTarget } from '../autocapture-utils'\nimport { DeadClickCandidate, DeadClicksAutoCaptureConfig, Properties } from '../types'\nimport { autocapturePropertiesForElement } from '../autocapture'\nimport { isElementInToolbar, isElementNode, isTag } from '../utils/element-utils'\nimport { getNativeMutationObserverImplementation } from '../utils/prototype-utils'\nimport { addEventListener } from '../utils'\n\nfunction asClick(event: MouseEvent): DeadClickCandidate | null {\n const eventTarget = getEventTarget(event)\n if (eventTarget) {\n return {\n node: eventTarget,\n originalEvent: event,\n timestamp: Date.now(),\n }\n }\n return null\n}\n\nfunction checkTimeout(value: number | undefined, thresholdMs: number) {\n return isNumber(value) && value >= thresholdMs\n}\n\nclass LazyLoadedDeadClicksAutocapture implements LazyLoadedDeadClicksAutocaptureInterface {\n private _mutationObserver: MutationObserver | undefined\n private _lastMutation: number | undefined\n private _lastSelectionChanged: number | undefined\n private _clicks: DeadClickCandidate[] = []\n private _checkClickTimer: number | undefined\n private _config: Required<DeadClicksAutoCaptureConfig>\n private _onCapture: (click: DeadClickCandidate, properties: Properties) => void\n\n private _defaultConfig = (defaultOnCapture: (click: DeadClickCandidate, properties: Properties) => void) => ({\n element_attribute_ignorelist: [],\n scroll_threshold_ms: 100,\n selection_change_threshold_ms: 100,\n mutation_threshold_ms: 2500,\n __onCapture: defaultOnCapture,\n })\n\n private _asRequiredConfig(providedConfig?: DeadClicksAutoCaptureConfig): Required<DeadClicksAutoCaptureConfig> {\n const defaultConfig = this._defaultConfig(providedConfig?.__onCapture || this._captureDeadClick.bind(this))\n return {\n element_attribute_ignorelist:\n providedConfig?.element_attribute_ignorelist ?? defaultConfig.element_attribute_ignorelist,\n scroll_threshold_ms: providedConfig?.scroll_threshold_ms ?? defaultConfig.scroll_threshold_ms,\n selection_change_threshold_ms:\n providedConfig?.selection_change_threshold_ms ?? defaultConfig.selection_change_threshold_ms,\n mutation_threshold_ms: providedConfig?.mutation_threshold_ms ?? defaultConfig.mutation_threshold_ms,\n __onCapture: defaultConfig.__onCapture,\n }\n }\n\n constructor(\n readonly instance: PostHog,\n config?: DeadClicksAutoCaptureConfig\n ) {\n this._config = this._asRequiredConfig(config)\n this._onCapture = this._config.__onCapture\n }\n\n start(observerTarget: Node) {\n this._startClickObserver()\n this._startScrollObserver()\n this._startSelectionChangedObserver()\n this._startMutationObserver(observerTarget)\n }\n\n private _startMutationObserver(observerTarget: Node) {\n if (!this._mutationObserver) {\n const NativeMutationObserver = getNativeMutationObserverImplementation(assignableWindow)\n this._mutationObserver = new NativeMutationObserver((mutations) => {\n this._onMutation(mutations)\n })\n this._mutationObserver.observe(observerTarget, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n })\n }\n }\n\n stop() {\n this._mutationObserver?.disconnect()\n this._mutationObserver = undefined\n assignableWindow.removeEventListener('click', this._onClick)\n assignableWindow.removeEventListener('scroll', this._onScroll, { capture: true })\n assignableWindow.removeEventListener('selectionchange', this._onSelectionChange)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n private _onMutation(_mutations: MutationRecord[]): void {\n // we don't actually care about the content of the mutations, right now\n this._lastMutation = Date.now()\n }\n\n private _startClickObserver() {\n addEventListener(assignableWindow, 'click', this._onClick)\n }\n\n private _onClick = (event: Event): void => {\n const click = asClick(event as MouseEvent)\n if (!isNull(click) && !this._ignoreClick(click)) {\n this._clicks.push(click)\n }\n\n if (this._clicks.length && isUndefined(this._checkClickTimer)) {\n this._checkClickTimer = assignableWindow.setTimeout(() => {\n this._checkClicks()\n }, 1000)\n }\n }\n\n // `capture: true` is required to get scroll events for other scrollable elements\n // on the page, not just the window\n // see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture\n //\n // `passive: true` is used to tell the browser that the scroll event handler will not call `preventDefault()`\n // This allows the browser to optimize scrolling performance by not waiting for our handling of the scroll event\n // see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#passive\n private _startScrollObserver() {\n addEventListener(assignableWindow, 'scroll', this._onScroll, { capture: true })\n }\n\n private _onScroll = (): void => {\n const candidateNow = Date.now()\n // very naive throttle\n if (candidateNow % 50 === 0) {\n // we can see many scrolls between scheduled checks,\n // so we update scroll delay as we see them\n // to avoid false positives\n this._clicks.forEach((click) => {\n if (isUndefined(click.scrollDelayMs)) {\n click.scrollDelayMs = candidateNow - click.timestamp\n }\n })\n }\n }\n\n private _startSelectionChangedObserver() {\n addEventListener(assignableWindow, 'selectionchange', this._onSelectionChange)\n }\n\n private _onSelectionChange = (): void => {\n this._lastSelectionChanged = Date.now()\n }\n\n private _ignoreClick(click: DeadClickCandidate | null): boolean {\n if (!click) {\n return true\n }\n\n if (isElementInToolbar(click.node)) {\n return true\n }\n\n const alreadyClickedInLastSecond = this._clicks.some((c) => {\n return c.node === click.node && Math.abs(c.timestamp - click.timestamp) < 1000\n })\n\n if (alreadyClickedInLastSecond) {\n return true\n }\n\n if (\n isTag(click.node, 'html') ||\n !isElementNode(click.node) ||\n autocaptureCompatibleElements.includes(click.node.tagName.toLowerCase())\n ) {\n return true\n }\n\n return false\n }\n\n private _checkClicks() {\n if (!this._clicks.length) {\n return\n }\n\n clearTimeout(this._checkClickTimer)\n this._checkClickTimer = undefined\n\n const clicksToCheck = this._clicks\n this._clicks = []\n\n for (const click of clicksToCheck) {\n click.mutationDelayMs =\n click.mutationDelayMs ??\n (this._lastMutation && click.timestamp <= this._lastMutation\n ? this._lastMutation - click.timestamp\n : undefined)\n click.absoluteDelayMs = Date.now() - click.timestamp\n click.selectionChangedDelayMs =\n this._lastSelectionChanged && click.timestamp <= this._lastSelectionChanged\n ? this._lastSelectionChanged - click.timestamp\n : undefined\n\n const scrollTimeout = checkTimeout(click.scrollDelayMs, this._config.scroll_threshold_ms)\n const selectionChangedTimeout = checkTimeout(\n click.selectionChangedDelayMs,\n this._config.selection_change_threshold_ms\n )\n const mutationTimeout = checkTimeout(click.mutationDelayMs, this._config.mutation_threshold_ms)\n // we want to timeout eventually even if nothing else catches it...\n // we leave a little longer than the maximum threshold to give the other checks a chance to catch it\n const absoluteTimeout = checkTimeout(click.absoluteDelayMs, this._config.mutation_threshold_ms * 1.1)\n\n const hadScroll = isNumber(click.scrollDelayMs) && click.scrollDelayMs < this._config.scroll_threshold_ms\n const hadMutation =\n isNumber(click.mutationDelayMs) && click.mutationDelayMs < this._config.mutation_threshold_ms\n const hadSelectionChange =\n isNumber(click.selectionChangedDelayMs) &&\n click.selectionChangedDelayMs < this._config.selection_change_threshold_ms\n\n if (hadScroll || hadMutation || hadSelectionChange) {\n // ignore clicks that had a scroll or mutation\n continue\n }\n\n if (scrollTimeout || mutationTimeout || absoluteTimeout || selectionChangedTimeout) {\n this._onCapture(click, {\n $dead_click_last_mutation_timestamp: this._lastMutation,\n $dead_click_event_timestamp: click.timestamp,\n $dead_click_scroll_timeout: scrollTimeout,\n $dead_click_mutation_timeout: mutationTimeout,\n $dead_click_absolute_timeout: absoluteTimeout,\n $dead_click_selection_changed_timeout: selectionChangedTimeout,\n })\n } else if (click.absoluteDelayMs < this._config.mutation_threshold_ms) {\n // keep waiting until next check\n this._clicks.push(click)\n }\n }\n\n if (this._clicks.length && isUndefined(this._checkClickTimer)) {\n this._checkClickTimer = assignableWindow.setTimeout(() => {\n this._checkClicks()\n }, 1000)\n }\n }\n\n private _captureDeadClick(click: DeadClickCandidate, properties: Properties) {\n // TODO need to check safe and captur-able as with autocapture\n // TODO autocaputure config\n this.instance.capture(\n '$dead_click',\n {\n ...properties,\n ...autocapturePropertiesForElement(click.node, {\n e: click.originalEvent,\n maskAllElementAttributes: this.instance.config.mask_all_element_attributes,\n maskAllText: this.instance.config.mask_all_text,\n elementAttributeIgnoreList: this._config.element_attribute_ignorelist,\n // TRICKY: it appears that we were moving to elementsChainAsString, but the UI still depends on elements, so :shrug:\n elementsChainAsString: false,\n }).props,\n $dead_click_scroll_delay_ms: click.scrollDelayMs,\n $dead_click_mutation_delay_ms: click.mutationDelayMs,\n $dead_click_absolute_delay_ms: click.absoluteDelayMs,\n $dead_click_selection_changed_delay_ms: click.selectionChangedDelayMs,\n },\n {\n timestamp: new Date(click.timestamp),\n }\n )\n }\n}\n\nassignableWindow.__PosthogExtensions__ = assignableWindow.__PosthogExtensions__ || {}\nassignableWindow.__PosthogExtensions__.initDeadClicksAutocapture = (ph, config) =>\n new LazyLoadedDeadClicksAutocapture(ph, config)\n\nexport default LazyLoadedDeadClicksAutocapture\n","/*\n * Constants\n */\n\n/* PROPERTY KEYS */\n\n// This key is deprecated, but we want to check for it to see whether aliasing is allowed.\nexport const PEOPLE_DISTINCT_ID_KEY = '$people_distinct_id'\nexport const DISTINCT_ID = 'distinct_id'\nexport const ALIAS_ID_KEY = '__alias'\nexport const CAMPAIGN_IDS_KEY = '__cmpns'\nexport const EVENT_TIMERS_KEY = '__timers'\nexport const AUTOCAPTURE_DISABLED_SERVER_SIDE = '$autocapture_disabled_server_side'\nexport const HEATMAPS_ENABLED_SERVER_SIDE = '$heatmaps_enabled_server_side'\nexport const EXCEPTION_CAPTURE_ENABLED_SERVER_SIDE = '$exception_capture_enabled_server_side'\nexport const ERROR_TRACKING_SUPPRESSION_RULES = '$error_tracking_suppression_rules'\nexport const ERROR_TRACKING_CAPTURE_EXTENSION_EXCEPTIONS = '$error_tracking_capture_extension_exceptions'\nexport const WEB_VITALS_ENABLED_SERVER_SIDE = '$web_vitals_enabled_server_side'\nexport const DEAD_CLICKS_ENABLED_SERVER_SIDE = '$dead_clicks_enabled_server_side'\nexport const WEB_VITALS_ALLOWED_METRICS = '$web_vitals_allowed_metrics'\nexport const SESSION_RECORDING_REMOTE_CONFIG = '$session_recording_remote_config'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_ENABLED_SERVER_SIDE = '$session_recording_enabled_server_side'\n// @deprecated can be removed along with eager loaded replay\nexport const CONSOLE_LOG_RECORDING_ENABLED_SERVER_SIDE = '$console_log_recording_enabled_server_side'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_NETWORK_PAYLOAD_CAPTURE = '$session_recording_network_payload_capture'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_MASKING = '$session_recording_masking'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_CANVAS_RECORDING = '$session_recording_canvas_recording'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_SAMPLE_RATE = '$replay_sample_rate'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_MINIMUM_DURATION = '$replay_minimum_duration'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_SCRIPT_CONFIG = '$replay_script_config'\nexport const SESSION_RECORDING_OVERRIDE_SAMPLING = '$replay_override_sampling'\nexport const SESSION_RECORDING_OVERRIDE_LINKED_FLAG = '$replay_override_linked_flag'\nexport const SESSION_RECORDING_OVERRIDE_URL_TRIGGER = '$replay_override_url_trigger'\nexport const SESSION_RECORDING_OVERRIDE_EVENT_TRIGGER = '$replay_override_event_trigger'\nexport const SESSION_ID = '$sesid'\nexport const SESSION_RECORDING_IS_SAMPLED = '$session_is_sampled'\nexport const SESSION_RECORDING_PAST_MINIMUM_DURATION = '$session_past_minimum_duration'\nexport const SESSION_RECORDING_URL_TRIGGER_ACTIVATED_SESSION = '$session_recording_url_trigger_activated_session'\nexport const SESSION_RECORDING_EVENT_TRIGGER_ACTIVATED_SESSION = '$session_recording_event_trigger_activated_session'\nexport const ENABLED_FEATURE_FLAGS = '$enabled_feature_flags'\nexport const PERSISTENCE_EARLY_ACCESS_FEATURES = '$early_access_features'\nexport const PERSISTENCE_FEATURE_FLAG_DETAILS = '$feature_flag_details'\nexport const STORED_PERSON_PROPERTIES_KEY = '$stored_person_properties'\nexport const STORED_GROUP_PROPERTIES_KEY = '$stored_group_properties'\nexport const SURVEYS = '$surveys'\nexport const SURVEYS_ACTIVATED = '$surveys_activated'\nexport const FLAG_CALL_REPORTED = '$flag_call_reported'\nexport const USER_STATE = '$user_state'\nexport const CLIENT_SESSION_PROPS = '$client_session_props'\nexport const CAPTURE_RATE_LIMIT = '$capture_rate_limit'\n\n/** @deprecated Delete this when INITIAL_PERSON_INFO has been around for long enough to ignore backwards compat */\nexport const INITIAL_CAMPAIGN_PARAMS = '$initial_campaign_params'\n/** @deprecated Delete this when INITIAL_PERSON_INFO has been around for long enough to ignore backwards compat */\nexport const INITIAL_REFERRER_INFO = '$initial_referrer_info'\nexport const INITIAL_PERSON_INFO = '$initial_person_info'\nexport const ENABLE_PERSON_PROCESSING = '$epp'\nexport const TOOLBAR_ID = '__POSTHOG_TOOLBAR__'\nexport const TOOLBAR_CONTAINER_CLASS = 'toolbar-global-fade-container'\n\n/**\n * PREVIEW - MAY CHANGE WITHOUT WARNING - DO NOT USE IN PRODUCTION\n * Sentinel value for distinct id, device id, session id. Signals that the server should generate the value\n * */\nexport const COOKIELESS_SENTINEL_VALUE = '$posthog_cookieless'\nexport const COOKIELESS_MODE_FLAG_PROPERTY = '$cookieless_mode'\n\nexport const WEB_EXPERIMENTS = '$web_experiments'\n\n// These are properties that are reserved and will not be automatically included in events\nexport const PERSISTENCE_RESERVED_PROPERTIES = [\n PEOPLE_DISTINCT_ID_KEY,\n ALIAS_ID_KEY,\n CAMPAIGN_IDS_KEY,\n EVENT_TIMERS_KEY,\n SESSION_RECORDING_ENABLED_SERVER_SIDE,\n HEATMAPS_ENABLED_SERVER_SIDE,\n SESSION_ID,\n ENABLED_FEATURE_FLAGS,\n ERROR_TRACKING_SUPPRESSION_RULES,\n USER_STATE,\n PERSISTENCE_EARLY_ACCESS_FEATURES,\n PERSISTENCE_FEATURE_FLAG_DETAILS,\n STORED_GROUP_PROPERTIES_KEY,\n STORED_PERSON_PROPERTIES_KEY,\n SURVEYS,\n FLAG_CALL_REPORTED,\n CLIENT_SESSION_PROPS,\n CAPTURE_RATE_LIMIT,\n INITIAL_CAMPAIGN_PARAMS,\n INITIAL_REFERRER_INFO,\n ENABLE_PERSON_PROCESSING,\n INITIAL_PERSON_INFO,\n]\n\nexport const SURVEYS_REQUEST_TIMEOUT_MS = 10000\n"],"names":["win","window","undefined","global","globalThis","nativeForEach","Array","prototype","forEach","navigator","document","location","fetch","XMLHttpRequest","AbortController","userAgent","assignableWindow","includes","str","needle","indexOf","trim","nativeIsArray","isArray","ObjProto","Object","type_utils_hasOwnProperty","hasOwnProperty","type_utils_toString","toString","obj","call","isFunction","x","isNativeFunction","isUndefined","isString","isNull","isNullish","isNumber","isFormData","FormData","_createLogger","prefix","logger","_log","level","console","consoleLog","_len","arguments","length","args","_key","info","_len2","_key2","warn","_len3","_key3","error","_len4","_key4","critical","_len5","_key5","uninitializedWarning","methodName","createLogger","additionalPrefix","breaker","eachArray","iterator","thisArg","i","l","each","pair","entries","key","extend","source","prop","ownProps","keys","resArray","addEventListener","element","event","callback","options","capture","passive","isElementNode","el","nodeType","isTag","tag","tagName","toLowerCase","splitClassString","s","split","getClassNames","className","baseVal","getAttribute","getSafeText","elText","shouldCaptureElement","isSensitiveElement","childNodes","child","_makeSafeText","isTextNode","textContent","filter","shouldCaptureValue","join","replace","substring","autocaptureCompatibleElements","curEl","parentNode","classes","type","name","id","test","coreCCPattern","anchoredCCRegex","RegExp","unanchoredCCRegex","coreSSNPattern","anchoredSSNRegex","unanchoredSSNRegex","value","anchorRegexes","getDirectAndNestedSpanText","target","text","getNestedSpanText","_child$tagName","spanText","e","getElementsChainString","elements","ret","map","_element$nth_child","_element$nth_of_type","el_string","tag_name","attr_class","single_class","sort","attributes","_extends","nth_child","nth_of_type","href","attr_id","sortedAttributes","_ref","_ref2","a","b","localeCompare","_ref3","escapeQuotes","_ref4","elementsToString","_el$$el_text","_el$attr__href","response","slice","extractAttrClass","_ref5","_ref6","extractElements","input","limitText","previousElementSibling","_el","previousSibling","getPropertiesFromElement","elem","maskAllAttributes","maskText","elementAttributeIgnorelist","props","c","attr","attributeName","nthChild","nthOfType","currentElem","autocapturePropertiesForElement","_elementsJson$","_elementsJson$2","maskAllElementAttributes","maskAllText","elementAttributeIgnoreList","targetElementList","push","host","externalHref","url","elementsJson","autocaptureAugmentProperties","explicitNoCapture","shouldCaptureEl","augmentProperties","propertyKey","propertyValue","getAugmentPropertiesFromElement","_convertToURL","_window$location","hrefHost","createElement","locationHost","$event_type","$ce_version","$elements","$elements_chain","$el_text","$external_click_url","isAngularZonePresent","Zone","cachedImplementations","getNativeMutationObserverImplementation","cached","impl","bind","sandbox","hidden","head","appendChild","contentWindow","removeChild","getNativeImplementation","asClick","_e$target","eventTarget","srcElement","shadowRoot","composedPath","node","originalEvent","timestamp","Date","now","checkTimeout","thresholdMs","LazyLoadedDeadClicksAutocapture","_asRequiredConfig","providedConfig","_providedConfig$eleme","_providedConfig$scrol","_providedConfig$selec","_providedConfig$mutat","defaultConfig","this","_defaultConfig","__onCapture","_captureDeadClick","element_attribute_ignorelist","scroll_threshold_ms","selection_change_threshold_ms","mutation_threshold_ms","constructor","instance","config","_clicks","defaultOnCapture","_onClick","click","_ignoreClick","_checkClickTimer","setTimeout","_checkClicks","_onScroll","candidateNow","scrollDelayMs","_onSelectionChange","_lastSelectionChanged","_config","_onCapture","start","observerTarget","_startClickObserver","_startScrollObserver","_startSelectionChangedObserver","_startMutationObserver","_mutationObserver","NativeMutationObserver","mutations","_onMutation","observe","characterData","childList","subtree","stop","_this$_mutationObserv","disconnect","removeEventListener","_mutations","_lastMutation","Element","closest","some","Math","abs","clearTimeout","clicksToCheck","_click$mutationDelayM","mutationDelayMs","absoluteDelayMs","selectionChangedDelayMs","scrollTimeout","selectionChangedTimeout","mutationTimeout","absoluteTimeout","hadScroll","hadMutation","hadSelectionChange","$dead_click_last_mutation_timestamp","$dead_click_event_timestamp","$dead_click_scroll_timeout","$dead_click_mutation_timeout","$dead_click_absolute_timeout","$dead_click_selection_changed_timeout","properties","mask_all_element_attributes","mask_all_text","$dead_click_scroll_delay_ms","$dead_click_mutation_delay_ms","$dead_click_absolute_delay_ms","$dead_click_selection_changed_delay_ms","__PosthogExtensions__","initDeadClicksAutocapture","ph"],"mappings":"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"}
1
+ {"version":3,"file":"dead-clicks-autocapture.js","sources":["../src/utils/globals.ts","../../core/dist/utils/string-utils.mjs","../../core/dist/utils/type-utils.mjs","../src/utils/logger.ts","../src/utils/index.ts","../src/utils/element-utils.ts","../src/autocapture-utils.ts","../src/autocapture.ts","../src/utils/request-utils.ts","../src/utils/type-utils.ts","../src/utils/prototype-utils.ts","../src/entrypoints/dead-clicks-autocapture.ts","../src/constants.ts"],"sourcesContent":["import type { PostHog } from '../posthog-core'\nimport { SessionIdManager } from '../sessionid'\nimport {\n DeadClicksAutoCaptureConfig,\n ExternalIntegrationKind,\n Properties,\n RemoteConfig,\n SiteAppLoader,\n SessionStartReason,\n} from '../types'\n// only importing types here, so won't affect the bundle\n// eslint-disable-next-line posthog-js/no-external-replay-imports\nimport type { SessionRecordingStatus, TriggerType } from '../extensions/replay/external/triggerMatching'\nimport { eventWithTime } from '../extensions/replay/types/rrweb-types'\nimport { ErrorTracking } from '@posthog/core'\n\n/*\n * Global helpers to protect access to browser globals in a way that is safer for different targets\n * like DOM, SSR, Web workers etc.\n *\n * NOTE: Typically we want the \"window\" but globalThis works for both the typical browser context as\n * well as other contexts such as the web worker context. Window is still exported for any bits that explicitly require it.\n * If in doubt - export the global you need from this file and use that as an optional value. This way the code path is forced\n * to handle the case where the global is not available.\n */\n\n// eslint-disable-next-line no-restricted-globals\nconst win: (Window & typeof globalThis) | undefined = typeof window !== 'undefined' ? window : undefined\n\nexport type AssignableWindow = Window &\n typeof globalThis & {\n /*\n * Main PostHog instance\n */\n posthog: any\n\n /*\n * This is our contract between (potentially) lazily loaded extensions and the SDK\n */\n __PosthogExtensions__?: PostHogExtensions\n\n /**\n * When loading remote config, we assign it to this global configuration\n * for ease of sharing it with the rest of the SDK\n */\n _POSTHOG_REMOTE_CONFIG?: Record<\n string,\n {\n config: RemoteConfig\n siteApps: SiteAppLoader[]\n }\n >\n\n /**\n * If this is set on the window, our logger will log to the console\n * for ease of debugging. Used for testing purposes only.\n *\n * @see {Config.DEBUG} from config.ts\n */\n POSTHOG_DEBUG: any\n\n // Exposed by the browser\n doNotTrack: any\n\n // See entrypoints/customizations.full.ts\n posthogCustomizations: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/exception-autocapture.ts\n *\n * @deprecated use `__PosthogExtensions__.errorWrappingFunctions` instead\n */\n posthogErrorWrappingFunctions: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/posthog-recorder.ts\n *\n * @deprecated use `__PosthogExtensions__.rrweb` instead\n */\n rrweb: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/posthog-recorder.ts\n *\n * @deprecated use `__PosthogExtensions__.rrwebConsoleRecord` instead\n */\n rrwebConsoleRecord: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/posthog-recorder.ts\n *\n * @deprecated use `__PosthogExtensions__.getRecordNetworkPlugin` instead\n */\n getRecordNetworkPlugin: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/web-vitals.ts\n *\n * @deprecated use `__PosthogExtensions__.postHogWebVitalsCallbacks` instead\n */\n postHogWebVitalsCallbacks: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/tracing-headers.ts\n *\n * @deprecated use `__PosthogExtensions__.postHogTracingHeadersPatchFns` instead\n */\n postHogTracingHeadersPatchFns: any\n\n /**\n * This is a legacy way to expose these functions, but we still need to support it for backwards compatibility\n * Can be removed once we drop support for 1.161.1\n *\n * See entrypoints/surveys.ts\n *\n * @deprecated use `__PosthogExtensions__.generateSurveys` instead\n */\n extendPostHogWithSurveys: any\n\n /*\n * These are used to handle our toolbar state.\n * @see {Toolbar} from extensions/toolbar.ts\n */\n ph_load_toolbar: any\n ph_load_editor: any\n ph_toolbar_state: any\n } & Record<`__$$ph_site_app_${string}`, any>\n\n/**\n * This is our contract between (potentially) lazily loaded extensions and the SDK\n * changes to this interface can be breaking changes for users of the SDK\n */\n\nexport type ExternalExtensionKind = 'intercom-integration' | 'crisp-chat-integration'\n\nexport type PostHogExtensionKind =\n | 'toolbar'\n | 'exception-autocapture'\n | 'web-vitals'\n | 'recorder'\n | 'lazy-recorder'\n | 'tracing-headers'\n | 'surveys'\n | 'dead-clicks-autocapture'\n | 'remote-config'\n | ExternalExtensionKind\n\nexport interface LazyLoadedSessionRecordingInterface {\n start: (startReason?: SessionStartReason) => void\n stop: () => void\n sessionId: string\n status: SessionRecordingStatus\n onRRwebEmit: (rawEvent: eventWithTime) => void\n log: (message: string, level: 'log' | 'warn' | 'error') => void\n sdkDebugProperties: Properties\n overrideLinkedFlag: () => void\n overrideSampling: () => void\n overrideTrigger: (triggerType: TriggerType) => void\n isStarted: boolean\n tryAddCustomEvent(tag: string, payload: any): boolean\n}\n\nexport interface LazyLoadedDeadClicksAutocaptureInterface {\n start: (observerTarget: Node) => void\n stop: () => void\n}\n\ninterface PostHogExtensions {\n loadExternalDependency?: (\n posthog: PostHog,\n kind: PostHogExtensionKind,\n callback: (error?: string | Event, event?: Event) => void\n ) => void\n\n loadSiteApp?: (posthog: PostHog, appUrl: string, callback: (error?: string | Event, event?: Event) => void) => void\n\n errorWrappingFunctions?: {\n wrapOnError: (captureFn: (props: ErrorTracking.ErrorProperties) => void) => () => void\n wrapUnhandledRejection: (captureFn: (props: ErrorTracking.ErrorProperties) => void) => () => void\n wrapConsoleError: (captureFn: (props: ErrorTracking.ErrorProperties) => void) => () => void\n }\n rrweb?: { record: any; version: string }\n rrwebPlugins?: { getRecordConsolePlugin: any; getRecordNetworkPlugin?: any }\n generateSurveys?: (posthog: PostHog, isSurveysEnabled: boolean) => any | undefined\n postHogWebVitalsCallbacks?: {\n onLCP: (metric: any) => void\n onCLS: (metric: any) => void\n onFCP: (metric: any) => void\n onINP: (metric: any) => void\n }\n tracingHeadersPatchFns?: {\n _patchFetch: (hostnames: string[], distinctId: string, sessionManager?: SessionIdManager) => () => void\n _patchXHR: (hostnames: string[], distinctId: string, sessionManager?: SessionIdManager) => () => void\n }\n initDeadClicksAutocapture?: (\n ph: PostHog,\n config: DeadClicksAutoCaptureConfig\n ) => LazyLoadedDeadClicksAutocaptureInterface\n integrations?: {\n [K in ExternalIntegrationKind]?: { start: (posthog: PostHog) => void; stop: () => void }\n }\n initSessionRecording?: (ph: PostHog) => LazyLoadedSessionRecordingInterface\n}\n\nconst global: typeof globalThis | undefined = typeof globalThis !== 'undefined' ? globalThis : win\n\nexport const ArrayProto = Array.prototype\nexport const nativeForEach = ArrayProto.forEach\nexport const nativeIndexOf = ArrayProto.indexOf\n\nexport const navigator = global?.navigator\nexport const document = global?.document\nexport const location = global?.location\nexport const fetch = global?.fetch\nexport const XMLHttpRequest =\n global?.XMLHttpRequest && 'withCredentials' in new global.XMLHttpRequest() ? global.XMLHttpRequest : undefined\nexport const AbortController = global?.AbortController\nexport const userAgent = navigator?.userAgent\nexport const assignableWindow: AssignableWindow = win ?? ({} as any)\n\nexport { win as window }\n","function includes(str, needle) {\n return -1 !== str.indexOf(needle);\n}\nconst trim = function(str) {\n return str.trim();\n};\nconst stripLeadingDollar = function(s) {\n return s.replace(/^\\$/, '');\n};\nfunction isDistinctIdStringLike(value) {\n return [\n 'distinct_id',\n 'distinctid'\n ].includes(value.toLowerCase());\n}\nexport { includes, isDistinctIdStringLike, stripLeadingDollar, trim };\n","import { knownUnsafeEditableEvent } from \"../types.mjs\";\nimport { includes } from \"./string-utils.mjs\";\nconst nativeIsArray = Array.isArray;\nconst ObjProto = Object.prototype;\nconst type_utils_hasOwnProperty = ObjProto.hasOwnProperty;\nconst type_utils_toString = ObjProto.toString;\nconst isArray = nativeIsArray || function(obj) {\n return '[object Array]' === type_utils_toString.call(obj);\n};\nconst isFunction = (x)=>'function' == typeof x;\nconst isNativeFunction = (x)=>isFunction(x) && -1 !== x.toString().indexOf('[native code]');\nconst isObject = (x)=>x === Object(x) && !isArray(x);\nconst isEmptyObject = (x)=>{\n if (isObject(x)) {\n for(const key in x)if (type_utils_hasOwnProperty.call(x, key)) return false;\n return true;\n }\n return false;\n};\nconst isUndefined = (x)=>void 0 === x;\nconst isString = (x)=>'[object String]' == type_utils_toString.call(x);\nconst isEmptyString = (x)=>isString(x) && 0 === x.trim().length;\nconst isNull = (x)=>null === x;\nconst isNullish = (x)=>isUndefined(x) || isNull(x);\nconst isNumber = (x)=>'[object Number]' == type_utils_toString.call(x);\nconst isBoolean = (x)=>'[object Boolean]' === type_utils_toString.call(x);\nconst isFormData = (x)=>x instanceof FormData;\nconst isFile = (x)=>x instanceof File;\nconst isPlainError = (x)=>x instanceof Error;\nconst isKnownUnsafeEditableEvent = (x)=>includes(knownUnsafeEditableEvent, x);\nfunction isInstanceOf(candidate, base) {\n try {\n return candidate instanceof base;\n } catch {\n return false;\n }\n}\nfunction isPrimitive(value) {\n return null === value || 'object' != typeof value;\n}\nfunction isBuiltin(candidate, className) {\n return Object.prototype.toString.call(candidate) === `[object ${className}]`;\n}\nfunction isError(candidate) {\n switch(Object.prototype.toString.call(candidate)){\n case '[object Error]':\n case '[object Exception]':\n case '[object DOMException]':\n case '[object DOMError]':\n case '[object WebAssembly.Exception]':\n return true;\n default:\n return isInstanceOf(candidate, Error);\n }\n}\nfunction isErrorEvent(event) {\n return isBuiltin(event, 'ErrorEvent');\n}\nfunction isEvent(candidate) {\n return !isUndefined(Event) && isInstanceOf(candidate, Event);\n}\nfunction isPlainObject(candidate) {\n return isBuiltin(candidate, 'Object');\n}\nconst yesLikeValues = [\n true,\n 'true',\n 1,\n '1',\n 'yes'\n];\nconst isYesLike = (val)=>includes(yesLikeValues, val);\nconst noLikeValues = [\n false,\n 'false',\n 0,\n '0',\n 'no'\n];\nconst isNoLike = (val)=>includes(noLikeValues, val);\nexport { type_utils_hasOwnProperty as hasOwnProperty, isArray, isBoolean, isBuiltin, isEmptyObject, isEmptyString, isError, isErrorEvent, isEvent, isFile, isFormData, isFunction, isInstanceOf, isKnownUnsafeEditableEvent, isNativeFunction, isNoLike, isNull, isNullish, isNumber, isObject, isPlainError, isPlainObject, isPrimitive, isString, isUndefined, isYesLike, noLikeValues, yesLikeValues };\n","import Config from '../config'\nimport { isUndefined } from '@posthog/core'\nimport { assignableWindow, window } from './globals'\nimport type { Logger } from '@posthog/core'\n\ntype PosthogJsLogger = Omit<Logger, 'createLogger'> & {\n _log: (level: 'log' | 'warn' | 'error', ...args: any[]) => void\n uninitializedWarning: (methodName: string) => void\n createLogger: (prefix: string) => PosthogJsLogger\n}\n\nconst _createLogger = (prefix: string): PosthogJsLogger => {\n const logger: PosthogJsLogger = {\n _log: (level: 'log' | 'warn' | 'error', ...args: any[]) => {\n if (\n window &&\n (Config.DEBUG || assignableWindow.POSTHOG_DEBUG) &&\n !isUndefined(window.console) &&\n window.console\n ) {\n const consoleLog =\n '__rrweb_original__' in window.console[level]\n ? (window.console[level] as any)['__rrweb_original__']\n : window.console[level]\n\n // eslint-disable-next-line no-console\n consoleLog(prefix, ...args)\n }\n },\n\n info: (...args: any[]) => {\n logger._log('log', ...args)\n },\n\n warn: (...args: any[]) => {\n logger._log('warn', ...args)\n },\n\n error: (...args: any[]) => {\n logger._log('error', ...args)\n },\n\n critical: (...args: any[]) => {\n // Critical errors are always logged to the console\n // eslint-disable-next-line no-console\n console.error(prefix, ...args)\n },\n\n uninitializedWarning: (methodName: string) => {\n logger.error(`You must initialize PostHog before calling ${methodName}`)\n },\n\n createLogger: (additionalPrefix: string) => _createLogger(`${prefix} ${additionalPrefix}`),\n }\n return logger\n}\n\nexport const logger = _createLogger('[PostHog.js]')\n\nexport const createLogger = logger.createLogger\n","import { Breaker, Properties } from '../types'\nimport { nativeForEach, nativeIndexOf } from './globals'\nimport { logger } from './logger'\nimport { isFormData, isNull, isNullish, isNumber, isString, isUndefined, hasOwnProperty, isArray } from '@posthog/core'\n\nconst breaker: Breaker = {}\n\nexport function eachArray<E = any>(\n obj: E[] | null | undefined,\n iterator: (value: E, key: number) => void | Breaker,\n thisArg?: any\n): void {\n if (isArray(obj)) {\n if (nativeForEach && obj.forEach === nativeForEach) {\n obj.forEach(iterator, thisArg)\n } else if ('length' in obj && obj.length === +obj.length) {\n for (let i = 0, l = obj.length; i < l; i++) {\n if (i in obj && iterator.call(thisArg, obj[i], i) === breaker) {\n return\n }\n }\n }\n }\n}\n\n/**\n * @param {*=} obj\n * @param {function(...*)=} iterator\n * @param {Object=} thisArg\n */\nexport function each(obj: any, iterator: (value: any, key: any) => void | Breaker, thisArg?: any): void {\n if (isNullish(obj)) {\n return\n }\n if (isArray(obj)) {\n return eachArray(obj, iterator, thisArg)\n }\n if (isFormData(obj)) {\n for (const pair of obj.entries()) {\n if (iterator.call(thisArg, pair[1], pair[0]) === breaker) {\n return\n }\n }\n return\n }\n for (const key in obj) {\n if (hasOwnProperty.call(obj, key)) {\n if (iterator.call(thisArg, obj[key], key) === breaker) {\n return\n }\n }\n }\n}\n\nexport const extend = function (obj: Record<string, any>, ...args: Record<string, any>[]): Record<string, any> {\n eachArray(args, function (source) {\n for (const prop in source) {\n if (source[prop] !== void 0) {\n obj[prop] = source[prop]\n }\n }\n })\n return obj\n}\n\nexport const extendArray = function <T>(obj: T[], ...args: T[][]): T[] {\n eachArray(args, function (source) {\n eachArray(source, function (item) {\n obj.push(item)\n })\n })\n return obj\n}\n\nexport const include = function (\n obj: null | string | Array<any> | Record<string, any>,\n target: any\n): boolean | Breaker {\n let found = false\n if (isNull(obj)) {\n return found\n }\n if (nativeIndexOf && obj.indexOf === nativeIndexOf) {\n return obj.indexOf(target) != -1\n }\n each(obj, function (value) {\n if (found || (found = value === target)) {\n return breaker\n }\n return\n })\n return found\n}\n\n/**\n * Object.entries() polyfill\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries\n */\nexport function entries<T = any>(obj: Record<string, T>): [string, T][] {\n const ownProps = Object.keys(obj)\n let i = ownProps.length\n const resArray = new Array(i) // preallocate the Array\n\n while (i--) {\n resArray[i] = [ownProps[i], obj[ownProps[i]]]\n }\n return resArray\n}\n\nexport const trySafe = function <T>(fn: () => T): T | undefined {\n try {\n return fn()\n } catch {\n return undefined\n }\n}\n\nexport const safewrap = function <F extends (...args: any[]) => any = (...args: any[]) => any>(f: F): F {\n return function (...args) {\n try {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return f.apply(this, args)\n } catch (e) {\n logger.critical(\n 'Implementation error. Please turn on debug mode and open a ticket on https://app.posthog.com/home#panel=support%3Asupport%3A.'\n )\n logger.critical(e)\n }\n } as F\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport const safewrapClass = function (klass: Function, functions: string[]): void {\n for (let i = 0; i < functions.length; i++) {\n klass.prototype[functions[i]] = safewrap(klass.prototype[functions[i]])\n }\n}\n\nexport const stripEmptyProperties = function (p: Properties): Properties {\n const ret: Properties = {}\n each(p, function (v, k) {\n if ((isString(v) && v.length > 0) || isNumber(v)) {\n ret[k] = v\n }\n })\n return ret\n}\n\n/**\n * Deep copies an object.\n * It handles cycles by replacing all references to them with `undefined`\n * Also supports customizing native values\n *\n * @param value\n * @param customizer\n * @returns {{}|undefined|*}\n */\nfunction deepCircularCopy<T extends Record<string, any> = Record<string, any>>(\n value: T,\n customizer?: <K extends keyof T = keyof T>(value: T[K], key?: K) => T[K]\n): T | undefined {\n const COPY_IN_PROGRESS_SET = new Set()\n\n function internalDeepCircularCopy(value: T, key?: string): T | undefined {\n if (value !== Object(value)) return customizer ? customizer(value as any, key) : value // primitive value\n\n if (COPY_IN_PROGRESS_SET.has(value)) return undefined\n COPY_IN_PROGRESS_SET.add(value)\n let result: T\n\n if (isArray(value)) {\n result = [] as any as T\n eachArray(value, (it) => {\n result.push(internalDeepCircularCopy(it))\n })\n } else {\n result = {} as T\n each(value, (val, key) => {\n if (!COPY_IN_PROGRESS_SET.has(val)) {\n ;(result as any)[key] = internalDeepCircularCopy(val, key)\n }\n })\n }\n return result\n }\n return internalDeepCircularCopy(value)\n}\n\nexport function _copyAndTruncateStrings<T extends Record<string, any> = Record<string, any>>(\n object: T,\n maxStringLength: number | null\n): T {\n return deepCircularCopy(object, (value: any) => {\n if (isString(value) && !isNull(maxStringLength)) {\n return (value as string).slice(0, maxStringLength)\n }\n return value\n }) as T\n}\n\n// NOTE: Update PostHogConfig docs if you change this list\n// We will not try to catch all bullets here, but we should make an effort to catch the most common ones\n// You should be highly against adding more to this list, because ultimately customers can configure\n// their `cross_subdomain_cookie` setting to anything they want.\nconst EXCLUDED_FROM_CROSS_SUBDOMAIN_COOKIE = ['herokuapp.com', 'vercel.app', 'netlify.app']\nexport function isCrossDomainCookie(documentLocation: Location | undefined) {\n const hostname = documentLocation?.hostname\n\n if (!isString(hostname)) {\n return false\n }\n // split and slice isn't a great way to match arbitrary domains,\n // but it's good enough for ensuring we only match herokuapp.com when it is the TLD\n // for the hostname\n const lastTwoParts = hostname.split('.').slice(-2).join('.')\n\n for (const excluded of EXCLUDED_FROM_CROSS_SUBDOMAIN_COOKIE) {\n if (lastTwoParts === excluded) {\n return false\n }\n }\n\n return true\n}\n\nexport function find<T>(value: T[], predicate: (value: T) => boolean): T | undefined {\n for (let i = 0; i < value.length; i++) {\n if (predicate(value[i])) {\n return value[i]\n }\n }\n return undefined\n}\n\n// Use this instead of element.addEventListener to avoid eslint errors\n// this properly implements the default options for passive event listeners\nexport function addEventListener(\n element: Window | Document | Element | undefined,\n event: string,\n callback: EventListener,\n options?: AddEventListenerOptions\n): void {\n const { capture = false, passive = true } = options ?? {}\n\n // This is the only place where we are allowed to call this function\n // because the whole idea is that we should be calling this instead of the built-in one\n // eslint-disable-next-line posthog-js/no-add-event-listener\n element?.addEventListener(event, callback, { capture, passive })\n}\n\n/**\n * Helper to migrate deprecated config fields to new field names with appropriate warnings\n * @param config - The config object to check\n * @param newField - The new field name to use\n * @param oldField - The deprecated field name to check for\n * @param defaultValue - The default value if neither field is set\n * @param loggerInstance - Optional logger instance for deprecation warnings\n * @returns The value to use (new field takes precedence over old field)\n */\nexport function migrateConfigField<T>(\n config: Record<string, any>,\n newField: string,\n oldField: string,\n defaultValue: T,\n loggerInstance?: { warn: (message: string) => void }\n): T {\n const hasNewField = newField in config && !isUndefined(config[newField])\n const hasOldField = oldField in config && !isUndefined(config[oldField])\n\n if (hasNewField) {\n return config[newField]\n }\n\n if (hasOldField) {\n if (loggerInstance) {\n loggerInstance.warn(\n `Config field '${oldField}' is deprecated. Please use '${newField}' instead. ` +\n `The old field will be removed in a future major version.`\n )\n }\n return config[oldField]\n }\n\n return defaultValue\n}\n","import { TOOLBAR_CONTAINER_CLASS, TOOLBAR_ID } from '../constants'\n\nexport function isElementInToolbar(el: EventTarget | null): boolean {\n if (el instanceof Element) {\n // closest isn't available in IE11, but we'll polyfill when bundling\n return el.id === TOOLBAR_ID || !!el.closest?.('.' + TOOLBAR_CONTAINER_CLASS)\n }\n return false\n}\n\n/*\n * Check whether an element has nodeType Node.ELEMENT_NODE\n * @param {Element} el - element to check\n * @returns {boolean} whether el is of the correct nodeType\n */\nexport function isElementNode(el: Node | Element | undefined | null): el is Element {\n return !!el && el.nodeType === 1 // Node.ELEMENT_NODE - use integer constant for browser portability\n}\n\n/*\n * Check whether an element is of a given tag type.\n * Due to potential reference discrepancies (such as the webcomponents.js polyfill),\n * we want to match tagNames instead of specific references because something like\n * element === document.body won't always work because element might not be a native\n * element.\n * @param {Element} el - element to check\n * @param {string} tag - tag name (e.g., \"div\")\n * @returns {boolean} whether el is of the given tag type\n */\nexport function isTag(el: Element | undefined | null, tag: string): el is HTMLElement {\n return !!el && !!el.tagName && el.tagName.toLowerCase() === tag.toLowerCase()\n}\n\n/*\n * Check whether an element has nodeType Node.TEXT_NODE\n * @param {Element} el - element to check\n * @returns {boolean} whether el is of the correct nodeType\n */\nexport function isTextNode(el: Element | undefined | null): el is HTMLElement {\n return !!el && el.nodeType === 3 // Node.TEXT_NODE - use integer constant for browser portability\n}\n\n/*\n * Check whether an element has nodeType Node.DOCUMENT_FRAGMENT_NODE\n * @param {Element} el - element to check\n * @returns {boolean} whether el is of the correct nodeType\n */\nexport function isDocumentFragment(el: Element | ParentNode | undefined | null): el is DocumentFragment {\n return !!el && el.nodeType === 11 // Node.DOCUMENT_FRAGMENT_NODE - use integer constant for browser portability\n}\n","import { AutocaptureConfig, PostHogConfig, Properties } from './types'\nimport { each, entries } from './utils'\n\nimport { isNullish, isString, isUndefined, isArray, isBoolean } from '@posthog/core'\nimport { logger } from './utils/logger'\nimport { window } from './utils/globals'\nimport { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils'\nimport { includes, trim } from '@posthog/core'\n\nexport function splitClassString(s: string): string[] {\n return s ? trim(s).split(/\\s+/) : []\n}\n\nfunction checkForURLMatches(urlsList: (string | RegExp)[]): boolean {\n const url = window?.location.href\n return !!(url && urlsList && urlsList.some((regex) => url.match(regex)))\n}\n\n/*\n * Get the className of an element, accounting for edge cases where element.className is an object\n *\n * Because this is a string it can contain unexpected characters\n * So, this method safely splits the className and returns that array.\n */\nexport function getClassNames(el: Element): string[] {\n let className = ''\n switch (typeof el.className) {\n case 'string':\n className = el.className\n break\n // TODO: when is this ever used?\n case 'object': // handle cases where className might be SVGAnimatedString or some other type\n className =\n (el.className && 'baseVal' in el.className ? (el.className as any).baseVal : null) ||\n el.getAttribute('class') ||\n ''\n break\n default:\n className = ''\n }\n\n return splitClassString(className)\n}\n\nexport function makeSafeText(s: string | null | undefined): string | null {\n if (isNullish(s)) {\n return null\n }\n\n return (\n trim(s)\n // scrub potentially sensitive values\n .split(/(\\s+)/)\n .filter((s) => shouldCaptureValue(s))\n .join('')\n // normalize whitespace\n .replace(/[\\r\\n]/g, ' ')\n .replace(/[ ]+/g, ' ')\n // truncate\n .substring(0, 255)\n )\n}\n\n/*\n * Get the direct text content of an element, protecting against sensitive data collection.\n * Concats textContent of each of the element's text node children; this avoids potential\n * collection of sensitive data that could happen if we used element.textContent and the\n * element had sensitive child elements, since element.textContent includes child content.\n * Scrubs values that look like they could be sensitive (i.e. cc or ssn number).\n * @param {Element} el - element to get the text of\n * @returns {string} the element's direct text content\n */\nexport function getSafeText(el: Element): string {\n let elText = ''\n\n if (shouldCaptureElement(el) && !isSensitiveElement(el) && el.childNodes && el.childNodes.length) {\n each(el.childNodes, function (child) {\n if (isTextNode(child) && child.textContent) {\n elText += makeSafeText(child.textContent) ?? ''\n }\n })\n }\n\n return trim(elText)\n}\n\nexport function getEventTarget(e: Event): Element | null {\n // https://developer.mozilla.org/en-US/docs/Web/API/Event/target#Compatibility_notes\n if (isUndefined(e.target)) {\n return (e.srcElement as Element) || null\n } else {\n if ((e.target as HTMLElement)?.shadowRoot) {\n return (e.composedPath()[0] as Element) || null\n }\n return (e.target as Element) || null\n }\n}\n\nexport const autocaptureCompatibleElements = ['a', 'button', 'form', 'input', 'select', 'textarea', 'label']\n\n/*\n if there is no config, then all elements are allowed\n if there is a config, and there is an allow list, then only elements in the allow list are allowed\n assumes that some other code is checking this element's parents\n */\nfunction checkIfElementTreePassesElementAllowList(\n elements: Element[],\n autocaptureConfig: AutocaptureConfig | undefined\n): boolean {\n const allowlist = autocaptureConfig?.element_allowlist\n if (isUndefined(allowlist)) {\n // everything is allowed, when there is no allow list\n return true\n }\n\n // check each element in the tree\n // if any of the elements are in the allow list, then the tree is allowed\n for (const el of elements) {\n if (allowlist.some((elementType) => el.tagName.toLowerCase() === elementType)) {\n return true\n }\n }\n\n // otherwise there is an allow list and this element tree didn't match it\n return false\n}\n\n/*\n if there is no selector list (i.e. it is undefined), then any elements matches\n if there is an empty list, then no elements match\n if there is a selector list, then check it against each element provided\n */\nfunction checkIfElementsMatchCSSSelector(elements: Element[], selectorList: string[] | undefined): boolean {\n if (isUndefined(selectorList)) {\n // everything is allowed, when there is no selector list\n return true\n }\n\n for (const el of elements) {\n if (selectorList.some((selector) => el.matches(selector))) {\n return true\n }\n }\n\n return false\n}\n\nexport function getParentElement(curEl: Element): Element | false {\n const parentNode = curEl.parentNode\n if (!parentNode || !isElementNode(parentNode)) return false\n return parentNode\n}\n\n// autocapture check will already filter for ph-no-capture,\n// but we include it here to protect against future changes accidentally removing that check\nconst DEFAULT_RAGE_CLICK_IGNORE_LIST = ['.ph-no-rageclick', '.ph-no-capture']\nexport function shouldCaptureRageclick(el: Element | null, _config: PostHogConfig['rageclick']) {\n if (!window || cannotCheckForAutocapture(el)) {\n return false\n }\n\n let selectorIgnoreList: string[] | boolean\n if (isBoolean(_config)) {\n selectorIgnoreList = _config ? DEFAULT_RAGE_CLICK_IGNORE_LIST : false\n } else {\n selectorIgnoreList = _config?.css_selector_ignorelist ?? DEFAULT_RAGE_CLICK_IGNORE_LIST\n }\n\n if (selectorIgnoreList === false) {\n return false\n }\n\n const { targetElementList } = getElementAndParentsForElement(el, false)\n // we don't capture if we match the ignore list\n return !checkIfElementsMatchCSSSelector(targetElementList, selectorIgnoreList)\n}\n\nconst cannotCheckForAutocapture = (el: Element | null) => {\n return !el || isTag(el, 'html') || !isElementNode(el)\n}\n\nconst getElementAndParentsForElement = (el: Element, captureOnAnyElement: false | true | undefined) => {\n if (!window || cannotCheckForAutocapture(el)) {\n return { parentIsUsefulElement: false, targetElementList: [] }\n }\n\n let parentIsUsefulElement = false\n const targetElementList: Element[] = [el]\n let curEl: Element = el\n while (curEl.parentNode && !isTag(curEl, 'body')) {\n // If element is a shadow root, we skip it\n if (isDocumentFragment(curEl.parentNode)) {\n targetElementList.push((curEl.parentNode as any).host)\n curEl = (curEl.parentNode as any).host\n continue\n }\n const parentNode = getParentElement(curEl)\n if (!parentNode) break\n if (captureOnAnyElement || autocaptureCompatibleElements.indexOf(parentNode.tagName.toLowerCase()) > -1) {\n parentIsUsefulElement = true\n } else {\n const compStyles = window.getComputedStyle(parentNode)\n if (compStyles && compStyles.getPropertyValue('cursor') === 'pointer') {\n parentIsUsefulElement = true\n }\n }\n\n targetElementList.push(parentNode)\n curEl = parentNode\n }\n return { parentIsUsefulElement, targetElementList }\n}\n\n/*\n * Check whether a DOM event should be \"captured\" or if it may contain sensitive data\n * using a variety of heuristics.\n * @param {Element} el - element to check\n * @param {Event} event - event to check\n * @param {Object} autocaptureConfig - autocapture config\n * @param {boolean} captureOnAnyElement - whether to capture on any element, clipboard autocapture doesn't restrict to \"clickable\" elements\n * @param {string[]} allowedEventTypes - event types to capture, normally just 'click', but some autocapture types react to different events, some elements have fixed events (e.g., form has \"submit\")\n * @returns {boolean} whether the event should be captured\n */\nexport function shouldCaptureDomEvent(\n el: Element,\n event: Event,\n autocaptureConfig: AutocaptureConfig | undefined = undefined,\n captureOnAnyElement?: boolean,\n allowedEventTypes?: string[]\n): boolean {\n if (!window || cannotCheckForAutocapture(el)) {\n return false\n }\n\n if (autocaptureConfig?.url_allowlist) {\n // if the current URL is not in the allow list, don't capture\n if (!checkForURLMatches(autocaptureConfig.url_allowlist)) {\n return false\n }\n }\n\n if (autocaptureConfig?.url_ignorelist) {\n // if the current URL is in the ignore list, don't capture\n if (checkForURLMatches(autocaptureConfig.url_ignorelist)) {\n return false\n }\n }\n\n if (autocaptureConfig?.dom_event_allowlist) {\n const allowlist = autocaptureConfig.dom_event_allowlist\n if (allowlist && !allowlist.some((eventType) => event.type === eventType)) {\n return false\n }\n }\n\n const { parentIsUsefulElement, targetElementList } = getElementAndParentsForElement(el, captureOnAnyElement)\n\n if (!checkIfElementTreePassesElementAllowList(targetElementList, autocaptureConfig)) {\n return false\n }\n\n if (!checkIfElementsMatchCSSSelector(targetElementList, autocaptureConfig?.css_selector_allowlist)) {\n return false\n }\n\n const compStyles = window.getComputedStyle(el)\n if (compStyles && compStyles.getPropertyValue('cursor') === 'pointer' && event.type === 'click') {\n return true\n }\n\n const tag = el.tagName.toLowerCase()\n switch (tag) {\n case 'html':\n return false\n case 'form':\n return (allowedEventTypes || ['submit']).indexOf(event.type) >= 0\n case 'input':\n case 'select':\n case 'textarea':\n return (allowedEventTypes || ['change', 'click']).indexOf(event.type) >= 0\n default:\n if (parentIsUsefulElement) return (allowedEventTypes || ['click']).indexOf(event.type) >= 0\n return (\n (allowedEventTypes || ['click']).indexOf(event.type) >= 0 &&\n (autocaptureCompatibleElements.indexOf(tag) > -1 || el.getAttribute('contenteditable') === 'true')\n )\n }\n}\n\n/*\n * Check whether a DOM element should be \"captured\" or if it may contain sensitive data\n * using a variety of heuristics.\n * @param {Element} el - element to check\n * @returns {boolean} whether the element should be captured\n */\nexport function shouldCaptureElement(el: Element): boolean {\n for (let curEl = el; curEl.parentNode && !isTag(curEl, 'body'); curEl = curEl.parentNode as Element) {\n const classes = getClassNames(curEl)\n if (includes(classes, 'ph-sensitive') || includes(classes, 'ph-no-capture')) {\n return false\n }\n }\n\n if (includes(getClassNames(el), 'ph-include')) {\n return true\n }\n\n // don't include hidden or password fields\n const type = (el as HTMLInputElement).type || ''\n if (isString(type)) {\n // it's possible for el.type to be a DOM element if el is a form with a child input[name=\"type\"]\n switch (type.toLowerCase()) {\n case 'hidden':\n return false\n case 'password':\n return false\n }\n }\n\n // filter out data from fields that look like sensitive fields\n const name = (el as HTMLInputElement).name || el.id || ''\n // See https://github.com/posthog/posthog-js/issues/165\n // Under specific circumstances a bug caused .replace to be called on a DOM element\n // instead of a string, removing the element from the page. Ensure this issue is mitigated.\n if (isString(name)) {\n // it's possible for el.name or el.id to be a DOM element if el is a form with a child input[name=\"name\"]\n const sensitiveNameRegex =\n /^cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i\n if (sensitiveNameRegex.test(name.replace(/[^a-zA-Z0-9]/g, ''))) {\n return false\n }\n }\n\n return true\n}\n\n/*\n * Check whether a DOM element is 'sensitive' and we should only capture limited data\n * @param {Element} el - element to check\n * @returns {boolean} whether the element should be captured\n */\nexport function isSensitiveElement(el: Element): boolean {\n // don't send data from inputs or similar elements since there will always be\n // a risk of clientside javascript placing sensitive data in attributes\n const allowedInputTypes = ['button', 'checkbox', 'submit', 'reset']\n if (\n (isTag(el, 'input') && !allowedInputTypes.includes((el as HTMLInputElement).type)) ||\n isTag(el, 'select') ||\n isTag(el, 'textarea') ||\n el.getAttribute('contenteditable') === 'true'\n ) {\n return true\n }\n return false\n}\n\n// Define the core pattern for matching credit card numbers\nconst coreCCPattern = `(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11})`\n// Create the Anchored version of the regex by adding '^' at the start and '$' at the end\nconst anchoredCCRegex = new RegExp(`^(?:${coreCCPattern})$`)\n// The Unanchored version is essentially the core pattern, usable as is for partial matches\nconst unanchoredCCRegex = new RegExp(coreCCPattern)\n\n// Define the core pattern for matching SSNs with optional dashes\nconst coreSSNPattern = `\\\\d{3}-?\\\\d{2}-?\\\\d{4}`\n// Create the Anchored version of the regex by adding '^' at the start and '$' at the end\nconst anchoredSSNRegex = new RegExp(`^(${coreSSNPattern})$`)\n// The Unanchored version is essentially the core pattern itself, usable for partial matches\nconst unanchoredSSNRegex = new RegExp(`(${coreSSNPattern})`)\n\n/*\n * Check whether a string value should be \"captured\" or if it may contain sensitive data\n * using a variety of heuristics.\n * @param {string} value - string value to check\n * @param {boolean} anchorRegexes - whether to anchor the regexes to the start and end of the string\n * @returns {boolean} whether the element should be captured\n */\nexport function shouldCaptureValue(value: string, anchorRegexes = true): boolean {\n if (isNullish(value)) {\n return false\n }\n\n if (isString(value)) {\n value = trim(value)\n\n // check to see if input value looks like a credit card number\n // see: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s20.html\n const ccRegex = anchorRegexes ? anchoredCCRegex : unanchoredCCRegex\n if (ccRegex.test((value || '').replace(/[- ]/g, ''))) {\n return false\n }\n\n // check to see if input value looks like a social security number\n const ssnRegex = anchorRegexes ? anchoredSSNRegex : unanchoredSSNRegex\n if (ssnRegex.test(value)) {\n return false\n }\n }\n\n return true\n}\n\n/*\n * Check whether an attribute name is an Angular style attr (either _ngcontent or _nghost)\n * These update on each build and lead to noise in the element chain\n * More details on the attributes here: https://angular.io/guide/view-encapsulation\n * @param {string} attributeName - string value to check\n * @returns {boolean} whether the element is an angular tag\n */\nexport function isAngularStyleAttr(attributeName: string): boolean {\n if (isString(attributeName)) {\n return attributeName.substring(0, 10) === '_ngcontent' || attributeName.substring(0, 7) === '_nghost'\n }\n return false\n}\n\n/*\n * Iterate through children of a target element looking for span tags\n * and return the text content of the span tags, separated by spaces,\n * along with the direct text content of the target element\n * @param {Element} target - element to check\n * @returns {string} text content of the target element and its child span tags\n */\nexport function getDirectAndNestedSpanText(target: Element): string {\n let text = getSafeText(target)\n text = `${text} ${getNestedSpanText(target)}`.trim()\n return shouldCaptureValue(text) ? text : ''\n}\n\n/*\n * Iterate through children of a target element looking for span tags\n * and return the text content of the span tags, separated by spaces\n * @param {Element} target - element to check\n * @returns {string} text content of span tags\n */\nexport function getNestedSpanText(target: Element): string {\n let text = ''\n if (target && target.childNodes && target.childNodes.length) {\n each(target.childNodes, function (child) {\n if (child && child.tagName?.toLowerCase() === 'span') {\n try {\n const spanText = getSafeText(child)\n text = `${text} ${spanText}`.trim()\n\n if (child.childNodes && child.childNodes.length) {\n text = `${text} ${getNestedSpanText(child)}`.trim()\n }\n } catch (e) {\n logger.error('[AutoCapture]', e)\n }\n }\n })\n }\n return text\n}\n\n/*\nBack in the day storing events in Postgres we use Elements for autocapture events.\nNow we're using elements_chain. We used to do this parsing/processing during ingestion.\nThis code is just copied over from ingestion, but we should optimize it\nto create elements_chain string directly.\n*/\nexport function getElementsChainString(elements: Properties[]): string {\n return elementsToString(extractElements(elements))\n}\n\n// This interface is called 'Element' in plugin-scaffold https://github.com/PostHog/plugin-scaffold/blob/b07d3b879796ecc7e22deb71bf627694ba05386b/src/types.ts#L200\n// However 'Element' is a DOM Element when run in the browser, so we have to rename it\ninterface PHElement {\n text?: string\n tag_name?: string\n href?: string\n attr_id?: string\n attr_class?: string[]\n nth_child?: number\n nth_of_type?: number\n attributes?: Record<string, any>\n event_id?: number\n order?: number\n group_id?: number\n}\n\nfunction escapeQuotes(input: string): string {\n return input.replace(/\"|\\\\\"/g, '\\\\\"')\n}\n\nfunction elementsToString(elements: PHElement[]): string {\n const ret = elements.map((element) => {\n let el_string = ''\n if (element.tag_name) {\n el_string += element.tag_name\n }\n if (element.attr_class) {\n element.attr_class.sort()\n for (const single_class of element.attr_class) {\n el_string += `.${single_class.replace(/\"/g, '')}`\n }\n }\n const attributes: Record<string, any> = {\n ...(element.text ? { text: element.text } : {}),\n 'nth-child': element.nth_child ?? 0,\n 'nth-of-type': element.nth_of_type ?? 0,\n ...(element.href ? { href: element.href } : {}),\n ...(element.attr_id ? { attr_id: element.attr_id } : {}),\n ...element.attributes,\n }\n const sortedAttributes: Record<string, any> = {}\n entries(attributes)\n .sort(([a], [b]) => a.localeCompare(b))\n .forEach(\n ([key, value]) => (sortedAttributes[escapeQuotes(key.toString())] = escapeQuotes(value.toString()))\n )\n el_string += ':'\n el_string += entries(sortedAttributes)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join('')\n return el_string\n })\n return ret.join(';')\n}\n\nfunction extractElements(elements: Properties[]): PHElement[] {\n return elements.map((el) => {\n const response = {\n text: el['$el_text']?.slice(0, 400),\n tag_name: el['tag_name'],\n href: el['attr__href']?.slice(0, 2048),\n attr_class: extractAttrClass(el),\n attr_id: el['attr__id'],\n nth_child: el['nth_child'],\n nth_of_type: el['nth_of_type'],\n attributes: {} as { [id: string]: any },\n }\n\n entries(el)\n .filter(([key]) => key.indexOf('attr__') === 0)\n .forEach(([key, value]) => (response.attributes[key] = value))\n return response\n })\n}\n\nfunction extractAttrClass(el: Properties): PHElement['attr_class'] {\n const attr_class = el['attr__class']\n if (!attr_class) {\n return undefined\n } else if (isArray(attr_class)) {\n return attr_class\n } else {\n return splitClassString(attr_class)\n }\n}\n","import { addEventListener, each, extend } from './utils'\nimport {\n autocaptureCompatibleElements,\n getClassNames,\n getDirectAndNestedSpanText,\n getElementsChainString,\n getEventTarget,\n getSafeText,\n isAngularStyleAttr,\n isSensitiveElement,\n makeSafeText,\n shouldCaptureDomEvent,\n shouldCaptureElement,\n shouldCaptureRageclick,\n shouldCaptureValue,\n splitClassString,\n} from './autocapture-utils'\n\nimport RageClick from './extensions/rageclick'\nimport { AutocaptureConfig, COPY_AUTOCAPTURE_EVENT, EventName, Properties, RemoteConfig } from './types'\nimport { PostHog } from './posthog-core'\nimport { AUTOCAPTURE_DISABLED_SERVER_SIDE } from './constants'\n\nimport { isBoolean, isFunction, isNull, isObject } from '@posthog/core'\nimport { createLogger } from './utils/logger'\nimport { document, window } from './utils/globals'\nimport { convertToURL } from './utils/request-utils'\nimport { isDocumentFragment, isElementNode, isTag, isTextNode } from './utils/element-utils'\nimport { includes } from '@posthog/core'\n\nconst logger = createLogger('[AutoCapture]')\n\nfunction limitText(length: number, text: string): string {\n if (text.length > length) {\n return text.slice(0, length) + '...'\n }\n return text\n}\n\nexport function getAugmentPropertiesFromElement(elem: Element): Properties {\n const shouldCaptureEl = shouldCaptureElement(elem)\n if (!shouldCaptureEl) {\n return {}\n }\n\n const props: Properties = {}\n\n each(elem.attributes, function (attr: Attr) {\n if (attr.name && attr.name.indexOf('data-ph-capture-attribute') === 0) {\n const propertyKey = attr.name.replace('data-ph-capture-attribute-', '')\n const propertyValue = attr.value\n if (propertyKey && propertyValue && shouldCaptureValue(propertyValue)) {\n props[propertyKey] = propertyValue\n }\n }\n })\n\n return props\n}\n\nexport function previousElementSibling(el: Element): Element | null {\n if (el.previousElementSibling) {\n return el.previousElementSibling\n }\n let _el: Element | null = el\n do {\n _el = _el.previousSibling as Element | null // resolves to ChildNode->Node, which is Element's parent class\n } while (_el && !isElementNode(_el))\n return _el\n}\n\nexport function getDefaultProperties(eventType: string): Properties {\n return {\n $event_type: eventType,\n $ce_version: 1,\n }\n}\n\nexport function getPropertiesFromElement(\n elem: Element,\n maskAllAttributes: boolean,\n maskText: boolean,\n elementAttributeIgnorelist: string[] | undefined\n): Properties {\n const tag_name = elem.tagName.toLowerCase()\n const props: Properties = {\n tag_name: tag_name,\n }\n if (autocaptureCompatibleElements.indexOf(tag_name) > -1 && !maskText) {\n if (tag_name.toLowerCase() === 'a' || tag_name.toLowerCase() === 'button') {\n props['$el_text'] = limitText(1024, getDirectAndNestedSpanText(elem))\n } else {\n props['$el_text'] = limitText(1024, getSafeText(elem))\n }\n }\n\n const classes = getClassNames(elem)\n if (classes.length > 0)\n props['classes'] = classes.filter(function (c) {\n return c !== ''\n })\n\n // capture the deny list here because this not-a-class class makes it tricky to use this.config in the function below\n each(elem.attributes, function (attr: Attr) {\n // Only capture attributes we know are safe\n if (isSensitiveElement(elem) && ['name', 'id', 'class', 'aria-label'].indexOf(attr.name) === -1) return\n\n if (elementAttributeIgnorelist?.includes(attr.name)) return\n\n if (!maskAllAttributes && shouldCaptureValue(attr.value) && !isAngularStyleAttr(attr.name)) {\n let value = attr.value\n if (attr.name === 'class') {\n // html attributes can _technically_ contain linebreaks,\n // but we're very intolerant of them in the class string,\n // so we strip them.\n value = splitClassString(value).join(' ')\n }\n props['attr__' + attr.name] = limitText(1024, value)\n }\n })\n\n let nthChild = 1\n let nthOfType = 1\n let currentElem: Element | null = elem\n while ((currentElem = previousElementSibling(currentElem))) {\n // eslint-disable-line no-cond-assign\n nthChild++\n if (currentElem.tagName === elem.tagName) {\n nthOfType++\n }\n }\n props['nth_child'] = nthChild\n props['nth_of_type'] = nthOfType\n\n return props\n}\n\nexport function autocapturePropertiesForElement(\n target: Element,\n {\n e,\n maskAllElementAttributes,\n maskAllText,\n elementAttributeIgnoreList,\n elementsChainAsString,\n }: {\n e: Event\n maskAllElementAttributes: boolean\n maskAllText: boolean\n elementAttributeIgnoreList?: string[] | undefined\n elementsChainAsString: boolean\n }\n): { props: Properties; explicitNoCapture?: boolean } {\n const targetElementList = [target]\n let curEl = target\n while (curEl.parentNode && !isTag(curEl, 'body')) {\n if (isDocumentFragment(curEl.parentNode)) {\n targetElementList.push((curEl.parentNode as any).host)\n curEl = (curEl.parentNode as any).host\n continue\n }\n targetElementList.push(curEl.parentNode as Element)\n curEl = curEl.parentNode as Element\n }\n\n const elementsJson: Properties[] = []\n const autocaptureAugmentProperties: Properties = {}\n let href: string | false = false\n let explicitNoCapture = false\n\n each(targetElementList, (el) => {\n const shouldCaptureEl = shouldCaptureElement(el)\n\n // if the element or a parent element is an anchor tag\n // include the href as a property\n if (el.tagName.toLowerCase() === 'a') {\n href = el.getAttribute('href')\n href = shouldCaptureEl && href && shouldCaptureValue(href) && href\n }\n\n // allow users to programmatically prevent capturing of elements by adding class 'ph-no-capture'\n const classes = getClassNames(el)\n if (includes(classes, 'ph-no-capture')) {\n explicitNoCapture = true\n }\n\n elementsJson.push(\n getPropertiesFromElement(el, maskAllElementAttributes, maskAllText, elementAttributeIgnoreList)\n )\n\n const augmentProperties = getAugmentPropertiesFromElement(el)\n extend(autocaptureAugmentProperties, augmentProperties)\n })\n\n if (explicitNoCapture) {\n return { props: {}, explicitNoCapture }\n }\n\n if (!maskAllText) {\n // if the element is a button or anchor tag get the span text from any\n // children and include it as/with the text property on the parent element\n if (target.tagName.toLowerCase() === 'a' || target.tagName.toLowerCase() === 'button') {\n elementsJson[0]['$el_text'] = getDirectAndNestedSpanText(target)\n } else {\n elementsJson[0]['$el_text'] = getSafeText(target)\n }\n }\n\n let externalHref: string | undefined\n if (href) {\n elementsJson[0]['attr__href'] = href\n const hrefHost = convertToURL(href)?.host\n const locationHost = window?.location?.host\n if (hrefHost && locationHost && hrefHost !== locationHost) {\n externalHref = href\n }\n }\n\n const props = extend(\n getDefaultProperties(e.type),\n // Sending \"$elements\" is deprecated. Only one client on US cloud uses this.\n !elementsChainAsString ? { $elements: elementsJson } : {},\n // Always send $elements_chain, as it's needed downstream in site app filtering\n { $elements_chain: getElementsChainString(elementsJson) },\n elementsJson[0]?.['$el_text'] ? { $el_text: elementsJson[0]?.['$el_text'] } : {},\n externalHref && e.type === 'click' ? { $external_click_url: externalHref } : {},\n autocaptureAugmentProperties\n )\n\n return { props }\n}\n\nexport class Autocapture {\n instance: PostHog\n _initialized: boolean = false\n _isDisabledServerSide: boolean | null = null\n _elementSelectors: Set<string> | null\n rageclicks: RageClick\n _elementsChainAsString = false\n\n constructor(instance: PostHog) {\n this.instance = instance\n this.rageclicks = new RageClick(instance.config.rageclick)\n this._elementSelectors = null\n }\n\n private get _config(): AutocaptureConfig {\n const config = isObject(this.instance.config.autocapture) ? this.instance.config.autocapture : {}\n // precompile the regex\n config.url_allowlist = config.url_allowlist?.map((url) => new RegExp(url))\n config.url_ignorelist = config.url_ignorelist?.map((url) => new RegExp(url))\n return config\n }\n\n _addDomEventHandlers(): void {\n if (!this.isBrowserSupported()) {\n logger.info('Disabling Automatic Event Collection because this browser is not supported')\n return\n }\n\n if (!window || !document) {\n return\n }\n\n const handler = (e: Event) => {\n e = e || window?.event\n try {\n this._captureEvent(e)\n } catch (error) {\n logger.error('Failed to capture event', error)\n }\n }\n\n addEventListener(document, 'submit', handler, { capture: true })\n addEventListener(document, 'change', handler, { capture: true })\n addEventListener(document, 'click', handler, { capture: true })\n\n if (this._config.capture_copied_text) {\n const copiedTextHandler = (e: Event) => {\n e = e || window?.event\n this._captureEvent(e, COPY_AUTOCAPTURE_EVENT)\n }\n\n addEventListener(document, 'copy', copiedTextHandler, { capture: true })\n addEventListener(document, 'cut', copiedTextHandler, { capture: true })\n }\n }\n\n public startIfEnabled() {\n if (this.isEnabled && !this._initialized) {\n this._addDomEventHandlers()\n this._initialized = true\n }\n }\n\n public onRemoteConfig(response: RemoteConfig) {\n if (response.elementsChainAsString) {\n this._elementsChainAsString = response.elementsChainAsString\n }\n\n if (this.instance.persistence) {\n this.instance.persistence.register({\n [AUTOCAPTURE_DISABLED_SERVER_SIDE]: !!response['autocapture_opt_out'],\n })\n }\n // store this in-memory in case persistence is disabled\n this._isDisabledServerSide = !!response['autocapture_opt_out']\n this.startIfEnabled()\n }\n\n public setElementSelectors(selectors: Set<string>): void {\n this._elementSelectors = selectors\n }\n\n public getElementSelectors(element: Element | null): string[] | null {\n const elementSelectors: string[] = []\n\n this._elementSelectors?.forEach((selector) => {\n const matchedElements = document?.querySelectorAll(selector)\n matchedElements?.forEach((matchedElement: Element) => {\n if (element === matchedElement) {\n elementSelectors.push(selector)\n }\n })\n })\n\n return elementSelectors\n }\n\n public get isEnabled(): boolean {\n const persistedServerDisabled = this.instance.persistence?.props[AUTOCAPTURE_DISABLED_SERVER_SIDE]\n const memoryDisabled = this._isDisabledServerSide\n\n if (isNull(memoryDisabled) && !isBoolean(persistedServerDisabled) && !this.instance._shouldDisableFlags()) {\n // We only enable if we know that the server has not disabled it (unless /flags is disabled)\n return false\n }\n\n const disabledServer = this._isDisabledServerSide ?? !!persistedServerDisabled\n const disabledClient = !this.instance.config.autocapture\n return !disabledClient && !disabledServer\n }\n\n private _captureEvent(e: Event, eventName: EventName = '$autocapture'): boolean | void {\n if (!this.isEnabled) {\n return\n }\n\n /*** Don't mess with this code without running IE8 tests on it ***/\n let target = getEventTarget(e)\n if (isTextNode(target)) {\n // defeat Safari bug (see: http://www.quirksmode.org/js/events_properties.html)\n target = (target.parentNode || null) as Element | null\n }\n\n if (eventName === '$autocapture' && e.type === 'click' && e instanceof MouseEvent) {\n if (\n !!this.instance.config.rageclick &&\n this.rageclicks?.isRageClick(e.clientX, e.clientY, new Date().getTime())\n ) {\n if (shouldCaptureRageclick(target, this.instance.config.rageclick)) {\n this._captureEvent(e, '$rageclick')\n }\n }\n }\n\n const isCopyAutocapture = eventName === COPY_AUTOCAPTURE_EVENT\n if (\n target &&\n shouldCaptureDomEvent(\n target,\n e,\n this._config,\n // mostly this method cares about the target element, but in the case of copy events,\n // we want some of the work this check does without insisting on the target element's type\n isCopyAutocapture,\n // we also don't want to restrict copy checks to clicks,\n // so we pass that knowledge in here, rather than add the logic inside the check\n isCopyAutocapture ? ['copy', 'cut'] : undefined\n )\n ) {\n const { props, explicitNoCapture } = autocapturePropertiesForElement(target, {\n e,\n maskAllElementAttributes: this.instance.config.mask_all_element_attributes,\n maskAllText: this.instance.config.mask_all_text,\n elementAttributeIgnoreList: this._config.element_attribute_ignorelist,\n elementsChainAsString: this._elementsChainAsString,\n })\n\n if (explicitNoCapture) {\n return false\n }\n\n const elementSelectors = this.getElementSelectors(target)\n if (elementSelectors && elementSelectors.length > 0) {\n props['$element_selectors'] = elementSelectors\n }\n\n if (eventName === COPY_AUTOCAPTURE_EVENT) {\n // you can't read the data from the clipboard event,\n // but you can guess that you can read it from the window's current selection\n const selectedContent = makeSafeText(window?.getSelection()?.toString())\n const clipType = (e as ClipboardEvent).type || 'clipboard'\n if (!selectedContent) {\n return false\n }\n props['$selected_content'] = selectedContent\n props['$copy_type'] = clipType\n }\n\n this.instance.capture(eventName, props)\n return true\n }\n }\n\n isBrowserSupported(): boolean {\n return isFunction(document?.querySelectorAll)\n }\n}\n","import { each } from './'\n\nimport { isArray, isFile, isUndefined } from '@posthog/core'\nimport { logger } from './logger'\nimport { document } from './globals'\n\nconst localDomains = ['localhost', '127.0.0.1']\n\n/**\n * IE11 doesn't support `new URL`\n * so we can create an anchor element and use that to parse the URL\n * there's a lot of overlap between HTMLHyperlinkElementUtils and URL\n * meaning useful properties like `pathname` are available on both\n */\nexport const convertToURL = (url: string): HTMLAnchorElement | null => {\n const location = document?.createElement('a')\n if (isUndefined(location)) {\n return null\n }\n\n location.href = url\n return location\n}\n\nexport const formDataToQuery = function (formdata: Record<string, any> | FormData, arg_separator = '&'): string {\n let use_val: string\n let use_key: string\n const tph_arr: string[] = []\n\n each(formdata, function (val: File | string | undefined, key: string | undefined) {\n // the key might be literally the string undefined for e.g. if {undefined: 'something'}\n if (isUndefined(val) || isUndefined(key) || key === 'undefined') {\n return\n }\n\n use_val = encodeURIComponent(isFile(val) ? val.name : val.toString())\n use_key = encodeURIComponent(key)\n tph_arr[tph_arr.length] = use_key + '=' + use_val\n })\n\n return tph_arr.join(arg_separator)\n}\n\n// NOTE: Once we get rid of IE11/op_mini we can start using URLSearchParams\nexport const getQueryParam = function (url: string, param: string): string {\n const withoutHash: string = url.split('#')[0] || ''\n\n // Split only on the first ? to sort problem out for those with multiple ?s\n // and then remove them\n const queryParams: string = withoutHash.split(/\\?(.*)/)[1] || ''\n const cleanedQueryParams = queryParams.replace(/^\\?+/g, '')\n\n const queryParts = cleanedQueryParams.split('&')\n let keyValuePair\n\n for (let i = 0; i < queryParts.length; i++) {\n const parts = queryParts[i].split('=')\n if (parts[0] === param) {\n keyValuePair = parts\n break\n }\n }\n\n if (!isArray(keyValuePair) || keyValuePair.length < 2) {\n return ''\n } else {\n let result = keyValuePair[1]\n try {\n result = decodeURIComponent(result)\n } catch {\n logger.error('Skipping decoding for malformed query param: ' + result)\n }\n return result.replace(/\\+/g, ' ')\n }\n}\n\n// replace any query params in the url with the provided mask value. Tries to keep the URL as instant as possible,\n// including preserving malformed text in most cases\nexport const maskQueryParams = function <T extends string | undefined>(\n url: T,\n maskedParams: string[] | undefined,\n mask: string\n): T extends string ? string : undefined {\n if (!url || !maskedParams || !maskedParams.length) {\n return url as any\n }\n\n const splitHash = url.split('#')\n const withoutHash: string = splitHash[0] || ''\n const hash = splitHash[1]\n\n const splitQuery: string[] = withoutHash.split('?')\n const queryString: string = splitQuery[1]\n const urlWithoutQueryAndHash: string = splitQuery[0]\n const queryParts = (queryString || '').split('&')\n\n // use an array of strings rather than an object to preserve ordering and duplicates\n const paramStrings: string[] = []\n\n for (let i = 0; i < queryParts.length; i++) {\n const keyValuePair = queryParts[i].split('=')\n if (!isArray(keyValuePair)) {\n continue\n } else if (maskedParams.includes(keyValuePair[0])) {\n paramStrings.push(keyValuePair[0] + '=' + mask)\n } else {\n paramStrings.push(queryParts[i])\n }\n }\n\n let result = urlWithoutQueryAndHash\n if (queryString != null) {\n result += '?' + paramStrings.join('&')\n }\n if (hash != null) {\n result += '#' + hash\n }\n\n return result as any\n}\n\nexport const _getHashParam = function (hash: string, param: string): string | null {\n const matches = hash.match(new RegExp(param + '=([^&]*)'))\n return matches ? matches[1] : null\n}\n\nexport const isLocalhost = (): boolean => {\n return localDomains.includes(location.hostname)\n}\n","import { window } from './globals'\n\n// When angular patches functions they pass the above `isNativeFunction` check (at least the MutationObserver)\nexport const isAngularZonePresent = (): boolean => {\n return !!(window as any).Zone\n}\n\nexport const isDocument = (x: unknown): x is Document => {\n // eslint-disable-next-line posthog-js/no-direct-document-check\n return x instanceof Document\n}\n","/**\n * adapted from https://github.com/getsentry/sentry-javascript/blob/72751dacb88c5b970d8bac15052ee8e09b28fd5d/packages/browser-utils/src/getNativeImplementation.ts#L27\n * and https://github.com/PostHog/rrweb/blob/804380afbb1b9bed70b8792cb5a25d827f5c0cb5/packages/utils/src/index.ts#L31\n * after a number of performance reports from Angular users\n */\n\nimport { AssignableWindow } from './globals'\nimport { isAngularZonePresent } from './type-utils'\nimport { isFunction, isNativeFunction } from '@posthog/core'\nimport { logger } from './logger'\n\ninterface NativeImplementationsCache {\n MutationObserver: typeof MutationObserver\n}\n\nconst cachedImplementations: Partial<NativeImplementationsCache> = {}\n\nexport function getNativeImplementation<T extends keyof NativeImplementationsCache>(\n name: T,\n assignableWindow: AssignableWindow\n): NativeImplementationsCache[T] {\n const cached = cachedImplementations[name]\n if (cached) {\n return cached\n }\n\n let impl = assignableWindow[name] as NativeImplementationsCache[T]\n\n if (isNativeFunction(impl) && !isAngularZonePresent()) {\n return (cachedImplementations[name] = impl.bind(assignableWindow) as NativeImplementationsCache[T])\n }\n\n const document = assignableWindow.document\n if (document && isFunction(document.createElement)) {\n try {\n const sandbox = document.createElement('iframe')\n sandbox.hidden = true\n document.head.appendChild(sandbox)\n const contentWindow = sandbox.contentWindow\n if (contentWindow && (contentWindow as any)[name]) {\n impl = (contentWindow as any)[name] as NativeImplementationsCache[T]\n }\n document.head.removeChild(sandbox)\n } catch (e) {\n // Could not create sandbox iframe, just use assignableWindow.xxx\n logger.warn(`Could not create sandbox iframe for ${name} check, bailing to assignableWindow.${name}: `, e)\n }\n }\n\n // Sanity check: This _should_ not happen, but if it does, we just skip caching...\n // This can happen e.g. in tests where fetch may not be available in the env, or similar.\n if (!impl || !isFunction(impl)) {\n return impl\n }\n\n return (cachedImplementations[name] = impl.bind(assignableWindow) as NativeImplementationsCache[T])\n}\n\nexport function getNativeMutationObserverImplementation(assignableWindow: AssignableWindow): typeof MutationObserver {\n return getNativeImplementation('MutationObserver', assignableWindow)\n}\n","import { assignableWindow, LazyLoadedDeadClicksAutocaptureInterface } from '../utils/globals'\nimport { PostHog } from '../posthog-core'\nimport { isNull, isNumber, isUndefined } from '@posthog/core'\nimport { autocaptureCompatibleElements, getEventTarget } from '../autocapture-utils'\nimport { DeadClickCandidate, DeadClicksAutoCaptureConfig, Properties } from '../types'\nimport { autocapturePropertiesForElement } from '../autocapture'\nimport { isElementInToolbar, isElementNode, isTag } from '../utils/element-utils'\nimport { getNativeMutationObserverImplementation } from '../utils/prototype-utils'\nimport { addEventListener } from '../utils'\n\nfunction asClick(event: MouseEvent): DeadClickCandidate | null {\n const eventTarget = getEventTarget(event)\n if (eventTarget) {\n return {\n node: eventTarget,\n originalEvent: event,\n timestamp: Date.now(),\n }\n }\n return null\n}\n\nfunction checkTimeout(value: number | undefined, thresholdMs: number) {\n return isNumber(value) && value >= thresholdMs\n}\n\nclass LazyLoadedDeadClicksAutocapture implements LazyLoadedDeadClicksAutocaptureInterface {\n private _mutationObserver: MutationObserver | undefined\n private _lastMutation: number | undefined\n private _lastSelectionChanged: number | undefined\n private _clicks: DeadClickCandidate[] = []\n private _checkClickTimer: number | undefined\n private _config: Required<DeadClicksAutoCaptureConfig>\n private _onCapture: (click: DeadClickCandidate, properties: Properties) => void\n\n private _defaultConfig = (defaultOnCapture: (click: DeadClickCandidate, properties: Properties) => void) => ({\n element_attribute_ignorelist: [],\n scroll_threshold_ms: 100,\n selection_change_threshold_ms: 100,\n mutation_threshold_ms: 2500,\n __onCapture: defaultOnCapture,\n })\n\n private _asRequiredConfig(providedConfig?: DeadClicksAutoCaptureConfig): Required<DeadClicksAutoCaptureConfig> {\n const defaultConfig = this._defaultConfig(providedConfig?.__onCapture || this._captureDeadClick.bind(this))\n return {\n element_attribute_ignorelist:\n providedConfig?.element_attribute_ignorelist ?? defaultConfig.element_attribute_ignorelist,\n scroll_threshold_ms: providedConfig?.scroll_threshold_ms ?? defaultConfig.scroll_threshold_ms,\n selection_change_threshold_ms:\n providedConfig?.selection_change_threshold_ms ?? defaultConfig.selection_change_threshold_ms,\n mutation_threshold_ms: providedConfig?.mutation_threshold_ms ?? defaultConfig.mutation_threshold_ms,\n __onCapture: defaultConfig.__onCapture,\n }\n }\n\n constructor(\n readonly instance: PostHog,\n config?: DeadClicksAutoCaptureConfig\n ) {\n this._config = this._asRequiredConfig(config)\n this._onCapture = this._config.__onCapture\n }\n\n start(observerTarget: Node) {\n this._startClickObserver()\n this._startScrollObserver()\n this._startSelectionChangedObserver()\n this._startMutationObserver(observerTarget)\n }\n\n private _startMutationObserver(observerTarget: Node) {\n if (!this._mutationObserver) {\n const NativeMutationObserver = getNativeMutationObserverImplementation(assignableWindow)\n this._mutationObserver = new NativeMutationObserver((mutations) => {\n this._onMutation(mutations)\n })\n this._mutationObserver.observe(observerTarget, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n })\n }\n }\n\n stop() {\n this._mutationObserver?.disconnect()\n this._mutationObserver = undefined\n assignableWindow.removeEventListener('click', this._onClick)\n assignableWindow.removeEventListener('scroll', this._onScroll, { capture: true })\n assignableWindow.removeEventListener('selectionchange', this._onSelectionChange)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n private _onMutation(_mutations: MutationRecord[]): void {\n // we don't actually care about the content of the mutations, right now\n this._lastMutation = Date.now()\n }\n\n private _startClickObserver() {\n addEventListener(assignableWindow, 'click', this._onClick)\n }\n\n private _onClick = (event: Event): void => {\n const click = asClick(event as MouseEvent)\n if (!isNull(click) && !this._ignoreClick(click)) {\n this._clicks.push(click)\n }\n\n if (this._clicks.length && isUndefined(this._checkClickTimer)) {\n this._checkClickTimer = assignableWindow.setTimeout(() => {\n this._checkClicks()\n }, 1000)\n }\n }\n\n // `capture: true` is required to get scroll events for other scrollable elements\n // on the page, not just the window\n // see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#usecapture\n //\n // `passive: true` is used to tell the browser that the scroll event handler will not call `preventDefault()`\n // This allows the browser to optimize scrolling performance by not waiting for our handling of the scroll event\n // see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#passive\n private _startScrollObserver() {\n addEventListener(assignableWindow, 'scroll', this._onScroll, { capture: true })\n }\n\n private _onScroll = (): void => {\n const candidateNow = Date.now()\n // very naive throttle\n if (candidateNow % 50 === 0) {\n // we can see many scrolls between scheduled checks,\n // so we update scroll delay as we see them\n // to avoid false positives\n this._clicks.forEach((click) => {\n if (isUndefined(click.scrollDelayMs)) {\n click.scrollDelayMs = candidateNow - click.timestamp\n }\n })\n }\n }\n\n private _startSelectionChangedObserver() {\n addEventListener(assignableWindow, 'selectionchange', this._onSelectionChange)\n }\n\n private _onSelectionChange = (): void => {\n this._lastSelectionChanged = Date.now()\n }\n\n private _ignoreClick(click: DeadClickCandidate | null): boolean {\n if (!click) {\n return true\n }\n\n if (isElementInToolbar(click.node)) {\n return true\n }\n\n const alreadyClickedInLastSecond = this._clicks.some((c) => {\n return c.node === click.node && Math.abs(c.timestamp - click.timestamp) < 1000\n })\n\n if (alreadyClickedInLastSecond) {\n return true\n }\n\n if (\n isTag(click.node, 'html') ||\n !isElementNode(click.node) ||\n autocaptureCompatibleElements.includes(click.node.tagName.toLowerCase())\n ) {\n return true\n }\n\n return false\n }\n\n private _checkClicks() {\n if (!this._clicks.length) {\n return\n }\n\n clearTimeout(this._checkClickTimer)\n this._checkClickTimer = undefined\n\n const clicksToCheck = this._clicks\n this._clicks = []\n\n for (const click of clicksToCheck) {\n click.mutationDelayMs =\n click.mutationDelayMs ??\n (this._lastMutation && click.timestamp <= this._lastMutation\n ? this._lastMutation - click.timestamp\n : undefined)\n click.absoluteDelayMs = Date.now() - click.timestamp\n click.selectionChangedDelayMs =\n this._lastSelectionChanged && click.timestamp <= this._lastSelectionChanged\n ? this._lastSelectionChanged - click.timestamp\n : undefined\n\n const scrollTimeout = checkTimeout(click.scrollDelayMs, this._config.scroll_threshold_ms)\n const selectionChangedTimeout = checkTimeout(\n click.selectionChangedDelayMs,\n this._config.selection_change_threshold_ms\n )\n const mutationTimeout = checkTimeout(click.mutationDelayMs, this._config.mutation_threshold_ms)\n // we want to timeout eventually even if nothing else catches it...\n // we leave a little longer than the maximum threshold to give the other checks a chance to catch it\n const absoluteTimeout = checkTimeout(click.absoluteDelayMs, this._config.mutation_threshold_ms * 1.1)\n\n const hadScroll = isNumber(click.scrollDelayMs) && click.scrollDelayMs < this._config.scroll_threshold_ms\n const hadMutation =\n isNumber(click.mutationDelayMs) && click.mutationDelayMs < this._config.mutation_threshold_ms\n const hadSelectionChange =\n isNumber(click.selectionChangedDelayMs) &&\n click.selectionChangedDelayMs < this._config.selection_change_threshold_ms\n\n if (hadScroll || hadMutation || hadSelectionChange) {\n // ignore clicks that had a scroll or mutation\n continue\n }\n\n if (scrollTimeout || mutationTimeout || absoluteTimeout || selectionChangedTimeout) {\n this._onCapture(click, {\n $dead_click_last_mutation_timestamp: this._lastMutation,\n $dead_click_event_timestamp: click.timestamp,\n $dead_click_scroll_timeout: scrollTimeout,\n $dead_click_mutation_timeout: mutationTimeout,\n $dead_click_absolute_timeout: absoluteTimeout,\n $dead_click_selection_changed_timeout: selectionChangedTimeout,\n })\n } else if (click.absoluteDelayMs < this._config.mutation_threshold_ms) {\n // keep waiting until next check\n this._clicks.push(click)\n }\n }\n\n if (this._clicks.length && isUndefined(this._checkClickTimer)) {\n this._checkClickTimer = assignableWindow.setTimeout(() => {\n this._checkClicks()\n }, 1000)\n }\n }\n\n private _captureDeadClick(click: DeadClickCandidate, properties: Properties) {\n // TODO need to check safe and captur-able as with autocapture\n // TODO autocaputure config\n this.instance.capture(\n '$dead_click',\n {\n ...properties,\n ...autocapturePropertiesForElement(click.node, {\n e: click.originalEvent,\n maskAllElementAttributes: this.instance.config.mask_all_element_attributes,\n maskAllText: this.instance.config.mask_all_text,\n elementAttributeIgnoreList: this._config.element_attribute_ignorelist,\n // TRICKY: it appears that we were moving to elementsChainAsString, but the UI still depends on elements, so :shrug:\n elementsChainAsString: false,\n }).props,\n $dead_click_scroll_delay_ms: click.scrollDelayMs,\n $dead_click_mutation_delay_ms: click.mutationDelayMs,\n $dead_click_absolute_delay_ms: click.absoluteDelayMs,\n $dead_click_selection_changed_delay_ms: click.selectionChangedDelayMs,\n },\n {\n timestamp: new Date(click.timestamp),\n }\n )\n }\n}\n\nassignableWindow.__PosthogExtensions__ = assignableWindow.__PosthogExtensions__ || {}\nassignableWindow.__PosthogExtensions__.initDeadClicksAutocapture = (ph, config) =>\n new LazyLoadedDeadClicksAutocapture(ph, config)\n\nexport default LazyLoadedDeadClicksAutocapture\n","/*\n * Constants\n */\n\n/* PROPERTY KEYS */\n\n// This key is deprecated, but we want to check for it to see whether aliasing is allowed.\nexport const PEOPLE_DISTINCT_ID_KEY = '$people_distinct_id'\nexport const DISTINCT_ID = 'distinct_id'\nexport const ALIAS_ID_KEY = '__alias'\nexport const CAMPAIGN_IDS_KEY = '__cmpns'\nexport const EVENT_TIMERS_KEY = '__timers'\nexport const AUTOCAPTURE_DISABLED_SERVER_SIDE = '$autocapture_disabled_server_side'\nexport const HEATMAPS_ENABLED_SERVER_SIDE = '$heatmaps_enabled_server_side'\nexport const EXCEPTION_CAPTURE_ENABLED_SERVER_SIDE = '$exception_capture_enabled_server_side'\nexport const ERROR_TRACKING_SUPPRESSION_RULES = '$error_tracking_suppression_rules'\nexport const ERROR_TRACKING_CAPTURE_EXTENSION_EXCEPTIONS = '$error_tracking_capture_extension_exceptions'\nexport const WEB_VITALS_ENABLED_SERVER_SIDE = '$web_vitals_enabled_server_side'\nexport const DEAD_CLICKS_ENABLED_SERVER_SIDE = '$dead_clicks_enabled_server_side'\nexport const WEB_VITALS_ALLOWED_METRICS = '$web_vitals_allowed_metrics'\nexport const SESSION_RECORDING_REMOTE_CONFIG = '$session_recording_remote_config'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_ENABLED_SERVER_SIDE = '$session_recording_enabled_server_side'\n// @deprecated can be removed along with eager loaded replay\nexport const CONSOLE_LOG_RECORDING_ENABLED_SERVER_SIDE = '$console_log_recording_enabled_server_side'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_NETWORK_PAYLOAD_CAPTURE = '$session_recording_network_payload_capture'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_MASKING = '$session_recording_masking'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_CANVAS_RECORDING = '$session_recording_canvas_recording'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_SAMPLE_RATE = '$replay_sample_rate'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_MINIMUM_DURATION = '$replay_minimum_duration'\n// @deprecated can be removed along with eager loaded replay\nexport const SESSION_RECORDING_SCRIPT_CONFIG = '$replay_script_config'\nexport const SESSION_RECORDING_OVERRIDE_SAMPLING = '$replay_override_sampling'\nexport const SESSION_RECORDING_OVERRIDE_LINKED_FLAG = '$replay_override_linked_flag'\nexport const SESSION_RECORDING_OVERRIDE_URL_TRIGGER = '$replay_override_url_trigger'\nexport const SESSION_RECORDING_OVERRIDE_EVENT_TRIGGER = '$replay_override_event_trigger'\nexport const SESSION_ID = '$sesid'\nexport const SESSION_RECORDING_IS_SAMPLED = '$session_is_sampled'\nexport const SESSION_RECORDING_PAST_MINIMUM_DURATION = '$session_past_minimum_duration'\nexport const SESSION_RECORDING_URL_TRIGGER_ACTIVATED_SESSION = '$session_recording_url_trigger_activated_session'\nexport const SESSION_RECORDING_EVENT_TRIGGER_ACTIVATED_SESSION = '$session_recording_event_trigger_activated_session'\nexport const ENABLED_FEATURE_FLAGS = '$enabled_feature_flags'\nexport const PERSISTENCE_EARLY_ACCESS_FEATURES = '$early_access_features'\nexport const PERSISTENCE_FEATURE_FLAG_DETAILS = '$feature_flag_details'\nexport const STORED_PERSON_PROPERTIES_KEY = '$stored_person_properties'\nexport const STORED_GROUP_PROPERTIES_KEY = '$stored_group_properties'\nexport const SURVEYS = '$surveys'\nexport const SURVEYS_ACTIVATED = '$surveys_activated'\nexport const FLAG_CALL_REPORTED = '$flag_call_reported'\nexport const USER_STATE = '$user_state'\nexport const CLIENT_SESSION_PROPS = '$client_session_props'\nexport const CAPTURE_RATE_LIMIT = '$capture_rate_limit'\n\n/** @deprecated Delete this when INITIAL_PERSON_INFO has been around for long enough to ignore backwards compat */\nexport const INITIAL_CAMPAIGN_PARAMS = '$initial_campaign_params'\n/** @deprecated Delete this when INITIAL_PERSON_INFO has been around for long enough to ignore backwards compat */\nexport const INITIAL_REFERRER_INFO = '$initial_referrer_info'\nexport const INITIAL_PERSON_INFO = '$initial_person_info'\nexport const ENABLE_PERSON_PROCESSING = '$epp'\nexport const TOOLBAR_ID = '__POSTHOG_TOOLBAR__'\nexport const TOOLBAR_CONTAINER_CLASS = 'toolbar-global-fade-container'\n\n/**\n * PREVIEW - MAY CHANGE WITHOUT WARNING - DO NOT USE IN PRODUCTION\n * Sentinel value for distinct id, device id, session id. Signals that the server should generate the value\n * */\nexport const COOKIELESS_SENTINEL_VALUE = '$posthog_cookieless'\nexport const COOKIELESS_MODE_FLAG_PROPERTY = '$cookieless_mode'\n\nexport const WEB_EXPERIMENTS = '$web_experiments'\n\n// These are properties that are reserved and will not be automatically included in events\nexport const PERSISTENCE_RESERVED_PROPERTIES = [\n PEOPLE_DISTINCT_ID_KEY,\n ALIAS_ID_KEY,\n CAMPAIGN_IDS_KEY,\n EVENT_TIMERS_KEY,\n SESSION_RECORDING_ENABLED_SERVER_SIDE,\n HEATMAPS_ENABLED_SERVER_SIDE,\n SESSION_ID,\n ENABLED_FEATURE_FLAGS,\n ERROR_TRACKING_SUPPRESSION_RULES,\n USER_STATE,\n PERSISTENCE_EARLY_ACCESS_FEATURES,\n PERSISTENCE_FEATURE_FLAG_DETAILS,\n STORED_GROUP_PROPERTIES_KEY,\n STORED_PERSON_PROPERTIES_KEY,\n SURVEYS,\n FLAG_CALL_REPORTED,\n CLIENT_SESSION_PROPS,\n CAPTURE_RATE_LIMIT,\n INITIAL_CAMPAIGN_PARAMS,\n INITIAL_REFERRER_INFO,\n ENABLE_PERSON_PROCESSING,\n INITIAL_PERSON_INFO,\n]\n\nexport const SURVEYS_REQUEST_TIMEOUT_MS = 10000\n"],"names":["win","window","undefined","global","globalThis","nativeForEach","Array","prototype","forEach","navigator","document","location","fetch","XMLHttpRequest","AbortController","userAgent","assignableWindow","includes","str","needle","indexOf","trim","nativeIsArray","isArray","ObjProto","Object","type_utils_hasOwnProperty","hasOwnProperty","type_utils_toString","toString","obj","call","isFunction","x","isNativeFunction","isUndefined","isString","isNull","isNullish","isNumber","isFormData","FormData","_createLogger","prefix","logger","_log","level","console","consoleLog","_len","arguments","length","args","_key","info","_len2","_key2","warn","_len3","_key3","error","_len4","_key4","critical","_len5","_key5","uninitializedWarning","methodName","createLogger","additionalPrefix","breaker","eachArray","iterator","thisArg","i","l","each","pair","entries","key","extend","source","prop","ownProps","keys","resArray","addEventListener","element","event","callback","options","capture","passive","isElementNode","el","nodeType","isTag","tag","tagName","toLowerCase","splitClassString","s","split","getClassNames","className","baseVal","getAttribute","getSafeText","elText","shouldCaptureElement","isSensitiveElement","childNodes","child","_makeSafeText","isTextNode","textContent","filter","shouldCaptureValue","join","replace","substring","autocaptureCompatibleElements","curEl","parentNode","classes","type","name","id","test","coreCCPattern","anchoredCCRegex","RegExp","unanchoredCCRegex","coreSSNPattern","anchoredSSNRegex","unanchoredSSNRegex","value","anchorRegexes","getDirectAndNestedSpanText","target","text","getNestedSpanText","_child$tagName","spanText","e","getElementsChainString","elements","ret","map","_element$nth_child","_element$nth_of_type","el_string","tag_name","attr_class","single_class","sort","attributes","_extends","nth_child","nth_of_type","href","attr_id","sortedAttributes","_ref","_ref2","a","b","localeCompare","_ref3","escapeQuotes","_ref4","elementsToString","_el$$el_text","_el$attr__href","response","slice","extractAttrClass","_ref5","_ref6","extractElements","input","limitText","previousElementSibling","_el","previousSibling","getPropertiesFromElement","elem","maskAllAttributes","maskText","elementAttributeIgnorelist","props","c","attr","attributeName","nthChild","nthOfType","currentElem","autocapturePropertiesForElement","_elementsJson$","_elementsJson$2","maskAllElementAttributes","maskAllText","elementAttributeIgnoreList","targetElementList","push","host","externalHref","url","elementsJson","autocaptureAugmentProperties","explicitNoCapture","shouldCaptureEl","augmentProperties","propertyKey","propertyValue","getAugmentPropertiesFromElement","_convertToURL","_window$location","hrefHost","createElement","locationHost","$event_type","$ce_version","$elements","$elements_chain","$el_text","$external_click_url","isAngularZonePresent","Zone","cachedImplementations","getNativeMutationObserverImplementation","cached","impl","bind","sandbox","hidden","head","appendChild","contentWindow","removeChild","getNativeImplementation","asClick","_e$target","eventTarget","srcElement","shadowRoot","composedPath","node","originalEvent","timestamp","Date","now","checkTimeout","thresholdMs","LazyLoadedDeadClicksAutocapture","_asRequiredConfig","providedConfig","_providedConfig$eleme","_providedConfig$scrol","_providedConfig$selec","_providedConfig$mutat","defaultConfig","this","_defaultConfig","__onCapture","_captureDeadClick","element_attribute_ignorelist","scroll_threshold_ms","selection_change_threshold_ms","mutation_threshold_ms","constructor","instance","config","_clicks","defaultOnCapture","_onClick","click","_ignoreClick","_checkClickTimer","setTimeout","_checkClicks","_onScroll","candidateNow","scrollDelayMs","_onSelectionChange","_lastSelectionChanged","_config","_onCapture","start","observerTarget","_startClickObserver","_startScrollObserver","_startSelectionChangedObserver","_startMutationObserver","_mutationObserver","NativeMutationObserver","mutations","_onMutation","observe","characterData","childList","subtree","stop","_this$_mutationObserv","disconnect","removeEventListener","_mutations","_lastMutation","Element","closest","some","Math","abs","clearTimeout","clicksToCheck","_click$mutationDelayM","mutationDelayMs","absoluteDelayMs","selectionChangedDelayMs","scrollTimeout","selectionChangedTimeout","mutationTimeout","absoluteTimeout","hadScroll","hadMutation","hadSelectionChange","$dead_click_last_mutation_timestamp","$dead_click_event_timestamp","$dead_click_scroll_timeout","$dead_click_mutation_timeout","$dead_click_absolute_timeout","$dead_click_selection_changed_timeout","properties","mask_all_element_attributes","mask_all_text","$dead_click_scroll_delay_ms","$dead_click_mutation_delay_ms","$dead_click_absolute_delay_ms","$dead_click_selection_changed_delay_ms","__PosthogExtensions__","initDeadClicksAutocapture","ph"],"mappings":"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"}