@croct/plug
Version:
A fully-featured devkit for building natively personalized applications.
143 lines (142 loc) • 4.8 kB
JavaScript
import { parseEntity } from "./structuredData.js";
class AutoTrackingPlugin {
constructor(configuration) {
this.scanTimeout = null;
this.tab = configuration.tab;
this.tracker = configuration.tracker;
this.options = configuration.options;
this.trackStructuredData = this.trackStructuredData.bind(this);
this.trackLinkOpened = this.trackLinkOpened.bind(this);
this.handleUrlChange = this.handleUrlChange.bind(this);
}
isDisabled() {
return this.options?.disablePostViewed === true && this.options?.disableProductViewed === true && this.options?.disableLinkOpened === true;
}
enable() {
if (this.isDisabled()) {
return;
}
this.trackStructuredData();
this.tab.addListener("urlChange", this.handleUrlChange);
if (this.options?.disableLinkOpened !== true) {
document.addEventListener("click", this.trackLinkOpened, true);
}
}
disable() {
if (this.scanTimeout !== null) {
clearTimeout(this.scanTimeout);
this.scanTimeout = null;
}
this.tab.removeListener("urlChange", this.handleUrlChange);
document.removeEventListener("click", this.trackLinkOpened, true);
}
handleUrlChange() {
if (this.scanTimeout !== null) {
clearTimeout(this.scanTimeout);
}
this.scanTimeout = setTimeout(
() => {
this.scanTimeout = null;
this.trackStructuredData();
},
1e3
);
}
trackStructuredData() {
const structuredDataElements = document.querySelectorAll('script[type="application/ld+json"]');
for (const element of structuredDataElements) {
const entity = parseEntity(element.textContent ?? "");
switch (entity?.type) {
case "post":
if (this.options?.disablePostViewed !== true) {
this.trackPostViewed(entity);
}
break;
case "product":
case "service":
if (this.options?.disableProductViewed !== true) {
this.trackProductViewed(entity);
}
break;
}
}
}
trackPostViewed(info) {
let postId = info.id;
if (postId === void 0 && info.url !== void 0) {
const parsedUrl = new URL(info.url);
const pathSegments = parsedUrl.pathname.split("/").filter((segment) => segment.length > 0);
if (pathSegments.length > 0) {
postId = pathSegments[pathSegments.length - 1];
}
}
if (postId === void 0 || info.title === void 0) {
return;
}
this.tracker.track("postViewed", {
post: AutoTrackingPlugin.clean({
postId: AutoTrackingPlugin.truncate(postId, 200),
title: AutoTrackingPlugin.truncate(info.title, 200),
url: info.url,
tags: info.tags?.map((tag) => AutoTrackingPlugin.truncate(tag, 50)),
categories: info.categories?.map((category) => AutoTrackingPlugin.truncate(category, 50)),
authors: info.authors?.map((author) => AutoTrackingPlugin.truncate(author, 100)),
publishTime: info.publishTime ?? Date.now(),
updateTime: info.updateTime
})
});
}
trackProductViewed(info) {
if (info.id === void 0 || info.name === void 0 || info.displayPrice === void 0) {
return;
}
this.tracker.track("productViewed", {
product: AutoTrackingPlugin.clean({
productId: AutoTrackingPlugin.truncate(info.id, 50),
name: AutoTrackingPlugin.truncate(info.name, 200),
displayPrice: info.displayPrice,
url: info.url,
sku: info.sku !== void 0 ? AutoTrackingPlugin.truncate(info.sku, 50) : void 0,
brand: info.brand !== void 0 ? AutoTrackingPlugin.truncate(info.brand, 100) : void 0,
variant: info.variant !== void 0 ? AutoTrackingPlugin.truncate(info.variant, 50) : void 0,
category: info.category !== void 0 ? AutoTrackingPlugin.truncate(info.category, 100) : void 0,
originalPrice: info.originalPrice,
imageUrl: info.imageUrl
})
});
}
trackLinkOpened(event) {
if (event.target instanceof HTMLElement) {
const link = event.target.closest("a");
if (link?.href !== void 0 && URL.canParse(link.href, document.baseURI)) {
this.tracker.track("linkOpened", {
link: new URL(link.href, document.baseURI).toString()
});
}
}
}
static truncate(value, maxLength) {
if (value.length <= maxLength) {
return value;
}
return value.slice(0, maxLength);
}
static clean(obj) {
const result = { ...obj };
for (const key of Object.keys(result)) {
if (result[key] === void 0) {
delete result[key];
}
}
return result;
}
}
const factory = ((props) => new AutoTrackingPlugin({
tab: props.sdk.tab,
tracker: props.sdk.tracker,
options: props.options
}));
export {
AutoTrackingPlugin,
factory
};