Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] #2

Open
wants to merge 2 commits into
base: sufy
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 127 additions & 53 deletions src/tracker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@
const _false = 'false';
const attr = currentScript.getAttribute.bind(currentScript);
const website = attr(_data + 'website-id');
const hostUrl = attr(_data + 'host-url');
// const hostUrl = attr(_data + 'host-url');
const autoTrack = attr(_data + 'auto-track') !== _false;
const dnt = attr(_data + 'do-not-track');
const domain = attr(_data + 'domains') || '';
const domains = domain.split(',').map(n => n.trim());
const root = hostUrl
? hostUrl.replace(/\/$/, '')
: currentScript.src.split('/').slice(0, -1).join('/');
const endpoint = `${root}/api/send`;
// const root = hostUrl
// ? hostUrl.replace(/\/$/, '')
// : currentScript.src.split('/').slice(0, -1).join('/');
// const endpoint = `${root}/api/send`;
const endpoint = '/api/portal/info/biz';
const screen = `${width}x${height}`;
const eventRegex = /data-umami-event-([\w-_]+)/;
const eventNameAttribute = _data + 'umami-event';
Expand All @@ -50,6 +51,14 @@
}
};

const getPayloadBaseData = () => {
const result = {
'c-client-time': Date.now(),
'c-timezone-offset': new Date().getTimezoneOffset(),
};
return Object.keys(result).length === 0 ? undefined : result;
};

const getPayload = () => ({
website,
hostname,
Expand All @@ -58,6 +67,7 @@
title,
url: currentUrl,
referrer: currentRef,
data: getPayloadBaseData(),
});

/* Tracking functions */
Expand Down Expand Up @@ -92,31 +102,16 @@
};

const handleClick = () => {
const trackElement = el => {
const attr = el.getAttribute.bind(el);
const eventName = attr(eventNameAttribute);

if (eventName) {
const eventData = {};

el.getAttributeNames().forEach(name => {
const match = name.match(eventRegex);

if (match) {
eventData[match[1]] = attr(name);
}
});

return track(eventName, eventData);
function handleFullClick(event) {
function isTag(element, tagName) {
return element.tagName === tagName.toUpperCase();
}
return Promise.resolve();
};

const callback = e => {
const findATagParent = (rootElem, maxSearchDepth) => {
let currentElement = rootElem;
function findTagParent(rootElement, tagName) {
const maxSearchDepth = 10;
let currentElement = rootElement;
for (let i = 0; i < maxSearchDepth; i++) {
if (currentElement.tagName === 'A') {
if (isTag(currentElement, tagName)) {
return currentElement;
}
currentElement = currentElement.parentElement;
Expand All @@ -125,35 +120,109 @@
}
}
return null;
};
}

const ele = event.target;

let elementTagName;
let elementType;
let elementRole;
let elementId;
let elementName;
let elementTitle;
let elementAlt;
let elementClassName;

let elementContent;
let elementUrl;

function setElementBaseInfo(element) {
elementTagName = element.tagName.toUpperCase() || undefined;
elementType = element.type || undefined;
elementRole = element.role || undefined;
elementId = element.id || undefined;
elementName = element.name || undefined;
elementTitle = element.title || undefined;
elementAlt = element.alt || undefined;
elementClassName = element.className || undefined;
}

function tryMatchElement() {
if (isTag(ele, 'TEXTAREA')) {
setElementBaseInfo(ele);
return ele;
}

if (isTag(ele, 'SELECT')) {
setElementBaseInfo(ele);
try {
elementContent = ele
.querySelector('option[value="' + ele.value + '"]')
.innerText.trim();
} catch (err) {
//
}
return ele;
}

if (isTag(ele, 'INPUT')) {
setElementBaseInfo(ele);

const el = e.target;
const anchor = el.tagName === 'A' ? el : findATagParent(el, 10);

if (anchor) {
const { href, target } = anchor;
const external =
target === '_blank' ||
e.ctrlKey ||
e.shiftKey ||
e.metaKey ||
(e.button && e.button === 1);
const eventName = anchor.getAttribute(eventNameAttribute);

if (eventName && href) {
if (!external) {
e.preventDefault();
const type = ele.type;
if (type === 'button' || type === 'reset' || type === 'submit') {
elementContent = ele.value;
}
return trackElement(anchor).then(() => {
if (!external) location.href = href;
});

return ele;
}

const anchor = findTagParent(ele, 'A');
if (anchor) {
setElementBaseInfo(anchor);
elementContent = anchor.innerText.trim() || undefined;
elementUrl = anchor.href || undefined;
return anchor;
}

const button = findTagParent(ele, 'BUTTON');
if (button) {
setElementBaseInfo(button);
elementContent = button.innerText.trim() || undefined;
return button;
}
} else {
trackElement(el);
}
};

document.addEventListener('click', callback, true);
function report(targetElement) {
const getAttr = targetElement.getAttribute.bind(targetElement);
const eventName = getAttr(eventNameAttribute) || 'full-click';
const eventData = {
'c-element-tag-name': elementTagName || undefined,
'c-element-type': elementType || undefined,
'c-element-role': elementRole || undefined,
'c-element-id': elementId || undefined,
'c-element-name': elementName || undefined,
'c-element-title': elementTitle || undefined,
'c-element-alt': elementAlt || undefined,
'c-element-class-name': elementClassName || undefined,
'c-element-content': elementContent || undefined,
'c-element-url': elementUrl || undefined,
};
targetElement.getAttributeNames().forEach(name => {
const match = name.match(eventRegex);
if (match) {
eventData[match[1]] = getAttr(name);
}
});
track(eventName, eventData);
}

const target = tryMatchElement();
if (target != null) {
report(target);
}
}

document.addEventListener('click', handleFullClick, true);
};

const observeTitle = () => {
Expand Down Expand Up @@ -194,10 +263,15 @@

const track = (obj, data) => {
if (typeof obj === 'string') {
const payload = getPayload();
const payloadData = {
...(payload.data || {}),
...(typeof data === 'object' ? data : {}),
};
return send({
...getPayload(),
...payload,
name: obj,
data: typeof data === 'object' ? data : undefined,
data: Object.keys(payloadData).length === 0 ? undefined : payloadData,
});
} else if (typeof obj === 'object') {
return send(obj);
Expand Down