@phantomstudios/ft-lib 0.4.1-rc1 → 0.5.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/CODEOWNERS +1 -1
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/pr.yml +4 -1
- package/.github/workflows/release.yml +9 -2
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +3 -5
- package/SUPPORT.md +3 -3
- package/eslint.config.mjs +91 -0
- package/jest.config.js +1 -6
- package/lib/FTTracking/index.js +1 -1
- package/lib/FTTracking/index.js.map +1 -1
- package/lib/cmp/loadFtCmp.d.ts +3 -0
- package/lib/cmp/loadFtCmp.d.ts.map +1 -0
- package/lib/cmp/loadFtCmp.js +31 -0
- package/lib/cmp/loadFtCmp.js.map +1 -0
- package/lib/consentMonitor/index.d.ts +12 -14
- package/lib/consentMonitor/index.d.ts.map +1 -1
- package/lib/consentMonitor/index.js +86 -73
- package/lib/consentMonitor/index.js.map +1 -1
- package/lib/gaTracker/index.js +1 -2
- package/lib/gaTracker/index.js.map +1 -1
- package/lib/index.d.ts +7 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -4
- package/lib/index.js.map +1 -1
- package/lib/oTracker/index.js.map +1 -1
- package/lib/permutiveVideoUtils/index.d.ts.map +1 -1
- package/lib/permutiveVideoUtils/index.js +1 -1
- package/lib/permutiveVideoUtils/index.js.map +1 -1
- package/lib/reactPlayerTracking/index.d.ts.map +1 -1
- package/lib/reactPlayerTracking/index.js +1 -1
- package/lib/reactPlayerTracking/index.js.map +1 -1
- package/lib/utils/attention.js.map +1 -1
- package/lib/utils/cookies.js +1 -2
- package/lib/utils/cookies.js.map +1 -1
- package/lib/utils/getTrace.js.map +1 -1
- package/lib/utils/intersectionObserverPolyfill.d.ts +1 -0
- package/lib/utils/intersectionObserverPolyfill.js +1 -0
- package/lib/utils/intersectionObserverPolyfill.js.map +1 -1
- package/lib/utils/scroll.js.map +1 -1
- package/lib/utils/yupValidator.d.ts.map +1 -1
- package/lib/utils/yupValidator.js +2 -2
- package/lib/utils/yupValidator.js.map +1 -1
- package/lib/ytIframeTracking/index.d.ts +0 -1
- package/lib/ytIframeTracking/index.d.ts.map +1 -1
- package/lib/ytIframeTracking/index.js.map +1 -1
- package/package.json +25 -17
- package/src/FTTracking/index.ts +2 -2
- package/src/cmp/loadFtCmp.ts +28 -0
- package/src/consentMonitor/index.ts +82 -90
- package/src/gaTracker/index.ts +4 -4
- package/src/index.ts +10 -3
- package/src/oTracker/index.ts +5 -5
- package/src/permutiveVideoUtils/index.ts +2 -2
- package/src/reactPlayerTracking/index.ts +4 -4
- package/src/types.d.ts +13 -5
- package/src/utils/attention.js +9 -9
- package/src/utils/getTrace.js +2 -2
- package/src/utils/scroll.ts +2 -2
- package/src/utils/yupValidator.ts +6 -6
- package/src/ytIframeTracking/index.ts +9 -9
- package/test/index.test.tsx +13 -12
- package/tsconfig.json +3 -1
- package/.eslintrc +0 -58
- package/.nvmrc +0 -1
|
@@ -1,120 +1,112 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { enqueueCmpCallback, loadFtCmpScript } from "../cmp/loadFtCmp";
|
|
4
|
+
|
|
5
|
+
const debug = Debug("@phantomstudios/ft-lib/consentMonitor");
|
|
3
6
|
|
|
4
7
|
const DEFAULT_DEV_HOSTS = ["localhost", "phq", ".app", "preview"];
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
interface ConsentReadyInfo {
|
|
10
|
+
consentedToAll: boolean;
|
|
11
|
+
}
|
|
12
|
+
type ConsentReadyHandler = (
|
|
13
|
+
legislation: string,
|
|
14
|
+
uuid: string,
|
|
15
|
+
tcData: unknown,
|
|
16
|
+
info: ConsentReadyInfo,
|
|
17
|
+
) => void;
|
|
18
|
+
type MessageChoiceHandler = (
|
|
19
|
+
legislation: string,
|
|
20
|
+
choiceId: number,
|
|
21
|
+
choiceTypeId: number,
|
|
22
|
+
) => void;
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this._devHosts = devHosts.concat(DEFAULT_DEV_HOSTS);
|
|
16
|
-
} else if (devHosts === undefined) {
|
|
17
|
-
this._devHosts = DEFAULT_DEV_HOSTS;
|
|
18
|
-
} else {
|
|
19
|
-
this._devHosts = DEFAULT_DEV_HOSTS;
|
|
20
|
-
this._devHosts.push(devHosts);
|
|
21
|
-
}
|
|
24
|
+
const CMP_CHOICE_ACCEPT_ALL = 11;
|
|
25
|
+
const CMP_CHOICE_REJECT_ALL = 13;
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
export class ConsentMonitor {
|
|
28
|
+
private _consent = false;
|
|
29
|
+
private _devHosts: string[];
|
|
30
|
+
private _isDevEnvironment = false;
|
|
31
|
+
private _isInitialized = false;
|
|
26
32
|
|
|
27
|
-
get consent(): boolean {
|
|
33
|
+
public get consent(): boolean {
|
|
28
34
|
return this._consent;
|
|
29
35
|
}
|
|
30
|
-
|
|
31
|
-
get devHosts(): string[] | string {
|
|
36
|
+
public get devHosts(): string[] {
|
|
32
37
|
return this._devHosts;
|
|
33
38
|
}
|
|
34
|
-
|
|
35
|
-
get isDevEnvironment(): boolean {
|
|
39
|
+
public get isDevEnvironment(): boolean {
|
|
36
40
|
return this._isDevEnvironment;
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
get isInitialized(): boolean {
|
|
42
|
+
public get isInitialized(): boolean {
|
|
40
43
|
return this._isInitialized;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
public get userHasConsented(): boolean {
|
|
47
|
+
return this._consent;
|
|
48
|
+
}
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
constructor(hostname?: string, devHosts?: string[] | string) {
|
|
51
|
+
if (Array.isArray(devHosts)) {
|
|
52
|
+
this._devHosts = [...devHosts, ...DEFAULT_DEV_HOSTS];
|
|
53
|
+
} else if (devHosts === undefined) {
|
|
54
|
+
this._devHosts = [...DEFAULT_DEV_HOSTS];
|
|
55
|
+
} else {
|
|
56
|
+
this._devHosts = [...DEFAULT_DEV_HOSTS, devHosts];
|
|
57
|
+
}
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
this._devHosts.
|
|
52
|
-
(
|
|
53
|
-
this._hostname.includes(devHost) && this.setDevCookieHandler()
|
|
59
|
+
const hostToCheck = hostname ?? window.location.hostname;
|
|
60
|
+
this._isDevEnvironment = this._devHosts.some((h) =>
|
|
61
|
+
hostToCheck.includes(h),
|
|
54
62
|
);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
loadFtCmpScript()
|
|
65
|
+
.then(() => {
|
|
66
|
+
this.attachCmpListeners();
|
|
67
|
+
this._isInitialized = true;
|
|
68
|
+
})
|
|
69
|
+
.catch((err) => console.error(err));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private attachCmpListeners(): void {
|
|
73
|
+
enqueueCmpCallback(() => {
|
|
74
|
+
const onReady: ConsentReadyHandler = (_l, _u, _t, info) => {
|
|
75
|
+
debug("onConsentReady:", info);
|
|
76
|
+
if (info.consentedToAll) {
|
|
77
|
+
this.enablePermutive();
|
|
64
78
|
} else {
|
|
65
|
-
this.
|
|
79
|
+
this.disablePermutive();
|
|
66
80
|
}
|
|
67
|
-
this._isInitialized = true;
|
|
68
|
-
} else if (
|
|
69
|
-
this.getCookieValue("FTConsent").includes(
|
|
70
|
-
"behaviouraladsOnsite%3Aon"
|
|
71
|
-
) &&
|
|
72
|
-
!this.consent
|
|
73
|
-
) {
|
|
74
|
-
debug("setting permutive tracking consent: on");
|
|
75
|
-
this.permutiveConsentOn();
|
|
76
|
-
} else if (
|
|
77
|
-
!this.getCookieValue("FTConsent").includes(
|
|
78
|
-
"behaviouraladsOnsite%3Aon"
|
|
79
|
-
) &&
|
|
80
|
-
this.consent
|
|
81
|
-
) {
|
|
82
|
-
debug("setting permutive tracking consent: off");
|
|
83
|
-
this.permutiveConsentOff();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
setDevCookieHandler = () => {
|
|
89
|
-
this._isDevEnvironment = true;
|
|
90
|
-
debug("setting development environment from host match");
|
|
91
|
-
const oCookieMessage =
|
|
92
|
-
document.getElementsByClassName("o-cookie-message")[0];
|
|
93
|
-
if (oCookieMessage) {
|
|
94
|
-
const onCookieMessageAct = () => {
|
|
95
|
-
debug("setting development FT consent cookies");
|
|
96
|
-
document.cookie = "FTConsent=behaviouraladsOnsite%3Aon";
|
|
97
|
-
document.cookie = "FTCookieConsentGDPR=true";
|
|
98
|
-
oCookieMessage.removeEventListener(
|
|
99
|
-
"oCookieMessage.act",
|
|
100
|
-
onCookieMessageAct,
|
|
101
|
-
false
|
|
102
|
-
);
|
|
103
81
|
};
|
|
104
|
-
oCookieMessage.addEventListener("oCookieMessage.act", onCookieMessageAct);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
82
|
|
|
108
|
-
|
|
109
|
-
|
|
83
|
+
const onChoice: MessageChoiceHandler = (_l, _c, typeId) => {
|
|
84
|
+
debug("onMessageChoiceSelect:", typeId);
|
|
85
|
+
if (typeId === CMP_CHOICE_ACCEPT_ALL) this.enablePermutive();
|
|
86
|
+
else if (typeId === CMP_CHOICE_REJECT_ALL) this.disablePermutive();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
window._sp_.addEventListener?.("onConsentReady", onReady);
|
|
90
|
+
window._sp_.addEventListener?.("onMessageChoiceSelect", onChoice);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private enablePermutive(): void {
|
|
95
|
+
if (this._consent) return;
|
|
96
|
+
debug("Permutive consent: ON");
|
|
97
|
+
window.permutive?.consent({
|
|
110
98
|
opt_in: true,
|
|
111
99
|
token: "behaviouraladsOnsite:on",
|
|
112
100
|
});
|
|
113
101
|
this._consent = true;
|
|
114
|
-
}
|
|
102
|
+
}
|
|
115
103
|
|
|
116
|
-
|
|
117
|
-
|
|
104
|
+
private disablePermutive(): void {
|
|
105
|
+
if (!this._consent) return;
|
|
106
|
+
debug("Permutive consent: OFF");
|
|
107
|
+
window.permutive?.consent({ opt_in: false });
|
|
118
108
|
this._consent = false;
|
|
119
|
-
}
|
|
109
|
+
}
|
|
120
110
|
}
|
|
111
|
+
|
|
112
|
+
export { ConsentMonitor as consentMonitor };
|
package/src/gaTracker/index.ts
CHANGED
|
@@ -55,13 +55,13 @@ export class gaTracker {
|
|
|
55
55
|
this.GTMEventDispatcher(
|
|
56
56
|
category,
|
|
57
57
|
action,
|
|
58
|
-
label || window.location.pathname //use pathname if label section has not been defined
|
|
58
|
+
label || window.location.pathname, //use pathname if label section has not been defined
|
|
59
59
|
);
|
|
60
60
|
} else {
|
|
61
61
|
this.GtagEventDispatcher(
|
|
62
62
|
category,
|
|
63
63
|
action,
|
|
64
|
-
label || window.location.pathname
|
|
64
|
+
label || window.location.pathname,
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -70,7 +70,7 @@ export class gaTracker {
|
|
|
70
70
|
|
|
71
71
|
setUpFTNavigationLinksTracking() {
|
|
72
72
|
const FTNavigationLinks = Array.from(
|
|
73
|
-
document.querySelectorAll(".o-header__drawer-menu-link")
|
|
73
|
+
document.querySelectorAll(".o-header__drawer-menu-link"),
|
|
74
74
|
);
|
|
75
75
|
if (FTNavigationLinks) {
|
|
76
76
|
FTNavigationLinks.map((navLink) => {
|
|
@@ -119,7 +119,7 @@ export class gaTracker {
|
|
|
119
119
|
|
|
120
120
|
setupFooterLinksTracking() {
|
|
121
121
|
const FTFooterLinks = Array.from(
|
|
122
|
-
document.querySelectorAll(".o-footer__legal-links li a")
|
|
122
|
+
document.querySelectorAll(".o-footer__legal-links li a"),
|
|
123
123
|
);
|
|
124
124
|
if (FTFooterLinks) {
|
|
125
125
|
FTFooterLinks.forEach((footerLink) => {
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { FTTracking, TrackingOptions } from "./FTTracking";
|
|
2
|
+
import { ConfigType, OrigamiEventType } from "./utils/yupValidator";
|
|
3
|
+
|
|
4
|
+
export { FTTracking };
|
|
5
|
+
export type { TrackingOptions };
|
|
6
|
+
|
|
7
|
+
export { loadFtCmpScript, enqueueCmpCallback } from "./cmp/loadFtCmp";
|
|
8
|
+
|
|
9
|
+
export { ConsentMonitor as consentMonitor } from "./consentMonitor";
|
|
2
10
|
export { permutiveVideoUtils } from "./permutiveVideoUtils";
|
|
3
11
|
export { reactPlayerTracking } from "./reactPlayerTracking";
|
|
4
12
|
export { gaTracker } from "./gaTracker";
|
|
5
13
|
export { oTracker } from "./oTracker";
|
|
6
14
|
export { ytIframeTracking } from "./ytIframeTracking";
|
|
7
|
-
export {
|
|
8
|
-
export { ConfigType, OrigamiEventType } from "./utils/yupValidator";
|
|
15
|
+
export type { ConfigType, OrigamiEventType };
|
package/src/oTracker/index.ts
CHANGED
|
@@ -21,12 +21,12 @@ export class oTracker {
|
|
|
21
21
|
constructor(config: ConfigType, options: TrackingOptions) {
|
|
22
22
|
this._config = parseConfig(
|
|
23
23
|
config,
|
|
24
|
-
options.disableAppFormatTransform || false
|
|
24
|
+
options.disableAppFormatTransform || false,
|
|
25
25
|
);
|
|
26
26
|
validateConfig(
|
|
27
27
|
this._config,
|
|
28
28
|
options.disableAppFormatTransform || false,
|
|
29
|
-
options.logValidationErrors || false
|
|
29
|
+
options.logValidationErrors || false,
|
|
30
30
|
);
|
|
31
31
|
this._config.source_id = oTracking.getRootID();
|
|
32
32
|
this.options = options;
|
|
@@ -68,7 +68,7 @@ export class oTracker {
|
|
|
68
68
|
detail.app = this.config.app;
|
|
69
69
|
detail.product = this.config.product;
|
|
70
70
|
document.body.dispatchEvent(
|
|
71
|
-
new CustomEvent("oTracking.event", { detail, bubbles: true })
|
|
71
|
+
new CustomEvent("oTracking.event", { detail, bubbles: true }),
|
|
72
72
|
);
|
|
73
73
|
} else {
|
|
74
74
|
throw "Invalid event type";
|
|
@@ -92,7 +92,7 @@ export class oTracker {
|
|
|
92
92
|
category: "brandedContent",
|
|
93
93
|
},
|
|
94
94
|
bubbles: true,
|
|
95
|
-
})
|
|
95
|
+
}),
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -104,7 +104,7 @@ export class oTracker {
|
|
|
104
104
|
(elm) =>
|
|
105
105
|
elm instanceof HTMLElement &&
|
|
106
106
|
elm.hasAttribute &&
|
|
107
|
-
elm.hasAttribute("data-o-event")
|
|
107
|
+
elm.hasAttribute("data-o-event"),
|
|
108
108
|
);
|
|
109
109
|
if (trackableElement && trackableElement instanceof HTMLElement) {
|
|
110
110
|
const data = trackableElement.getAttribute("data-o-event");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
|
-
const debug = Debug("@
|
|
2
|
+
const debug = Debug("@phantomstudios/ft-lib");
|
|
3
3
|
export class permutiveVideoUtils {
|
|
4
4
|
protected progressPermutiveMilestones: number[] = [0, 0.25, 0.5, 0.75, 1];
|
|
5
5
|
protected _campaign: string;
|
|
@@ -26,7 +26,7 @@ export class permutiveVideoUtils {
|
|
|
26
26
|
emitPermutiveProgressEvent = (
|
|
27
27
|
duration: number,
|
|
28
28
|
currentTime: number,
|
|
29
|
-
interval: any = undefined
|
|
29
|
+
interval: any = undefined,
|
|
30
30
|
) => {
|
|
31
31
|
const progress = currentTime / (Math.floor(duration) - 1);
|
|
32
32
|
|
|
@@ -3,7 +3,7 @@ import Debug from "debug";
|
|
|
3
3
|
import { FTTracking } from "../FTTracking";
|
|
4
4
|
import { permutiveVideoUtils } from "../permutiveVideoUtils";
|
|
5
5
|
import { OrigamiEventType } from "../utils/yupValidator";
|
|
6
|
-
const debug = Debug("@
|
|
6
|
+
const debug = Debug("@phantomstudios/ft-lib");
|
|
7
7
|
interface PlayerProgressEvent {
|
|
8
8
|
played: number;
|
|
9
9
|
loaded: number;
|
|
@@ -31,7 +31,7 @@ export class reactPlayerTracking {
|
|
|
31
31
|
this.permutiveTracker = new permutiveVideoUtils(
|
|
32
32
|
this.FTTracker.config.campaign,
|
|
33
33
|
this.videoTitle,
|
|
34
|
-
this.videoUrl
|
|
34
|
+
this.videoUrl,
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -79,7 +79,7 @@ export class reactPlayerTracking {
|
|
|
79
79
|
this.playedSeconds = reactPlayerProgress.playedSeconds;
|
|
80
80
|
if (this.duration > 0) {
|
|
81
81
|
this.playedPercent = Math.floor(
|
|
82
|
-
(Math.ceil(reactPlayerProgress.playedSeconds) / this.duration) * 100
|
|
82
|
+
(Math.ceil(reactPlayerProgress.playedSeconds) / this.duration) * 100,
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -99,7 +99,7 @@ export class reactPlayerTracking {
|
|
|
99
99
|
debug("edit permutive progress event- " + this.playedSeconds);
|
|
100
100
|
this.permutiveTracker?.emitPermutiveProgressEvent(
|
|
101
101
|
this.duration,
|
|
102
|
-
this.playedSeconds
|
|
102
|
+
this.playedSeconds,
|
|
103
103
|
);
|
|
104
104
|
};
|
|
105
105
|
}
|
package/src/types.d.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
declare module "@financial-times/o-tracking";
|
|
2
2
|
declare module "@financial-times/o-viewport";
|
|
3
3
|
|
|
4
|
+
interface ConsentOptions {
|
|
5
|
+
opt_in?: boolean;
|
|
6
|
+
token?: string;
|
|
7
|
+
}
|
|
8
|
+
interface SourcepointCmpAPI {
|
|
9
|
+
queue?: Array<() => void>;
|
|
10
|
+
addEventListener?: (
|
|
11
|
+
eventName: string,
|
|
12
|
+
|
|
13
|
+
callback: (...args: any[]) => void,
|
|
14
|
+
) => void;
|
|
15
|
+
}
|
|
4
16
|
interface Window {
|
|
17
|
+
_sp_: SourcepointCmpAPI;
|
|
5
18
|
dataLayer: any;
|
|
6
19
|
gtag: any;
|
|
7
20
|
permutive: {
|
|
@@ -13,11 +26,6 @@ interface Window {
|
|
|
13
26
|
isOvideoPlayer?: boolean; //channels only
|
|
14
27
|
}
|
|
15
28
|
|
|
16
|
-
interface ConsentOptions {
|
|
17
|
-
opt_in?: boolean;
|
|
18
|
-
token?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
29
|
interface CallableConsent {
|
|
22
30
|
({}: ConsentOptions): void;
|
|
23
31
|
}
|
package/src/utils/attention.js
CHANGED
|
@@ -31,13 +31,13 @@ export class Attention {
|
|
|
31
31
|
//Add events for all the other Attention events
|
|
32
32
|
for (let i = 0; i < ATTENTION_EVENTS.length; i++) {
|
|
33
33
|
window.addEventListener(ATTENTION_EVENTS[i], (ev) =>
|
|
34
|
-
this.startAttention(ev)
|
|
34
|
+
this.startAttention(ev),
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
for (let i = 0; i < UNATTENTION_EVENTS.length; i++) {
|
|
39
39
|
window.addEventListener(UNATTENTION_EVENTS[i], (ev) =>
|
|
40
|
-
this.endAttention(ev)
|
|
40
|
+
this.endAttention(ev),
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -46,7 +46,7 @@ export class Attention {
|
|
|
46
46
|
document.body.addEventListener(
|
|
47
47
|
"oViewport.visibility",
|
|
48
48
|
(ev) => this.handleVisibilityChange(ev),
|
|
49
|
-
false
|
|
49
|
+
false,
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
this.addVideoEvents();
|
|
@@ -79,14 +79,14 @@ export class Attention {
|
|
|
79
79
|
}
|
|
80
80
|
this.attentionTimeout = setTimeout(
|
|
81
81
|
() => this.endAttention(),
|
|
82
|
-
ATTENTION_INTERVAL
|
|
82
|
+
ATTENTION_INTERVAL,
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
startConstantAttention() {
|
|
87
87
|
this.constantAttentionInterval = setInterval(
|
|
88
88
|
() => this.startAttention(),
|
|
89
|
-
ATTENTION_INTERVAL
|
|
89
|
+
ATTENTION_INTERVAL,
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -99,7 +99,7 @@ export class Attention {
|
|
|
99
99
|
if (this.startAttentionTime) {
|
|
100
100
|
this.endAttentionTime = new Date().getTime();
|
|
101
101
|
this.totalAttentionTime += Math.round(
|
|
102
|
-
(this.endAttentionTime - this.startAttentionTime) / 1000
|
|
102
|
+
(this.endAttentionTime - this.startAttentionTime) / 1000,
|
|
103
103
|
);
|
|
104
104
|
clearTimeout(this.attentionTimeout);
|
|
105
105
|
this.startAttentionTime = null;
|
|
@@ -117,13 +117,13 @@ export class Attention {
|
|
|
117
117
|
this.videoPlayers = document.getElementsByTagName("video");
|
|
118
118
|
for (let i = 0; i < this.videoPlayers.length; i++) {
|
|
119
119
|
this.videoPlayers[i].addEventListener("playing", (ev) =>
|
|
120
|
-
this.startConstantAttention(ev)
|
|
120
|
+
this.startConstantAttention(ev),
|
|
121
121
|
);
|
|
122
122
|
this.videoPlayers[i].addEventListener("pause", (ev) =>
|
|
123
|
-
this.endConstantAttention(ev)
|
|
123
|
+
this.endConstantAttention(ev),
|
|
124
124
|
);
|
|
125
125
|
this.videoPlayers[i].addEventListener("ended", (ev) =>
|
|
126
|
-
this.endConstantAttention(ev)
|
|
126
|
+
this.endConstantAttention(ev),
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
}
|
package/src/utils/getTrace.js
CHANGED
|
@@ -53,7 +53,7 @@ const getDomPathProps = (attrs, props) => {
|
|
|
53
53
|
// Collect any attribute that matches given strings.
|
|
54
54
|
attrs
|
|
55
55
|
.filter((attribute) =>
|
|
56
|
-
attribute.name.match(/^data-trackable|^data-o-|^aria-/i)
|
|
56
|
+
attribute.name.match(/^data-trackable|^data-o-|^aria-/i),
|
|
57
57
|
)
|
|
58
58
|
.forEach((attribute) => {
|
|
59
59
|
props[attribute.name] = attribute.value;
|
|
@@ -104,7 +104,7 @@ function getTrace(el) {
|
|
|
104
104
|
if (domPathProps["data-trackable"]) {
|
|
105
105
|
domPathProps = Object.assign(
|
|
106
106
|
domPathProps,
|
|
107
|
-
getSiblingsAndPosition(el, originalEl, selector)
|
|
107
|
+
getSiblingsAndPosition(el, originalEl, selector),
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
|
package/src/utils/scroll.ts
CHANGED
|
@@ -56,7 +56,7 @@ export class ScrollTracker {
|
|
|
56
56
|
|
|
57
57
|
intersectionCallback(
|
|
58
58
|
observer: IntersectionObserver,
|
|
59
|
-
changes: IntersectionObserverEntry[]
|
|
59
|
+
changes: IntersectionObserverEntry[],
|
|
60
60
|
) {
|
|
61
61
|
changes.forEach((change) => {
|
|
62
62
|
if (change.isIntersecting || change.intersectionRatio > 0) {
|
|
@@ -81,7 +81,7 @@ export class ScrollTracker {
|
|
|
81
81
|
this.gaTracker.GtagEventDispatcher(
|
|
82
82
|
"Scroll",
|
|
83
83
|
`scroll - ${percentagesViewed}%`,
|
|
84
|
-
window.location.pathname
|
|
84
|
+
window.location.pathname,
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -54,7 +54,7 @@ const configSchema = object({
|
|
|
54
54
|
.required()
|
|
55
55
|
.defined()
|
|
56
56
|
.transform((value) =>
|
|
57
|
-
unifyValuesTransform(value, disableAppFormatTransformValue)
|
|
57
|
+
unifyValuesTransform(value, disableAppFormatTransformValue),
|
|
58
58
|
)
|
|
59
59
|
.oneOf([
|
|
60
60
|
"Stream",
|
|
@@ -112,14 +112,14 @@ const origamiEventSchema = object({
|
|
|
112
112
|
.nullable()
|
|
113
113
|
.notRequired()
|
|
114
114
|
.when("category", {
|
|
115
|
-
is: "video" || "audio",
|
|
115
|
+
is: (value: string) => value === "video" || "audio",
|
|
116
116
|
then: () => number().required(),
|
|
117
117
|
}),
|
|
118
118
|
progress: number()
|
|
119
119
|
.nullable()
|
|
120
120
|
.notRequired()
|
|
121
121
|
.when("category", {
|
|
122
|
-
is: "video" || "audio",
|
|
122
|
+
is: (value: string) => value === "video" || "audio",
|
|
123
123
|
then: () => number().required(),
|
|
124
124
|
}),
|
|
125
125
|
});
|
|
@@ -129,7 +129,7 @@ export type OrigamiEventType = InferType<typeof origamiEventSchema>;
|
|
|
129
129
|
|
|
130
130
|
export const parseConfig = (
|
|
131
131
|
config: ConfigType,
|
|
132
|
-
disableAppFormatTransform: boolean
|
|
132
|
+
disableAppFormatTransform: boolean,
|
|
133
133
|
): ConfigType => {
|
|
134
134
|
disableAppFormatTransformValue = disableAppFormatTransform;
|
|
135
135
|
try {
|
|
@@ -155,7 +155,7 @@ export const parseConfig = (
|
|
|
155
155
|
export const validateConfig = (
|
|
156
156
|
config: ConfigType,
|
|
157
157
|
disableAppFormatTransform: boolean,
|
|
158
|
-
logValidationErrors: boolean
|
|
158
|
+
logValidationErrors: boolean,
|
|
159
159
|
): ValidationError[] | undefined => {
|
|
160
160
|
disableAppFormatTransformValue = disableAppFormatTransform;
|
|
161
161
|
try {
|
|
@@ -174,7 +174,7 @@ export const validateConfig = (
|
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
export const validateOrigamiEvent = (
|
|
177
|
-
config: OrigamiEventType
|
|
177
|
+
config: OrigamiEventType,
|
|
178
178
|
): ValidationError[] | undefined => {
|
|
179
179
|
try {
|
|
180
180
|
const parsedConfig = origamiEventSchema.cast(config);
|
|
@@ -17,7 +17,7 @@ export class ytIframeTracking {
|
|
|
17
17
|
this.videoUrl = videoUrl;
|
|
18
18
|
this.permutiveUtils = new permutiveVideoUtils(
|
|
19
19
|
this.FTTracker.config.campaign,
|
|
20
|
-
this.videoTitle
|
|
20
|
+
this.videoTitle,
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -27,13 +27,13 @@ export class ytIframeTracking {
|
|
|
27
27
|
emitProgressEvents = (
|
|
28
28
|
progress: number,
|
|
29
29
|
duration: number,
|
|
30
|
-
isYoutube: boolean
|
|
30
|
+
isYoutube: boolean,
|
|
31
31
|
) => {
|
|
32
32
|
while (progress >= this.progressMilestones[0]) {
|
|
33
33
|
this.FTTracker.gaEvent(
|
|
34
34
|
`Video${window.isOvideoPlayer ? ":fallback" : ""}`,
|
|
35
35
|
`${this.progressMilestones[0]}% watched`,
|
|
36
|
-
this.videoTitle
|
|
36
|
+
this.videoTitle,
|
|
37
37
|
);
|
|
38
38
|
if (isYoutube) {
|
|
39
39
|
this.FTTracker.oEvent({
|
|
@@ -103,7 +103,7 @@ export class ytIframeTracking {
|
|
|
103
103
|
this.permutiveUtils.emitPermutiveProgressEvent(
|
|
104
104
|
duration - 1,
|
|
105
105
|
currentTime,
|
|
106
|
-
this.videoProgressInterval
|
|
106
|
+
this.videoProgressInterval,
|
|
107
107
|
);
|
|
108
108
|
const progress = this.progressPercentage(duration, currentTime);
|
|
109
109
|
this.emitProgressEvents(progress, duration, true);
|
|
@@ -115,7 +115,7 @@ export class ytIframeTracking {
|
|
|
115
115
|
this.FTTracker.gaEvent(
|
|
116
116
|
`Video${window.isOvideoPlayer ? ":fallback" : ""}`,
|
|
117
117
|
`playing`,
|
|
118
|
-
this.videoTitle
|
|
118
|
+
this.videoTitle,
|
|
119
119
|
);
|
|
120
120
|
this.FTTracker.oEvent({
|
|
121
121
|
category: "video",
|
|
@@ -131,7 +131,7 @@ export class ytIframeTracking {
|
|
|
131
131
|
this.FTTracker.gaEvent(
|
|
132
132
|
`Video${window.isOvideoPlayer ? ":fallback" : ""}`,
|
|
133
133
|
`pause`,
|
|
134
|
-
`${this.videoTitle}
|
|
134
|
+
`${this.videoTitle}`,
|
|
135
135
|
);
|
|
136
136
|
this.FTTracker.oEvent({
|
|
137
137
|
category: "video",
|
|
@@ -146,7 +146,7 @@ export class ytIframeTracking {
|
|
|
146
146
|
this.FTTracker.gaEvent(
|
|
147
147
|
`Video${window.isOvideoPlayer ? ":fallback" : ""}`,
|
|
148
148
|
`100% watched`,
|
|
149
|
-
`${this.videoTitle}
|
|
149
|
+
`${this.videoTitle}`,
|
|
150
150
|
);
|
|
151
151
|
this.FTTracker.oEvent({
|
|
152
152
|
category: "video",
|
|
@@ -159,7 +159,7 @@ export class ytIframeTracking {
|
|
|
159
159
|
this.permutiveUtils.emitPermutiveProgressEvent(
|
|
160
160
|
currentTime,
|
|
161
161
|
currentTime,
|
|
162
|
-
this.videoProgressInterval
|
|
162
|
+
this.videoProgressInterval,
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
@@ -172,7 +172,7 @@ export class ytIframeTracking {
|
|
|
172
172
|
this.permutiveUtils.emitPermutiveProgressEvent(
|
|
173
173
|
duration - 1,
|
|
174
174
|
currentTime,
|
|
175
|
-
this.videoProgressInterval
|
|
175
|
+
this.videoProgressInterval,
|
|
176
176
|
);
|
|
177
177
|
}, 1000);
|
|
178
178
|
const progress = this.progressPercentage(duration, currentTime);
|
package/test/index.test.tsx
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as cmpLoader from "../src/cmp/loadFtCmp";
|
|
6
|
+
import { ConsentMonitor, consentMonitor } from "../src/consentMonitor";
|
|
2
7
|
import { permutiveVideoUtils } from "../src/permutiveVideoUtils";
|
|
3
8
|
|
|
4
9
|
let windowSpy: jest.SpyInstance;
|
|
5
10
|
|
|
11
|
+
jest.spyOn(cmpLoader, "loadFtCmpScript").mockResolvedValue(undefined);
|
|
12
|
+
|
|
6
13
|
beforeEach(() => {
|
|
7
14
|
windowSpy = jest.spyOn(window, "window", "get");
|
|
8
15
|
});
|
|
@@ -30,23 +37,17 @@ describe("consentMonitor", () => {
|
|
|
30
37
|
expect(obj.isDevEnvironment).toBe(true);
|
|
31
38
|
});
|
|
32
39
|
|
|
33
|
-
it("Initializes correctly", async () => {
|
|
34
|
-
|
|
35
|
-
windowSpy.mockImplementation(() => ({
|
|
36
|
-
permutive: {
|
|
37
|
-
consent: ({}) => {
|
|
38
|
-
return {};
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
}));
|
|
42
|
-
const obj = new consentMonitor("https://FT-staging.devhost.com", [
|
|
40
|
+
it("Initializes correctly and sets the isInitialized flag", async () => {
|
|
41
|
+
const obj = new ConsentMonitor("https://FT-staging.devhost.com", [
|
|
43
42
|
"localhost",
|
|
44
|
-
"vercel.app",
|
|
45
43
|
"phq",
|
|
46
44
|
"devhost",
|
|
47
45
|
]);
|
|
46
|
+
|
|
47
|
+
await new Promise(process.nextTick);
|
|
48
48
|
expect(obj.isInitialized).toBe(true);
|
|
49
49
|
});
|
|
50
|
+
|
|
50
51
|
//TODO - mock document.cookie get/set and test consent value
|
|
51
52
|
describe("Permutive video progress utils", () => {
|
|
52
53
|
it("can be initiated", () => {
|