mirror of
https://github.com/plausible/analytics.git
synced 2024-11-24 04:32:57 +03:00
67be43b6e6
* Add tagged events support for revenue goals This commit adds two special classes for tracking revenue with tagged events. The following example sends an event with revenue data when the tracker script has revenue and tagged-events script extensions: ```html <button class="plausible-event-revenue-amount=10.29 plausible-event-revenue-currency=EUR"></button> ``` * Rename special revenue tracking class name
229 lines
6.7 KiB
JavaScript
229 lines
6.7 KiB
JavaScript
function getLinkEl(link) {
|
|
while (link && (typeof link.tagName === 'undefined' || !isLink(link) || !link.href)) {
|
|
link = link.parentNode
|
|
}
|
|
return link
|
|
}
|
|
|
|
function isLink(element) {
|
|
return element && element.tagName && element.tagName.toLowerCase() === 'a'
|
|
}
|
|
|
|
function shouldFollowLink(event, link) {
|
|
// If default has been prevented by an external script, Plausible should not intercept navigation.
|
|
if (event.defaultPrevented) { return false }
|
|
|
|
var targetsCurrentWindow = !link.target || link.target.match(/^_(self|parent|top)$/i)
|
|
var isRegularClick = !(event.ctrlKey || event.metaKey || event.shiftKey) && event.type === 'click'
|
|
return targetsCurrentWindow && isRegularClick
|
|
}
|
|
|
|
var MIDDLE_MOUSE_BUTTON = 1
|
|
|
|
function handleLinkClickEvent(event) {
|
|
if (event.type === 'auxclick' && event.button !== MIDDLE_MOUSE_BUTTON) { return }
|
|
|
|
var link = getLinkEl(event.target)
|
|
var hrefWithoutQuery = link && link.href && link.href.split('?')[0]
|
|
|
|
{{#if tagged_events}}
|
|
if (isElementOrParentTagged(link, 0)) {
|
|
// Return to prevent sending multiple events with the same action.
|
|
// Clicks on tagged links are handled by another function.
|
|
return
|
|
}
|
|
{{/if}}
|
|
|
|
{{#if outbound_links}}
|
|
if (isOutboundLink(link)) {
|
|
return sendLinkClickEvent(event, link, { name: 'Outbound Link: Click', props: { url: link.href } })
|
|
}
|
|
{{/if}}
|
|
|
|
{{#if file_downloads}}
|
|
if (isDownloadToTrack(hrefWithoutQuery)) {
|
|
return sendLinkClickEvent(event, link, { name: 'File Download', props: { url: hrefWithoutQuery } })
|
|
}
|
|
{{/if}}
|
|
}
|
|
|
|
function sendLinkClickEvent(event, link, eventAttrs) {
|
|
var followedLink = false
|
|
|
|
function followLink() {
|
|
if (!followedLink) {
|
|
followedLink = true
|
|
window.location = link.href
|
|
}
|
|
}
|
|
|
|
if (shouldFollowLink(event, link)) {
|
|
var attrs = { props: eventAttrs.props, callback: followLink }
|
|
{{#if revenue}}
|
|
attrs.revenue = eventAttrs.revenue
|
|
{{/if}}
|
|
plausible(eventAttrs.name, attrs)
|
|
setTimeout(followLink, 5000)
|
|
event.preventDefault()
|
|
} else {
|
|
var attrs = { props: eventAttrs.props }
|
|
{{#if revenue}}
|
|
attrs.revenue = eventAttrs.revenue
|
|
{{/if}}
|
|
plausible(eventAttrs.name, attrs)
|
|
}
|
|
}
|
|
|
|
document.addEventListener('click', handleLinkClickEvent)
|
|
document.addEventListener('auxclick', handleLinkClickEvent)
|
|
|
|
{{#if outbound_links}}
|
|
function isOutboundLink(link) {
|
|
return link && link.href && link.host && link.host !== location.host
|
|
}
|
|
{{/if}}
|
|
|
|
{{#if file_downloads}}
|
|
var defaultFileTypes = ['pdf', 'xlsx', 'docx', 'txt', 'rtf', 'csv', 'exe', 'key', 'pps', 'ppt', 'pptx', '7z', 'pkg', 'rar', 'gz', 'zip', 'avi', 'mov', 'mp4', 'mpeg', 'wmv', 'midi', 'mp3', 'wav', 'wma']
|
|
var fileTypesAttr = scriptEl.getAttribute('file-types')
|
|
var addFileTypesAttr = scriptEl.getAttribute('add-file-types')
|
|
var fileTypesToTrack = (fileTypesAttr && fileTypesAttr.split(",")) || (addFileTypesAttr && addFileTypesAttr.split(",").concat(defaultFileTypes)) || defaultFileTypes;
|
|
|
|
function isDownloadToTrack(url) {
|
|
if (!url) { return false }
|
|
|
|
var fileType = url.split('.').pop();
|
|
return fileTypesToTrack.some(function (fileTypeToTrack) {
|
|
return fileTypeToTrack === fileType
|
|
})
|
|
}
|
|
{{/if}}
|
|
|
|
{{#if tagged_events}}
|
|
// Finds event attributes by iterating over the given element's (or its
|
|
// parent's) classList. Returns an object with `name` and `props` keys.
|
|
function getTaggedEventAttributes(htmlElement) {
|
|
var taggedElement = isTagged(htmlElement) ? htmlElement : htmlElement && htmlElement.parentNode
|
|
var eventAttrs = { name: null, props: {} }
|
|
{{#if revenue}}
|
|
eventAttrs.revenue = {}
|
|
{{/if}}
|
|
|
|
var classList = taggedElement && taggedElement.classList
|
|
if (!classList) { return eventAttrs }
|
|
|
|
for (var i = 0; i < classList.length; i++) {
|
|
var className = classList.item(i)
|
|
|
|
var matchList = className.match(/plausible-event-(.+)(=|--)(.+)/)
|
|
if (matchList) {
|
|
var key = matchList[1]
|
|
var value = matchList[3].replace(/\+/g, ' ')
|
|
|
|
if (key.toLowerCase() == 'name') {
|
|
eventAttrs.name = value
|
|
} else {
|
|
eventAttrs.props[key] = value
|
|
}
|
|
}
|
|
|
|
{{#if revenue}}
|
|
var revenueMatchList = className.match(/plausible-revenue-(.+)(=|--)(.+)/)
|
|
if (revenueMatchList) {
|
|
var key = revenueMatchList[1]
|
|
var value = revenueMatchList[3]
|
|
eventAttrs.revenue[key] = value
|
|
}
|
|
{{/if}}
|
|
}
|
|
|
|
return eventAttrs
|
|
}
|
|
|
|
function handleTaggedFormSubmitEvent(event) {
|
|
var form = event.target
|
|
var eventAttrs = getTaggedEventAttributes(form)
|
|
if (!eventAttrs.name) { return }
|
|
|
|
event.preventDefault()
|
|
var formSubmitted = false
|
|
|
|
function submitForm() {
|
|
if (!formSubmitted) {
|
|
formSubmitted = true
|
|
form.submit()
|
|
}
|
|
}
|
|
|
|
setTimeout(submitForm, 5000)
|
|
|
|
var attrs = { props: eventAttrs.props, callback: submitForm }
|
|
{{#if revenue}}
|
|
attrs.revenue = eventAttrs.revenue
|
|
{{/if}}
|
|
plausible(eventAttrs.name, attrs)
|
|
}
|
|
|
|
function isForm(element) {
|
|
return element && element.tagName && element.tagName.toLowerCase() === 'form'
|
|
}
|
|
|
|
var PARENTS_TO_SEARCH_LIMIT = 3
|
|
|
|
function handleTaggedElementClickEvent(event) {
|
|
if (event.type === 'auxclick' && event.button !== MIDDLE_MOUSE_BUTTON) { return }
|
|
|
|
var clicked = event.target
|
|
|
|
var clickedLink
|
|
var taggedElement
|
|
// Iterate over parents to find the tagged element. Also search for
|
|
// a link element to call for different tracking behavior if found.
|
|
for (var i = 0; i <= PARENTS_TO_SEARCH_LIMIT; i++) {
|
|
if (!clicked) { break }
|
|
|
|
// Clicks inside forms are not tracked. Only form submits are.
|
|
if (isForm(clicked)) { return }
|
|
if (isLink(clicked)) { clickedLink = clicked }
|
|
if (isTagged(clicked)) { taggedElement = clicked }
|
|
clicked = clicked.parentNode
|
|
}
|
|
|
|
if (taggedElement) {
|
|
var eventAttrs = getTaggedEventAttributes(taggedElement)
|
|
|
|
if (clickedLink) {
|
|
eventAttrs.props.url = clickedLink.href
|
|
sendLinkClickEvent(event, clickedLink, eventAttrs)
|
|
} else {
|
|
var attrs = {}
|
|
attrs.props = eventAttrs.props
|
|
{{#if revenue}}
|
|
attrs.revenue = eventAttrs.revenue
|
|
{{/if}}
|
|
plausible(eventAttrs.name, attrs)
|
|
}
|
|
}
|
|
}
|
|
|
|
function isTagged(element) {
|
|
var classList = element && element.classList
|
|
if (classList) {
|
|
for (var i = 0; i < classList.length; i++) {
|
|
if (classList.item(i).match(/plausible-event-name(=|--)(.+)/)) { return true }
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
function isElementOrParentTagged(element, parentsChecked) {
|
|
if (!element || parentsChecked > PARENTS_TO_SEARCH_LIMIT) { return false }
|
|
if (isTagged(element)) { return true }
|
|
return isElementOrParentTagged(element.parentNode, parentsChecked + 1)
|
|
}
|
|
|
|
document.addEventListener('submit', handleTaggedFormSubmitEvent)
|
|
document.addEventListener('click', handleTaggedElementClickEvent)
|
|
document.addEventListener('auxclick', handleTaggedElementClickEvent)
|
|
{{/if}}
|