@active-reach/web-sdk 1.39.0 → 1.41.0
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/dist/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/{analytics-DoHdxjjh.mjs → analytics-C-Go3Pm5.mjs} +15 -1
- package/dist/{analytics-DoHdxjjh.mjs.map → analytics-C-Go3Pm5.mjs.map} +1 -1
- package/dist/core/analytics.d.ts +12 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/prefetch-bundle-client.d.ts +3 -0
- package/dist/core/prefetch-bundle-client.d.ts.map +1 -1
- package/dist/inapp/AegisInAppManager.d.ts +3 -9
- package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
- package/dist/inbox/AegisInbox.d.ts +3 -0
- package/dist/inbox/AegisInbox.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +165 -853
- package/dist/index.js.map +1 -1
- package/dist/react.js +1 -1
- package/dist/runtime/AegisMessageRuntime.d.ts +19 -11
- package/dist/runtime/AegisMessageRuntime.d.ts.map +1 -1
- package/dist/triggers/ContactScoresFetcher.d.ts +1 -0
- package/dist/triggers/ContactScoresFetcher.d.ts.map +1 -1
- package/dist/triggers/TriggerEngine.d.ts.map +1 -1
- package/dist/triggers/behaviouralTriggers.d.ts +41 -0
- package/dist/triggers/behaviouralTriggers.d.ts.map +1 -0
- package/dist/widgets/AegisWidgetManager.d.ts +8 -72
- package/dist/widgets/AegisWidgetManager.d.ts.map +1 -1
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Storage, r as redactUrl, l as logger, A as Aegis } from "./analytics-
|
|
2
|
-
import { B, E, N, R, U, m } from "./analytics-
|
|
1
|
+
import { S as Storage, r as redactUrl, l as logger, A as Aegis } from "./analytics-C-Go3Pm5.mjs";
|
|
2
|
+
import { B, E, N, R, U, m } from "./analytics-C-Go3Pm5.mjs";
|
|
3
3
|
import { AegisWebPush } from "./push/AegisWebPush.js";
|
|
4
4
|
function debounce(func, wait) {
|
|
5
5
|
let timeoutId = null;
|
|
@@ -5037,22 +5037,35 @@ class TriggerEngine {
|
|
|
5037
5037
|
callbacks.delete(callback);
|
|
5038
5038
|
}
|
|
5039
5039
|
}
|
|
5040
|
+
// A register* call after start() takes effect at once: in-app campaigns
|
|
5041
|
+
// (and so their triggers) arrive after the engine is already running.
|
|
5040
5042
|
registerScrollDepth(depthPercent) {
|
|
5043
|
+
const firstTarget = this.scrollDepthTargets.size === 0;
|
|
5041
5044
|
this.scrollDepthTargets.add(depthPercent);
|
|
5045
|
+
if (!this.isStarted) return;
|
|
5046
|
+
if (firstTarget) {
|
|
5047
|
+
this.attachScrollListener();
|
|
5048
|
+
} else {
|
|
5049
|
+
this.handleScroll();
|
|
5050
|
+
}
|
|
5042
5051
|
}
|
|
5043
5052
|
registerTimeOnPage(seconds) {
|
|
5044
5053
|
if (!this.timeOnPageTargets.has(seconds)) {
|
|
5054
|
+
const elapsedMs = this.isStarted && this.pageLoadTime ? Date.now() - this.pageLoadTime : 0;
|
|
5045
5055
|
const timerId = window.setTimeout(() => {
|
|
5046
5056
|
this.emit(`time_on_page_${seconds}`, {
|
|
5047
5057
|
seconds,
|
|
5048
5058
|
page_url: redactUrl(window.location.href)
|
|
5049
5059
|
});
|
|
5050
5060
|
this.timeOnPageTargets.delete(seconds);
|
|
5051
|
-
}, seconds * 1e3);
|
|
5061
|
+
}, Math.max(0, seconds * 1e3 - elapsedMs));
|
|
5052
5062
|
this.timeOnPageTargets.set(seconds, timerId);
|
|
5053
5063
|
}
|
|
5054
5064
|
}
|
|
5055
5065
|
registerExitIntent() {
|
|
5066
|
+
if (this.isStarted && !this.exitIntentEnabled) {
|
|
5067
|
+
this.attachExitIntentListener();
|
|
5068
|
+
}
|
|
5056
5069
|
this.exitIntentEnabled = true;
|
|
5057
5070
|
}
|
|
5058
5071
|
registerInactivity(idleSeconds) {
|
|
@@ -5064,13 +5077,16 @@ class TriggerEngine {
|
|
|
5064
5077
|
idle_seconds: idleSeconds,
|
|
5065
5078
|
actual_idle_time: idleTime
|
|
5066
5079
|
});
|
|
5080
|
+
this.inactivityTargets.delete(idleSeconds);
|
|
5067
5081
|
}
|
|
5068
|
-
this.inactivityTargets.delete(idleSeconds);
|
|
5069
5082
|
}, idleSeconds * 1e3);
|
|
5070
5083
|
this.inactivityTargets.set(idleSeconds, timerId);
|
|
5071
5084
|
}
|
|
5072
5085
|
}
|
|
5073
5086
|
registerScrollVelocity(config) {
|
|
5087
|
+
if (this.isStarted && !this.scrollVelocityEnabled) {
|
|
5088
|
+
this.attachScrollVelocityListener();
|
|
5089
|
+
}
|
|
5074
5090
|
this.scrollVelocityEnabled = true;
|
|
5075
5091
|
if (config) {
|
|
5076
5092
|
this.scrollVelocityConfig = {
|
|
@@ -6013,6 +6029,70 @@ function markReturningVisitor() {
|
|
|
6013
6029
|
} catch {
|
|
6014
6030
|
}
|
|
6015
6031
|
}
|
|
6032
|
+
const DEFAULT_SCROLL_DEPTH_PERCENT = 50;
|
|
6033
|
+
const DEFAULT_TIME_ON_PAGE_SECONDS = 30;
|
|
6034
|
+
const DEFAULT_IDLE_SECONDS = 30;
|
|
6035
|
+
function positiveNumber(raw, fallback) {
|
|
6036
|
+
const n = Number(raw);
|
|
6037
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
6038
|
+
}
|
|
6039
|
+
function behaviouralTriggerEvent(trigger) {
|
|
6040
|
+
if (!trigger) return null;
|
|
6041
|
+
const cfg = trigger.config || {};
|
|
6042
|
+
switch (trigger.type) {
|
|
6043
|
+
case "exit_intent":
|
|
6044
|
+
return "exit_intent";
|
|
6045
|
+
case "scroll_depth":
|
|
6046
|
+
return `scroll_depth_${positiveNumber(cfg.depth_percent, DEFAULT_SCROLL_DEPTH_PERCENT)}`;
|
|
6047
|
+
case "time_on_page":
|
|
6048
|
+
return `time_on_page_${positiveNumber(cfg.seconds, DEFAULT_TIME_ON_PAGE_SECONDS)}`;
|
|
6049
|
+
case "inactivity":
|
|
6050
|
+
return `inactivity_${positiveNumber(cfg.idle_seconds, DEFAULT_IDLE_SECONDS)}`;
|
|
6051
|
+
default:
|
|
6052
|
+
return null;
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
function triggerFromEngineEvent(eventName) {
|
|
6056
|
+
if (eventName === "exit_intent") return { type: "exit_intent" };
|
|
6057
|
+
const match = /^(inactivity|time_on_page|scroll_depth)_(\d+)$/.exec(eventName);
|
|
6058
|
+
if (!match) return null;
|
|
6059
|
+
const n = Number(match[2]);
|
|
6060
|
+
if (match[1] === "inactivity") return { type: "inactivity", config: { idle_seconds: n } };
|
|
6061
|
+
if (match[1] === "time_on_page") return { type: "time_on_page", config: { seconds: n } };
|
|
6062
|
+
return { type: "scroll_depth", config: { depth_percent: n } };
|
|
6063
|
+
}
|
|
6064
|
+
function armBehaviouralTriggers(engine, triggers, armed, fire, isMobile) {
|
|
6065
|
+
const newlyArmed = [];
|
|
6066
|
+
for (const trigger of triggers) {
|
|
6067
|
+
const eventName = behaviouralTriggerEvent(trigger);
|
|
6068
|
+
if (!eventName || armed.has(eventName)) continue;
|
|
6069
|
+
armed.add(eventName);
|
|
6070
|
+
newlyArmed.push(eventName);
|
|
6071
|
+
const cfg = trigger.config || {};
|
|
6072
|
+
switch (trigger.type) {
|
|
6073
|
+
case "exit_intent":
|
|
6074
|
+
if (isMobile) {
|
|
6075
|
+
engine.registerScrollVelocity();
|
|
6076
|
+
engine.on("mobile_exit_intent", () => fire("exit_intent"));
|
|
6077
|
+
} else {
|
|
6078
|
+
engine.registerExitIntent();
|
|
6079
|
+
engine.on("exit_intent", () => fire("exit_intent"));
|
|
6080
|
+
}
|
|
6081
|
+
continue;
|
|
6082
|
+
case "scroll_depth":
|
|
6083
|
+
engine.registerScrollDepth(positiveNumber(cfg.depth_percent, DEFAULT_SCROLL_DEPTH_PERCENT));
|
|
6084
|
+
break;
|
|
6085
|
+
case "time_on_page":
|
|
6086
|
+
engine.registerTimeOnPage(positiveNumber(cfg.seconds, DEFAULT_TIME_ON_PAGE_SECONDS));
|
|
6087
|
+
break;
|
|
6088
|
+
case "inactivity":
|
|
6089
|
+
engine.registerInactivity(positiveNumber(cfg.idle_seconds, DEFAULT_IDLE_SECONDS));
|
|
6090
|
+
break;
|
|
6091
|
+
}
|
|
6092
|
+
engine.on(eventName, () => fire(eventName));
|
|
6093
|
+
}
|
|
6094
|
+
return newlyArmed;
|
|
6095
|
+
}
|
|
6016
6096
|
function readAnonIdFromStorage$1() {
|
|
6017
6097
|
if (typeof document === "undefined") return void 0;
|
|
6018
6098
|
try {
|
|
@@ -6108,7 +6188,6 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6108
6188
|
this.propertyId = config.propertyId;
|
|
6109
6189
|
this.debugMode = config.debugMode || false;
|
|
6110
6190
|
this.enableSSE = config.enableSSE === true;
|
|
6111
|
-
this.onInteractiveCampaign = config.onInteractiveCampaign;
|
|
6112
6191
|
this.onActionHandler = config.onAction;
|
|
6113
6192
|
if (config.productUrl) this.productUrl = config.productUrl;
|
|
6114
6193
|
this.getWorkspaceId = config.getWorkspaceId;
|
|
@@ -6536,6 +6615,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6536
6615
|
if (this.contactId) {
|
|
6537
6616
|
headers["X-Contact-ID"] = this.contactId;
|
|
6538
6617
|
}
|
|
6618
|
+
if (this.contactId && this.contactToken) {
|
|
6619
|
+
headers["X-Contact-Token"] = this.contactToken;
|
|
6620
|
+
}
|
|
6539
6621
|
Object.entries(headers).forEach(([key, value]) => {
|
|
6540
6622
|
url.searchParams.append(key, value);
|
|
6541
6623
|
});
|
|
@@ -6614,6 +6696,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
6614
6696
|
if (this.contactId) {
|
|
6615
6697
|
headers["X-Contact-ID"] = this.contactId;
|
|
6616
6698
|
}
|
|
6699
|
+
if (this.contactId && this.contactToken) {
|
|
6700
|
+
headers["X-Contact-Token"] = this.contactToken;
|
|
6701
|
+
}
|
|
6617
6702
|
if (this.organizationId) {
|
|
6618
6703
|
headers["X-Organization-ID"] = this.organizationId;
|
|
6619
6704
|
}
|
|
@@ -9030,6 +9115,9 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9030
9115
|
* matches config.product_id (string or string[])
|
|
9031
9116
|
* - `delay` : client-side setTimeout — evaluated at armeng
|
|
9032
9117
|
* time (kicked off from `displayCampaign`), not here.
|
|
9118
|
+
* - `exit_intent` / `scroll_depth` / `time_on_page` / `inactivity` : the
|
|
9119
|
+
* TriggerEngine event named by `behaviouralTriggerEvent`;
|
|
9120
|
+
* AegisMessageRuntime arms the engine and forwards it.
|
|
9033
9121
|
*/
|
|
9034
9122
|
onClientEvent(eventName, eventData = {}) {
|
|
9035
9123
|
for (const c of this.campaigns) {
|
|
@@ -9059,8 +9147,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9059
9147
|
);
|
|
9060
9148
|
return wanted.includes(actual);
|
|
9061
9149
|
}
|
|
9062
|
-
default:
|
|
9063
|
-
|
|
9150
|
+
default: {
|
|
9151
|
+
const behavioural = behaviouralTriggerEvent(trigger);
|
|
9152
|
+
return behavioural !== null && behavioural === eventName;
|
|
9153
|
+
}
|
|
9064
9154
|
}
|
|
9065
9155
|
}
|
|
9066
9156
|
displayCampaign(campaign) {
|
|
@@ -9284,11 +9374,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
9284
9374
|
break;
|
|
9285
9375
|
case "spin_wheel":
|
|
9286
9376
|
case "scratch_card":
|
|
9287
|
-
|
|
9288
|
-
this.onInteractiveCampaign(campaign);
|
|
9289
|
-
} else {
|
|
9290
|
-
this.renderGamificationOverlay(campaign, ic, bg, text);
|
|
9291
|
-
}
|
|
9377
|
+
this.renderGamificationOverlay(campaign, ic, bg, text);
|
|
9292
9378
|
break;
|
|
9293
9379
|
default:
|
|
9294
9380
|
this.log(`Unknown interactive sub_type: ${campaign.sub_type}`, "warn");
|
|
@@ -11918,6 +12004,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
11918
12004
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
11919
12005
|
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
11920
12006
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
12007
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
11921
12008
|
const workspaceId = (_a = this.getWorkspaceId) == null ? void 0 : _a.call(this);
|
|
11922
12009
|
const storedAnonId = readAnonIdFromStorage$1();
|
|
11923
12010
|
const identifiedUserId = this.userId && this.userId !== storedAnonId ? this.userId : void 0;
|
|
@@ -12018,6 +12105,7 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
12018
12105
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
12019
12106
|
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
12020
12107
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
12108
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
12021
12109
|
let retryable = true;
|
|
12022
12110
|
try {
|
|
12023
12111
|
const response = await fetch(
|
|
@@ -12530,6 +12618,9 @@ class ContactScoresFetcher {
|
|
|
12530
12618
|
"X-Contact-ID": contactId,
|
|
12531
12619
|
"X-User-ID": contactId
|
|
12532
12620
|
};
|
|
12621
|
+
if (opts.contactToken) {
|
|
12622
|
+
headers["X-Contact-Token"] = opts.contactToken;
|
|
12623
|
+
}
|
|
12533
12624
|
if (this.organizationId) {
|
|
12534
12625
|
headers["X-Organization-ID"] = this.organizationId;
|
|
12535
12626
|
}
|
|
@@ -12692,6 +12783,7 @@ class PrefetchBundleClient {
|
|
|
12692
12783
|
this.writeKey = options.writeKey;
|
|
12693
12784
|
this.organizationId = options.organizationId;
|
|
12694
12785
|
this.contactId = options.contactId;
|
|
12786
|
+
this.contactToken = options.contactToken;
|
|
12695
12787
|
this.userId = options.userId;
|
|
12696
12788
|
this.propertyId = options.propertyId;
|
|
12697
12789
|
this.abAssignments = options.abAssignments ?? {};
|
|
@@ -12756,7 +12848,10 @@ class PrefetchBundleClient {
|
|
|
12756
12848
|
let changed = false;
|
|
12757
12849
|
if (partial.contactId !== void 0 && partial.contactId !== this.contactId) {
|
|
12758
12850
|
this.contactId = partial.contactId;
|
|
12851
|
+
this.contactToken = partial.contactToken;
|
|
12759
12852
|
changed = true;
|
|
12853
|
+
} else if (partial.contactToken !== void 0 && partial.contactToken !== this.contactToken) {
|
|
12854
|
+
this.contactToken = partial.contactToken;
|
|
12760
12855
|
}
|
|
12761
12856
|
if (partial.userId !== void 0 && partial.userId !== this.userId) {
|
|
12762
12857
|
this.userId = partial.userId;
|
|
@@ -12829,6 +12924,7 @@ class PrefetchBundleClient {
|
|
|
12829
12924
|
};
|
|
12830
12925
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
12831
12926
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
12927
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
12832
12928
|
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
12833
12929
|
if (this.propertyId) headers["X-Property-Id"] = this.propertyId;
|
|
12834
12930
|
if (this.currentETag) headers["If-None-Match"] = this.currentETag;
|
|
@@ -12888,6 +12984,7 @@ class AegisInbox {
|
|
|
12888
12984
|
this.writeKey = config.writeKey;
|
|
12889
12985
|
this.organizationId = config.organizationId;
|
|
12890
12986
|
this.contactId = config.contactId;
|
|
12987
|
+
this.contactToken = config.contactToken;
|
|
12891
12988
|
this.propertyId = config.propertyId;
|
|
12892
12989
|
this.bundleClient = config.bundleClient;
|
|
12893
12990
|
this.unreadPollIntervalMs = config.unreadPollIntervalMs ?? DEFAULT_UNREAD_POLL;
|
|
@@ -12936,7 +13033,10 @@ class AegisInbox {
|
|
|
12936
13033
|
let changed = false;
|
|
12937
13034
|
if (partial.contactId !== void 0 && partial.contactId !== this.contactId) {
|
|
12938
13035
|
this.contactId = partial.contactId;
|
|
13036
|
+
this.contactToken = partial.contactToken;
|
|
12939
13037
|
changed = true;
|
|
13038
|
+
} else if (partial.contactToken !== void 0 && partial.contactToken !== this.contactToken) {
|
|
13039
|
+
this.contactToken = partial.contactToken;
|
|
12940
13040
|
}
|
|
12941
13041
|
if (partial.propertyId !== void 0 && partial.propertyId !== this.propertyId) {
|
|
12942
13042
|
this.propertyId = partial.propertyId;
|
|
@@ -13107,6 +13207,7 @@ class AegisInbox {
|
|
|
13107
13207
|
};
|
|
13108
13208
|
if (this.organizationId) h["X-Organization-ID"] = this.organizationId;
|
|
13109
13209
|
if (this.contactId) h["X-Contact-ID"] = this.contactId;
|
|
13210
|
+
if (this.contactId && this.contactToken) h["X-Contact-Token"] = this.contactToken;
|
|
13110
13211
|
if (this.propertyId) h["X-Property-Id"] = this.propertyId;
|
|
13111
13212
|
return h;
|
|
13112
13213
|
}
|
|
@@ -13221,6 +13322,7 @@ class AegisWidgetManager {
|
|
|
13221
13322
|
this.apiHost = config.apiHost || "https://api.aegis.ai";
|
|
13222
13323
|
this.userId = config.userId ?? readAnonIdFromStorage();
|
|
13223
13324
|
this.contactId = config.contactId;
|
|
13325
|
+
this.contactToken = config.contactToken;
|
|
13224
13326
|
this.organizationId = config.organizationId;
|
|
13225
13327
|
this.debugMode = config.debugMode || false;
|
|
13226
13328
|
this.triggerEngine = config.triggerEngine;
|
|
@@ -13243,48 +13345,16 @@ class AegisWidgetManager {
|
|
|
13243
13345
|
* configs so subsequent renders target the new identity. Does NOT
|
|
13244
13346
|
* re-render already-visible widgets — a full teardown requires destroy().
|
|
13245
13347
|
*/
|
|
13246
|
-
async updateContactId(contactId) {
|
|
13247
|
-
if (this.contactId === contactId) return;
|
|
13348
|
+
async updateContactId(contactId, contactToken) {
|
|
13349
|
+
if (this.contactId === contactId && this.contactToken === contactToken) return;
|
|
13248
13350
|
this.contactId = contactId;
|
|
13351
|
+
this.contactToken = contactToken;
|
|
13249
13352
|
this.log(`Contact ID updated → ${contactId ?? "(cleared)"}`);
|
|
13250
13353
|
if (this.isInitialized && !this.isDestroyed && this.enablePrefetch && contactId) {
|
|
13251
13354
|
await this.fetchPrefetchConfigs();
|
|
13252
13355
|
this.preloadWidgetAssets();
|
|
13253
13356
|
}
|
|
13254
13357
|
}
|
|
13255
|
-
/**
|
|
13256
|
-
* Render an in-app campaign whose sub_type routes it to the
|
|
13257
|
-
* gamification-widget code path (spin_wheel / scratch_card).
|
|
13258
|
-
* InAppCampaign is adapted to the minimal WidgetConfig shape that
|
|
13259
|
-
* the internal renderers already understand — no rewrite needed.
|
|
13260
|
-
*
|
|
13261
|
-
* AegisMessageRuntime wires this method as AegisInAppManager's
|
|
13262
|
-
* `onInteractiveCampaign` callback so impressions from the inbox
|
|
13263
|
-
* prefetch bundle actually land on screen.
|
|
13264
|
-
*/
|
|
13265
|
-
renderInteractiveCampaign(campaign) {
|
|
13266
|
-
if (this.isDestroyed) return;
|
|
13267
|
-
if (typeof document === "undefined") return;
|
|
13268
|
-
const subType = campaign.sub_type;
|
|
13269
|
-
if (subType !== "spin_wheel" && subType !== "scratch_card") {
|
|
13270
|
-
this.log(`renderInteractiveCampaign: unsupported sub_type "${subType}"`, true);
|
|
13271
|
-
return;
|
|
13272
|
-
}
|
|
13273
|
-
const widget = {
|
|
13274
|
-
widget_id: campaign.id,
|
|
13275
|
-
widget_type: subType,
|
|
13276
|
-
name: campaign.title,
|
|
13277
|
-
config: campaign.interactive_config ?? {},
|
|
13278
|
-
priority: campaign.priority ?? 0
|
|
13279
|
-
};
|
|
13280
|
-
if (this.renderedWidgets.has(widget.widget_id)) return;
|
|
13281
|
-
this.renderedWidgets.add(widget.widget_id);
|
|
13282
|
-
if (subType === "spin_wheel") {
|
|
13283
|
-
this.renderSpinWheel(widget);
|
|
13284
|
-
} else {
|
|
13285
|
-
this.renderScratchCard(widget);
|
|
13286
|
-
}
|
|
13287
|
-
}
|
|
13288
13358
|
/**
|
|
13289
13359
|
* Tear down everything the manager installed: trigger listeners (if
|
|
13290
13360
|
* we own the TriggerEngine), injected style sheets, and any rendered
|
|
@@ -13460,15 +13530,6 @@ class AegisWidgetManager {
|
|
|
13460
13530
|
this.log(`Preloading exit intent image: ${imageUrl}`);
|
|
13461
13531
|
}
|
|
13462
13532
|
}
|
|
13463
|
-
const spinWheelConfig = this.prefetchWidgetConfigs.spin_wheel;
|
|
13464
|
-
if (spinWheelConfig == null ? void 0 : spinWheelConfig.enabled) {
|
|
13465
|
-
const imageUrl = spinWheelConfig.image_url;
|
|
13466
|
-
if (imageUrl) {
|
|
13467
|
-
const img = new Image();
|
|
13468
|
-
img.src = imageUrl;
|
|
13469
|
-
this.log(`Preloading spin wheel image: ${imageUrl}`);
|
|
13470
|
-
}
|
|
13471
|
-
}
|
|
13472
13533
|
} catch (error) {
|
|
13473
13534
|
this.log(`Error preloading widget assets: ${error}`, true);
|
|
13474
13535
|
}
|
|
@@ -13481,6 +13542,7 @@ class AegisWidgetManager {
|
|
|
13481
13542
|
"X-Aegis-Write-Key": this.writeKey
|
|
13482
13543
|
};
|
|
13483
13544
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
13545
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
13484
13546
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
13485
13547
|
const response = await fetch(url, { headers, credentials: "include" });
|
|
13486
13548
|
if (!response.ok) {
|
|
@@ -13503,6 +13565,7 @@ class AegisWidgetManager {
|
|
|
13503
13565
|
};
|
|
13504
13566
|
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
13505
13567
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
13568
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
13506
13569
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
13507
13570
|
if (this.sourcePlatform) headers["X-Source-Platform"] = this.sourcePlatform;
|
|
13508
13571
|
const response = await fetch(url, { headers, credentials: "include" });
|
|
@@ -13563,67 +13626,7 @@ class AegisWidgetManager {
|
|
|
13563
13626
|
if (!this.enablePrefetch || !this.triggerEngine) {
|
|
13564
13627
|
return;
|
|
13565
13628
|
}
|
|
13566
|
-
const spinWheelConfig = this.prefetchWidgetConfigs.spin_wheel;
|
|
13567
13629
|
const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;
|
|
13568
|
-
if (spinWheelConfig && spinWheelConfig.enabled) {
|
|
13569
|
-
const isMobile2 = this.isMobileDevice();
|
|
13570
|
-
const mobileConfig2 = (exitIntentConfig == null ? void 0 : exitIntentConfig.mobile_triggers) || {};
|
|
13571
|
-
const handleSpinWheelIntent = () => {
|
|
13572
|
-
const widgetId = `prefetch_spin_wheel_${Date.now()}`;
|
|
13573
|
-
if (this.renderedWidgets.has(widgetId)) {
|
|
13574
|
-
return;
|
|
13575
|
-
}
|
|
13576
|
-
this.renderedWidgets.add(widgetId);
|
|
13577
|
-
const detectedCart = this.detectPlatformCart();
|
|
13578
|
-
if (spinWheelConfig.type === "cart_recovery" && detectedCart) {
|
|
13579
|
-
this.cartData = detectedCart;
|
|
13580
|
-
this.renderSpinWheelWidget(widgetId, spinWheelConfig);
|
|
13581
|
-
this.sendCartAbandonmentBeacon();
|
|
13582
|
-
} else if (spinWheelConfig.type === "lead_gen") {
|
|
13583
|
-
this.renderSpinWheelWidget(widgetId, spinWheelConfig);
|
|
13584
|
-
}
|
|
13585
|
-
};
|
|
13586
|
-
if (isMobile2) {
|
|
13587
|
-
const scrollVelocityEnabled = mobileConfig2.scroll_velocity !== false;
|
|
13588
|
-
if (scrollVelocityEnabled) {
|
|
13589
|
-
this.triggerEngine.registerScrollVelocity({
|
|
13590
|
-
threshold: mobileConfig2.scroll_threshold || 0.5,
|
|
13591
|
-
minScrollPosition: mobileConfig2.scroll_min_position || 100,
|
|
13592
|
-
cooldown: mobileConfig2.scroll_cooldown || 5e3
|
|
13593
|
-
});
|
|
13594
|
-
this.triggerEngine.on("mobile_exit_intent", handleSpinWheelIntent);
|
|
13595
|
-
this.log("Registered mobile scroll velocity trigger for spin wheel");
|
|
13596
|
-
}
|
|
13597
|
-
const idleTimerEnabled = mobileConfig2.idle_timer !== false;
|
|
13598
|
-
if (idleTimerEnabled) {
|
|
13599
|
-
const idleSeconds = mobileConfig2.idle_seconds || 15;
|
|
13600
|
-
this.triggerEngine.registerInactivity(idleSeconds);
|
|
13601
|
-
this.triggerEngine.on(`inactivity_${idleSeconds}`, handleSpinWheelIntent);
|
|
13602
|
-
this.log(`Registered mobile idle timer trigger for spin wheel: ${idleSeconds}s`);
|
|
13603
|
-
}
|
|
13604
|
-
const visibilityChangeEnabled = mobileConfig2.visibility_change !== false;
|
|
13605
|
-
if (visibilityChangeEnabled) {
|
|
13606
|
-
const detectedCart = this.detectPlatformCart();
|
|
13607
|
-
if (detectedCart && detectedCart.cart_items && detectedCart.cart_items.length > 0) {
|
|
13608
|
-
this.triggerEngine.registerVisibilityChange();
|
|
13609
|
-
this.triggerEngine.on("visibility_hidden", handleSpinWheelIntent);
|
|
13610
|
-
this.log("Registered mobile visibility change trigger for spin wheel (cart detected)");
|
|
13611
|
-
}
|
|
13612
|
-
}
|
|
13613
|
-
const backButtonEnabled = mobileConfig2.back_button === true;
|
|
13614
|
-
if (backButtonEnabled) {
|
|
13615
|
-
this.triggerEngine.registerBackButton();
|
|
13616
|
-
this.triggerEngine.on("back_button", handleSpinWheelIntent);
|
|
13617
|
-
this.log("Registered mobile back button trigger for spin wheel");
|
|
13618
|
-
}
|
|
13619
|
-
this.log(`Setup mobile spin wheel: type=${spinWheelConfig.type}, priority=${spinWheelConfig.priority}`);
|
|
13620
|
-
} else {
|
|
13621
|
-
this.triggerEngine.registerExitIntent();
|
|
13622
|
-
this.triggerEngine.on("exit_intent", handleSpinWheelIntent);
|
|
13623
|
-
this.log(`Setup desktop spin wheel exit intent: type=${spinWheelConfig.type}, priority=${spinWheelConfig.priority}`);
|
|
13624
|
-
}
|
|
13625
|
-
return;
|
|
13626
|
-
}
|
|
13627
13630
|
if (!exitIntentConfig || !exitIntentConfig.enabled) {
|
|
13628
13631
|
this.log("No exit intent config found in prefetch");
|
|
13629
13632
|
return;
|
|
@@ -13740,12 +13743,6 @@ class AegisWidgetManager {
|
|
|
13740
13743
|
case "chat_bubble":
|
|
13741
13744
|
this.renderChatBubble(widget);
|
|
13742
13745
|
break;
|
|
13743
|
-
case "spin_wheel":
|
|
13744
|
-
this.renderSpinWheel(widget);
|
|
13745
|
-
break;
|
|
13746
|
-
case "scratch_card":
|
|
13747
|
-
this.renderScratchCard(widget);
|
|
13748
|
-
break;
|
|
13749
13746
|
case "toast":
|
|
13750
13747
|
this.renderToast(widget);
|
|
13751
13748
|
break;
|
|
@@ -13814,531 +13811,6 @@ class AegisWidgetManager {
|
|
|
13814
13811
|
bubble.setAttribute("data-aegis-widget-root", "");
|
|
13815
13812
|
document.body.appendChild(bubble);
|
|
13816
13813
|
}
|
|
13817
|
-
renderSpinWheel(widget) {
|
|
13818
|
-
const overlay = document.createElement("div");
|
|
13819
|
-
overlay.className = "aegis-gamification-overlay";
|
|
13820
|
-
overlay.setAttribute("data-widget-id", widget.widget_id);
|
|
13821
|
-
overlay.style.cssText = `
|
|
13822
|
-
position: fixed;
|
|
13823
|
-
top: 0;
|
|
13824
|
-
left: 0;
|
|
13825
|
-
right: 0;
|
|
13826
|
-
bottom: 0;
|
|
13827
|
-
background: rgba(0, 0, 0, 0.7);
|
|
13828
|
-
z-index: 1000000;
|
|
13829
|
-
display: flex;
|
|
13830
|
-
align-items: center;
|
|
13831
|
-
justify-content: center;
|
|
13832
|
-
animation: aegisFadeIn 0.3s ease-out;
|
|
13833
|
-
`;
|
|
13834
|
-
const modal = document.createElement("div");
|
|
13835
|
-
modal.className = "aegis-spin-wheel-modal";
|
|
13836
|
-
modal.style.cssText = `
|
|
13837
|
-
background: white;
|
|
13838
|
-
border-radius: 16px;
|
|
13839
|
-
padding: 32px;
|
|
13840
|
-
max-width: 500px;
|
|
13841
|
-
width: 90%;
|
|
13842
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
|
13843
|
-
text-align: center;
|
|
13844
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
13845
|
-
animation: aegisScaleIn 0.3s ease-out;
|
|
13846
|
-
`;
|
|
13847
|
-
const title = document.createElement("h2");
|
|
13848
|
-
title.textContent = widget.config.title || "Spin to Win!";
|
|
13849
|
-
title.style.cssText = "margin: 0 0 16px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;";
|
|
13850
|
-
modal.appendChild(title);
|
|
13851
|
-
const description = document.createElement("p");
|
|
13852
|
-
description.textContent = widget.config.description || "Try your luck and win exclusive prizes!";
|
|
13853
|
-
description.style.cssText = "margin: 0 0 24px 0; font-size: 14px; color: #666;";
|
|
13854
|
-
modal.appendChild(description);
|
|
13855
|
-
const segments = Array.isArray(
|
|
13856
|
-
widget.config.segments
|
|
13857
|
-
) ? widget.config.segments : [
|
|
13858
|
-
{ label: "Prize 1", color: "#ff6b6b" },
|
|
13859
|
-
{ label: "Prize 2", color: "#feca57" },
|
|
13860
|
-
{ label: "Prize 3", color: "#48dbfb" },
|
|
13861
|
-
{ label: "Prize 4", color: "#ff6348" }
|
|
13862
|
-
];
|
|
13863
|
-
const SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
13864
|
-
const wheel = document.createElementNS(SVG_NS2, "svg");
|
|
13865
|
-
wheel.setAttribute("viewBox", "-160 -160 320 320");
|
|
13866
|
-
wheel.setAttribute("width", "300");
|
|
13867
|
-
wheel.setAttribute("height", "300");
|
|
13868
|
-
wheel.style.cssText = "margin: 0 auto 24px; display: block;";
|
|
13869
|
-
const n = segments.length;
|
|
13870
|
-
const anglePer = 2 * Math.PI / n;
|
|
13871
|
-
const radius = 140;
|
|
13872
|
-
for (let i = 0; i < n; i++) {
|
|
13873
|
-
const start = i * anglePer - Math.PI / 2;
|
|
13874
|
-
const end = start + anglePer;
|
|
13875
|
-
const x1 = Math.cos(start) * radius;
|
|
13876
|
-
const y1 = Math.sin(start) * radius;
|
|
13877
|
-
const x2 = Math.cos(end) * radius;
|
|
13878
|
-
const y2 = Math.sin(end) * radius;
|
|
13879
|
-
const largeArc = anglePer > Math.PI ? 1 : 0;
|
|
13880
|
-
const path = document.createElementNS(SVG_NS2, "path");
|
|
13881
|
-
path.setAttribute(
|
|
13882
|
-
"d",
|
|
13883
|
-
`M 0 0 L ${x1.toFixed(2)} ${y1.toFixed(2)} A ${radius} ${radius} 0 ${largeArc} 1 ${x2.toFixed(2)} ${y2.toFixed(2)} Z`
|
|
13884
|
-
);
|
|
13885
|
-
path.setAttribute("fill", this.sanitizeColor(segments[i].color || "#cccccc"));
|
|
13886
|
-
path.setAttribute("stroke", "#ffffff");
|
|
13887
|
-
path.setAttribute("stroke-width", "2");
|
|
13888
|
-
wheel.appendChild(path);
|
|
13889
|
-
const labelAngle = start + anglePer / 2;
|
|
13890
|
-
const lx = Math.cos(labelAngle) * radius * 0.65;
|
|
13891
|
-
const ly = Math.sin(labelAngle) * radius * 0.65;
|
|
13892
|
-
const text = document.createElementNS(SVG_NS2, "text");
|
|
13893
|
-
text.setAttribute("x", lx.toFixed(2));
|
|
13894
|
-
text.setAttribute("y", ly.toFixed(2));
|
|
13895
|
-
text.setAttribute("fill", "#ffffff");
|
|
13896
|
-
text.setAttribute("font-size", "13");
|
|
13897
|
-
text.setAttribute("font-weight", "600");
|
|
13898
|
-
text.setAttribute("text-anchor", "middle");
|
|
13899
|
-
text.setAttribute("dominant-baseline", "middle");
|
|
13900
|
-
text.textContent = segments[i].label || `#${i + 1}`;
|
|
13901
|
-
wheel.appendChild(text);
|
|
13902
|
-
}
|
|
13903
|
-
modal.appendChild(wheel);
|
|
13904
|
-
const spinButton = document.createElement("button");
|
|
13905
|
-
spinButton.textContent = "SPIN NOW!";
|
|
13906
|
-
spinButton.style.cssText = `
|
|
13907
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
13908
|
-
color: white;
|
|
13909
|
-
border: none;
|
|
13910
|
-
padding: 16px 48px;
|
|
13911
|
-
border-radius: 8px;
|
|
13912
|
-
font-weight: 700;
|
|
13913
|
-
font-size: 16px;
|
|
13914
|
-
cursor: pointer;
|
|
13915
|
-
margin-bottom: 16px;
|
|
13916
|
-
`;
|
|
13917
|
-
spinButton.addEventListener("click", async () => {
|
|
13918
|
-
spinButton.disabled = true;
|
|
13919
|
-
spinButton.textContent = "SPINNING...";
|
|
13920
|
-
try {
|
|
13921
|
-
const prize = await this.generatePrize(widget.config.config_id);
|
|
13922
|
-
wheel.style.animation = "aegisSpin 2s ease-out";
|
|
13923
|
-
setTimeout(() => {
|
|
13924
|
-
this.showPrizeResult(modal, prize);
|
|
13925
|
-
}, 2e3);
|
|
13926
|
-
} catch (error) {
|
|
13927
|
-
this.log(`Error generating prize: ${error}`, true);
|
|
13928
|
-
spinButton.disabled = false;
|
|
13929
|
-
spinButton.textContent = "TRY AGAIN";
|
|
13930
|
-
}
|
|
13931
|
-
});
|
|
13932
|
-
modal.appendChild(spinButton);
|
|
13933
|
-
const closeButton = document.createElement("button");
|
|
13934
|
-
closeButton.textContent = "×";
|
|
13935
|
-
closeButton.setAttribute("aria-label", "Close");
|
|
13936
|
-
closeButton.style.cssText = `
|
|
13937
|
-
position: absolute;
|
|
13938
|
-
top: 16px;
|
|
13939
|
-
right: 16px;
|
|
13940
|
-
background: transparent;
|
|
13941
|
-
border: none;
|
|
13942
|
-
font-size: 28px;
|
|
13943
|
-
cursor: pointer;
|
|
13944
|
-
color: #999;
|
|
13945
|
-
`;
|
|
13946
|
-
closeButton.addEventListener("click", () => {
|
|
13947
|
-
this.trackEvent(widget.widget_id, "dismiss");
|
|
13948
|
-
document.body.removeChild(overlay);
|
|
13949
|
-
this.renderedWidgets.delete(widget.widget_id);
|
|
13950
|
-
});
|
|
13951
|
-
modal.style.position = "relative";
|
|
13952
|
-
modal.appendChild(closeButton);
|
|
13953
|
-
overlay.appendChild(modal);
|
|
13954
|
-
this.addAnimationStyles();
|
|
13955
|
-
overlay.setAttribute("data-aegis-widget-root", "");
|
|
13956
|
-
document.body.appendChild(overlay);
|
|
13957
|
-
}
|
|
13958
|
-
renderSpinWheelWidget(widgetId, config) {
|
|
13959
|
-
if (this.isDestroyed) return;
|
|
13960
|
-
const cart = this.detectPlatformCart();
|
|
13961
|
-
if (config.type === "cart_recovery" && !cart) {
|
|
13962
|
-
this.log("Spin wheel requires cart, but none detected");
|
|
13963
|
-
return;
|
|
13964
|
-
}
|
|
13965
|
-
if (cart) {
|
|
13966
|
-
this.cartData = cart;
|
|
13967
|
-
}
|
|
13968
|
-
const customization = this.cssCustomization.spinWheel || {};
|
|
13969
|
-
const accentColor = customization.accentColor || config.accent_color || "#FF6B6B";
|
|
13970
|
-
const backgroundColor = customization.backgroundColor || config.background_color || "#FFFFFF";
|
|
13971
|
-
const textColor = customization.textColor || config.text_color || "#333333";
|
|
13972
|
-
const buttonColor = customization.buttonColor || config.button_color || accentColor;
|
|
13973
|
-
const wheelColors = customization.wheelColors || ["#FF6B6B", "#4ECDC4", "#FFE66D", "#95E1D3"];
|
|
13974
|
-
const overlay = document.createElement("div");
|
|
13975
|
-
overlay.className = "aegis-spin-wheel-overlay";
|
|
13976
|
-
overlay.setAttribute("data-widget-id", widgetId);
|
|
13977
|
-
overlay.style.cssText = `
|
|
13978
|
-
position: fixed;
|
|
13979
|
-
top: 0;
|
|
13980
|
-
left: 0;
|
|
13981
|
-
width: 100%;
|
|
13982
|
-
height: 100%;
|
|
13983
|
-
background: rgba(0, 0, 0, 0.7);
|
|
13984
|
-
display: flex;
|
|
13985
|
-
align-items: center;
|
|
13986
|
-
justify-content: center;
|
|
13987
|
-
z-index: 999999;
|
|
13988
|
-
animation: aegisFadeIn 0.3s ease;
|
|
13989
|
-
`;
|
|
13990
|
-
const modal = document.createElement("div");
|
|
13991
|
-
modal.className = "aegis-spin-wheel-modal";
|
|
13992
|
-
modal.style.cssText = `
|
|
13993
|
-
background: ${this.sanitizeColor(backgroundColor)};
|
|
13994
|
-
border-radius: 16px;
|
|
13995
|
-
padding: 32px;
|
|
13996
|
-
max-width: 500px;
|
|
13997
|
-
width: 90%;
|
|
13998
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
13999
|
-
position: relative;
|
|
14000
|
-
animation: aegisScaleIn 0.4s ease;
|
|
14001
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
14002
|
-
`;
|
|
14003
|
-
const closeBtn = document.createElement("button");
|
|
14004
|
-
closeBtn.innerHTML = lucideSvg("x", { size: 16 });
|
|
14005
|
-
closeBtn.setAttribute("aria-label", "Close");
|
|
14006
|
-
closeBtn.className = "aegis-spin-wheel-close";
|
|
14007
|
-
closeBtn.style.cssText = `
|
|
14008
|
-
position: absolute;
|
|
14009
|
-
top: 16px;
|
|
14010
|
-
right: 16px;
|
|
14011
|
-
background: none;
|
|
14012
|
-
border: none;
|
|
14013
|
-
font-size: 24px;
|
|
14014
|
-
cursor: pointer;
|
|
14015
|
-
color: #999;
|
|
14016
|
-
`;
|
|
14017
|
-
closeBtn.onclick = () => {
|
|
14018
|
-
this.trackEvent(widgetId, "dismiss", { type: "spin_wheel" });
|
|
14019
|
-
document.body.removeChild(overlay);
|
|
14020
|
-
this.renderedWidgets.delete(widgetId);
|
|
14021
|
-
};
|
|
14022
|
-
modal.appendChild(closeBtn);
|
|
14023
|
-
const title = document.createElement("h2");
|
|
14024
|
-
title.textContent = this.interpolateCartVariables(config.title || "Spin to Win!");
|
|
14025
|
-
title.style.cssText = `
|
|
14026
|
-
margin: 0 0 16px 0;
|
|
14027
|
-
font-size: 28px;
|
|
14028
|
-
font-weight: bold;
|
|
14029
|
-
text-align: center;
|
|
14030
|
-
color: ${this.sanitizeColor(textColor)};
|
|
14031
|
-
`;
|
|
14032
|
-
modal.appendChild(title);
|
|
14033
|
-
if (cart) {
|
|
14034
|
-
const subtitle = document.createElement("p");
|
|
14035
|
-
subtitle.textContent = `Complete your ${cart.cart_currency} ${cart.cart_total.toFixed(2)} order and save!`;
|
|
14036
|
-
subtitle.style.cssText = `
|
|
14037
|
-
margin: 0 0 24px 0;
|
|
14038
|
-
font-size: 16px;
|
|
14039
|
-
text-align: center;
|
|
14040
|
-
color: #666;
|
|
14041
|
-
`;
|
|
14042
|
-
modal.appendChild(subtitle);
|
|
14043
|
-
}
|
|
14044
|
-
const wheelContainer = document.createElement("div");
|
|
14045
|
-
wheelContainer.className = "aegis-wheel-container";
|
|
14046
|
-
wheelContainer.style.cssText = `
|
|
14047
|
-
width: 300px;
|
|
14048
|
-
height: 300px;
|
|
14049
|
-
margin: 0 auto 24px auto;
|
|
14050
|
-
background: conic-gradient(
|
|
14051
|
-
from 0deg,
|
|
14052
|
-
${wheelColors[0]} 0deg 90deg,
|
|
14053
|
-
${wheelColors[1]} 90deg 180deg,
|
|
14054
|
-
${wheelColors[2]} 180deg 270deg,
|
|
14055
|
-
${wheelColors[3]} 270deg 360deg
|
|
14056
|
-
);
|
|
14057
|
-
border-radius: 50%;
|
|
14058
|
-
position: relative;
|
|
14059
|
-
display: flex;
|
|
14060
|
-
align-items: center;
|
|
14061
|
-
justify-content: center;
|
|
14062
|
-
`;
|
|
14063
|
-
const form = document.createElement("form");
|
|
14064
|
-
form.className = "aegis-spin-form";
|
|
14065
|
-
form.style.cssText = "display: block;";
|
|
14066
|
-
const phoneInput = document.createElement("input");
|
|
14067
|
-
phoneInput.type = "tel";
|
|
14068
|
-
phoneInput.placeholder = "Enter your phone number";
|
|
14069
|
-
phoneInput.required = true;
|
|
14070
|
-
phoneInput.style.cssText = `
|
|
14071
|
-
width: 100%;
|
|
14072
|
-
padding: 14px;
|
|
14073
|
-
border: 2px solid #ddd;
|
|
14074
|
-
border-radius: 8px;
|
|
14075
|
-
font-size: 16px;
|
|
14076
|
-
margin-bottom: 12px;
|
|
14077
|
-
box-sizing: border-box;
|
|
14078
|
-
`;
|
|
14079
|
-
form.appendChild(phoneInput);
|
|
14080
|
-
const emailInput = document.createElement("input");
|
|
14081
|
-
emailInput.type = "email";
|
|
14082
|
-
emailInput.placeholder = "Enter your email (optional)";
|
|
14083
|
-
emailInput.style.cssText = `
|
|
14084
|
-
width: 100%;
|
|
14085
|
-
padding: 14px;
|
|
14086
|
-
border: 2px solid #ddd;
|
|
14087
|
-
border-radius: 8px;
|
|
14088
|
-
font-size: 16px;
|
|
14089
|
-
margin-bottom: 12px;
|
|
14090
|
-
box-sizing: border-box;
|
|
14091
|
-
`;
|
|
14092
|
-
form.appendChild(emailInput);
|
|
14093
|
-
const nameInput = document.createElement("input");
|
|
14094
|
-
nameInput.type = "text";
|
|
14095
|
-
nameInput.placeholder = "Enter your name (optional)";
|
|
14096
|
-
nameInput.style.cssText = `
|
|
14097
|
-
width: 100%;
|
|
14098
|
-
padding: 14px;
|
|
14099
|
-
border: 2px solid #ddd;
|
|
14100
|
-
border-radius: 8px;
|
|
14101
|
-
font-size: 16px;
|
|
14102
|
-
margin-bottom: 12px;
|
|
14103
|
-
box-sizing: border-box;
|
|
14104
|
-
`;
|
|
14105
|
-
form.appendChild(nameInput);
|
|
14106
|
-
const collectBirthday = config.collect_birthday === true;
|
|
14107
|
-
let birthdayInput = null;
|
|
14108
|
-
if (collectBirthday) {
|
|
14109
|
-
const birthdayId = `aegis-spin-dob-${widgetId}`;
|
|
14110
|
-
const birthdayLabel = document.createElement("label");
|
|
14111
|
-
birthdayLabel.htmlFor = birthdayId;
|
|
14112
|
-
birthdayLabel.textContent = "Birthday (optional) — for a birthday treat";
|
|
14113
|
-
birthdayLabel.style.cssText = `
|
|
14114
|
-
display: block;
|
|
14115
|
-
font-size: 12px;
|
|
14116
|
-
font-weight: 600;
|
|
14117
|
-
opacity: 0.75;
|
|
14118
|
-
margin-bottom: 4px;
|
|
14119
|
-
color: ${this.sanitizeColor(textColor)};
|
|
14120
|
-
`;
|
|
14121
|
-
form.appendChild(birthdayLabel);
|
|
14122
|
-
birthdayInput = document.createElement("input");
|
|
14123
|
-
birthdayInput.id = birthdayId;
|
|
14124
|
-
birthdayInput.type = "date";
|
|
14125
|
-
birthdayInput.max = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
14126
|
-
birthdayInput.style.cssText = `
|
|
14127
|
-
width: 100%;
|
|
14128
|
-
padding: 14px;
|
|
14129
|
-
border: 2px solid #ddd;
|
|
14130
|
-
border-radius: 8px;
|
|
14131
|
-
font-size: 16px;
|
|
14132
|
-
margin-bottom: 12px;
|
|
14133
|
-
box-sizing: border-box;
|
|
14134
|
-
`;
|
|
14135
|
-
form.appendChild(birthdayInput);
|
|
14136
|
-
}
|
|
14137
|
-
const submitButton = document.createElement("button");
|
|
14138
|
-
submitButton.type = "submit";
|
|
14139
|
-
submitButton.textContent = "Spin the Wheel!";
|
|
14140
|
-
submitButton.style.cssText = `
|
|
14141
|
-
width: 100%;
|
|
14142
|
-
padding: 14px;
|
|
14143
|
-
background: ${this.sanitizeColor(buttonColor)};
|
|
14144
|
-
color: white;
|
|
14145
|
-
border: none;
|
|
14146
|
-
border-radius: 8px;
|
|
14147
|
-
font-size: 16px;
|
|
14148
|
-
font-weight: 700;
|
|
14149
|
-
cursor: pointer;
|
|
14150
|
-
`;
|
|
14151
|
-
form.appendChild(submitButton);
|
|
14152
|
-
const errorMessage = document.createElement("div");
|
|
14153
|
-
errorMessage.className = "aegis-spin-error";
|
|
14154
|
-
errorMessage.style.cssText = `
|
|
14155
|
-
color: #d32f2f;
|
|
14156
|
-
font-size: 14px;
|
|
14157
|
-
margin-top: 12px;
|
|
14158
|
-
text-align: center;
|
|
14159
|
-
display: none;
|
|
14160
|
-
`;
|
|
14161
|
-
form.appendChild(errorMessage);
|
|
14162
|
-
form.addEventListener("submit", async (e) => {
|
|
14163
|
-
e.preventDefault();
|
|
14164
|
-
const phone = phoneInput.value.trim();
|
|
14165
|
-
const email = emailInput.value.trim();
|
|
14166
|
-
const name = nameInput.value.trim();
|
|
14167
|
-
const dateOfBirth = birthdayInput ? birthdayInput.value.trim() : "";
|
|
14168
|
-
if (!this.validatePhone(phone)) {
|
|
14169
|
-
errorMessage.textContent = "Please enter a valid phone number (e.g., +1234567890)";
|
|
14170
|
-
errorMessage.style.display = "block";
|
|
14171
|
-
return;
|
|
14172
|
-
}
|
|
14173
|
-
if (email && !this.validateEmail(email)) {
|
|
14174
|
-
errorMessage.textContent = "Please enter a valid email address";
|
|
14175
|
-
errorMessage.style.display = "block";
|
|
14176
|
-
return;
|
|
14177
|
-
}
|
|
14178
|
-
errorMessage.style.display = "none";
|
|
14179
|
-
submitButton.disabled = true;
|
|
14180
|
-
submitButton.textContent = "Spinning...";
|
|
14181
|
-
wheelContainer.style.animation = "aegisSpin 2s ease-out";
|
|
14182
|
-
setTimeout(async () => {
|
|
14183
|
-
try {
|
|
14184
|
-
const prize = await this.submitSpinWheel({
|
|
14185
|
-
phone,
|
|
14186
|
-
email,
|
|
14187
|
-
name,
|
|
14188
|
-
dateOfBirth,
|
|
14189
|
-
cart,
|
|
14190
|
-
widgetId
|
|
14191
|
-
});
|
|
14192
|
-
this.showSpinWheelPrize(modal, prize);
|
|
14193
|
-
this.trackEvent(widgetId, "submit", {
|
|
14194
|
-
type: "spin_wheel",
|
|
14195
|
-
prize_label: prize.prize_label,
|
|
14196
|
-
has_email: !!email
|
|
14197
|
-
});
|
|
14198
|
-
} catch (error) {
|
|
14199
|
-
this.log(`Error submitting spin wheel: ${error}`, true);
|
|
14200
|
-
errorMessage.textContent = "Failed to submit. Please try again.";
|
|
14201
|
-
errorMessage.style.display = "block";
|
|
14202
|
-
submitButton.disabled = false;
|
|
14203
|
-
submitButton.textContent = "Spin the Wheel!";
|
|
14204
|
-
wheelContainer.style.animation = "";
|
|
14205
|
-
}
|
|
14206
|
-
}, 2e3);
|
|
14207
|
-
});
|
|
14208
|
-
modal.appendChild(wheelContainer);
|
|
14209
|
-
modal.appendChild(form);
|
|
14210
|
-
overlay.appendChild(modal);
|
|
14211
|
-
this.addAnimationStyles();
|
|
14212
|
-
overlay.setAttribute("data-aegis-widget-root", "");
|
|
14213
|
-
document.body.appendChild(overlay);
|
|
14214
|
-
this.trackEvent(widgetId, "show", { type: "spin_wheel" });
|
|
14215
|
-
}
|
|
14216
|
-
validatePhone(phone) {
|
|
14217
|
-
const e164Regex = /^\+?[1-9]\d{1,14}$/;
|
|
14218
|
-
return e164Regex.test(phone.replace(/[\s()-]/g, ""));
|
|
14219
|
-
}
|
|
14220
|
-
validateEmail(email) {
|
|
14221
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
14222
|
-
return emailRegex.test(email);
|
|
14223
|
-
}
|
|
14224
|
-
async submitSpinWheel(data) {
|
|
14225
|
-
var _a, _b, _c, _d, _e;
|
|
14226
|
-
const url = `${this.apiHost}/v1/widgets/spin-wheel/submit`;
|
|
14227
|
-
const headers = {
|
|
14228
|
-
"X-Aegis-Write-Key": this.writeKey,
|
|
14229
|
-
"Content-Type": "application/json"
|
|
14230
|
-
};
|
|
14231
|
-
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
14232
|
-
const geoRegion = this.detectGeoRegion();
|
|
14233
|
-
const deviceType = this.getDeviceType();
|
|
14234
|
-
const utmParams = this.getUTMParams();
|
|
14235
|
-
const body = {
|
|
14236
|
-
phone: data.phone.replace(/[\s()-]/g, ""),
|
|
14237
|
-
email: data.email || void 0,
|
|
14238
|
-
name: data.name || void 0,
|
|
14239
|
-
// Omitted when blank — the server distinguishes "not given" from a value
|
|
14240
|
-
// it could not parse, and neither is an empty string.
|
|
14241
|
-
date_of_birth: data.dateOfBirth || void 0,
|
|
14242
|
-
cart_id: ((_a = data.cart) == null ? void 0 : _a.cart_id) || `web_${Date.now()}`,
|
|
14243
|
-
cart_token: (_b = data.cart) == null ? void 0 : _b.cart_id,
|
|
14244
|
-
cart_total: ((_c = data.cart) == null ? void 0 : _c.cart_total) || 0,
|
|
14245
|
-
cart_currency: ((_d = data.cart) == null ? void 0 : _d.cart_currency) || "USD",
|
|
14246
|
-
cart_items: ((_e = data.cart) == null ? void 0 : _e.cart_items) || [],
|
|
14247
|
-
// Redacted: a stored cart URL is re-sent later as a recovery link, and a
|
|
14248
|
-
// credential in it (`?t=` / `?k=`) must not outlive this page view.
|
|
14249
|
-
cart_url: redactUrl(window.location.href),
|
|
14250
|
-
platform: "web",
|
|
14251
|
-
geo_region: geoRegion,
|
|
14252
|
-
device_type: deviceType,
|
|
14253
|
-
session_id: this.getSessionId() || void 0,
|
|
14254
|
-
anonymous_id: this.getAnonymousId() || void 0,
|
|
14255
|
-
utm_source: utmParams.utm_source,
|
|
14256
|
-
utm_medium: utmParams.utm_medium,
|
|
14257
|
-
utm_campaign: utmParams.utm_campaign
|
|
14258
|
-
};
|
|
14259
|
-
const response = await fetch(url, {
|
|
14260
|
-
method: "POST",
|
|
14261
|
-
headers,
|
|
14262
|
-
body: JSON.stringify(body),
|
|
14263
|
-
credentials: "include"
|
|
14264
|
-
});
|
|
14265
|
-
if (!response.ok) {
|
|
14266
|
-
const errorText = await response.text();
|
|
14267
|
-
throw new Error(`Failed to submit spin wheel: ${response.status} - ${errorText}`);
|
|
14268
|
-
}
|
|
14269
|
-
return await response.json();
|
|
14270
|
-
}
|
|
14271
|
-
showSpinWheelPrize(modal, prize) {
|
|
14272
|
-
modal.innerHTML = "";
|
|
14273
|
-
const resultContainer = document.createElement("div");
|
|
14274
|
-
resultContainer.style.cssText = "text-align: center; padding: 40px 20px;";
|
|
14275
|
-
const emoji = document.createElement("div");
|
|
14276
|
-
emoji.textContent = "🎉";
|
|
14277
|
-
emoji.style.cssText = "font-size: 60px; margin-bottom: 20px;";
|
|
14278
|
-
resultContainer.appendChild(emoji);
|
|
14279
|
-
const title = document.createElement("h2");
|
|
14280
|
-
title.textContent = "Congratulations!";
|
|
14281
|
-
title.style.cssText = "margin: 0 0 12px 0; font-size: 28px; font-weight: 700; color: #1a73e8;";
|
|
14282
|
-
resultContainer.appendChild(title);
|
|
14283
|
-
const prizeLabel = document.createElement("p");
|
|
14284
|
-
prizeLabel.textContent = prize.prize_label;
|
|
14285
|
-
prizeLabel.style.cssText = "margin: 0 0 20px 0; font-size: 20px; font-weight: 600; color: #333;";
|
|
14286
|
-
resultContainer.appendChild(prizeLabel);
|
|
14287
|
-
if (prize.coupon_code) {
|
|
14288
|
-
const couponContainer = document.createElement("div");
|
|
14289
|
-
couponContainer.style.cssText = `
|
|
14290
|
-
background: #f5f5f5;
|
|
14291
|
-
padding: 16px;
|
|
14292
|
-
border-radius: 8px;
|
|
14293
|
-
margin-bottom: 20px;
|
|
14294
|
-
border: 2px dashed #1a73e8;
|
|
14295
|
-
`;
|
|
14296
|
-
const couponLabel = document.createElement("div");
|
|
14297
|
-
couponLabel.textContent = "Your coupon code:";
|
|
14298
|
-
couponLabel.style.cssText = "font-size: 12px; color: #666; margin-bottom: 8px;";
|
|
14299
|
-
couponContainer.appendChild(couponLabel);
|
|
14300
|
-
const couponCode = document.createElement("div");
|
|
14301
|
-
couponCode.textContent = prize.coupon_code;
|
|
14302
|
-
couponCode.style.cssText = "font-size: 24px; font-weight: 700; color: #1a73e8; letter-spacing: 2px;";
|
|
14303
|
-
couponContainer.appendChild(couponCode);
|
|
14304
|
-
resultContainer.appendChild(couponContainer);
|
|
14305
|
-
}
|
|
14306
|
-
const message = document.createElement("p");
|
|
14307
|
-
message.textContent = "Check your WhatsApp/SMS for your discount code and cart link!";
|
|
14308
|
-
message.style.cssText = "margin: 0 0 20px 0; font-size: 14px; color: #666;";
|
|
14309
|
-
resultContainer.appendChild(message);
|
|
14310
|
-
const closeButton = document.createElement("button");
|
|
14311
|
-
closeButton.textContent = "Close";
|
|
14312
|
-
closeButton.style.cssText = `
|
|
14313
|
-
background: #1a73e8;
|
|
14314
|
-
color: white;
|
|
14315
|
-
border: none;
|
|
14316
|
-
padding: 12px 32px;
|
|
14317
|
-
border-radius: 8px;
|
|
14318
|
-
font-weight: 600;
|
|
14319
|
-
font-size: 14px;
|
|
14320
|
-
cursor: pointer;
|
|
14321
|
-
`;
|
|
14322
|
-
closeButton.addEventListener("click", () => {
|
|
14323
|
-
const overlay = modal.parentElement;
|
|
14324
|
-
if (overlay && document.body.contains(overlay)) {
|
|
14325
|
-
document.body.removeChild(overlay);
|
|
14326
|
-
}
|
|
14327
|
-
});
|
|
14328
|
-
resultContainer.appendChild(closeButton);
|
|
14329
|
-
modal.appendChild(resultContainer);
|
|
14330
|
-
}
|
|
14331
|
-
detectGeoRegion() {
|
|
14332
|
-
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
14333
|
-
if (timezone.includes("America")) return "north_america";
|
|
14334
|
-
if (timezone.includes("Europe")) return "europe";
|
|
14335
|
-
if (timezone.includes("Asia/Kolkata") || timezone.includes("Asia/Calcutta")) return "india";
|
|
14336
|
-
if (timezone.includes("Asia/Singapore") || timezone.includes("Asia/Bangkok") || timezone.includes("Asia/Jakarta")) return "southeast_asia";
|
|
14337
|
-
if (timezone.includes("Asia/Dubai") || timezone.includes("Asia/Riyadh")) return "middle_east";
|
|
14338
|
-
if (timezone.includes("America") && (timezone.includes("Sao_Paulo") || timezone.includes("Buenos_Aires"))) return "latin_america";
|
|
14339
|
-
if (timezone.includes("Australia") || timezone.includes("Pacific/Auckland")) return "oceania";
|
|
14340
|
-
return "north_america";
|
|
14341
|
-
}
|
|
14342
13814
|
getUTMParams() {
|
|
14343
13815
|
const params = new URLSearchParams(window.location.search);
|
|
14344
13816
|
return {
|
|
@@ -14347,119 +13819,6 @@ class AegisWidgetManager {
|
|
|
14347
13819
|
utm_campaign: params.get("utm_campaign") || void 0
|
|
14348
13820
|
};
|
|
14349
13821
|
}
|
|
14350
|
-
getSessionId() {
|
|
14351
|
-
return sessionStorage.getItem("aegis_session_id");
|
|
14352
|
-
}
|
|
14353
|
-
getAnonymousId() {
|
|
14354
|
-
return localStorage.getItem("aegis_anonymous_id");
|
|
14355
|
-
}
|
|
14356
|
-
renderScratchCard(widget) {
|
|
14357
|
-
const overlay = document.createElement("div");
|
|
14358
|
-
overlay.className = "aegis-gamification-overlay";
|
|
14359
|
-
overlay.setAttribute("data-widget-id", widget.widget_id);
|
|
14360
|
-
overlay.style.cssText = `
|
|
14361
|
-
position: fixed;
|
|
14362
|
-
top: 0;
|
|
14363
|
-
left: 0;
|
|
14364
|
-
right: 0;
|
|
14365
|
-
bottom: 0;
|
|
14366
|
-
background: rgba(0, 0, 0, 0.7);
|
|
14367
|
-
z-index: 1000000;
|
|
14368
|
-
display: flex;
|
|
14369
|
-
align-items: center;
|
|
14370
|
-
justify-content: center;
|
|
14371
|
-
animation: aegisFadeIn 0.3s ease-out;
|
|
14372
|
-
`;
|
|
14373
|
-
const modal = document.createElement("div");
|
|
14374
|
-
modal.className = "aegis-scratch-card-modal";
|
|
14375
|
-
modal.style.cssText = `
|
|
14376
|
-
background: white;
|
|
14377
|
-
border-radius: 16px;
|
|
14378
|
-
padding: 32px;
|
|
14379
|
-
max-width: 500px;
|
|
14380
|
-
width: 90%;
|
|
14381
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
|
14382
|
-
text-align: center;
|
|
14383
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
14384
|
-
animation: aegisScaleIn 0.3s ease-out;
|
|
14385
|
-
`;
|
|
14386
|
-
const title = document.createElement("h2");
|
|
14387
|
-
title.textContent = widget.config.title || "Scratch & Win!";
|
|
14388
|
-
title.style.cssText = "margin: 0 0 16px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;";
|
|
14389
|
-
modal.appendChild(title);
|
|
14390
|
-
const description = document.createElement("p");
|
|
14391
|
-
description.textContent = widget.config.description || "Scratch to reveal your prize!";
|
|
14392
|
-
description.style.cssText = "margin: 0 0 24px 0; font-size: 14px; color: #666;";
|
|
14393
|
-
modal.appendChild(description);
|
|
14394
|
-
const canvas = document.createElement("canvas");
|
|
14395
|
-
canvas.width = 300;
|
|
14396
|
-
canvas.height = 200;
|
|
14397
|
-
canvas.style.cssText = `
|
|
14398
|
-
border-radius: 8px;
|
|
14399
|
-
cursor: pointer;
|
|
14400
|
-
margin: 0 auto 24px;
|
|
14401
|
-
display: block;
|
|
14402
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
14403
|
-
`;
|
|
14404
|
-
modal.appendChild(canvas);
|
|
14405
|
-
const ctx = canvas.getContext("2d");
|
|
14406
|
-
if (ctx) {
|
|
14407
|
-
ctx.fillStyle = "#c0c0c0";
|
|
14408
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
14409
|
-
ctx.fillStyle = "#888";
|
|
14410
|
-
ctx.font = "20px Arial";
|
|
14411
|
-
ctx.textAlign = "center";
|
|
14412
|
-
ctx.fillText("Scratch here!", canvas.width / 2, canvas.height / 2);
|
|
14413
|
-
let isScratching = false;
|
|
14414
|
-
const scratch = (x, y) => {
|
|
14415
|
-
ctx.globalCompositeOperation = "destination-out";
|
|
14416
|
-
ctx.beginPath();
|
|
14417
|
-
ctx.arc(x, y, 20, 0, Math.PI * 2);
|
|
14418
|
-
ctx.fill();
|
|
14419
|
-
};
|
|
14420
|
-
canvas.addEventListener("mousedown", () => {
|
|
14421
|
-
isScratching = true;
|
|
14422
|
-
});
|
|
14423
|
-
canvas.addEventListener("mouseup", () => {
|
|
14424
|
-
isScratching = false;
|
|
14425
|
-
});
|
|
14426
|
-
canvas.addEventListener("mousemove", (e) => {
|
|
14427
|
-
if (isScratching) {
|
|
14428
|
-
const rect = canvas.getBoundingClientRect();
|
|
14429
|
-
scratch(e.clientX - rect.left, e.clientY - rect.top);
|
|
14430
|
-
}
|
|
14431
|
-
});
|
|
14432
|
-
}
|
|
14433
|
-
const scratchButton = document.createElement("button");
|
|
14434
|
-
scratchButton.textContent = "REVEAL PRIZE";
|
|
14435
|
-
scratchButton.style.cssText = `
|
|
14436
|
-
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
14437
|
-
color: white;
|
|
14438
|
-
border: none;
|
|
14439
|
-
padding: 16px 48px;
|
|
14440
|
-
border-radius: 8px;
|
|
14441
|
-
font-weight: 700;
|
|
14442
|
-
font-size: 16px;
|
|
14443
|
-
cursor: pointer;
|
|
14444
|
-
`;
|
|
14445
|
-
scratchButton.addEventListener("click", async () => {
|
|
14446
|
-
scratchButton.disabled = true;
|
|
14447
|
-
scratchButton.textContent = "REVEALING...";
|
|
14448
|
-
try {
|
|
14449
|
-
const prize = await this.generatePrize(widget.config.config_id);
|
|
14450
|
-
this.showPrizeResult(modal, prize);
|
|
14451
|
-
} catch (error) {
|
|
14452
|
-
this.log(`Error generating prize: ${error}`, true);
|
|
14453
|
-
scratchButton.disabled = false;
|
|
14454
|
-
scratchButton.textContent = "TRY AGAIN";
|
|
14455
|
-
}
|
|
14456
|
-
});
|
|
14457
|
-
modal.appendChild(scratchButton);
|
|
14458
|
-
overlay.appendChild(modal);
|
|
14459
|
-
this.addAnimationStyles();
|
|
14460
|
-
overlay.setAttribute("data-aegis-widget-root", "");
|
|
14461
|
-
document.body.appendChild(overlay);
|
|
14462
|
-
}
|
|
14463
13822
|
renderToast(widget) {
|
|
14464
13823
|
const { message, icon, duration } = widget.config;
|
|
14465
13824
|
const position = widget.position || "bottom_left";
|
|
@@ -14732,82 +14091,6 @@ class AegisWidgetManager {
|
|
|
14732
14091
|
overlay.setAttribute("data-aegis-widget-root", "");
|
|
14733
14092
|
document.body.appendChild(overlay);
|
|
14734
14093
|
}
|
|
14735
|
-
async generatePrize(configId) {
|
|
14736
|
-
const url = `${this.apiHost}/v1/widgets/gamification/generate-prize`;
|
|
14737
|
-
const headers = {
|
|
14738
|
-
"X-Aegis-Write-Key": this.writeKey,
|
|
14739
|
-
"Content-Type": "application/json"
|
|
14740
|
-
};
|
|
14741
|
-
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
14742
|
-
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
14743
|
-
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
14744
|
-
const response = await fetch(url, {
|
|
14745
|
-
method: "POST",
|
|
14746
|
-
headers,
|
|
14747
|
-
body: JSON.stringify({ config_id: configId }),
|
|
14748
|
-
credentials: "include"
|
|
14749
|
-
});
|
|
14750
|
-
if (!response.ok) {
|
|
14751
|
-
throw new Error(`Failed to generate prize: ${response.status}`);
|
|
14752
|
-
}
|
|
14753
|
-
return await response.json();
|
|
14754
|
-
}
|
|
14755
|
-
showPrizeResult(modal, prize) {
|
|
14756
|
-
modal.innerHTML = "";
|
|
14757
|
-
const resultContainer = document.createElement("div");
|
|
14758
|
-
resultContainer.style.cssText = "text-align: center; padding: 40px 20px;";
|
|
14759
|
-
const emoji = document.createElement("div");
|
|
14760
|
-
emoji.textContent = "🎉";
|
|
14761
|
-
emoji.style.cssText = "font-size: 60px; margin-bottom: 20px;";
|
|
14762
|
-
resultContainer.appendChild(emoji);
|
|
14763
|
-
const title = document.createElement("h2");
|
|
14764
|
-
title.textContent = "Congratulations!";
|
|
14765
|
-
title.style.cssText = "margin: 0 0 12px 0; font-size: 28px; font-weight: 700; color: #1a73e8;";
|
|
14766
|
-
resultContainer.appendChild(title);
|
|
14767
|
-
const prizeLabel = document.createElement("p");
|
|
14768
|
-
prizeLabel.textContent = prize.prize_label;
|
|
14769
|
-
prizeLabel.style.cssText = "margin: 0 0 20px 0; font-size: 20px; font-weight: 600; color: #333;";
|
|
14770
|
-
resultContainer.appendChild(prizeLabel);
|
|
14771
|
-
if (prize.coupon_code) {
|
|
14772
|
-
const couponContainer = document.createElement("div");
|
|
14773
|
-
couponContainer.style.cssText = `
|
|
14774
|
-
background: #f5f5f5;
|
|
14775
|
-
padding: 16px;
|
|
14776
|
-
border-radius: 8px;
|
|
14777
|
-
margin-bottom: 20px;
|
|
14778
|
-
border: 2px dashed #1a73e8;
|
|
14779
|
-
`;
|
|
14780
|
-
const couponLabel = document.createElement("div");
|
|
14781
|
-
couponLabel.textContent = "Your coupon code:";
|
|
14782
|
-
couponLabel.style.cssText = "font-size: 12px; color: #666; margin-bottom: 8px;";
|
|
14783
|
-
couponContainer.appendChild(couponLabel);
|
|
14784
|
-
const couponCode = document.createElement("div");
|
|
14785
|
-
couponCode.textContent = prize.coupon_code;
|
|
14786
|
-
couponCode.style.cssText = "font-size: 24px; font-weight: 700; color: #1a73e8; letter-spacing: 2px;";
|
|
14787
|
-
couponContainer.appendChild(couponCode);
|
|
14788
|
-
resultContainer.appendChild(couponContainer);
|
|
14789
|
-
}
|
|
14790
|
-
const closeButton = document.createElement("button");
|
|
14791
|
-
closeButton.textContent = "Close";
|
|
14792
|
-
closeButton.style.cssText = `
|
|
14793
|
-
background: #1a73e8;
|
|
14794
|
-
color: white;
|
|
14795
|
-
border: none;
|
|
14796
|
-
padding: 12px 32px;
|
|
14797
|
-
border-radius: 8px;
|
|
14798
|
-
font-weight: 600;
|
|
14799
|
-
font-size: 14px;
|
|
14800
|
-
cursor: pointer;
|
|
14801
|
-
`;
|
|
14802
|
-
closeButton.addEventListener("click", () => {
|
|
14803
|
-
const overlay = modal.parentElement;
|
|
14804
|
-
if (overlay && document.body.contains(overlay)) {
|
|
14805
|
-
document.body.removeChild(overlay);
|
|
14806
|
-
}
|
|
14807
|
-
});
|
|
14808
|
-
resultContainer.appendChild(closeButton);
|
|
14809
|
-
modal.appendChild(resultContainer);
|
|
14810
|
-
}
|
|
14811
14094
|
async submitFeedback(widgetId, data) {
|
|
14812
14095
|
var _a;
|
|
14813
14096
|
const url = `${this.apiHost}/v1/widgets/track-event`;
|
|
@@ -14817,6 +14100,7 @@ class AegisWidgetManager {
|
|
|
14817
14100
|
};
|
|
14818
14101
|
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
14819
14102
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
14103
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
14820
14104
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
14821
14105
|
const workspaceId = (_a = this.getWorkspaceId) == null ? void 0 : _a.call(this);
|
|
14822
14106
|
const response = await fetch(url, {
|
|
@@ -14844,6 +14128,7 @@ class AegisWidgetManager {
|
|
|
14844
14128
|
};
|
|
14845
14129
|
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
14846
14130
|
if (this.contactId) headers["X-Contact-ID"] = this.contactId;
|
|
14131
|
+
if (this.contactId && this.contactToken) headers["X-Contact-Token"] = this.contactToken;
|
|
14847
14132
|
if (this.organizationId) headers["X-Organization-ID"] = this.organizationId;
|
|
14848
14133
|
const workspaceId = (_a = this.getWorkspaceId) == null ? void 0 : _a.call(this);
|
|
14849
14134
|
await fetch(url, {
|
|
@@ -15361,8 +14646,9 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15361
14646
|
constructor(config) {
|
|
15362
14647
|
var _a;
|
|
15363
14648
|
this.initialized = false;
|
|
14649
|
+
this.armedInAppTriggers = /* @__PURE__ */ new Set();
|
|
15364
14650
|
{
|
|
15365
|
-
const v = "1.
|
|
14651
|
+
const v = "1.41.0";
|
|
15366
14652
|
try {
|
|
15367
14653
|
globalThis.__AEGIS_SDK_VERSION__ = v;
|
|
15368
14654
|
} catch {
|
|
@@ -15381,13 +14667,17 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15381
14667
|
}
|
|
15382
14668
|
const triggerEngine = config.triggerEngine ?? new TriggerEngine();
|
|
15383
14669
|
const ownsTriggerEngine = !config.triggerEngine;
|
|
14670
|
+
this.triggerEngine = triggerEngine;
|
|
14671
|
+
this.ownsTriggerEngine = ownsTriggerEngine;
|
|
15384
14672
|
this.contactId = config.contactId;
|
|
14673
|
+
this.contactToken = config.contactToken;
|
|
15385
14674
|
this.pairingApiHost = config.apiHost ?? "";
|
|
15386
14675
|
this.widgets = new AegisWidgetManager({
|
|
15387
14676
|
writeKey: config.writeKey,
|
|
15388
14677
|
apiHost: config.apiHost,
|
|
15389
14678
|
userId: config.userId,
|
|
15390
14679
|
contactId: config.contactId,
|
|
14680
|
+
contactToken: config.contactToken,
|
|
15391
14681
|
organizationId: config.organizationId,
|
|
15392
14682
|
debugMode: config.debugMode,
|
|
15393
14683
|
triggerEngine,
|
|
@@ -15415,12 +14705,6 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15415
14705
|
propertyId: config.propertyId,
|
|
15416
14706
|
debugMode: config.debugMode,
|
|
15417
14707
|
enableSSE: config.enableSSE,
|
|
15418
|
-
// Route gamification sub_types straight to the widget renderer —
|
|
15419
|
-
// replaces the old CustomEvent('aegis:render-widget') bridge which
|
|
15420
|
-
// had no listener and silently dropped impressions.
|
|
15421
|
-
onInteractiveCampaign: (campaign) => {
|
|
15422
|
-
this.widgets.renderInteractiveCampaign(campaign);
|
|
15423
|
-
},
|
|
15424
14708
|
getWorkspaceId: config.getWorkspaceId,
|
|
15425
14709
|
// First-party cart reader — renderers (progress-bar) read live cart
|
|
15426
14710
|
// state from here instead of `window.*` cart globals. See setCartState().
|
|
@@ -15459,13 +14743,36 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15459
14743
|
debugMode: config.debugMode,
|
|
15460
14744
|
...config.chat
|
|
15461
14745
|
});
|
|
15462
|
-
const proactive = ((_a = config.chat) == null ? void 0 : _a.proactiveTriggers) ?? [];
|
|
15463
|
-
|
|
15464
|
-
triggerEngine
|
|
14746
|
+
const proactive = (((_a = config.chat) == null ? void 0 : _a.proactiveTriggers) ?? []).map(triggerFromEngineEvent);
|
|
14747
|
+
armBehaviouralTriggers(
|
|
14748
|
+
triggerEngine,
|
|
14749
|
+
proactive,
|
|
14750
|
+
/* @__PURE__ */ new Set(),
|
|
14751
|
+
() => {
|
|
15465
14752
|
var _a2;
|
|
15466
14753
|
return (_a2 = this.chat) == null ? void 0 : _a2.openPanel();
|
|
15467
|
-
}
|
|
15468
|
-
|
|
14754
|
+
},
|
|
14755
|
+
_AegisMessageRuntime.hasNoHover()
|
|
14756
|
+
);
|
|
14757
|
+
}
|
|
14758
|
+
this.unsubscribeCampaignsLoaded = this.inApp.onCampaignsLoaded((campaigns) => {
|
|
14759
|
+
armBehaviouralTriggers(
|
|
14760
|
+
triggerEngine,
|
|
14761
|
+
campaigns.map((c) => c.client_trigger),
|
|
14762
|
+
this.armedInAppTriggers,
|
|
14763
|
+
(eventName) => this.inApp.onClientEvent(eventName),
|
|
14764
|
+
_AegisMessageRuntime.hasNoHover()
|
|
14765
|
+
);
|
|
14766
|
+
});
|
|
14767
|
+
}
|
|
14768
|
+
/** No hovering pointer (phones, tablets): exit intent is a fast scroll up,
|
|
14769
|
+
* not the mouse leaving the window. */
|
|
14770
|
+
static hasNoHover() {
|
|
14771
|
+
var _a, _b;
|
|
14772
|
+
try {
|
|
14773
|
+
return typeof window !== "undefined" && ((_b = (_a = window.matchMedia) == null ? void 0 : _a.call(window, "(hover: none)")) == null ? void 0 : _b.matches) === true;
|
|
14774
|
+
} catch {
|
|
14775
|
+
return false;
|
|
15469
14776
|
}
|
|
15470
14777
|
}
|
|
15471
14778
|
/**
|
|
@@ -15482,8 +14789,11 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15482
14789
|
this.widgets.initialize()
|
|
15483
14790
|
]);
|
|
15484
14791
|
(_a = this.chat) == null ? void 0 : _a.initialize();
|
|
14792
|
+
if (this.ownsTriggerEngine) {
|
|
14793
|
+
this.triggerEngine.start();
|
|
14794
|
+
}
|
|
15485
14795
|
if (this.contactId) {
|
|
15486
|
-
void this.contactScores.fetchForContact(this.contactId);
|
|
14796
|
+
void this.contactScores.fetchForContact(this.contactId, { contactToken: this.contactToken });
|
|
15487
14797
|
}
|
|
15488
14798
|
}
|
|
15489
14799
|
/**
|
|
@@ -15497,10 +14807,11 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15497
14807
|
async updateContactId(contactId, contactToken) {
|
|
15498
14808
|
var _a, _b, _c;
|
|
15499
14809
|
this.contactId = contactId;
|
|
14810
|
+
this.contactToken = contactToken;
|
|
15500
14811
|
(_b = (_a = this.inApp).updateContactId) == null ? void 0 : _b.call(_a, contactId, contactToken);
|
|
15501
14812
|
(_c = this.chat) == null ? void 0 : _c.updateContactId(contactId);
|
|
15502
|
-
await this.widgets.updateContactId(contactId);
|
|
15503
|
-
void this.contactScores.fetchForContact(contactId);
|
|
14813
|
+
await this.widgets.updateContactId(contactId, contactToken);
|
|
14814
|
+
void this.contactScores.fetchForContact(contactId, { contactToken });
|
|
15504
14815
|
}
|
|
15505
14816
|
/**
|
|
15506
14817
|
* Forward a client-side event (product_viewed, cart_idle_90s, etc.)
|
|
@@ -15690,6 +15001,7 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15690
15001
|
*/
|
|
15691
15002
|
destroy() {
|
|
15692
15003
|
var _a, _b, _c;
|
|
15004
|
+
this.unsubscribeCampaignsLoaded();
|
|
15693
15005
|
(_b = (_a = this.inApp).destroy) == null ? void 0 : _b.call(_a);
|
|
15694
15006
|
this.widgets.destroy();
|
|
15695
15007
|
(_c = this.chat) == null ? void 0 : _c.destroy();
|
|
@@ -15698,8 +15010,8 @@ const _AegisMessageRuntime = class _AegisMessageRuntime {
|
|
|
15698
15010
|
/**
|
|
15699
15011
|
* Current armed campaigns visible to the InAppManager. Accessible for
|
|
15700
15012
|
* the Prefetch Inspector and for debugging. WidgetManager's prefetched
|
|
15701
|
-
*
|
|
15702
|
-
*
|
|
15013
|
+
* exit-intent popup config is NOT in this list — it lives in
|
|
15014
|
+
* `this.widgets` and has its own lifecycle.
|
|
15703
15015
|
*/
|
|
15704
15016
|
getCampaigns() {
|
|
15705
15017
|
return this.inApp.campaigns ?? [];
|