@segment/analytics-next
Version:
Analytics Next (aka Analytics 2.0) is the latest version of Segment’s JavaScript SDK - enabling you to send your data to any tool without having to learn, test, or use a new API every time.
153 lines (133 loc) • 4.05 kB
text/typescript
import { Analytics } from './analytics'
import { EventProperties, Options } from './events'
import { pTimeout } from './callback'
// Check if a user is opening the link in a new tab
function userNewTab(event: Event): boolean {
const typedEvent = event as Event & {
ctrlKey: boolean
shiftKey: boolean
metaKey: boolean
button: number
}
if (
typedEvent.ctrlKey ||
typedEvent.shiftKey ||
typedEvent.metaKey ||
(typedEvent.button && typedEvent.button == 1)
) {
return true
}
return false
}
// Check if the link opens in new tab
function linkNewTab(element: HTMLAnchorElement, href: string | null): boolean {
if (element.target === '_blank' && href) {
return true
}
return false
}
export interface JQueryShim<TElement = HTMLElement> {
toArray(): TElement[]
}
export function link(
this: Analytics,
links: Element | Array<Element> | JQueryShim | null,
event: string | Function,
properties?: EventProperties | Function,
options?: Options
): Analytics {
let elements: Array<Element> = []
// always arrays, handles jquery
if (!links) {
return this
}
if (links instanceof Element) {
elements = [links]
} else if ('toArray' in links) {
elements = links.toArray()
} else {
elements = links
}
elements.forEach((el: Element) => {
el.addEventListener(
'click',
(elementEvent: Event) => {
const ev = event instanceof Function ? event(el) : event
const props =
properties instanceof Function ? properties(el) : properties
const href =
el.getAttribute('href') ||
el.getAttributeNS('http://www.w3.org/1999/xlink', 'href') ||
el.getAttribute('xlink:href') ||
el.getElementsByTagName('a')[0]?.getAttribute('href')
const trackEvent = pTimeout(
this.track(ev, props, options ?? {}),
this.settings.timeout ?? 500
)
if (
!linkNewTab(el as HTMLAnchorElement, href) &&
!userNewTab(elementEvent)
) {
if (href) {
elementEvent.preventDefault
? elementEvent.preventDefault()
: (elementEvent.returnValue = false)
trackEvent
.catch(console.error)
.then(() => {
window.location.href = href
})
.catch(console.error)
}
}
},
false
)
})
return this
}
export type LinkArgs = Parameters<typeof link>
export function form(
this: Analytics,
forms: HTMLFormElement | Array<HTMLFormElement> | null,
event: string | Function,
properties?: EventProperties | Function,
options?: Options
): Analytics {
// always arrays, handles jquery
if (!forms) return this
if (forms instanceof HTMLFormElement) forms = [forms]
const elements = forms
elements.forEach((el) => {
if (!(el instanceof Element))
throw new TypeError('Must pass HTMLElement to trackForm/trackSubmit.')
const handler = (elementEvent: Event): void => {
elementEvent.preventDefault
? elementEvent.preventDefault()
: (elementEvent.returnValue = false)
const ev = event instanceof Function ? event(el) : event
const props = properties instanceof Function ? properties(el) : properties
const trackEvent = pTimeout(
this.track(ev, props, options ?? {}),
this.settings.timeout ?? 500
)
trackEvent
.catch(console.error)
.then(() => {
el.submit()
})
.catch(console.error)
}
// Support the events happening through jQuery or Zepto instead of through
// the normal DOM API, because `el.submit` doesn't bubble up events...
const $ = (window as any).jQuery || (window as any).Zepto
if ($) {
$(el).submit(handler)
} else {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
el.addEventListener('submit', handler, false)
}
})
return this
}
export type FormArgs = Parameters<typeof form>