mirror of
https://github.com/umami-software/umami.git
synced 2026-02-08 06:37:18 +01:00
Merge branch 'dev' into master
This commit is contained in:
commit
43a278c207
570 changed files with 20083 additions and 8319 deletions
2
src/tracker/index.d.ts
vendored
2
src/tracker/index.d.ts
vendored
|
|
@ -138,7 +138,7 @@ export type UmamiTracker = {
|
|||
|
||||
/**
|
||||
* Tracks an event with fully customizable dynamic data
|
||||
* Ilf you don't specify any `name` and/or `data`, it will be treated as a page view
|
||||
* If you don't specify any `name` and/or `data`, it will be treated as a page view
|
||||
*
|
||||
* @example ```
|
||||
* umami.track((props) => ({ ...props, url: path }));
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
const hostUrl = attr(_data + 'host-url');
|
||||
const autoTrack = attr(_data + 'auto-track') !== _false;
|
||||
const stripSearch = attr(_data + 'strip-search') === _true;
|
||||
const dnt = attr(_data + 'do-not-track');
|
||||
const domain = attr(_data + 'domains') || '';
|
||||
const domains = domain.split(',').map(n => n.trim());
|
||||
const root = hostUrl
|
||||
|
|
@ -34,16 +33,6 @@
|
|||
|
||||
/* Helper functions */
|
||||
|
||||
const hook = (_this, method, callback) => {
|
||||
const orig = _this[method];
|
||||
|
||||
return (...args) => {
|
||||
callback.apply(null, args);
|
||||
|
||||
return orig.apply(_this, args);
|
||||
};
|
||||
};
|
||||
|
||||
const getPath = url => {
|
||||
try {
|
||||
return new URL(url).pathname;
|
||||
|
|
@ -57,30 +46,12 @@
|
|||
hostname,
|
||||
screen,
|
||||
language,
|
||||
title,
|
||||
url: currentUrl,
|
||||
referrer: currentRef,
|
||||
title: encodeURIComponent(title),
|
||||
url: encodeURI(currentUrl),
|
||||
referrer: encodeURI(currentRef),
|
||||
});
|
||||
|
||||
/* Tracking functions */
|
||||
|
||||
const doNotTrack = () => {
|
||||
const { doNotTrack, navigator, external } = window;
|
||||
|
||||
const msTrackProtection = 'msTrackingProtectionEnabled';
|
||||
const msTracking = () => {
|
||||
return external && msTrackProtection in external && external[msTrackProtection]();
|
||||
};
|
||||
|
||||
const dnt = doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack || msTracking();
|
||||
|
||||
return dnt == '1' || dnt === 'yes';
|
||||
};
|
||||
|
||||
const trackingDisabled = () =>
|
||||
(localStorage && localStorage.getItem('umami.disabled')) ||
|
||||
(dnt && doNotTrack()) ||
|
||||
(domain && !domains.includes(hostname));
|
||||
/* Event handlers */
|
||||
|
||||
const handlePush = (state, title, url) => {
|
||||
if (!url) return;
|
||||
|
|
@ -93,77 +64,25 @@
|
|||
}
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
const trackElement = el => {
|
||||
const attr = el.getAttribute.bind(el);
|
||||
const eventName = attr(eventNameAttribute);
|
||||
const handlePathChanges = () => {
|
||||
const hook = (_this, method, callback) => {
|
||||
const orig = _this[method];
|
||||
|
||||
if (eventName) {
|
||||
const eventData = {};
|
||||
return (...args) => {
|
||||
callback.apply(null, args);
|
||||
|
||||
el.getAttributeNames().forEach(name => {
|
||||
const match = name.match(eventRegex);
|
||||
|
||||
if (match) {
|
||||
eventData[match[1]] = attr(name);
|
||||
}
|
||||
});
|
||||
|
||||
return track(eventName, eventData);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const callback = e => {
|
||||
const findATagParent = (rootElem, maxSearchDepth) => {
|
||||
let currentElement = rootElem;
|
||||
for (let i = 0; i < maxSearchDepth; i++) {
|
||||
if (currentElement.tagName === 'A') {
|
||||
return currentElement;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
if (!currentElement) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return orig.apply(_this, args);
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
return trackElement(anchor).then(() => {
|
||||
if (!external) location.href = href;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
trackElement(el);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', callback, true);
|
||||
history.pushState = hook(history, 'pushState', handlePush);
|
||||
history.replaceState = hook(history, 'replaceState', handlePush);
|
||||
};
|
||||
|
||||
const observeTitle = () => {
|
||||
const callback = ([entry]) => {
|
||||
const handleTitleChanges = () => {
|
||||
const observer = new MutationObserver(([entry]) => {
|
||||
title = entry && entry.target ? entry.target.text : undefined;
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(callback);
|
||||
});
|
||||
|
||||
const node = document.querySelector('head > title');
|
||||
|
||||
|
|
@ -176,7 +95,87 @@
|
|||
}
|
||||
};
|
||||
|
||||
const send = (payload, type = 'event') => {
|
||||
const handleClicks = () => {
|
||||
document.addEventListener(
|
||||
'click',
|
||||
async e => {
|
||||
const isSpecialTag = tagName => ['BUTTON', 'A'].includes(tagName);
|
||||
|
||||
const trackElement = async 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);
|
||||
}
|
||||
};
|
||||
|
||||
const findParentTag = (rootElem, maxSearchDepth) => {
|
||||
let currentElement = rootElem;
|
||||
for (let i = 0; i < maxSearchDepth; i++) {
|
||||
if (isSpecialTag(currentElement.tagName)) {
|
||||
return currentElement;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
if (!currentElement) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const el = e.target;
|
||||
const parentElement = isSpecialTag(el.tagName) ? el : findParentTag(el, 10);
|
||||
|
||||
if (parentElement) {
|
||||
const { href, target } = parentElement;
|
||||
const eventName = parentElement.getAttribute(eventNameAttribute);
|
||||
|
||||
if (eventName) {
|
||||
if (parentElement.tagName === 'A') {
|
||||
const external =
|
||||
target === '_blank' ||
|
||||
e.ctrlKey ||
|
||||
e.shiftKey ||
|
||||
e.metaKey ||
|
||||
(e.button && e.button === 1);
|
||||
|
||||
if (eventName && href) {
|
||||
if (!external) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return trackElement(parentElement).then(() => {
|
||||
if (!external) location.href = href;
|
||||
});
|
||||
}
|
||||
} else if (parentElement.tagName === 'BUTTON') {
|
||||
return trackElement(parentElement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return trackElement(el);
|
||||
}
|
||||
},
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
/* Tracking functions */
|
||||
|
||||
const trackingDisabled = () =>
|
||||
(localStorage && localStorage.getItem('umami.disabled')) ||
|
||||
(domain && !domains.includes(hostname));
|
||||
|
||||
const send = async (payload, type = 'event') => {
|
||||
if (trackingDisabled()) return;
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -184,14 +183,18 @@
|
|||
if (typeof cache !== 'undefined') {
|
||||
headers['x-umami-cache'] = cache;
|
||||
}
|
||||
return fetch(endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ type, payload }),
|
||||
headers,
|
||||
})
|
||||
.then(res => res.text())
|
||||
.then(text => (cache = text))
|
||||
.catch(() => {}); // no-op, gulp error
|
||||
try {
|
||||
const res = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ type, payload }),
|
||||
headers,
|
||||
});
|
||||
const text = await res.text();
|
||||
|
||||
return (cache = text);
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
};
|
||||
|
||||
const track = (obj, data) => {
|
||||
|
|
@ -227,10 +230,9 @@
|
|||
let initialized;
|
||||
|
||||
if (autoTrack && !trackingDisabled()) {
|
||||
history.pushState = hook(history, 'pushState', handlePush);
|
||||
history.replaceState = hook(history, 'replaceState', handlePush);
|
||||
handleClick();
|
||||
observeTitle();
|
||||
handlePathChanges();
|
||||
handleTitleChanges();
|
||||
handleClicks();
|
||||
|
||||
const init = () => {
|
||||
if (document.readyState === 'complete' && !initialized) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue