cdp-edge 2.3.9 → 2.5.2
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/README.md +54 -4
- package/bin/cdp-edge.js +3 -2
- package/contracts/agent-versions.json +383 -83
- package/dist/commands/validate.js +248 -84
- package/dist/sdk/cdpTrack.js +2095 -0
- package/dist/sdk/cdpTrack.min.js +64 -0
- package/dist/sdk/install-snippet.html +10 -0
- package/extracted-skill/tracking-events-generator/agents/ab-ltv-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/ab-testing-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +18 -18
- package/extracted-skill/tracking-events-generator/agents/bidding-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/bing-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/code-guardian-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +5 -5
- package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +10 -10
- package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/database-agent.md +17 -25
- package/extracted-skill/tracking-events-generator/agents/debug-agent.md +9 -9
- package/extracted-skill/tracking-events-generator/agents/devops-agent.md +18 -1
- package/extracted-skill/tracking-events-generator/agents/domain-setup-agent.md +5 -5
- package/extracted-skill/tracking-events-generator/agents/email-agent.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +4 -4
- package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +2 -0
- package/extracted-skill/tracking-events-generator/agents/google-agent.md +2 -2
- package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +23 -29
- package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/localization-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +16 -16
- package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +13 -13
- package/extracted-skill/tracking-events-generator/agents/memory-agent.md +14 -14
- package/extracted-skill/tracking-events-generator/agents/meta-agent.md +2 -2
- package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +9 -9
- package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +1 -0
- package/extracted-skill/tracking-events-generator/agents/performance-agent.md +12 -12
- package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +7 -7
- package/extracted-skill/tracking-events-generator/agents/pinterest-agent.md +6 -6
- package/extracted-skill/tracking-events-generator/agents/premium-tracking-intelligence-agent.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/r2-setup-agent.md +8 -8
- package/extracted-skill/tracking-events-generator/agents/reddit-agent.md +7 -7
- package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +21 -21
- package/extracted-skill/tracking-events-generator/agents/server-tracking.md +15 -15
- package/extracted-skill/tracking-events-generator/agents/spotify-agent.md +6 -6
- package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +5 -5
- package/extracted-skill/tracking-events-generator/agents/validator-agent.md +9 -9
- package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +4 -4
- package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +9 -9
- package/extracted-skill/tracking-events-generator/anti-blocking.js +1 -1
- package/extracted-skill/tracking-events-generator/cdpTrack.js +0 -10
- package/extracted-skill/tracking-events-generator/engagement-scoring.js +2 -2
- package/extracted-skill/tracking-events-generator/micro-events.js +1 -1
- package/extracted-skill/tracking-events-generator/tracking.config.js +3 -3
- package/package.json +5 -1
- package/scripts/build-sdk.js +106 -0
- package/server-edge-tracker/index.ts +93 -0
- package/server-edge-tracker/schema-utm.sql +5 -3
- package/server-edge-tracker/wrangler.toml +1 -1
- package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +0 -10
|
@@ -0,0 +1,2095 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* CDP Edge SDK v2.5.1
|
|
3
|
+
* (c) 2026 CDP Edge — Quantum Tracking
|
|
4
|
+
* Gerado em: 2026-04-15T01:32:42.616Z
|
|
5
|
+
* Endpoint padrão: /track (mesmo domínio — anti-adblock)
|
|
6
|
+
*/
|
|
7
|
+
"use strict";
|
|
8
|
+
var cdpTrack = (() => {
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// models/scenarios/behavior-engine.js
|
|
31
|
+
var behavior_engine_exports = {};
|
|
32
|
+
__export(behavior_engine_exports, {
|
|
33
|
+
default: () => behavior_engine_default
|
|
34
|
+
});
|
|
35
|
+
var BehaviorEngine, behavior_engine_default;
|
|
36
|
+
var init_behavior_engine = __esm({
|
|
37
|
+
"models/scenarios/behavior-engine.js"() {
|
|
38
|
+
"use strict";
|
|
39
|
+
BehaviorEngine = {
|
|
40
|
+
config: {
|
|
41
|
+
rageClickThreshold: 3,
|
|
42
|
+
rageClickTime: 700,
|
|
43
|
+
// ms
|
|
44
|
+
idleThreshold: 6e4,
|
|
45
|
+
// 60s
|
|
46
|
+
scoreThresholds: {
|
|
47
|
+
engaged: 40,
|
|
48
|
+
highIntent: 80
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
state: {
|
|
52
|
+
clickHistory: [],
|
|
53
|
+
startTime: Date.now(),
|
|
54
|
+
lastActivity: Date.now(),
|
|
55
|
+
lastPulse: Date.now(),
|
|
56
|
+
isIdle: false,
|
|
57
|
+
formStartTime: {},
|
|
58
|
+
userScore: 0,
|
|
59
|
+
firedScoreEvents: /* @__PURE__ */ new Set(),
|
|
60
|
+
abVariant: null
|
|
61
|
+
},
|
|
62
|
+
init() {
|
|
63
|
+
this.setupRageClicks();
|
|
64
|
+
this.setupVisibility();
|
|
65
|
+
this.setupHeartbeat();
|
|
66
|
+
this.setupScroll();
|
|
67
|
+
this.setupVideoTracking();
|
|
68
|
+
this.setupFormAnalytics();
|
|
69
|
+
this.setupFormAbandonment();
|
|
70
|
+
this.setupCopyPaste();
|
|
71
|
+
this.setupExitIntent();
|
|
72
|
+
this.setupOutboundLinks();
|
|
73
|
+
this.setupErrorTracking();
|
|
74
|
+
this.setupIdleDetection();
|
|
75
|
+
this.setupScoring();
|
|
76
|
+
this.setupABTesting();
|
|
77
|
+
console.log("[CDP Edge] Enterprise Behavior Engine Initialized");
|
|
78
|
+
},
|
|
79
|
+
// 0.1 A/B Testing (Mode 1 - Edge Sync)
|
|
80
|
+
setupABTesting() {
|
|
81
|
+
const getCookie = (name) => {
|
|
82
|
+
const value = `; ${document.cookie}`;
|
|
83
|
+
const parts = value.split(`; ${name}=`);
|
|
84
|
+
if (parts.length === 2) return parts.pop().split(";").shift();
|
|
85
|
+
};
|
|
86
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
87
|
+
this.state.abVariant = urlParams.get("cdp_variant") || getCookie("cdp_ab_variant") || "original";
|
|
88
|
+
console.log(`[CDP Edge] A/B Variant Detected: ${this.state.abVariant}`);
|
|
89
|
+
const originalTrack = cdpTrack.track;
|
|
90
|
+
cdpTrack.track = (eventName, eventParams = {}) => {
|
|
91
|
+
const enrichedParams = {
|
|
92
|
+
...eventParams,
|
|
93
|
+
ab_test_variant: this.state.abVariant,
|
|
94
|
+
user_score: this.state.userScore
|
|
95
|
+
};
|
|
96
|
+
return originalTrack.apply(cdpTrack, [eventName, enrichedParams]);
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
// 0. Scoring Engine (Internal)
|
|
100
|
+
setupScoring() {
|
|
101
|
+
console.log("[CDP Edge] Scoring Engine Online");
|
|
102
|
+
},
|
|
103
|
+
addScore(points, reason) {
|
|
104
|
+
this.state.userScore = Math.min(100, Math.max(0, this.state.userScore + points));
|
|
105
|
+
console.log(`[CDP Edge] Score Update: +${points} (${reason}) | Total: ${this.state.userScore}`);
|
|
106
|
+
if (this.state.userScore >= this.config.scoreThresholds.highIntent && !this.state.firedScoreEvents.has("highIntent")) {
|
|
107
|
+
this.state.firedScoreEvents.add("highIntent");
|
|
108
|
+
cdpTrack.track("High_Intent_Lead", { score: this.state.userScore, meta_intensity: "high" });
|
|
109
|
+
} else if (this.state.userScore >= this.config.scoreThresholds.engaged && !this.state.firedScoreEvents.has("engaged")) {
|
|
110
|
+
this.state.firedScoreEvents.add("engaged");
|
|
111
|
+
cdpTrack.track("Engaged_User", { score: this.state.userScore, meta_intensity: "medium" });
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
// 1. Rage Click Detector
|
|
115
|
+
setupRageClicks() {
|
|
116
|
+
document.addEventListener("click", (e) => {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
this.state.clickHistory.push(now);
|
|
119
|
+
this.state.clickHistory = this.state.clickHistory.filter((t) => now - t < this.config.rageClickTime);
|
|
120
|
+
if (this.state.clickHistory.length >= this.config.rageClickThreshold) {
|
|
121
|
+
cdpTrack.track("rage_click", {
|
|
122
|
+
element_id: e.target.id || "",
|
|
123
|
+
element_class: e.target.className || "",
|
|
124
|
+
x: e.pageX,
|
|
125
|
+
y: e.pageY,
|
|
126
|
+
meta_intensity: "low"
|
|
127
|
+
// Envia sinal leve para Meta
|
|
128
|
+
});
|
|
129
|
+
this.addScore(-10, "rage_click");
|
|
130
|
+
this.state.clickHistory = [];
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
// 1.2 Scroll Depth (Quantum Tier)
|
|
135
|
+
setupScroll() {
|
|
136
|
+
const markers = [25, 50, 75, 90];
|
|
137
|
+
const fired = /* @__PURE__ */ new Set();
|
|
138
|
+
window.addEventListener("scroll", () => {
|
|
139
|
+
const scrollPct = Math.round(window.scrollY / (document.documentElement.scrollHeight - window.innerHeight) * 100);
|
|
140
|
+
markers.forEach((m) => {
|
|
141
|
+
if (scrollPct >= m && !fired.has(m)) {
|
|
142
|
+
fired.add(m);
|
|
143
|
+
this.addScore(m === 25 ? 5 : m === 50 ? 5 : 10, `scroll_${m}%`);
|
|
144
|
+
cdpTrack.track("scroll_depth", {
|
|
145
|
+
percent: m,
|
|
146
|
+
meta_intensity: m >= 50 ? "medium" : "low"
|
|
147
|
+
// Envia como sinal mais forte após 50%
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}, { passive: true });
|
|
152
|
+
},
|
|
153
|
+
// 2. Tab Visibility (VSL Focus)
|
|
154
|
+
setupVisibility() {
|
|
155
|
+
document.addEventListener("visibilitychange", () => {
|
|
156
|
+
const status = document.visibilityState;
|
|
157
|
+
cdpTrack.track("tab_visibility_change", {
|
|
158
|
+
status,
|
|
159
|
+
time_since_start: Math.floor((Date.now() - this.state.startTime) / 1e3),
|
|
160
|
+
meta_intensity: status === "visible" ? "high" : "low"
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
// 3. Click Heatmap (D1 Only)
|
|
165
|
+
setupHeatmap() {
|
|
166
|
+
document.addEventListener("click", (e) => {
|
|
167
|
+
if (typeof cdpTrack.sendServerEvent === "function") {
|
|
168
|
+
cdpTrack.sendServerEvent("click_heatmap", null, {
|
|
169
|
+
x: e.pageX,
|
|
170
|
+
y: e.pageY,
|
|
171
|
+
element: e.target.tagName.toLowerCase(),
|
|
172
|
+
id: e.target.id || "",
|
|
173
|
+
classes: e.target.className || ""
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
// 4. Retention Pulse (Heartbeat)
|
|
179
|
+
setupHeartbeat() {
|
|
180
|
+
setInterval(() => {
|
|
181
|
+
const activeTime = Math.floor((Date.now() - this.state.startTime) / 1e3);
|
|
182
|
+
cdpTrack.track("pulse_heartbeat", {
|
|
183
|
+
duration_seconds: activeTime,
|
|
184
|
+
is_visible: document.visibilityState === "visible"
|
|
185
|
+
});
|
|
186
|
+
}, this.config.heartbeatInterval);
|
|
187
|
+
},
|
|
188
|
+
// 5. VSL / Video Tracking (YouTube & Vimeo)
|
|
189
|
+
setupVideoTracking() {
|
|
190
|
+
const iframes = document.querySelectorAll("iframe");
|
|
191
|
+
const fired = /* @__PURE__ */ new Set();
|
|
192
|
+
iframes.forEach((iframe) => {
|
|
193
|
+
const src = iframe.src || "";
|
|
194
|
+
const isYT = src.includes("youtube.com/embed");
|
|
195
|
+
const isVimeo = src.includes("vimeo.com/video");
|
|
196
|
+
if (isYT || isVimeo) {
|
|
197
|
+
window.addEventListener("message", (event) => {
|
|
198
|
+
try {
|
|
199
|
+
const data = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
|
|
200
|
+
if (isYT && data.event === "infoDelivery" && data.info && data.info.currentTime) {
|
|
201
|
+
this.handleVideoProgress(iframe, data.info.currentTime, data.info.duration, fired, "YouTube");
|
|
202
|
+
}
|
|
203
|
+
if (isVimeo && data.event === "timeupdate") {
|
|
204
|
+
this.handleVideoProgress(iframe, data.data.seconds, data.data.duration, fired, "Vimeo");
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
if (isYT && !src.includes("enablejsapi=1")) {
|
|
210
|
+
const url = new URL(src);
|
|
211
|
+
url.searchParams.set("enablejsapi", "1");
|
|
212
|
+
iframe.src = url.toString();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
handleVideoProgress(iframe, current, total, firedSet, platform) {
|
|
218
|
+
if (!total) return;
|
|
219
|
+
const pct = Math.floor(current / total * 100);
|
|
220
|
+
const milestones = [25, 50, 75, 100];
|
|
221
|
+
const videoId = iframe.id || iframe.src.split("/").pop().split("?")[0];
|
|
222
|
+
milestones.forEach((m) => {
|
|
223
|
+
const key = `${videoId}_${m}`;
|
|
224
|
+
if (pct >= m && !firedSet.has(key)) {
|
|
225
|
+
firedSet.add(key);
|
|
226
|
+
this.addScore(m === 25 ? 10 : m === 50 ? 15 : 25, `vsl_${m}%`);
|
|
227
|
+
cdpTrack.track("video_milestone", {
|
|
228
|
+
video_id: videoId,
|
|
229
|
+
platform,
|
|
230
|
+
percent: m,
|
|
231
|
+
meta_intensity: m >= 50 ? "high" : "medium"
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
// 6. Form Analytics (Friction & Abandonment)
|
|
237
|
+
setupFormAnalytics() {
|
|
238
|
+
const inputs = document.querySelectorAll("input, select, textarea");
|
|
239
|
+
inputs.forEach((input) => {
|
|
240
|
+
input.addEventListener("focus", () => {
|
|
241
|
+
const name = input.name || input.id;
|
|
242
|
+
if (!this.state.formStartTime[name]) {
|
|
243
|
+
this.state.formStartTime[name] = Date.now();
|
|
244
|
+
this.addScore(10, `form_interaction_${name}`);
|
|
245
|
+
cdpTrack.track("form_field_focus", { field: name });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
input.addEventListener("blur", () => {
|
|
249
|
+
const name = input.name || input.id;
|
|
250
|
+
const duration = Math.round((Date.now() - this.state.formStartTime[name]) / 1e3);
|
|
251
|
+
if (duration > 0) {
|
|
252
|
+
cdpTrack.track("form_field_blur", { field: name, duration_sec: duration });
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
// 6.1 Form Abandonment — saiu da página após interagir com formulário sem submeter
|
|
258
|
+
setupFormAbandonment() {
|
|
259
|
+
const formInteracted = /* @__PURE__ */ new Set();
|
|
260
|
+
let formSubmitted = false;
|
|
261
|
+
document.addEventListener("focusin", (e) => {
|
|
262
|
+
const el = e.target;
|
|
263
|
+
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT") {
|
|
264
|
+
const name = el.name || el.id || el.type;
|
|
265
|
+
if (name) formInteracted.add(name);
|
|
266
|
+
}
|
|
267
|
+
}, true);
|
|
268
|
+
document.addEventListener("submit", () => {
|
|
269
|
+
formSubmitted = true;
|
|
270
|
+
}, true);
|
|
271
|
+
document.addEventListener("visibilitychange", () => {
|
|
272
|
+
if (document.visibilityState === "hidden" && formInteracted.size > 0 && !formSubmitted) {
|
|
273
|
+
this.addScore(-5, "form_abandonment");
|
|
274
|
+
cdpTrack.track("form_abandonment", {
|
|
275
|
+
fields_interacted: Array.from(formInteracted),
|
|
276
|
+
fields_count: formInteracted.size,
|
|
277
|
+
meta_intensity: "medium",
|
|
278
|
+
time_on_page: Math.floor((Date.now() - this.state.startTime) / 1e3)
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
window.addEventListener("beforeunload", () => {
|
|
283
|
+
if (formInteracted.size > 0 && !formSubmitted) {
|
|
284
|
+
if (navigator.sendBeacon && typeof cdpTrack !== "undefined") {
|
|
285
|
+
const data = JSON.stringify({
|
|
286
|
+
eventName: "form_abandonment",
|
|
287
|
+
behavioral_data: { user_score: this.state.userScore },
|
|
288
|
+
fields_interacted: Array.from(formInteracted),
|
|
289
|
+
fields_count: formInteracted.size,
|
|
290
|
+
meta_intensity: "medium"
|
|
291
|
+
});
|
|
292
|
+
navigator.sendBeacon("/api/tracking", new Blob([data], { type: "application/json" }));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
// 6.2 Exit Intent — mouse saindo pelo topo da viewport
|
|
298
|
+
setupExitIntent() {
|
|
299
|
+
let exitFired = false;
|
|
300
|
+
document.addEventListener("mousemove", (e) => {
|
|
301
|
+
if (e.clientY < 20 && !exitFired) {
|
|
302
|
+
exitFired = true;
|
|
303
|
+
this.addScore(15, "exit_intent");
|
|
304
|
+
cdpTrack.track("exit_intent", {
|
|
305
|
+
mouse_y: e.clientY,
|
|
306
|
+
time_on_page: Math.floor((Date.now() - this.state.startTime) / 1e3),
|
|
307
|
+
user_score: this.state.userScore,
|
|
308
|
+
meta_intensity: "high"
|
|
309
|
+
// Alta intenção — usuário está prestes a sair
|
|
310
|
+
});
|
|
311
|
+
setTimeout(() => {
|
|
312
|
+
exitFired = false;
|
|
313
|
+
}, 3e4);
|
|
314
|
+
}
|
|
315
|
+
}, { passive: true });
|
|
316
|
+
let mobileExitFired = false;
|
|
317
|
+
document.addEventListener("visibilitychange", () => {
|
|
318
|
+
if (document.visibilityState === "hidden" && !mobileExitFired) {
|
|
319
|
+
const timeOnPage = Math.floor((Date.now() - this.state.startTime) / 1e3);
|
|
320
|
+
if (timeOnPage > 5) {
|
|
321
|
+
mobileExitFired = true;
|
|
322
|
+
cdpTrack.track("exit_intent_mobile", {
|
|
323
|
+
time_on_page: timeOnPage,
|
|
324
|
+
user_score: this.state.userScore,
|
|
325
|
+
meta_intensity: "medium"
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
// 7. Copy-Paste Intent (High Conv.)
|
|
332
|
+
setupCopyPaste() {
|
|
333
|
+
document.addEventListener("copy", () => {
|
|
334
|
+
const selectedText = window.getSelection().toString();
|
|
335
|
+
if (selectedText.length > 0) {
|
|
336
|
+
this.addScore(20, "text_copy");
|
|
337
|
+
cdpTrack.track("content_copy", {
|
|
338
|
+
text_length: selectedText.length,
|
|
339
|
+
meta_intensity: "medium"
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
},
|
|
344
|
+
// 8. Outbound Link Tracking (Whitelist: WhatsApp/Checkouts)
|
|
345
|
+
setupOutboundLinks() {
|
|
346
|
+
document.addEventListener("click", (e) => {
|
|
347
|
+
const link = e.target.closest("a");
|
|
348
|
+
if (link && link.href) {
|
|
349
|
+
const url = link.href;
|
|
350
|
+
const isExternal = !url.includes(window.location.hostname);
|
|
351
|
+
const isWhitelisted = url.includes("wa.me") || url.includes("hotmart.com") || url.includes("kiwify.com") || url.includes("checkout");
|
|
352
|
+
if (isExternal && isWhitelisted) {
|
|
353
|
+
this.addScore(100, "outbound_conversion_intent");
|
|
354
|
+
cdpTrack.track("outbound_click", {
|
|
355
|
+
destination: url,
|
|
356
|
+
meta_intensity: "high"
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
},
|
|
362
|
+
// 9. JS Error Tracking (D1 Only)
|
|
363
|
+
setupErrorTracking() {
|
|
364
|
+
window.addEventListener("error", (event) => {
|
|
365
|
+
if (typeof cdpTrack.sendServerEvent === "function") {
|
|
366
|
+
cdpTrack.sendServerEvent("js_error", null, {
|
|
367
|
+
message: event.message,
|
|
368
|
+
source: event.filename,
|
|
369
|
+
lineno: event.lineno,
|
|
370
|
+
colno: event.colno
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
// 10. Idle Detection (Active Time)
|
|
376
|
+
setupIdleDetection() {
|
|
377
|
+
const activityEvents = ["mousedown", "mousemove", "keydown", "scroll", "touchstart"];
|
|
378
|
+
activityEvents.forEach((evt) => {
|
|
379
|
+
document.addEventListener(evt, () => {
|
|
380
|
+
this.state.lastActivity = Date.now();
|
|
381
|
+
if (this.state.isIdle) {
|
|
382
|
+
this.state.isIdle = false;
|
|
383
|
+
cdpTrack.track("user_active", { status: "back_online" });
|
|
384
|
+
}
|
|
385
|
+
}, { passive: true });
|
|
386
|
+
});
|
|
387
|
+
setInterval(() => {
|
|
388
|
+
if (Date.now() - this.state.lastActivity > this.config.idleThreshold && !this.state.isIdle) {
|
|
389
|
+
this.state.isIdle = true;
|
|
390
|
+
cdpTrack.track("user_idle", { idle_duration: this.config.idleThreshold / 1e3 });
|
|
391
|
+
}
|
|
392
|
+
}, 1e4);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
if (typeof cdpTrack !== "undefined") {
|
|
396
|
+
BehaviorEngine.init();
|
|
397
|
+
}
|
|
398
|
+
behavior_engine_default = BehaviorEngine;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// cdpTrack.js
|
|
403
|
+
var cdpTrack_exports = {};
|
|
404
|
+
__export(cdpTrack_exports, {
|
|
405
|
+
generateId: () => generateId,
|
|
406
|
+
getSessionId: () => getSessionId,
|
|
407
|
+
getUTMs: () => getUTMs,
|
|
408
|
+
getUTMsWithFallback: () => getUTMsWithFallback,
|
|
409
|
+
getUserId: () => getUserId,
|
|
410
|
+
getUserIdWithFallback: () => getUserIdWithFallback,
|
|
411
|
+
init: () => init,
|
|
412
|
+
passCheckoutParams: () => passCheckoutParams,
|
|
413
|
+
setupAutoFormCapture: () => setupAutoFormCapture,
|
|
414
|
+
track: () => track,
|
|
415
|
+
trackAddToCart: () => trackAddToCart,
|
|
416
|
+
trackLead: () => trackLead,
|
|
417
|
+
trackPurchase: () => trackPurchase,
|
|
418
|
+
trackViewContent: () => trackViewContent,
|
|
419
|
+
updateConsent: () => updateConsent
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// tracking.config.js
|
|
423
|
+
var CONFIG = {
|
|
424
|
+
// ── Endpoint do Cloudflare Worker ────────────────────────────────────────────
|
|
425
|
+
// Same-domain: evita ad-blockers e CORS.
|
|
426
|
+
// Em produção: seu-dominio.com.br/track (Worker roteado via Custom Domain)
|
|
427
|
+
// Em dev: use o URL do Worker diretamente (ex: https://worker.seu-dominio.workers.dev/track)
|
|
428
|
+
endpoint: "/track",
|
|
429
|
+
// ── Plataformas de checkout habilitadas ──────────────────────────────────────
|
|
430
|
+
// Controla quais plataformas recebem passCheckoutParams() automaticamente.
|
|
431
|
+
// Adicione apenas as plataformas usadas no projeto.
|
|
432
|
+
platforms: ["hotmart", "kiwify", "eduzz", "monetizze", "cartpanda", "ticto"],
|
|
433
|
+
// ── Domínio do site ──────────────────────────────────────────────────────────
|
|
434
|
+
// Usado para cookies first-party e CORS.
|
|
435
|
+
siteDomain: "",
|
|
436
|
+
// Ex: 'meusite.com.br' — deixar vazio para auto-detect
|
|
437
|
+
// ── Debug mode ───────────────────────────────────────────────────────────────
|
|
438
|
+
// true = loga todos os eventos no console
|
|
439
|
+
// false = silencioso em produção
|
|
440
|
+
debug: false,
|
|
441
|
+
// ── Consent Mode v2 (LGPD/GDPR) ─────────────────────────────────────────────
|
|
442
|
+
// Padrão: tudo negado. Sites devem chamar cdpTrack.updateConsent() após
|
|
443
|
+
// o usuário aceitar o banner de cookies.
|
|
444
|
+
consent: {
|
|
445
|
+
defaultDenied: true,
|
|
446
|
+
// inicializa com ad_storage=denied
|
|
447
|
+
urlPassthrough: true,
|
|
448
|
+
// preserva gclid/fbclid mesmo sem consent
|
|
449
|
+
waitForUpdate: 500
|
|
450
|
+
// ms aguardando CMP antes de disparar hits
|
|
451
|
+
},
|
|
452
|
+
// ── Comportamentos automáticos ───────────────────────────────────────────────
|
|
453
|
+
autoCaptureForms: true,
|
|
454
|
+
// intercepta formulários de lead automaticamente
|
|
455
|
+
passCheckoutParams: true,
|
|
456
|
+
// adiciona UTMs + userId nos links de checkout
|
|
457
|
+
initBehaviorEngine: true
|
|
458
|
+
// carrega rage-click, exit intent, form abandonment
|
|
459
|
+
};
|
|
460
|
+
var tracking_config_default = CONFIG;
|
|
461
|
+
|
|
462
|
+
// micro-events.js
|
|
463
|
+
var isBrowser = typeof window !== "undefined";
|
|
464
|
+
var SCROLL_CONFIG = {
|
|
465
|
+
thresholds: [25, 50, 75, 100],
|
|
466
|
+
signalStrength: {
|
|
467
|
+
"25%": 1.5,
|
|
468
|
+
"50%": 2,
|
|
469
|
+
"75%": 3,
|
|
470
|
+
"100%": 4
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
var TIME_CONFIG = {
|
|
474
|
+
thresholds: {
|
|
475
|
+
curioso: 1e4,
|
|
476
|
+
// 10 segundos
|
|
477
|
+
interessado: 6e4,
|
|
478
|
+
// 60 segundos
|
|
479
|
+
comprador: 18e4,
|
|
480
|
+
// 180 segundos (3 minutos)
|
|
481
|
+
profundao: 60
|
|
482
|
+
},
|
|
483
|
+
signalStrength: {
|
|
484
|
+
curioso: 1,
|
|
485
|
+
interessado: 2.5,
|
|
486
|
+
comprador: 4,
|
|
487
|
+
profundao: 3.5
|
|
488
|
+
},
|
|
489
|
+
intentionLevels: {
|
|
490
|
+
curioso: "curioso",
|
|
491
|
+
interessado: "interessado",
|
|
492
|
+
comprador: "comprador",
|
|
493
|
+
profundao: "profundo"
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
var VIDEO_CONFIG = {
|
|
497
|
+
progressThresholds: [25, 50, 75, 100],
|
|
498
|
+
// Dropout: captura o segundo exato em que o usuário parou de assistir
|
|
499
|
+
dropoutEnabled: true,
|
|
500
|
+
signalStrength: {
|
|
501
|
+
play: 2,
|
|
502
|
+
progress25: 3,
|
|
503
|
+
progress50: 4,
|
|
504
|
+
progress75: 5,
|
|
505
|
+
complete: 5,
|
|
506
|
+
pause: 1,
|
|
507
|
+
resume: 1.5,
|
|
508
|
+
seek: 0.5,
|
|
509
|
+
dropout: 2.5
|
|
510
|
+
// pausa final — não retomou até sair da página
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var CLICK_CONFIG = {
|
|
514
|
+
maxRapidClicks: 3,
|
|
515
|
+
rapidClickWindow: 1e3,
|
|
516
|
+
// 1 segundo
|
|
517
|
+
clickCategories: ["button", "link", "input", "cta", "price", "generic"],
|
|
518
|
+
signalStrength: {
|
|
519
|
+
button: 2,
|
|
520
|
+
link: 1.5,
|
|
521
|
+
cta: 2.5,
|
|
522
|
+
input: 1,
|
|
523
|
+
price: 2,
|
|
524
|
+
generic: 0.5
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
var CTA_HOVER_CONFIG = {
|
|
528
|
+
minHoverTimeForSignal: 3e3,
|
|
529
|
+
// 3 segundos
|
|
530
|
+
maxHoverTime: 3e4,
|
|
531
|
+
// 30 segundos (para evitar overflow)
|
|
532
|
+
signalStrength: 3
|
|
533
|
+
};
|
|
534
|
+
var state = {
|
|
535
|
+
pageLoadTime: Date.now(),
|
|
536
|
+
scrollTracking: {
|
|
537
|
+
lastScrollPercent: 0,
|
|
538
|
+
trackedThresholds: /* @__PURE__ */ new Set()
|
|
539
|
+
},
|
|
540
|
+
timeTracking: {
|
|
541
|
+
lastCheckTime: Date.now(),
|
|
542
|
+
currentLevel: "curioso",
|
|
543
|
+
timeOnPage: 0
|
|
544
|
+
},
|
|
545
|
+
// videoTracking: { [videoId]: { playTime, totalWatchTime, lastProgress, started, paused, dropoutPercent, dropoutSecond } }
|
|
546
|
+
videoTracking: {},
|
|
547
|
+
// Resumo agregado de todos os vídeos na página (para páginas VSL com múltiplos vídeos)
|
|
548
|
+
videoSummary: {
|
|
549
|
+
totalVideos: 0,
|
|
550
|
+
startedCount: 0,
|
|
551
|
+
completedCount: 0,
|
|
552
|
+
maxProgress: 0
|
|
553
|
+
// maior % atingida entre todos os vídeos
|
|
554
|
+
},
|
|
555
|
+
clickTracking: {
|
|
556
|
+
clicks: [],
|
|
557
|
+
lastClickTime: 0,
|
|
558
|
+
rapidClickDetected: false
|
|
559
|
+
},
|
|
560
|
+
ctaHoverTracking: {
|
|
561
|
+
hoverStartTime: null,
|
|
562
|
+
hoveredElement: null
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
function initScrollTracking() {
|
|
566
|
+
if (!isBrowser) return;
|
|
567
|
+
const observer = new IntersectionObserver((entries) => {
|
|
568
|
+
entries.forEach((entry) => {
|
|
569
|
+
if (!entry.isIntersecting) return;
|
|
570
|
+
const scrollPercent = Math.round(
|
|
571
|
+
(window.scrollY + window.innerHeight) / document.body.scrollHeight * 100
|
|
572
|
+
);
|
|
573
|
+
SCROLL_CONFIG.thresholds.forEach((threshold) => {
|
|
574
|
+
const key = `scroll_${threshold}`;
|
|
575
|
+
if (scrollPercent >= threshold && !state.scrollTracking.trackedThresholds.has(key)) {
|
|
576
|
+
const signal = SCROLL_CONFIG.signalStrength[`${threshold}%`] || 0;
|
|
577
|
+
dispatchEvent("Scroll", {
|
|
578
|
+
scroll_depth: threshold,
|
|
579
|
+
scroll_percent: scrollPercent,
|
|
580
|
+
signal_strength: signal,
|
|
581
|
+
engagement_type: "scroll",
|
|
582
|
+
time_on_page: Math.round((Date.now() - state.pageLoadTime) / 1e3)
|
|
583
|
+
});
|
|
584
|
+
state.scrollTracking.trackedThresholds.add(key);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
}, { threshold: SCROLL_CONFIG.thresholds.map((t) => t / 100) });
|
|
589
|
+
observer.observe(document.body);
|
|
590
|
+
}
|
|
591
|
+
function initTimeOnPageTracking() {
|
|
592
|
+
if (!isBrowser) return;
|
|
593
|
+
setInterval(() => {
|
|
594
|
+
const elapsed = Date.now() - state.pageLoadTime;
|
|
595
|
+
const timeOnPage = Math.round(elapsed / 1e3);
|
|
596
|
+
let currentLevel = state.timeTracking.currentLevel;
|
|
597
|
+
let signal = state.timeTracking.currentLevel;
|
|
598
|
+
if (timeOnPage >= TIME_CONFIG.thresholds.comprador) {
|
|
599
|
+
currentLevel = "comprador";
|
|
600
|
+
signal = TIME_CONFIG.signalStrength.comprador;
|
|
601
|
+
} else if (timeOnPage >= TIME_CONFIG.thresholds.interessado) {
|
|
602
|
+
currentLevel = "interessado";
|
|
603
|
+
signal = TIME_CONFIG.signalStrength.interessado;
|
|
604
|
+
} else if (timeOnPage >= TIME_CONFIG.thresholds.curioso) {
|
|
605
|
+
currentLevel = "curioso";
|
|
606
|
+
signal = TIME_CONFIG.signalStrength.curioso;
|
|
607
|
+
}
|
|
608
|
+
if (currentLevel !== state.timeTracking.currentLevel) {
|
|
609
|
+
state.timeTracking.currentLevel = currentLevel;
|
|
610
|
+
dispatchEvent("TimeOnPage", {
|
|
611
|
+
time_on_page: timeOnPage,
|
|
612
|
+
intention_level: currentLevel,
|
|
613
|
+
signal_strength: signal,
|
|
614
|
+
engagement_type: "time",
|
|
615
|
+
previous_level: state.timeTracking.previousLevel
|
|
616
|
+
});
|
|
617
|
+
state.timeTracking.previousLevel = currentLevel;
|
|
618
|
+
}
|
|
619
|
+
}, 5e3);
|
|
620
|
+
}
|
|
621
|
+
function _createVideoState() {
|
|
622
|
+
return {
|
|
623
|
+
playTime: 0,
|
|
624
|
+
totalWatchTime: 0,
|
|
625
|
+
lastProgress: -1,
|
|
626
|
+
// -1 = nenhum threshold disparado ainda (fix do bug original)
|
|
627
|
+
lastSecond: 0,
|
|
628
|
+
// segundo exato do último timeupdate (para dropout heatmap)
|
|
629
|
+
started: false,
|
|
630
|
+
paused: false,
|
|
631
|
+
completed: false,
|
|
632
|
+
dropoutPercent: null,
|
|
633
|
+
// % onde parou definitivamente
|
|
634
|
+
dropoutSecond: null
|
|
635
|
+
// segundo onde parou definitivamente
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function _updateVideoSummary(videoId) {
|
|
639
|
+
const s = state.videoTracking[videoId];
|
|
640
|
+
const sum = state.videoSummary;
|
|
641
|
+
sum.totalVideos = Object.keys(state.videoTracking).length;
|
|
642
|
+
sum.startedCount = Object.values(state.videoTracking).filter((v) => v.started).length;
|
|
643
|
+
sum.completedCount = Object.values(state.videoTracking).filter((v) => v.completed).length;
|
|
644
|
+
sum.maxProgress = Math.max(...Object.values(state.videoTracking).map((v) => v.lastProgress < 0 ? 0 : v.lastProgress));
|
|
645
|
+
}
|
|
646
|
+
function _recordDropout(videoId, percent, second) {
|
|
647
|
+
const v = state.videoTracking[videoId];
|
|
648
|
+
if (!v || v.completed) return;
|
|
649
|
+
v.dropoutPercent = percent;
|
|
650
|
+
v.dropoutSecond = second;
|
|
651
|
+
if (VIDEO_CONFIG.dropoutEnabled) {
|
|
652
|
+
dispatchEvent("VideoDropout", {
|
|
653
|
+
video_id: videoId,
|
|
654
|
+
dropout_percent: percent,
|
|
655
|
+
dropout_second: second,
|
|
656
|
+
engagement_score: VIDEO_CONFIG.signalStrength.dropout,
|
|
657
|
+
engagement_type: "video",
|
|
658
|
+
video_summary: { ...state.videoSummary }
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function initVideoTracking() {
|
|
663
|
+
if (!isBrowser) return;
|
|
664
|
+
const videos = document.querySelectorAll('video, iframe[src*="youtube"], iframe[src*="vimeo"]');
|
|
665
|
+
videos.forEach((video, index) => {
|
|
666
|
+
const videoId = video.id || video.getAttribute("data-video-id") || `video_${index}`;
|
|
667
|
+
state.videoTracking[videoId] = _createVideoState();
|
|
668
|
+
_updateVideoSummary(videoId);
|
|
669
|
+
if (video.tagName === "IFRAME" && video.src.includes("youtube")) {
|
|
670
|
+
initYouTubeTracking(video, videoId);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (video.tagName === "IFRAME" && video.src.includes("vimeo")) {
|
|
674
|
+
initVimeoTracking(video, videoId);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
_attachHtml5Tracking(video, videoId);
|
|
678
|
+
});
|
|
679
|
+
window.addEventListener("beforeunload", () => {
|
|
680
|
+
Object.entries(state.videoTracking).forEach(([videoId, v]) => {
|
|
681
|
+
if (v.started && !v.completed) {
|
|
682
|
+
_recordDropout(videoId, v.lastProgress < 0 ? 0 : v.lastProgress, v.lastSecond);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function _attachHtml5Tracking(video, videoId) {
|
|
688
|
+
const v = () => state.videoTracking[videoId];
|
|
689
|
+
video.addEventListener("play", () => {
|
|
690
|
+
v().started = true;
|
|
691
|
+
v().paused = false;
|
|
692
|
+
if (!v().playTime) v().playTime = Date.now();
|
|
693
|
+
_updateVideoSummary(videoId);
|
|
694
|
+
dispatchEvent("VideoPlay", {
|
|
695
|
+
video_id: videoId,
|
|
696
|
+
video_platform: "html5",
|
|
697
|
+
progress_percent: calculateVideoProgress(video),
|
|
698
|
+
engagement_score: VIDEO_CONFIG.signalStrength.play,
|
|
699
|
+
engagement_type: "video"
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
video.addEventListener("pause", () => {
|
|
703
|
+
v().paused = true;
|
|
704
|
+
const progress = calculateVideoProgress(video);
|
|
705
|
+
const second = Math.round(video.currentTime);
|
|
706
|
+
dispatchEvent("VideoPause", {
|
|
707
|
+
video_id: videoId,
|
|
708
|
+
video_platform: "html5",
|
|
709
|
+
progress_percent: progress,
|
|
710
|
+
pause_second: second,
|
|
711
|
+
engagement_score: VIDEO_CONFIG.signalStrength.pause,
|
|
712
|
+
engagement_type: "video"
|
|
713
|
+
});
|
|
714
|
+
_recordDropout(videoId, progress, second);
|
|
715
|
+
});
|
|
716
|
+
video.addEventListener("play", () => {
|
|
717
|
+
v().dropoutPercent = null;
|
|
718
|
+
v().dropoutSecond = null;
|
|
719
|
+
});
|
|
720
|
+
video.addEventListener("seeked", () => {
|
|
721
|
+
dispatchEvent("VideoSeek", {
|
|
722
|
+
video_id: videoId,
|
|
723
|
+
video_platform: "html5",
|
|
724
|
+
progress_percent: calculateVideoProgress(video),
|
|
725
|
+
seek_to_second: Math.round(video.currentTime),
|
|
726
|
+
engagement_score: VIDEO_CONFIG.signalStrength.seek,
|
|
727
|
+
engagement_type: "video"
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
video.addEventListener("timeupdate", () => {
|
|
731
|
+
if (video.paused || video.ended) return;
|
|
732
|
+
const progress = calculateVideoProgress(video);
|
|
733
|
+
const second = Math.round(video.currentTime);
|
|
734
|
+
v().lastSecond = second;
|
|
735
|
+
VIDEO_CONFIG.progressThresholds.forEach((threshold) => {
|
|
736
|
+
if (progress >= threshold && v().lastProgress < threshold) {
|
|
737
|
+
v().lastProgress = threshold;
|
|
738
|
+
dispatchEvent("VideoProgress", {
|
|
739
|
+
video_id: videoId,
|
|
740
|
+
video_platform: "html5",
|
|
741
|
+
progress_percent: threshold,
|
|
742
|
+
progress_second: second,
|
|
743
|
+
engagement_score: VIDEO_CONFIG.signalStrength[`progress${threshold}`] || 0,
|
|
744
|
+
engagement_type: "video",
|
|
745
|
+
video_summary: { ...state.videoSummary }
|
|
746
|
+
});
|
|
747
|
+
_updateVideoSummary(videoId);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
});
|
|
751
|
+
video.addEventListener("ended", () => {
|
|
752
|
+
v().paused = false;
|
|
753
|
+
v().completed = true;
|
|
754
|
+
v().dropoutPercent = null;
|
|
755
|
+
v().dropoutSecond = null;
|
|
756
|
+
const totalTime = v().playTime ? Math.round((Date.now() - v().playTime) / 1e3) : 0;
|
|
757
|
+
_updateVideoSummary(videoId);
|
|
758
|
+
dispatchEvent("VideoComplete", {
|
|
759
|
+
video_id: videoId,
|
|
760
|
+
video_platform: "html5",
|
|
761
|
+
progress_percent: 100,
|
|
762
|
+
total_watch_time: totalTime,
|
|
763
|
+
engagement_score: VIDEO_CONFIG.signalStrength.complete,
|
|
764
|
+
engagement_type: "video",
|
|
765
|
+
video_summary: { ...state.videoSummary }
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
function calculateVideoProgress(video) {
|
|
770
|
+
if (!video.duration || video.duration === 0) return 0;
|
|
771
|
+
return Math.round(video.currentTime / video.duration * 100);
|
|
772
|
+
}
|
|
773
|
+
function initYouTubeTracking(iframe, videoId) {
|
|
774
|
+
try {
|
|
775
|
+
const url = new URL(iframe.src);
|
|
776
|
+
if (!url.searchParams.get("enablejsapi")) {
|
|
777
|
+
url.searchParams.set("enablejsapi", "1");
|
|
778
|
+
iframe.src = url.toString();
|
|
779
|
+
}
|
|
780
|
+
} catch (e) {
|
|
781
|
+
}
|
|
782
|
+
if (!window._ytApiLoading) {
|
|
783
|
+
window._ytApiLoading = true;
|
|
784
|
+
const tag = document.createElement("script");
|
|
785
|
+
tag.src = "https://www.youtube.com/iframe_api";
|
|
786
|
+
document.head.appendChild(tag);
|
|
787
|
+
}
|
|
788
|
+
const initPlayer = () => {
|
|
789
|
+
if (typeof YT === "undefined" || !YT.Player) {
|
|
790
|
+
setTimeout(initPlayer, 300);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const v = () => state.videoTracking[videoId];
|
|
794
|
+
let progressInterval = null;
|
|
795
|
+
new YT.Player(iframe, {
|
|
796
|
+
events: {
|
|
797
|
+
onStateChange: (event) => {
|
|
798
|
+
const YTState = YT.PlayerState;
|
|
799
|
+
const player = event.target;
|
|
800
|
+
const duration = player.getDuration() || 0;
|
|
801
|
+
const currentTime = player.getCurrentTime() || 0;
|
|
802
|
+
const progress = duration > 0 ? Math.round(currentTime / duration * 100) : 0;
|
|
803
|
+
const second = Math.round(currentTime);
|
|
804
|
+
if (event.data === YTState.PLAYING) {
|
|
805
|
+
v().started = true;
|
|
806
|
+
v().paused = false;
|
|
807
|
+
if (!v().playTime) v().playTime = Date.now();
|
|
808
|
+
v().dropoutPercent = null;
|
|
809
|
+
v().dropoutSecond = null;
|
|
810
|
+
_updateVideoSummary(videoId);
|
|
811
|
+
dispatchEvent("VideoPlay", {
|
|
812
|
+
video_id: videoId,
|
|
813
|
+
video_platform: "youtube",
|
|
814
|
+
progress_percent: progress,
|
|
815
|
+
engagement_score: VIDEO_CONFIG.signalStrength.play,
|
|
816
|
+
engagement_type: "video"
|
|
817
|
+
});
|
|
818
|
+
clearInterval(progressInterval);
|
|
819
|
+
progressInterval = setInterval(() => {
|
|
820
|
+
if (!player || typeof player.getPlayerState !== "function") return;
|
|
821
|
+
if (player.getPlayerState() !== YTState.PLAYING) return;
|
|
822
|
+
const ct = player.getCurrentTime() || 0;
|
|
823
|
+
const dur = player.getDuration() || 0;
|
|
824
|
+
const pct = dur > 0 ? Math.round(ct / dur * 100) : 0;
|
|
825
|
+
v().lastSecond = Math.round(ct);
|
|
826
|
+
VIDEO_CONFIG.progressThresholds.forEach((threshold) => {
|
|
827
|
+
if (pct >= threshold && v().lastProgress < threshold) {
|
|
828
|
+
v().lastProgress = threshold;
|
|
829
|
+
dispatchEvent("VideoProgress", {
|
|
830
|
+
video_id: videoId,
|
|
831
|
+
video_platform: "youtube",
|
|
832
|
+
progress_percent: threshold,
|
|
833
|
+
progress_second: Math.round(ct),
|
|
834
|
+
engagement_score: VIDEO_CONFIG.signalStrength[`progress${threshold}`] || 0,
|
|
835
|
+
engagement_type: "video",
|
|
836
|
+
video_summary: { ...state.videoSummary }
|
|
837
|
+
});
|
|
838
|
+
_updateVideoSummary(videoId);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}, 2e3);
|
|
842
|
+
} else if (event.data === YTState.PAUSED) {
|
|
843
|
+
clearInterval(progressInterval);
|
|
844
|
+
v().paused = true;
|
|
845
|
+
dispatchEvent("VideoPause", {
|
|
846
|
+
video_id: videoId,
|
|
847
|
+
video_platform: "youtube",
|
|
848
|
+
progress_percent: progress,
|
|
849
|
+
pause_second: second,
|
|
850
|
+
engagement_score: VIDEO_CONFIG.signalStrength.pause,
|
|
851
|
+
engagement_type: "video"
|
|
852
|
+
});
|
|
853
|
+
_recordDropout(videoId, progress, second);
|
|
854
|
+
} else if (event.data === YTState.ENDED) {
|
|
855
|
+
clearInterval(progressInterval);
|
|
856
|
+
v().completed = true;
|
|
857
|
+
v().dropoutPercent = null;
|
|
858
|
+
v().dropoutSecond = null;
|
|
859
|
+
const totalTime = v().playTime ? Math.round((Date.now() - v().playTime) / 1e3) : 0;
|
|
860
|
+
_updateVideoSummary(videoId);
|
|
861
|
+
dispatchEvent("VideoComplete", {
|
|
862
|
+
video_id: videoId,
|
|
863
|
+
video_platform: "youtube",
|
|
864
|
+
progress_percent: 100,
|
|
865
|
+
total_watch_time: totalTime,
|
|
866
|
+
engagement_score: VIDEO_CONFIG.signalStrength.complete,
|
|
867
|
+
engagement_type: "video",
|
|
868
|
+
video_summary: { ...state.videoSummary }
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
};
|
|
875
|
+
if (typeof YT !== "undefined" && YT.Player) {
|
|
876
|
+
initPlayer();
|
|
877
|
+
} else {
|
|
878
|
+
const prev = window.onYouTubeIframeAPIReady;
|
|
879
|
+
window.onYouTubeIframeAPIReady = () => {
|
|
880
|
+
if (prev) prev();
|
|
881
|
+
initPlayer();
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
function initVimeoTracking(iframe, videoId) {
|
|
886
|
+
const v = () => state.videoTracking[videoId];
|
|
887
|
+
let duration = 0;
|
|
888
|
+
const send = (method, value) => {
|
|
889
|
+
iframe.contentWindow.postMessage(JSON.stringify({ method, value }), "https://player.vimeo.com");
|
|
890
|
+
};
|
|
891
|
+
const enableListeners = () => {
|
|
892
|
+
["play", "pause", "ended", "timeupdate", "seeked"].forEach((evt) => {
|
|
893
|
+
send("addEventListener", evt);
|
|
894
|
+
});
|
|
895
|
+
send("getDuration");
|
|
896
|
+
};
|
|
897
|
+
window.addEventListener("message", (event) => {
|
|
898
|
+
var _a;
|
|
899
|
+
if (!event.origin.includes("vimeo.com")) return;
|
|
900
|
+
let data;
|
|
901
|
+
try {
|
|
902
|
+
data = JSON.parse(event.data);
|
|
903
|
+
} catch (e) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (!data || data.player_id !== iframe.id) return;
|
|
907
|
+
if (data.event === "ready") {
|
|
908
|
+
enableListeners();
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
if (data.method === "getDuration") {
|
|
912
|
+
duration = data.value || 0;
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
const currentTime = ((_a = data.data) == null ? void 0 : _a.seconds) || 0;
|
|
916
|
+
const progress = duration > 0 ? Math.round(currentTime / duration * 100) : 0;
|
|
917
|
+
const second = Math.round(currentTime);
|
|
918
|
+
if (data.event === "play") {
|
|
919
|
+
v().started = true;
|
|
920
|
+
v().paused = false;
|
|
921
|
+
if (!v().playTime) v().playTime = Date.now();
|
|
922
|
+
v().dropoutPercent = null;
|
|
923
|
+
v().dropoutSecond = null;
|
|
924
|
+
_updateVideoSummary(videoId);
|
|
925
|
+
dispatchEvent("VideoPlay", {
|
|
926
|
+
video_id: videoId,
|
|
927
|
+
video_platform: "vimeo",
|
|
928
|
+
progress_percent: progress,
|
|
929
|
+
engagement_score: VIDEO_CONFIG.signalStrength.play,
|
|
930
|
+
engagement_type: "video"
|
|
931
|
+
});
|
|
932
|
+
} else if (data.event === "pause") {
|
|
933
|
+
v().paused = true;
|
|
934
|
+
v().lastSecond = second;
|
|
935
|
+
dispatchEvent("VideoPause", {
|
|
936
|
+
video_id: videoId,
|
|
937
|
+
video_platform: "vimeo",
|
|
938
|
+
progress_percent: progress,
|
|
939
|
+
pause_second: second,
|
|
940
|
+
engagement_score: VIDEO_CONFIG.signalStrength.pause,
|
|
941
|
+
engagement_type: "video"
|
|
942
|
+
});
|
|
943
|
+
_recordDropout(videoId, progress, second);
|
|
944
|
+
} else if (data.event === "timeupdate") {
|
|
945
|
+
v().lastSecond = second;
|
|
946
|
+
VIDEO_CONFIG.progressThresholds.forEach((threshold) => {
|
|
947
|
+
if (progress >= threshold && v().lastProgress < threshold) {
|
|
948
|
+
v().lastProgress = threshold;
|
|
949
|
+
dispatchEvent("VideoProgress", {
|
|
950
|
+
video_id: videoId,
|
|
951
|
+
video_platform: "vimeo",
|
|
952
|
+
progress_percent: threshold,
|
|
953
|
+
progress_second: second,
|
|
954
|
+
engagement_score: VIDEO_CONFIG.signalStrength[`progress${threshold}`] || 0,
|
|
955
|
+
engagement_type: "video",
|
|
956
|
+
video_summary: { ...state.videoSummary }
|
|
957
|
+
});
|
|
958
|
+
_updateVideoSummary(videoId);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
} else if (data.event === "ended") {
|
|
962
|
+
v().completed = true;
|
|
963
|
+
v().dropoutPercent = null;
|
|
964
|
+
v().dropoutSecond = null;
|
|
965
|
+
const totalTime = v().playTime ? Math.round((Date.now() - v().playTime) / 1e3) : 0;
|
|
966
|
+
_updateVideoSummary(videoId);
|
|
967
|
+
dispatchEvent("VideoComplete", {
|
|
968
|
+
video_id: videoId,
|
|
969
|
+
video_platform: "vimeo",
|
|
970
|
+
progress_percent: 100,
|
|
971
|
+
total_watch_time: totalTime,
|
|
972
|
+
engagement_score: VIDEO_CONFIG.signalStrength.complete,
|
|
973
|
+
engagement_type: "video",
|
|
974
|
+
video_summary: { ...state.videoSummary }
|
|
975
|
+
});
|
|
976
|
+
} else if (data.event === "seeked") {
|
|
977
|
+
dispatchEvent("VideoSeek", {
|
|
978
|
+
video_id: videoId,
|
|
979
|
+
video_platform: "vimeo",
|
|
980
|
+
progress_percent: progress,
|
|
981
|
+
seek_to_second: second,
|
|
982
|
+
engagement_score: VIDEO_CONFIG.signalStrength.seek,
|
|
983
|
+
engagement_type: "video"
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
if (iframe.contentDocument) {
|
|
988
|
+
enableListeners();
|
|
989
|
+
} else {
|
|
990
|
+
iframe.addEventListener("load", enableListeners);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
function initClickHeatmapTracking() {
|
|
994
|
+
if (!isBrowser) return;
|
|
995
|
+
document.addEventListener("click", (e) => {
|
|
996
|
+
const target = e.target;
|
|
997
|
+
const clickCategory = categorizeClick(target);
|
|
998
|
+
const position = calculateClickPosition(e);
|
|
999
|
+
const timeOnPage = Math.round((Date.now() - state.pageLoadTime) / 1e3);
|
|
1000
|
+
const now = Date.now();
|
|
1001
|
+
const timeSinceLastClick = now - state.clickTracking.lastClickTime;
|
|
1002
|
+
if (timeSinceLastClick < CLICK_CONFIG.rapidClickWindow) {
|
|
1003
|
+
state.clickTracking.clicks.push({
|
|
1004
|
+
time: now,
|
|
1005
|
+
target
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
setTimeout(() => {
|
|
1009
|
+
const recentClicks = state.clickTracking.clicks.filter(
|
|
1010
|
+
(c) => now - c.time < CLICK_CONFIG.rapidClickWindow
|
|
1011
|
+
);
|
|
1012
|
+
if (recentClicks.length >= CLICK_CONFIG.maxRapidClicks) {
|
|
1013
|
+
state.clickTracking.rapidClickDetected = true;
|
|
1014
|
+
dispatchEvent("RapidClicks", {
|
|
1015
|
+
click_count: recentClicks.length,
|
|
1016
|
+
time_window: CLICK_CONFIG.rapidClickWindow,
|
|
1017
|
+
behavior: "nervous",
|
|
1018
|
+
engagement_score: 3,
|
|
1019
|
+
engagement_type: "clicks",
|
|
1020
|
+
time_on_page: timeOnPage,
|
|
1021
|
+
description: `Usu\xE1rio clicou ${recentClicks.length} vezes em ${CLICK_CONFIG.rapidClickWindow / 1e3} segundos`
|
|
1022
|
+
});
|
|
1023
|
+
} else {
|
|
1024
|
+
state.clickTracking.rapidClickDetected = false;
|
|
1025
|
+
state.clickTracking.clicks = [];
|
|
1026
|
+
}
|
|
1027
|
+
}, CLICK_CONFIG.rapidClickWindow);
|
|
1028
|
+
state.clickTracking.lastClickTime = now;
|
|
1029
|
+
dispatchEvent("Click", {
|
|
1030
|
+
click_category: clickCategory,
|
|
1031
|
+
target_info: extractTargetInfo(target),
|
|
1032
|
+
position,
|
|
1033
|
+
time_on_page: timeOnPage,
|
|
1034
|
+
click_heatmap: true,
|
|
1035
|
+
engagement_score: calculateClickEngagementScore(clickCategory, position),
|
|
1036
|
+
engagement_type: "clicks"
|
|
1037
|
+
});
|
|
1038
|
+
}, true);
|
|
1039
|
+
}
|
|
1040
|
+
function categorizeClick(target) {
|
|
1041
|
+
const closest = (selector) => target.closest(selector);
|
|
1042
|
+
const className = target.className || "";
|
|
1043
|
+
if (closest("button") || className.includes("btn")) return "button";
|
|
1044
|
+
if (closest("a")) return "link";
|
|
1045
|
+
if (closest("input")) return "input";
|
|
1046
|
+
if (closest(".cta") || className.includes("cta")) return "cta";
|
|
1047
|
+
if (closest(".price") || className.includes("price")) return "price";
|
|
1048
|
+
if (closest(".whatsapp") || className.includes("whatsapp")) return "whatsapp";
|
|
1049
|
+
if (closest(".email") || className.includes("email")) return "email";
|
|
1050
|
+
if (closest(".phone") || className.includes("phone")) return "phone";
|
|
1051
|
+
return "generic";
|
|
1052
|
+
}
|
|
1053
|
+
function calculateClickPosition(e) {
|
|
1054
|
+
return {
|
|
1055
|
+
x: Math.round(e.clientX),
|
|
1056
|
+
y: Math.round(e.clientY),
|
|
1057
|
+
relativeX: Math.round(e.clientX / window.innerWidth * 100),
|
|
1058
|
+
relativeY: Math.round(e.clientY / window.innerHeight * 100),
|
|
1059
|
+
viewportWidth: window.innerWidth,
|
|
1060
|
+
viewportHeight: window.innerHeight
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function extractTargetInfo(target) {
|
|
1064
|
+
var _a;
|
|
1065
|
+
return {
|
|
1066
|
+
tag: target.tagName,
|
|
1067
|
+
class: target.className,
|
|
1068
|
+
id: target.id,
|
|
1069
|
+
text: ((_a = target.textContent) == null ? void 0 : _a.substring(0, 30)) || "",
|
|
1070
|
+
attributes: Array.from(target.attributes).map((a) => `${a.name}="${a.value}"`).join(" ")
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
function calculateClickEngagementScore(clickCategory, position) {
|
|
1074
|
+
const baseScore = CLICK_CONFIG.signalStrength[clickCategory] || 0.5;
|
|
1075
|
+
const timeScore = Math.min(position.timeOnPage / 60, 2);
|
|
1076
|
+
const depthBonus = Math.min(position.scrollDepth / 100, 0.5);
|
|
1077
|
+
return baseScore + timeScore + depthBonus;
|
|
1078
|
+
}
|
|
1079
|
+
function initCTAHoverTracking() {
|
|
1080
|
+
if (!isBrowser) return;
|
|
1081
|
+
const ctaSelectors = [
|
|
1082
|
+
'a[href*="checkout"]',
|
|
1083
|
+
'a[href*="comprar"]',
|
|
1084
|
+
"button.cta",
|
|
1085
|
+
".btn-comprar",
|
|
1086
|
+
".btn-checkout",
|
|
1087
|
+
".cta-button",
|
|
1088
|
+
'[data-cta="true"]',
|
|
1089
|
+
'button[type="submit"]'
|
|
1090
|
+
];
|
|
1091
|
+
ctaSelectors.forEach((selector) => {
|
|
1092
|
+
document.querySelectorAll(selector).forEach((element) => {
|
|
1093
|
+
element.addEventListener("mouseenter", (e) => {
|
|
1094
|
+
const ctaElement = e.target.closest(selector);
|
|
1095
|
+
if (!ctaElement) return;
|
|
1096
|
+
state.ctaHoverTracking.hoverStartTime = Date.now();
|
|
1097
|
+
state.ctaHoverTracking.hoveredElement = selector;
|
|
1098
|
+
});
|
|
1099
|
+
element.addEventListener("mouseleave", () => {
|
|
1100
|
+
if (state.ctaHoverTracking.hoverStartTime) {
|
|
1101
|
+
const hoverTime = Date.now() - state.ctaHoverTracking.hoverStartTime;
|
|
1102
|
+
if (hoverTime >= CTA_HOVER_CONFIG.minHoverTimeForSignal) {
|
|
1103
|
+
dispatchEvent("CTAHover", {
|
|
1104
|
+
cta_selector: selector,
|
|
1105
|
+
hover_time_seconds: Math.round(hoverTime / 1e3),
|
|
1106
|
+
engagement_score: CTA_HOVER_CONFIG.signalStrength,
|
|
1107
|
+
engagement_type: "hover",
|
|
1108
|
+
time_on_page: Math.round((Date.now() - state.pageLoadTime) / 1e3)
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
state.ctaHoverTracking.hoverStartTime = null;
|
|
1113
|
+
state.ctaHoverTracking.hoveredElement = null;
|
|
1114
|
+
});
|
|
1115
|
+
});
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
function dispatchEvent(eventName, data = {}) {
|
|
1119
|
+
if (typeof cdpTrack !== "undefined" && cdpTrack.track) {
|
|
1120
|
+
cdpTrack.track(eventName, data);
|
|
1121
|
+
} else {
|
|
1122
|
+
console.warn("cdpTrack n\xE3o dispon\xEDvel - evento n\xE3o enviado:", eventName, data);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
function initMicroEvents() {
|
|
1126
|
+
if (!isBrowser) return;
|
|
1127
|
+
state.pageLoadTime = Date.now();
|
|
1128
|
+
initScrollTracking();
|
|
1129
|
+
initTimeOnPageTracking();
|
|
1130
|
+
initVideoTracking();
|
|
1131
|
+
initClickHeatmapTracking();
|
|
1132
|
+
initCTAHoverTracking();
|
|
1133
|
+
console.log("\u2705 Micro-Events inicializados");
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// engagement-scoring.js
|
|
1137
|
+
var ENGAGEMENT_CONFIG = {
|
|
1138
|
+
timeWeights: {
|
|
1139
|
+
"curioso": 1,
|
|
1140
|
+
// < 10s: score base 1.0
|
|
1141
|
+
"interessado": 2.5,
|
|
1142
|
+
// 10-60s: score base 2.5
|
|
1143
|
+
"comprador": 4
|
|
1144
|
+
// > 60s: score base 4.0
|
|
1145
|
+
},
|
|
1146
|
+
scrollWeights: {
|
|
1147
|
+
"baixo": 1,
|
|
1148
|
+
// < 25%: score base 1.0
|
|
1149
|
+
"medio": 2,
|
|
1150
|
+
// 25-75%: score base 2.0
|
|
1151
|
+
"alto": 3
|
|
1152
|
+
// > 75%: score base 3.0
|
|
1153
|
+
},
|
|
1154
|
+
clickWeights: {
|
|
1155
|
+
"generico": 1,
|
|
1156
|
+
// clique sem categoria
|
|
1157
|
+
"cta": 2.5,
|
|
1158
|
+
// clique em CTA
|
|
1159
|
+
"button": 2,
|
|
1160
|
+
// clique em botão
|
|
1161
|
+
"link": 1.5,
|
|
1162
|
+
// clique em link
|
|
1163
|
+
"input": 0.5
|
|
1164
|
+
// clique em input
|
|
1165
|
+
},
|
|
1166
|
+
videoWeights: {
|
|
1167
|
+
"play": 1.5,
|
|
1168
|
+
// vídeo iniciou
|
|
1169
|
+
"progress25": 2,
|
|
1170
|
+
// 25% assistido
|
|
1171
|
+
"progress50": 3,
|
|
1172
|
+
// 50% assistido
|
|
1173
|
+
"progress75": 4,
|
|
1174
|
+
// 75% assistido
|
|
1175
|
+
"complete": 5
|
|
1176
|
+
// vídeo completo
|
|
1177
|
+
},
|
|
1178
|
+
hoverWeights: {
|
|
1179
|
+
"curto": 1,
|
|
1180
|
+
// < 3s: hover curto
|
|
1181
|
+
"medio": 2,
|
|
1182
|
+
// 3-10s: hover médio
|
|
1183
|
+
"longo": 3
|
|
1184
|
+
// > 10s: hover longo
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
var engagementState = {
|
|
1188
|
+
timeOnPage: 0,
|
|
1189
|
+
scrollDepth: 0,
|
|
1190
|
+
clicksCount: 0,
|
|
1191
|
+
videoEngagement: 0,
|
|
1192
|
+
ctaHoverTime: 0,
|
|
1193
|
+
lastActivityTime: Date.now()
|
|
1194
|
+
};
|
|
1195
|
+
function calculateTimeScore(timeOnPage) {
|
|
1196
|
+
let level = "curioso";
|
|
1197
|
+
let score = ENGAGEMENT_CONFIG.timeWeights.curioso;
|
|
1198
|
+
if (timeOnPage >= 10 && timeOnPage < 60) {
|
|
1199
|
+
level = "interessado";
|
|
1200
|
+
score = ENGAGEMENT_CONFIG.timeWeights.interessado;
|
|
1201
|
+
} else if (timeOnPage >= 60) {
|
|
1202
|
+
level = "comprador";
|
|
1203
|
+
score = ENGAGEMENT_CONFIG.timeWeights.comprador;
|
|
1204
|
+
}
|
|
1205
|
+
return { score, level };
|
|
1206
|
+
}
|
|
1207
|
+
function calculateScrollScore(scrollDepth) {
|
|
1208
|
+
let level = "baixo";
|
|
1209
|
+
let score = ENGAGEMENT_CONFIG.scrollWeights.baixo;
|
|
1210
|
+
if (scrollDepth >= 25 && scrollDepth < 75) {
|
|
1211
|
+
level = "medio";
|
|
1212
|
+
score = ENGAGEMENT_CONFIG.scrollWeights.medio;
|
|
1213
|
+
} else if (scrollDepth >= 75) {
|
|
1214
|
+
level = "alto";
|
|
1215
|
+
score = ENGAGEMENT_CONFIG.scrollWeights.alto;
|
|
1216
|
+
}
|
|
1217
|
+
return { score, level };
|
|
1218
|
+
}
|
|
1219
|
+
function calculateClickScore(clicksCount, clickCategory) {
|
|
1220
|
+
const baseScore = ENGAGEMENT_CONFIG.clickWeights.generico;
|
|
1221
|
+
const categoryBonus = ENGAGEMENT_CONFIG.clickWeights[clickCategory] || 1;
|
|
1222
|
+
const clickPenalty = Math.max(0, 1 - clicksCount / 50);
|
|
1223
|
+
return baseScore + categoryBonus;
|
|
1224
|
+
}
|
|
1225
|
+
function calculateHoverScore(hoverTime) {
|
|
1226
|
+
let level = "curto";
|
|
1227
|
+
let score = ENGAGEMENT_CONFIG.hoverWeights.curto;
|
|
1228
|
+
if (hoverTime >= 3 && hoverTime < 10) {
|
|
1229
|
+
level = "medio";
|
|
1230
|
+
score = ENGAGEMENT_CONFIG.hoverWeights.medio;
|
|
1231
|
+
} else if (hoverTime >= 10) {
|
|
1232
|
+
level = "longo";
|
|
1233
|
+
score = ENGAGEMENT_CONFIG.hoverWeights.longo;
|
|
1234
|
+
}
|
|
1235
|
+
return { score, level };
|
|
1236
|
+
}
|
|
1237
|
+
function calculateEngagementScore() {
|
|
1238
|
+
const { score: timeScore, level: timeLevel } = calculateTimeScore(engagementState.timeOnPage);
|
|
1239
|
+
const { score: scrollScore } = calculateScrollScore(engagementState.scrollDepth);
|
|
1240
|
+
const clickScore = engagementState.clicksCount > 0 ? calculateClickScore(engagementState.clicksCount, "generico") : 0;
|
|
1241
|
+
const videoScore = engagementState.videoEngagement;
|
|
1242
|
+
const hoverScore = engagementState.ctaHoverTime > 0 ? calculateHoverScore(engagementState.ctaHoverTime / 1e3) : 0;
|
|
1243
|
+
const totalScore = timeScore * 0.3 + // 30% peso para tempo
|
|
1244
|
+
scrollScore * 0.2 + // 20% peso para scroll
|
|
1245
|
+
clickScore * 0.15 + // 15% peso para cliques
|
|
1246
|
+
videoScore * 0.25 + // 25% peso para vídeo
|
|
1247
|
+
hoverScore * 0.1;
|
|
1248
|
+
let intentionLevel = timeLevel;
|
|
1249
|
+
if (totalScore < 1.5) {
|
|
1250
|
+
intentionLevel = "curioso";
|
|
1251
|
+
} else if (totalScore < 2.5) {
|
|
1252
|
+
intentionLevel = "interessado";
|
|
1253
|
+
} else {
|
|
1254
|
+
intentionLevel = "comprador";
|
|
1255
|
+
}
|
|
1256
|
+
return {
|
|
1257
|
+
totalScore: Math.min(totalScore, 5),
|
|
1258
|
+
timeScore,
|
|
1259
|
+
scrollScore,
|
|
1260
|
+
clickScore,
|
|
1261
|
+
videoScore,
|
|
1262
|
+
hoverScore,
|
|
1263
|
+
intentionLevel,
|
|
1264
|
+
components: {
|
|
1265
|
+
timeOnPage: engagementState.timeOnPage,
|
|
1266
|
+
scrollDepth: engagementState.scrollDepth,
|
|
1267
|
+
clicksCount: engagementState.clicksCount,
|
|
1268
|
+
videoEngagement: engagementState.videoEngagement,
|
|
1269
|
+
ctaHoverTime: engagementState.ctaHoverTime
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
function updateEngagementState(component, value) {
|
|
1274
|
+
engagementState[component] = value;
|
|
1275
|
+
engagementState.lastActivityTime = Date.now();
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// advanced-matching.js
|
|
1279
|
+
var NORMALIZATION_CONFIG = {
|
|
1280
|
+
email: {
|
|
1281
|
+
trim: true,
|
|
1282
|
+
lowercase: true,
|
|
1283
|
+
removeDots: false,
|
|
1284
|
+
// Gmail: user.name@gmail.com vs username@gmail.com (diferentes)
|
|
1285
|
+
removePlus: true
|
|
1286
|
+
// Gmail: user+tag@gmail.com = user@gmail.com (mesmo)
|
|
1287
|
+
},
|
|
1288
|
+
phone: {
|
|
1289
|
+
keepOnlyDigits: true,
|
|
1290
|
+
addCountryCode: true,
|
|
1291
|
+
// Adiciona DDI se não tiver (55 para Brasil)
|
|
1292
|
+
defaultCountryCode: "55"
|
|
1293
|
+
},
|
|
1294
|
+
name: {
|
|
1295
|
+
trim: true,
|
|
1296
|
+
lowercase: true,
|
|
1297
|
+
removeAccents: true,
|
|
1298
|
+
removeExtraSpaces: true,
|
|
1299
|
+
maxLength: 100
|
|
1300
|
+
},
|
|
1301
|
+
city: {
|
|
1302
|
+
trim: true,
|
|
1303
|
+
lowercase: true,
|
|
1304
|
+
removeAccents: true,
|
|
1305
|
+
maxLength: 50
|
|
1306
|
+
},
|
|
1307
|
+
state: {
|
|
1308
|
+
trim: true,
|
|
1309
|
+
lowercase: true,
|
|
1310
|
+
removeAccents: true,
|
|
1311
|
+
maxLength: 50
|
|
1312
|
+
},
|
|
1313
|
+
zip: {
|
|
1314
|
+
trim: true,
|
|
1315
|
+
keepOnlyDigits: true,
|
|
1316
|
+
maxLength: 10
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
function normalizeEmail(email) {
|
|
1320
|
+
if (!email || typeof email !== "string") return "";
|
|
1321
|
+
let normalized = email.trim();
|
|
1322
|
+
if (normalized.includes("@gmail.com")) {
|
|
1323
|
+
normalized = normalized.split("+")[0] + "@gmail.com";
|
|
1324
|
+
}
|
|
1325
|
+
if (normalized.includes("@googlemail.com")) {
|
|
1326
|
+
normalized = normalized.split("+")[0] + "@googlemail.com";
|
|
1327
|
+
}
|
|
1328
|
+
return normalized.toLowerCase();
|
|
1329
|
+
}
|
|
1330
|
+
function normalizePhone(phone) {
|
|
1331
|
+
if (!phone || typeof phone !== "string") return "";
|
|
1332
|
+
let normalized = phone.replace(/\D/g, "");
|
|
1333
|
+
if (normalized.length === 11 || normalized.length === 10) {
|
|
1334
|
+
normalized = "55" + normalized;
|
|
1335
|
+
}
|
|
1336
|
+
return normalized.substring(0, 15);
|
|
1337
|
+
}
|
|
1338
|
+
function normalizeName(name) {
|
|
1339
|
+
if (!name || typeof name !== "string") return "";
|
|
1340
|
+
let normalized = name.trim();
|
|
1341
|
+
normalized = normalized.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
1342
|
+
normalized = normalized.toLowerCase();
|
|
1343
|
+
normalized = normalized.replace(/\s+/g, " ");
|
|
1344
|
+
return normalized.substring(0, NORMALIZATION_CONFIG.name.maxLength);
|
|
1345
|
+
}
|
|
1346
|
+
function normalizeCity(city) {
|
|
1347
|
+
if (!city || typeof city !== "string") return "";
|
|
1348
|
+
let normalized = city.trim();
|
|
1349
|
+
normalized = normalized.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
1350
|
+
normalized = normalized.toLowerCase();
|
|
1351
|
+
normalized = normalized.replace(/\s+/g, " ");
|
|
1352
|
+
return normalized.substring(0, NORMALIZATION_CONFIG.city.maxLength);
|
|
1353
|
+
}
|
|
1354
|
+
function normalizeState(state2) {
|
|
1355
|
+
if (!state2 || typeof state2 !== "string") return "";
|
|
1356
|
+
let normalized = state2.trim();
|
|
1357
|
+
normalized = normalized.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
1358
|
+
normalized = normalized.toLowerCase();
|
|
1359
|
+
return normalized.substring(0, NORMALIZATION_CONFIG.state.maxLength);
|
|
1360
|
+
}
|
|
1361
|
+
function normalizeZip(zip) {
|
|
1362
|
+
if (!zip || typeof zip !== "string") return "";
|
|
1363
|
+
const normalized = zip.replace(/\D/g, "");
|
|
1364
|
+
return normalized.substring(0, NORMALIZATION_CONFIG.zip.maxLength);
|
|
1365
|
+
}
|
|
1366
|
+
function normalizeDOB(dob) {
|
|
1367
|
+
if (!dob || typeof dob !== "string") return "";
|
|
1368
|
+
const formats = [
|
|
1369
|
+
/(\d{4})-(\d{2})-(\d{2})/,
|
|
1370
|
+
// YYYY-MM-DD
|
|
1371
|
+
/(\d{2})\/(\d{2})\/(\d{4})/,
|
|
1372
|
+
// DD/MM/YYYY
|
|
1373
|
+
/(\d{2})-(\d{2})-(\d{4})/
|
|
1374
|
+
// DD-MM-YYYY
|
|
1375
|
+
];
|
|
1376
|
+
for (const format of formats) {
|
|
1377
|
+
const match = dob.match(format);
|
|
1378
|
+
if (match) {
|
|
1379
|
+
let year, month, day;
|
|
1380
|
+
if (match[1].length === 4) {
|
|
1381
|
+
[year, month, day] = [match[1], match[2], match[3]];
|
|
1382
|
+
} else {
|
|
1383
|
+
[day, month, year] = [match[1], match[2], match[3]];
|
|
1384
|
+
}
|
|
1385
|
+
return `${year}${month}${day}`;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return "";
|
|
1389
|
+
}
|
|
1390
|
+
function extractFormPII(form) {
|
|
1391
|
+
if (!form || typeof form.elements !== "object") return {};
|
|
1392
|
+
const formData = new FormData(form);
|
|
1393
|
+
const piiData = {};
|
|
1394
|
+
const email = formData.get("email") || formData.get("user_email") || formData.get("useremail");
|
|
1395
|
+
if (email) piiData.email = normalizeEmail(email);
|
|
1396
|
+
const phone = formData.get("phone") || formData.get("telephone") || formData.get("telefone") || formData.get("celular");
|
|
1397
|
+
if (phone) piiData.phone = normalizePhone(phone);
|
|
1398
|
+
const firstName = formData.get("first_name") || formData.get("nome") || formData.get("firstname");
|
|
1399
|
+
const lastName = formData.get("last_name") || formData.get("sobrenome") || formData.get("lastname");
|
|
1400
|
+
const fullName = formData.get("name") || formData.get("fullname") || formData.get("nome_completo");
|
|
1401
|
+
if (fullName) {
|
|
1402
|
+
const names = fullName.split(" ");
|
|
1403
|
+
piiData.first_name = normalizeName(names[0]);
|
|
1404
|
+
piiData.last_name = normalizeName(names.slice(1).join(" "));
|
|
1405
|
+
} else {
|
|
1406
|
+
if (firstName) piiData.first_name = normalizeName(firstName);
|
|
1407
|
+
if (lastName) piiData.last_name = normalizeName(lastName);
|
|
1408
|
+
}
|
|
1409
|
+
const city = formData.get("city") || formData.get("cidade");
|
|
1410
|
+
if (city) piiData.city = normalizeCity(city);
|
|
1411
|
+
const state2 = formData.get("state") || formData.get("estado") || formData.get("uf");
|
|
1412
|
+
if (state2) piiData.state = normalizeState(state2);
|
|
1413
|
+
const zip = formData.get("zip") || formData.get("cep") || formData.get("postalcode");
|
|
1414
|
+
if (zip) piiData.zip = normalizeZip(zip);
|
|
1415
|
+
const dob = formData.get("dob") || formData.get("date_of_birth") || formData.get("nascimento");
|
|
1416
|
+
if (dob) piiData.dob = normalizeDOB(dob);
|
|
1417
|
+
return piiData;
|
|
1418
|
+
}
|
|
1419
|
+
function isValidPII(piiData) {
|
|
1420
|
+
if (!piiData || typeof piiData !== "object") return false;
|
|
1421
|
+
const hasEmail = piiData.email && piiData.email.includes("@");
|
|
1422
|
+
const hasPhone = piiData.phone && piiData.phone.length >= 10;
|
|
1423
|
+
return hasEmail || hasPhone;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// anti-blocking.js
|
|
1427
|
+
var isBrowser2 = typeof window !== "undefined";
|
|
1428
|
+
var ANTI_BLOCKING_CONFIG = {
|
|
1429
|
+
// Mesmo domínio evita bloqueios de CORS e ad-blockers
|
|
1430
|
+
endpoint: "/track",
|
|
1431
|
+
// Retries com exponential backoff
|
|
1432
|
+
maxRetries: 3,
|
|
1433
|
+
retryDelays: [1e3, 3e3, 6e3],
|
|
1434
|
+
// 1s, 3s, 6s
|
|
1435
|
+
// Fallback para Beacon API (quando fetch falha)
|
|
1436
|
+
useBeaconFallback: true,
|
|
1437
|
+
// First-party cookies (ad-block proof)
|
|
1438
|
+
cookieDuration: 60 * 60 * 24 * 365,
|
|
1439
|
+
// 365 dias
|
|
1440
|
+
cookieDomain: "",
|
|
1441
|
+
// Será definido dinamicamente
|
|
1442
|
+
// Detectar ad-blockers
|
|
1443
|
+
detectAdBlocker: true,
|
|
1444
|
+
adBlockerBaitClass: "adsbox adbanner pub_300x250",
|
|
1445
|
+
// Lightweight code (evitar patterns de bloqueio)
|
|
1446
|
+
minify: false,
|
|
1447
|
+
// Opcional: usar código minificado em produção
|
|
1448
|
+
noConsoleLogs: false
|
|
1449
|
+
// Opcional: remover console.logs em produção
|
|
1450
|
+
};
|
|
1451
|
+
function detectAdBlocker() {
|
|
1452
|
+
if (!isBrowser2 || !ANTI_BLOCKING_CONFIG.detectAdBlocker) return false;
|
|
1453
|
+
const baitElement = document.createElement("div");
|
|
1454
|
+
baitElement.innerHTML = " ";
|
|
1455
|
+
baitElement.className = ANTI_BLOCKING_CONFIG.adBlockerBaitClass;
|
|
1456
|
+
baitElement.style.cssText = "position: absolute; top: -1000px; left: -1000px;";
|
|
1457
|
+
document.body.appendChild(baitElement);
|
|
1458
|
+
const isBlocked = getComputedStyle(baitElement).display === "none";
|
|
1459
|
+
document.body.removeChild(baitElement);
|
|
1460
|
+
try {
|
|
1461
|
+
const testPixel = new Image();
|
|
1462
|
+
testPixel.src = "/pixel-test.png?t=" + Date.now();
|
|
1463
|
+
testPixel.onload = () => console.log("\u2705 Pixel n\xE3o bloqueado");
|
|
1464
|
+
testPixel.onerror = () => console.warn("\u26A0\uFE0F Pixel pode estar bloqueado");
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
console.warn("\u26A0\uFE0F Erro ao testar pixel:", error);
|
|
1467
|
+
}
|
|
1468
|
+
return isBlocked;
|
|
1469
|
+
}
|
|
1470
|
+
async function sendWithRetry(data, endpoint = ANTI_BLOCKING_CONFIG.endpoint) {
|
|
1471
|
+
if (!isBrowser2) return { success: false, error: "Not in browser" };
|
|
1472
|
+
let lastError = null;
|
|
1473
|
+
for (let attempt = 0; attempt < ANTI_BLOCKING_CONFIG.maxRetries; attempt++) {
|
|
1474
|
+
try {
|
|
1475
|
+
const response = await fetch(endpoint, {
|
|
1476
|
+
method: "POST",
|
|
1477
|
+
headers: {
|
|
1478
|
+
"Content-Type": "application/json"
|
|
1479
|
+
},
|
|
1480
|
+
body: JSON.stringify(data),
|
|
1481
|
+
keepalive: true,
|
|
1482
|
+
// Garante envio mesmo se usuário fechar aba
|
|
1483
|
+
credentials: "same-origin",
|
|
1484
|
+
// First-party cookies
|
|
1485
|
+
cache: "no-cache"
|
|
1486
|
+
// Evitar cache de requests de tracking
|
|
1487
|
+
});
|
|
1488
|
+
if (response.ok) {
|
|
1489
|
+
console.log(`\u2705 Envio bem-sucedido (tentativa ${attempt + 1})`);
|
|
1490
|
+
return await response.json();
|
|
1491
|
+
} else {
|
|
1492
|
+
const errorText = await response.text();
|
|
1493
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1494
|
+
}
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
lastError = error;
|
|
1497
|
+
console.warn(`\u26A0\uFE0F Tentativa ${attempt + 1} falhou:`, error.message);
|
|
1498
|
+
if (attempt < ANTI_BLOCKING_CONFIG.maxRetries - 1) {
|
|
1499
|
+
const delay = ANTI_BLOCKING_CONFIG.retryDelays[attempt];
|
|
1500
|
+
console.log(`\u23F3 Aguardando ${delay}ms antes de retry...`);
|
|
1501
|
+
await sleep(delay);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
console.error("\u274C Todas as tentativas de envio falharam:", lastError);
|
|
1506
|
+
if (ANTI_BLOCKING_CONFIG.useBeaconFallback && navigator.sendBeacon) {
|
|
1507
|
+
console.log("\u{1F504} Tentando Beacon API como fallback...");
|
|
1508
|
+
const beaconSuccess = navigator.sendBeacon(endpoint, JSON.stringify(data));
|
|
1509
|
+
if (beaconSuccess) {
|
|
1510
|
+
console.log("\u2705 Beacon API bem-sucedido");
|
|
1511
|
+
return { success: true, method: "beacon" };
|
|
1512
|
+
} else {
|
|
1513
|
+
console.error("\u274C Beacon API tamb\xE9m falhou");
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return { success: false, error: lastError == null ? void 0 : lastError.message, attempts: ANTI_BLOCKING_CONFIG.maxRetries };
|
|
1517
|
+
}
|
|
1518
|
+
function sleep(ms) {
|
|
1519
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1520
|
+
}
|
|
1521
|
+
function setFirstPartyCookie(name, value, maxAge = ANTI_BLOCKING_CONFIG.cookieDuration) {
|
|
1522
|
+
if (!isBrowser2) return;
|
|
1523
|
+
const currentDomain = window.location.hostname;
|
|
1524
|
+
const rootDomain = currentDomain.split(".").slice(-2).join(".");
|
|
1525
|
+
const cookieOptions = [
|
|
1526
|
+
`${name}=${value}`,
|
|
1527
|
+
`max-age=${maxAge}`,
|
|
1528
|
+
"path=/",
|
|
1529
|
+
`domain=.${rootDomain}`,
|
|
1530
|
+
// Umbrella domain para subdomínios
|
|
1531
|
+
"SameSite=Lax",
|
|
1532
|
+
"Secure"
|
|
1533
|
+
].join("; ");
|
|
1534
|
+
document.cookie = cookieOptions;
|
|
1535
|
+
}
|
|
1536
|
+
function getFirstPartyCookie(name) {
|
|
1537
|
+
if (!isBrowser2) return null;
|
|
1538
|
+
const value = `; ${document.cookie}`;
|
|
1539
|
+
const parts = value.split(`; ${name}=`);
|
|
1540
|
+
if (parts.length === 2) {
|
|
1541
|
+
return parts.pop().split(";").shift();
|
|
1542
|
+
}
|
|
1543
|
+
return null;
|
|
1544
|
+
}
|
|
1545
|
+
function configureConsoleLogs(remove = ANTI_BLOCKING_CONFIG.noConsoleLogs) {
|
|
1546
|
+
if (!isBrowser2 || !remove) return;
|
|
1547
|
+
const noop = () => {
|
|
1548
|
+
};
|
|
1549
|
+
console.log = noop;
|
|
1550
|
+
console.warn = noop;
|
|
1551
|
+
console.error = noop;
|
|
1552
|
+
}
|
|
1553
|
+
function initAntiBlocking() {
|
|
1554
|
+
if (!isBrowser2) return;
|
|
1555
|
+
console.log("\u{1F6E1}\uFE0F Inicializando Anti-Blocking System...");
|
|
1556
|
+
const adBlockerActive = detectAdBlocker();
|
|
1557
|
+
if (adBlockerActive) {
|
|
1558
|
+
console.warn("\u26A0\uFE0F Ad-Blocker detectado - usando estrat\xE9gias de resili\xEAncia");
|
|
1559
|
+
if (typeof cdpTrack !== "undefined" && cdpTrack.track) {
|
|
1560
|
+
cdpTrack.track("adblocker_detected", {
|
|
1561
|
+
user_agent: navigator.userAgent,
|
|
1562
|
+
timestamp: Date.now()
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
const userId = getFirstPartyCookie("_cdp_uid");
|
|
1567
|
+
if (!userId) {
|
|
1568
|
+
const newUserId = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
1569
|
+
setFirstPartyCookie("_cdp_uid", newUserId);
|
|
1570
|
+
}
|
|
1571
|
+
if (ANTI_BLOCKING_CONFIG.noConsoleLogs) {
|
|
1572
|
+
configureConsoleLogs(true);
|
|
1573
|
+
}
|
|
1574
|
+
console.log("\u2705 Anti-Blocking System inicializado");
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// route-intent-capture.js
|
|
1578
|
+
var isBrowser3 = typeof window !== "undefined";
|
|
1579
|
+
function initRouteIntentCapture(options = {}) {
|
|
1580
|
+
if (!isBrowser3) return;
|
|
1581
|
+
const {
|
|
1582
|
+
whatsappNumber,
|
|
1583
|
+
propertyName = "o im\xF3vel",
|
|
1584
|
+
propertyId = null,
|
|
1585
|
+
propertyLat = null,
|
|
1586
|
+
propertyLng = null,
|
|
1587
|
+
routeSelector = '[data-route-intent], a[href*="maps/dir"], a[href*="maps?q="]',
|
|
1588
|
+
distanceBucket = null,
|
|
1589
|
+
distanceKm = null,
|
|
1590
|
+
metaSignalBucket = null,
|
|
1591
|
+
brokerName = null
|
|
1592
|
+
} = options;
|
|
1593
|
+
if (!whatsappNumber) {
|
|
1594
|
+
console.warn("[RouteIntent] whatsappNumber \xE9 obrigat\xF3rio.");
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
_injectStyles();
|
|
1598
|
+
document.addEventListener("click", (e) => {
|
|
1599
|
+
const btn = e.target.closest(routeSelector);
|
|
1600
|
+
if (!btn) return;
|
|
1601
|
+
_showWidget(btn, { whatsappNumber, propertyName, propertyId, propertyLat, propertyLng, distanceBucket, distanceKm, metaSignalBucket, brokerName });
|
|
1602
|
+
}, true);
|
|
1603
|
+
}
|
|
1604
|
+
function _showWidget(anchorEl, opts) {
|
|
1605
|
+
var _a;
|
|
1606
|
+
(_a = document.getElementById("cdp-ri-widget")) == null ? void 0 : _a.remove();
|
|
1607
|
+
const travelMinutes = _estimateTravelMinutes(opts.distanceKm);
|
|
1608
|
+
const travelText = travelMinutes ? `<p class="cdp-ri-travel">\u{1F4CD} Voc\xEA est\xE1 a cerca de <strong>${travelMinutes} min</strong> daqui</p>` : "";
|
|
1609
|
+
const brokerLine = opts.brokerName ? `<p class="cdp-ri-broker">Ao chegar, pergunte pelo <strong>${opts.brokerName}</strong> \u2014 ele vai estar te aguardando com tudo pronto \u{1F91D}</p>` : "";
|
|
1610
|
+
const widget = document.createElement("div");
|
|
1611
|
+
widget.id = "cdp-ri-widget";
|
|
1612
|
+
widget.innerHTML = `
|
|
1613
|
+
<div class="cdp-ri-inner">
|
|
1614
|
+
${travelText}
|
|
1615
|
+
<p class="cdp-ri-headline">Vi que voc\xEA quer visitar o local! Confirme sua vinda enviando a mensagem abaixo \u2014 nossa equipe j\xE1 fica de prontid\xE3o pra te receber \u{1F447}</p>
|
|
1616
|
+
${brokerLine}
|
|
1617
|
+
<button id="cdp-ri-btn" type="button">
|
|
1618
|
+
${_waIcon()} Confirmar minha visita
|
|
1619
|
+
</button>
|
|
1620
|
+
<button id="cdp-ri-dismiss" type="button" class="cdp-ri-dismiss">Agora n\xE3o</button>
|
|
1621
|
+
</div>
|
|
1622
|
+
`;
|
|
1623
|
+
anchorEl.insertAdjacentElement("afterend", widget);
|
|
1624
|
+
setTimeout(() => widget.scrollIntoView({ behavior: "smooth", block: "nearest" }), 150);
|
|
1625
|
+
document.getElementById("cdp-ri-btn").addEventListener("click", () => _handleClick(widget, opts));
|
|
1626
|
+
document.getElementById("cdp-ri-dismiss").addEventListener("click", () => {
|
|
1627
|
+
var _a2, _b;
|
|
1628
|
+
(_b = (_a2 = window.cdpTrack) == null ? void 0 : _a2.track) == null ? void 0 : _b.call(_a2, "ViewContent", {
|
|
1629
|
+
content_name: "rota_dispensada",
|
|
1630
|
+
property_id: opts.propertyId,
|
|
1631
|
+
funnel_stage: "route_dismiss",
|
|
1632
|
+
intent_score: "medium",
|
|
1633
|
+
distance_bucket: opts.distanceBucket || void 0,
|
|
1634
|
+
meta_signal_bucket: opts.metaSignalBucket || void 0
|
|
1635
|
+
});
|
|
1636
|
+
widget.remove();
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
async function _handleClick(widget, opts) {
|
|
1640
|
+
var _a, _b;
|
|
1641
|
+
const btn = document.getElementById("cdp-ri-btn");
|
|
1642
|
+
if (btn) btn.disabled = true;
|
|
1643
|
+
(_b = (_a = window.cdpTrack) == null ? void 0 : _a.track) == null ? void 0 : _b.call(_a, "Contact", {
|
|
1644
|
+
content_name: "aviso_chegada_whatsapp",
|
|
1645
|
+
property_id: opts.propertyId,
|
|
1646
|
+
property_lat: opts.propertyLat,
|
|
1647
|
+
property_lng: opts.propertyLng,
|
|
1648
|
+
funnel_stage: "route_click",
|
|
1649
|
+
intent_score: "high",
|
|
1650
|
+
distance_bucket: opts.distanceBucket || void 0,
|
|
1651
|
+
meta_signal_bucket: opts.metaSignalBucket || void 0
|
|
1652
|
+
});
|
|
1653
|
+
const travelMinutes = _estimateTravelMinutes(opts.distanceKm);
|
|
1654
|
+
const travelLine = travelMinutes ? `Estou a cerca de ${travelMinutes} minutos da\xED.` : "";
|
|
1655
|
+
const brokerLine = opts.brokerName ? `Vou procurar pelo ${opts.brokerName} ao chegar.` : "";
|
|
1656
|
+
const msg = [
|
|
1657
|
+
`Oi! Estou interessado(a) em visitar o ${opts.propertyName} e gostaria de confirmar minha visita.`,
|
|
1658
|
+
travelLine,
|
|
1659
|
+
brokerLine || `Voc\xEAs conseguem me receber agora ou preciso marcar hor\xE1rio?`
|
|
1660
|
+
].filter(Boolean).join(" ");
|
|
1661
|
+
_showSuccess(widget, opts.whatsappNumber, msg);
|
|
1662
|
+
}
|
|
1663
|
+
function _showSuccess(widget, whatsappNumber, msg) {
|
|
1664
|
+
widget.innerHTML = `
|
|
1665
|
+
<div class="cdp-ri-inner cdp-ri-ok">
|
|
1666
|
+
<span class="cdp-ri-check">\u2705</span>
|
|
1667
|
+
<p><strong>Abrindo WhatsApp...</strong><br><span>A equipe j\xE1 foi avisada!</span></p>
|
|
1668
|
+
</div>
|
|
1669
|
+
`;
|
|
1670
|
+
setTimeout(() => {
|
|
1671
|
+
window.open(`https://wa.me/${whatsappNumber}?text=${encodeURIComponent(msg)}`, "_blank");
|
|
1672
|
+
setTimeout(() => widget == null ? void 0 : widget.remove(), 3500);
|
|
1673
|
+
}, 600);
|
|
1674
|
+
}
|
|
1675
|
+
function _estimateTravelMinutes(distanceKm) {
|
|
1676
|
+
if (!distanceKm || distanceKm <= 0) return null;
|
|
1677
|
+
return Math.max(5, Math.round(distanceKm * 60 / 25 / 5) * 5);
|
|
1678
|
+
}
|
|
1679
|
+
function _waIcon() {
|
|
1680
|
+
return `<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
1681
|
+
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347z"/>
|
|
1682
|
+
<path d="M12 0C5.373 0 0 5.373 0 12c0 2.122.554 4.118 1.528 5.852L0 24l6.335-1.513A11.933 11.933 0 0012 24c6.627 0 12-5.373 12-12S18.627 0 12 0zm0 21.818a9.818 9.818 0 01-5.002-1.368l-.359-.213-3.722.888.924-3.617-.234-.372A9.818 9.818 0 012.182 12C2.182 6.57 6.57 2.182 12 2.182S21.818 6.57 21.818 12 17.43 21.818 12 21.818z"/>
|
|
1683
|
+
</svg>`;
|
|
1684
|
+
}
|
|
1685
|
+
function _injectStyles() {
|
|
1686
|
+
if (document.getElementById("cdp-ri-styles")) return;
|
|
1687
|
+
const s = document.createElement("style");
|
|
1688
|
+
s.id = "cdp-ri-styles";
|
|
1689
|
+
s.textContent = `
|
|
1690
|
+
#cdp-ri-widget {
|
|
1691
|
+
margin-top: 12px;
|
|
1692
|
+
padding: 16px 18px;
|
|
1693
|
+
background: #f0fdf4;
|
|
1694
|
+
border: 1.5px solid #22c55e;
|
|
1695
|
+
border-radius: 12px;
|
|
1696
|
+
font-family: Arial, sans-serif;
|
|
1697
|
+
animation: cdp-ri-in .25s ease;
|
|
1698
|
+
}
|
|
1699
|
+
@keyframes cdp-ri-in {
|
|
1700
|
+
from { opacity: 0; transform: translateY(-8px); }
|
|
1701
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1702
|
+
}
|
|
1703
|
+
.cdp-ri-inner { display: flex; flex-direction: column; gap: 10px; }
|
|
1704
|
+
.cdp-ri-travel { margin: 0; font-size: 13px; color: #555; }
|
|
1705
|
+
.cdp-ri-travel strong { color: #0f766e; }
|
|
1706
|
+
.cdp-ri-headline { margin: 0; font-size: 14px; color: #15803d; font-weight: bold; line-height: 1.4; }
|
|
1707
|
+
.cdp-ri-broker { margin: 0; font-size: 13px; color: #555; line-height: 1.4; }
|
|
1708
|
+
.cdp-ri-broker strong { color: #0f766e; }
|
|
1709
|
+
#cdp-ri-btn {
|
|
1710
|
+
display: flex; align-items: center; justify-content: center; gap: 8px;
|
|
1711
|
+
width: 100%; padding: 13px 16px;
|
|
1712
|
+
background: #25D366; color: #fff;
|
|
1713
|
+
border: none; border-radius: 10px;
|
|
1714
|
+
font-size: 15px; font-weight: bold; cursor: pointer;
|
|
1715
|
+
}
|
|
1716
|
+
#cdp-ri-btn:hover { background: #1ebe5a; }
|
|
1717
|
+
#cdp-ri-btn:disabled { background: #86efac; cursor: not-allowed; }
|
|
1718
|
+
.cdp-ri-dismiss {
|
|
1719
|
+
background: none; border: none;
|
|
1720
|
+
color: #aaa; font-size: 12px;
|
|
1721
|
+
cursor: pointer; padding: 2px 0; text-align: center;
|
|
1722
|
+
}
|
|
1723
|
+
.cdp-ri-dismiss:hover { color: #666; }
|
|
1724
|
+
.cdp-ri-ok { align-items: center; text-align: center; gap: 8px; }
|
|
1725
|
+
.cdp-ri-check { font-size: 28px; }
|
|
1726
|
+
.cdp-ri-ok p { margin: 0; font-size: 14px; color: #15803d; line-height: 1.5; }
|
|
1727
|
+
.cdp-ri-ok span { font-size: 13px; color: #555; }
|
|
1728
|
+
`;
|
|
1729
|
+
document.head.appendChild(s);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
// cdpTrack.js
|
|
1733
|
+
var isBrowser4 = typeof window !== "undefined";
|
|
1734
|
+
var CONSENT_KEY = "_cdp_consent";
|
|
1735
|
+
var _defaultConsent = {
|
|
1736
|
+
ad_storage: "denied",
|
|
1737
|
+
analytics_storage: "denied",
|
|
1738
|
+
ad_user_data: "denied",
|
|
1739
|
+
ad_personalization: "denied"
|
|
1740
|
+
};
|
|
1741
|
+
function _readStoredConsent() {
|
|
1742
|
+
if (!isBrowser4) return null;
|
|
1743
|
+
try {
|
|
1744
|
+
const raw = localStorage.getItem(CONSENT_KEY);
|
|
1745
|
+
return raw ? JSON.parse(raw) : null;
|
|
1746
|
+
} catch (e) {
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
function initConsentMode() {
|
|
1751
|
+
var _a, _b, _c, _d;
|
|
1752
|
+
if (!isBrowser4) return;
|
|
1753
|
+
if (((_a = tracking_config_default.consent) == null ? void 0 : _a.defaultDenied) === false) return;
|
|
1754
|
+
window.dataLayer = window.dataLayer || [];
|
|
1755
|
+
function gtag() {
|
|
1756
|
+
window.dataLayer.push(arguments);
|
|
1757
|
+
}
|
|
1758
|
+
const waitForUpdate = (_c = (_b = tracking_config_default.consent) == null ? void 0 : _b.waitForUpdate) != null ? _c : 500;
|
|
1759
|
+
gtag("consent", "default", {
|
|
1760
|
+
..._defaultConsent,
|
|
1761
|
+
wait_for_update: waitForUpdate
|
|
1762
|
+
});
|
|
1763
|
+
if (((_d = tracking_config_default.consent) == null ? void 0 : _d.urlPassthrough) !== false) {
|
|
1764
|
+
gtag("set", "url_passthrough", true);
|
|
1765
|
+
}
|
|
1766
|
+
const stored = _readStoredConsent();
|
|
1767
|
+
if (stored) {
|
|
1768
|
+
gtag("consent", "update", stored);
|
|
1769
|
+
if (tracking_config_default.debug) console.log("\u2705 Consent Mode: consentimento anterior restaurado");
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
function updateConsent(params = {}) {
|
|
1773
|
+
if (!isBrowser4) return;
|
|
1774
|
+
window.dataLayer = window.dataLayer || [];
|
|
1775
|
+
function gtag() {
|
|
1776
|
+
window.dataLayer.push(arguments);
|
|
1777
|
+
}
|
|
1778
|
+
const analyticsGranted = params.analytics === true ? "granted" : "denied";
|
|
1779
|
+
const adsGranted = params.ads === true ? "granted" : "denied";
|
|
1780
|
+
const consentUpdate = {
|
|
1781
|
+
analytics_storage: analyticsGranted,
|
|
1782
|
+
ad_storage: adsGranted,
|
|
1783
|
+
ad_user_data: adsGranted,
|
|
1784
|
+
ad_personalization: adsGranted
|
|
1785
|
+
};
|
|
1786
|
+
gtag("consent", "update", consentUpdate);
|
|
1787
|
+
try {
|
|
1788
|
+
localStorage.setItem(CONSENT_KEY, JSON.stringify(consentUpdate));
|
|
1789
|
+
} catch (e) {
|
|
1790
|
+
}
|
|
1791
|
+
console.log("\u2705 Consent Mode atualizado:", consentUpdate);
|
|
1792
|
+
}
|
|
1793
|
+
var _urlParams = isBrowser4 ? new URLSearchParams(window.location.search) : new URLSearchParams();
|
|
1794
|
+
var _fbclid = _urlParams.get("fbclid") || "";
|
|
1795
|
+
var _gclid = _urlParams.get("gclid") || "";
|
|
1796
|
+
var _wbraid = _urlParams.get("wbraid") || "";
|
|
1797
|
+
var _gbraid = _urlParams.get("gbraid") || "";
|
|
1798
|
+
var _ttclid = _urlParams.get("ttclid") || "";
|
|
1799
|
+
var _utms = {
|
|
1800
|
+
utm_source: _urlParams.get("utm_source") || "",
|
|
1801
|
+
utm_medium: _urlParams.get("utm_medium") || "",
|
|
1802
|
+
utm_campaign: _urlParams.get("utm_campaign") || "",
|
|
1803
|
+
utm_content: _urlParams.get("utm_content") || "",
|
|
1804
|
+
utm_term: _urlParams.get("utm_term") || ""
|
|
1805
|
+
};
|
|
1806
|
+
var _getUserId = () => {
|
|
1807
|
+
var _a;
|
|
1808
|
+
if (!isBrowser4) return "";
|
|
1809
|
+
const KEY = "_cdp_uid";
|
|
1810
|
+
let uid = (_a = document.cookie.match(new RegExp(`${KEY}=([^;]+)`))) == null ? void 0 : _a[1];
|
|
1811
|
+
if (!uid) {
|
|
1812
|
+
uid = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
1813
|
+
document.cookie = `${KEY}=${uid}; max-age=${60 * 60 * 24 * 365}; path=/; SameSite=Lax`;
|
|
1814
|
+
}
|
|
1815
|
+
return uid;
|
|
1816
|
+
};
|
|
1817
|
+
var _userId = isBrowser4 ? _getUserId() : "";
|
|
1818
|
+
var _getSessionId = () => {
|
|
1819
|
+
if (!isBrowser4) return "";
|
|
1820
|
+
const sessionId = "cdp_session";
|
|
1821
|
+
let sid = sessionStorage.getItem(sessionId);
|
|
1822
|
+
if (!sid) {
|
|
1823
|
+
sid = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
1824
|
+
sessionStorage.setItem(sessionId, sid);
|
|
1825
|
+
}
|
|
1826
|
+
return sid;
|
|
1827
|
+
};
|
|
1828
|
+
var _sessionId = isBrowser4 ? _getSessionId() : "";
|
|
1829
|
+
var getUTMs = () => ({ ..._utms });
|
|
1830
|
+
var getUserId = () => _userId;
|
|
1831
|
+
var getSessionId = () => _sessionId;
|
|
1832
|
+
var generateId = () => {
|
|
1833
|
+
return `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
1834
|
+
};
|
|
1835
|
+
var _getClickIDs = () => {
|
|
1836
|
+
var _a, _b, _c;
|
|
1837
|
+
return {
|
|
1838
|
+
fbp: ((_a = document.cookie.match(/_fbp=([^;]+)/)) == null ? void 0 : _a[1]) || void 0,
|
|
1839
|
+
fbc: ((_b = document.cookie.match(/_fbc=([^;]+)/)) == null ? void 0 : _b[1]) || void 0,
|
|
1840
|
+
fbclid: _fbclid || void 0,
|
|
1841
|
+
// click ID Meta → Worker gera fbc se cookie _fbc ausente
|
|
1842
|
+
gclid: _gclid || void 0,
|
|
1843
|
+
gbraid: _gbraid || void 0,
|
|
1844
|
+
wbraid: _wbraid || void 0,
|
|
1845
|
+
ttclid: _ttclid || void 0,
|
|
1846
|
+
ttp: ((_c = document.cookie.match(/_ttp=([^;]+)/)) == null ? void 0 : _c[1]) || void 0,
|
|
1847
|
+
// TikTok Pixel cookie — EMQ TikTok
|
|
1848
|
+
rclid: _urlParams.get("rclid") || void 0
|
|
1849
|
+
};
|
|
1850
|
+
};
|
|
1851
|
+
var passCheckoutParams = (options = {}) => {
|
|
1852
|
+
if (!isBrowser4) return;
|
|
1853
|
+
const {
|
|
1854
|
+
platforms = ["hotmart", "kiwify", "eduzz", "monetizze", "cartpanda", "ticto"],
|
|
1855
|
+
domains = []
|
|
1856
|
+
} = options;
|
|
1857
|
+
const utms = getUTMs();
|
|
1858
|
+
const userId = getUserId();
|
|
1859
|
+
const PLATFORM_DOMAINS = {
|
|
1860
|
+
hotmart: ["hotmart.com", "pay.hotmart.com", "payment.hotmart.com"],
|
|
1861
|
+
kiwify: ["kiwify.com.br", "checkout.kiwify.com.br"],
|
|
1862
|
+
eduzz: ["eduzz.com", "sun.eduzz.com"],
|
|
1863
|
+
monetizze: ["monetizze.com.br"],
|
|
1864
|
+
cartpanda: ["cartpanda.com", "pay.cartpanda.com"],
|
|
1865
|
+
ticto: ["ticto.app", "pay.ticto.app", "checkout.ticto.app"]
|
|
1866
|
+
};
|
|
1867
|
+
const buildSck = () => [utms.utm_source, utms.utm_medium, utms.utm_campaign, utms.utm_content, utms.utm_term].map((v) => v || "direto").join("|");
|
|
1868
|
+
const getParamsForUrl = (href) => {
|
|
1869
|
+
const params = { ...domains };
|
|
1870
|
+
const url = href.toLowerCase();
|
|
1871
|
+
const isMatch = (list) => list.some((d) => url.includes(d));
|
|
1872
|
+
if (platforms.includes("hotmart") && isMatch(PLATFORM_DOMAINS.hotmart)) {
|
|
1873
|
+
if (userId) params.xcod = userId;
|
|
1874
|
+
if (utms.utm_source) params.sck = buildSck();
|
|
1875
|
+
} else if (platforms.includes("kiwify") && isMatch(PLATFORM_DOMAINS.kiwify)) {
|
|
1876
|
+
if (utms.utm_source) params.src = utms.utm_source;
|
|
1877
|
+
if (utms.utm_medium) params.utm_medium = utms.utm_medium;
|
|
1878
|
+
if (utms.utm_campaign) params.utm_campaign = utms.utm_campaign;
|
|
1879
|
+
} else if (platforms.includes("eduzz") && isMatch(PLATFORM_DOMAINS.eduzz)) {
|
|
1880
|
+
if (utms.utm_source) params.src = utms.utm_source;
|
|
1881
|
+
} else if (platforms.includes("monetizze") && isMatch(PLATFORM_DOMAINS.monetizze)) {
|
|
1882
|
+
if (utms.utm_source) params.src = utms.utm_source;
|
|
1883
|
+
} else if (platforms.includes("cartpanda") && isMatch(PLATFORM_DOMAINS.cartpanda)) {
|
|
1884
|
+
Object.entries(utms).forEach(([k, v]) => {
|
|
1885
|
+
if (v) params[k] = v;
|
|
1886
|
+
});
|
|
1887
|
+
} else if (platforms.includes("ticto") && isMatch(PLATFORM_DOMAINS.ticto)) {
|
|
1888
|
+
Object.entries(utms).forEach(([k, v]) => {
|
|
1889
|
+
if (v) params[k] = v;
|
|
1890
|
+
});
|
|
1891
|
+
if (userId) params.user_id = userId;
|
|
1892
|
+
} else {
|
|
1893
|
+
const allDomains = Object.values(PLATFORM_DOMAINS).flat().concat(domains);
|
|
1894
|
+
if (!isMatch(allDomains)) return null;
|
|
1895
|
+
Object.entries(utms).forEach(([k, v]) => {
|
|
1896
|
+
if (v) params[k] = v;
|
|
1897
|
+
});
|
|
1898
|
+
if (userId) params.user_id = userId;
|
|
1899
|
+
}
|
|
1900
|
+
return Object.keys(params).length ? params : null;
|
|
1901
|
+
};
|
|
1902
|
+
const applyParams = (link) => {
|
|
1903
|
+
if (!link.href || link.href.startsWith("javascript")) return;
|
|
1904
|
+
const p = getParamsForUrl(link);
|
|
1905
|
+
if (!p) return;
|
|
1906
|
+
try {
|
|
1907
|
+
const url = new URL(link.href);
|
|
1908
|
+
Object.entries(p).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
1909
|
+
link.href = url.toString();
|
|
1910
|
+
} catch (e) {
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1913
|
+
document.querySelectorAll("a[href]").forEach(applyParams);
|
|
1914
|
+
new MutationObserver((mutations) => {
|
|
1915
|
+
mutations.forEach((m) => m.addedNodes.forEach((node) => {
|
|
1916
|
+
var _a;
|
|
1917
|
+
if (node.nodeType !== 1) return;
|
|
1918
|
+
if (node.tagName === "A") applyParams(node);
|
|
1919
|
+
(_a = node.querySelectorAll) == null ? void 0 : _a.call(node, "a[href]").forEach(applyParams);
|
|
1920
|
+
}));
|
|
1921
|
+
}).observe(document.body, { childList: true, subtree: true });
|
|
1922
|
+
console.log("\u2705 PassCheckoutParams inicializado");
|
|
1923
|
+
};
|
|
1924
|
+
var _AFFILIATE_STORAGE_KEY = "_cdp_aff";
|
|
1925
|
+
function _saveAffiliateContext() {
|
|
1926
|
+
if (!isBrowser4) return;
|
|
1927
|
+
try {
|
|
1928
|
+
const hasUtms = Object.values(_utms).some((v) => !!v);
|
|
1929
|
+
const existing = _loadAffiliateContext();
|
|
1930
|
+
if (hasUtms || !existing) {
|
|
1931
|
+
localStorage.setItem(_AFFILIATE_STORAGE_KEY, JSON.stringify({
|
|
1932
|
+
uid: _userId,
|
|
1933
|
+
utms: _utms,
|
|
1934
|
+
ts: Date.now()
|
|
1935
|
+
}));
|
|
1936
|
+
}
|
|
1937
|
+
} catch (e) {
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
function _loadAffiliateContext() {
|
|
1941
|
+
if (!isBrowser4) return null;
|
|
1942
|
+
try {
|
|
1943
|
+
const raw = localStorage.getItem(_AFFILIATE_STORAGE_KEY);
|
|
1944
|
+
if (!raw) return null;
|
|
1945
|
+
const ctx = JSON.parse(raw);
|
|
1946
|
+
if (Date.now() - ctx.ts > 30 * 24 * 60 * 60 * 1e3) {
|
|
1947
|
+
localStorage.removeItem(_AFFILIATE_STORAGE_KEY);
|
|
1948
|
+
return null;
|
|
1949
|
+
}
|
|
1950
|
+
return ctx;
|
|
1951
|
+
} catch (e) {
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function getUTMsWithFallback() {
|
|
1956
|
+
const live = getUTMs();
|
|
1957
|
+
if (Object.values(live).some((v) => !!v)) return live;
|
|
1958
|
+
const stored = _loadAffiliateContext();
|
|
1959
|
+
if ((stored == null ? void 0 : stored.utms) && Object.values(stored.utms).some((v) => !!v)) {
|
|
1960
|
+
if (tracking_config_default.debug) console.log("\u2139\uFE0F UTMs restaurados do localStorage (affiliate fallback)");
|
|
1961
|
+
return stored.utms;
|
|
1962
|
+
}
|
|
1963
|
+
return live;
|
|
1964
|
+
}
|
|
1965
|
+
function getUserIdWithFallback() {
|
|
1966
|
+
var _a;
|
|
1967
|
+
return _userId || ((_a = _loadAffiliateContext()) == null ? void 0 : _a.uid) || "";
|
|
1968
|
+
}
|
|
1969
|
+
async function track(eventName, data = {}, options = {}) {
|
|
1970
|
+
if (!isBrowser4) {
|
|
1971
|
+
console.warn("\u26A0\uFE0F cdpTrack.track() deve ser chamado no browser");
|
|
1972
|
+
return { success: false, error: "Not in browser" };
|
|
1973
|
+
}
|
|
1974
|
+
const event_id = generateId();
|
|
1975
|
+
const utms = getUTMs();
|
|
1976
|
+
const clickIds = _getClickIDs();
|
|
1977
|
+
const userId = getUserId();
|
|
1978
|
+
const sessionId = getSessionId();
|
|
1979
|
+
const timestamp = Date.now();
|
|
1980
|
+
const engagementScore = calculateEngagementScore();
|
|
1981
|
+
const { totalScore, timeLevel, scrollScore, clickScore, videoScore, hoverScore, intentionLevel } = engagementScore;
|
|
1982
|
+
const payload = {
|
|
1983
|
+
event_id,
|
|
1984
|
+
event_name: eventName,
|
|
1985
|
+
user_id: userId,
|
|
1986
|
+
session_id: sessionId,
|
|
1987
|
+
utms,
|
|
1988
|
+
click_ids: clickIds,
|
|
1989
|
+
timestamp,
|
|
1990
|
+
page_url: window.location.href,
|
|
1991
|
+
referrer: document.referrer,
|
|
1992
|
+
user_agent: navigator.userAgent,
|
|
1993
|
+
behavioral_data: {
|
|
1994
|
+
...data,
|
|
1995
|
+
engagement_score: totalScore,
|
|
1996
|
+
time_level: timeLevel,
|
|
1997
|
+
scroll_score: scrollScore,
|
|
1998
|
+
click_score: clickScore,
|
|
1999
|
+
video_score: videoScore,
|
|
2000
|
+
hover_score: hoverScore,
|
|
2001
|
+
intention_level: intentionLevel
|
|
2002
|
+
},
|
|
2003
|
+
...options
|
|
2004
|
+
};
|
|
2005
|
+
try {
|
|
2006
|
+
const result = await sendWithRetry(payload, "/track");
|
|
2007
|
+
if (result.success) {
|
|
2008
|
+
console.log(`\u2705 Evento ${eventName} enviado com sucesso:`, event_id, result);
|
|
2009
|
+
updateEngagementState("timeOnPage", Math.round((Date.now() - timestamp) / 1e3));
|
|
2010
|
+
updateEngagementState("totalScore", totalScore);
|
|
2011
|
+
return { success: true, event_id, result };
|
|
2012
|
+
} else {
|
|
2013
|
+
throw new Error(result.error || "Envio falhou ap\xF3s todas as tentativas");
|
|
2014
|
+
}
|
|
2015
|
+
} catch (error) {
|
|
2016
|
+
console.error(`\u274C Erro ao enviar evento ${eventName}:`, error);
|
|
2017
|
+
return { success: false, error: error.message, event_id, attempts: ANTI_BLOCKING_CONFIG.maxRetries };
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
async function trackLead(userData, form = null) {
|
|
2021
|
+
let piiData = { ...userData };
|
|
2022
|
+
if (form) {
|
|
2023
|
+
const formPII = extractFormPII(form);
|
|
2024
|
+
piiData = { ...piiData, ...formPII };
|
|
2025
|
+
}
|
|
2026
|
+
return track("Lead", piiData);
|
|
2027
|
+
}
|
|
2028
|
+
async function trackPurchase(orderData, form = null) {
|
|
2029
|
+
let purchaseData = { ...orderData };
|
|
2030
|
+
if (form) {
|
|
2031
|
+
const formPII = extractFormPII(form);
|
|
2032
|
+
purchaseData = { ...purchaseData, ...formPII };
|
|
2033
|
+
}
|
|
2034
|
+
return track("Purchase", purchaseData);
|
|
2035
|
+
}
|
|
2036
|
+
function setupAutoFormCapture() {
|
|
2037
|
+
if (!isBrowser4) return;
|
|
2038
|
+
document.addEventListener("submit", async (e) => {
|
|
2039
|
+
const form = e.target;
|
|
2040
|
+
const hasEmailField = form.querySelector('[name*="email"]');
|
|
2041
|
+
const hasPhoneField = form.querySelector('[name*="phone"], [name*="telefone"], [name*="celular"]');
|
|
2042
|
+
if (hasEmailField || hasPhoneField) {
|
|
2043
|
+
e.preventDefault();
|
|
2044
|
+
const piiData = extractFormPII(form);
|
|
2045
|
+
if (isValidPII(piiData)) {
|
|
2046
|
+
await trackLead(piiData, form);
|
|
2047
|
+
console.log("\u2705 Lead capturado automaticamente com Advanced Matching:", piiData);
|
|
2048
|
+
}
|
|
2049
|
+
setTimeout(() => form.submit(), 100);
|
|
2050
|
+
}
|
|
2051
|
+
}, true);
|
|
2052
|
+
}
|
|
2053
|
+
async function trackViewContent(contentData) {
|
|
2054
|
+
return track("ViewContent", contentData);
|
|
2055
|
+
}
|
|
2056
|
+
async function trackAddToCart(cartData) {
|
|
2057
|
+
return track("AddToCart", cartData);
|
|
2058
|
+
}
|
|
2059
|
+
async function init() {
|
|
2060
|
+
var _a, _b;
|
|
2061
|
+
if (!isBrowser4) return;
|
|
2062
|
+
if (tracking_config_default.debug) console.log("\u{1F680} Inicializando cdpTrack SDK...");
|
|
2063
|
+
initConsentMode();
|
|
2064
|
+
initAntiBlocking();
|
|
2065
|
+
_saveAffiliateContext();
|
|
2066
|
+
initMicroEvents();
|
|
2067
|
+
if (tracking_config_default.autoCaptureForms !== false) {
|
|
2068
|
+
setupAutoFormCapture();
|
|
2069
|
+
}
|
|
2070
|
+
window.cdpTrack = { track, trackLead, trackPurchase, trackViewContent, trackAddToCart, getUserId, getUserIdWithFallback, getSessionId, getUTMs, getUTMsWithFallback, updateConsent };
|
|
2071
|
+
if (tracking_config_default.initBehaviorEngine !== false) {
|
|
2072
|
+
try {
|
|
2073
|
+
const behaviorModule = await Promise.resolve().then(() => (init_behavior_engine(), behavior_engine_exports));
|
|
2074
|
+
const BehaviorEngine2 = behaviorModule.default;
|
|
2075
|
+
if (BehaviorEngine2 && typeof BehaviorEngine2.init === "function") {
|
|
2076
|
+
BehaviorEngine2.init();
|
|
2077
|
+
if (tracking_config_default.debug) console.log("\u2705 Behavior Engine inicializado");
|
|
2078
|
+
}
|
|
2079
|
+
} catch (error) {
|
|
2080
|
+
if (tracking_config_default.debug) console.info("\u2139\uFE0F Behavior Engine n\xE3o dispon\xEDvel:", error.message);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
if (tracking_config_default.passCheckoutParams !== false && ((_a = tracking_config_default.platforms) == null ? void 0 : _a.length) > 0) {
|
|
2084
|
+
passCheckoutParams({ platforms: tracking_config_default.platforms });
|
|
2085
|
+
}
|
|
2086
|
+
if ((_b = tracking_config_default.routeIntent) == null ? void 0 : _b.whatsappNumber) {
|
|
2087
|
+
initRouteIntentCapture(tracking_config_default.routeIntent);
|
|
2088
|
+
}
|
|
2089
|
+
if (tracking_config_default.debug) console.log("\u2705 cdpTrack SDK inicializado (Quantum Tier)");
|
|
2090
|
+
}
|
|
2091
|
+
if (isBrowser4) {
|
|
2092
|
+
window.addEventListener("DOMContentLoaded", init);
|
|
2093
|
+
}
|
|
2094
|
+
return __toCommonJS(cdpTrack_exports);
|
|
2095
|
+
})();
|