@prosdevlab/experience-sdk-plugins 0.1.4 → 0.2.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/index.js CHANGED
@@ -42,7 +42,7 @@ function sanitizeHTML(html) {
42
42
  }
43
43
  }
44
44
  }
45
- const attrString = attrs.length > 0 ? " " + attrs.join(" ") : "";
45
+ const attrString = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
46
46
  let innerHTML = "";
47
47
  for (const child of Array.from(element.childNodes)) {
48
48
  innerHTML += sanitizeNode(child);
@@ -561,6 +561,176 @@ var debugPlugin = (plugin, instance, config) => {
561
561
  });
562
562
  }
563
563
  };
564
+
565
+ // src/exit-intent/exit-intent.ts
566
+ function isMobileDevice(userAgent) {
567
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
568
+ }
569
+ function hasMinTimeElapsed(pageLoadTime, minTime, currentTime) {
570
+ return currentTime - pageLoadTime >= minTime;
571
+ }
572
+ function addPositionToHistory(positions, newPosition, maxSize) {
573
+ const updated = [...positions, newPosition];
574
+ if (updated.length > maxSize) {
575
+ return updated.slice(1);
576
+ }
577
+ return updated;
578
+ }
579
+ function calculateVelocity(lastY, previousY) {
580
+ return Math.abs(lastY - previousY);
581
+ }
582
+ function shouldTriggerExitIntent(positions, sensitivity, relatedTarget) {
583
+ if (positions.length < 2) {
584
+ return { shouldTrigger: false, lastY: 0, previousY: 0, velocity: 0 };
585
+ }
586
+ if (relatedTarget && relatedTarget.nodeName !== "HTML") {
587
+ return { shouldTrigger: false, lastY: 0, previousY: 0, velocity: 0 };
588
+ }
589
+ const lastY = positions[positions.length - 1].y;
590
+ const previousY = positions[positions.length - 2].y;
591
+ const velocity = calculateVelocity(lastY, previousY);
592
+ const isMovingUp = lastY < previousY;
593
+ const isNearTop = lastY - velocity <= sensitivity;
594
+ return {
595
+ shouldTrigger: isMovingUp && isNearTop,
596
+ lastY,
597
+ previousY,
598
+ velocity
599
+ };
600
+ }
601
+ function createExitIntentEvent(lastY, previousY, velocity, pageLoadTime, timestamp) {
602
+ return {
603
+ timestamp,
604
+ lastY,
605
+ previousY,
606
+ velocity,
607
+ timeOnPage: timestamp - pageLoadTime
608
+ };
609
+ }
610
+ var exitIntentPlugin = (plugin, instance, config) => {
611
+ plugin.ns("experiences.exitIntent");
612
+ plugin.defaults({
613
+ exitIntent: {
614
+ sensitivity: 50,
615
+ minTimeOnPage: 2e3,
616
+ delay: 0,
617
+ positionHistorySize: 30,
618
+ disableOnMobile: true
619
+ }
620
+ });
621
+ const exitIntentConfig = config.get("exitIntent");
622
+ if (!exitIntentConfig) {
623
+ return;
624
+ }
625
+ let positions = [];
626
+ let triggered = false;
627
+ const pageLoadTime = Date.now();
628
+ let mouseMoveListener = null;
629
+ let mouseOutListener = null;
630
+ function shouldDisable() {
631
+ if (!exitIntentConfig?.disableOnMobile) {
632
+ return false;
633
+ }
634
+ return isMobileDevice(navigator.userAgent);
635
+ }
636
+ function trackPosition(e) {
637
+ const newPosition = { x: e.clientX, y: e.clientY };
638
+ const maxSize = exitIntentConfig?.positionHistorySize ?? 30;
639
+ positions = addPositionToHistory(positions, newPosition, maxSize);
640
+ }
641
+ function handleExitIntent(e) {
642
+ if (triggered) {
643
+ return;
644
+ }
645
+ const minTime = exitIntentConfig?.minTimeOnPage ?? 2e3;
646
+ if (!hasMinTimeElapsed(pageLoadTime, minTime, Date.now())) {
647
+ return;
648
+ }
649
+ const sensitivity = exitIntentConfig?.sensitivity ?? 50;
650
+ const relatedTarget = e.relatedTarget || e.toElement;
651
+ const result = shouldTriggerExitIntent(positions, sensitivity, relatedTarget);
652
+ if (result.shouldTrigger) {
653
+ triggered = true;
654
+ const eventPayload = createExitIntentEvent(
655
+ result.lastY,
656
+ result.previousY,
657
+ result.velocity,
658
+ pageLoadTime,
659
+ Date.now()
660
+ );
661
+ const delay = exitIntentConfig?.delay ?? 0;
662
+ if (delay > 0) {
663
+ setTimeout(() => {
664
+ instance.emit("trigger:exitIntent", eventPayload);
665
+ }, delay);
666
+ } else {
667
+ instance.emit("trigger:exitIntent", eventPayload);
668
+ }
669
+ try {
670
+ sessionStorage.setItem("xp:exitIntent:triggered", Date.now().toString());
671
+ } catch (_e) {
672
+ }
673
+ cleanup();
674
+ }
675
+ }
676
+ function cleanup() {
677
+ if (mouseMoveListener) {
678
+ document.removeEventListener("mousemove", mouseMoveListener);
679
+ mouseMoveListener = null;
680
+ }
681
+ if (mouseOutListener) {
682
+ document.removeEventListener("mouseout", mouseOutListener);
683
+ mouseOutListener = null;
684
+ }
685
+ }
686
+ function initialize() {
687
+ if (shouldDisable()) {
688
+ return;
689
+ }
690
+ try {
691
+ const storedTrigger = sessionStorage.getItem("xp:exitIntent:triggered");
692
+ if (storedTrigger) {
693
+ triggered = true;
694
+ return;
695
+ }
696
+ } catch (_e) {
697
+ }
698
+ mouseMoveListener = trackPosition;
699
+ mouseOutListener = handleExitIntent;
700
+ document.addEventListener("mousemove", mouseMoveListener);
701
+ document.addEventListener("mouseout", mouseOutListener);
702
+ }
703
+ plugin.expose({
704
+ exitIntent: {
705
+ /**
706
+ * Check if exit intent has been triggered
707
+ */
708
+ isTriggered: () => triggered,
709
+ /**
710
+ * Reset exit intent state (useful for testing)
711
+ */
712
+ reset: () => {
713
+ triggered = false;
714
+ positions = [];
715
+ try {
716
+ sessionStorage.removeItem("xp:exitIntent:triggered");
717
+ } catch (_e) {
718
+ }
719
+ cleanup();
720
+ initialize();
721
+ },
722
+ /**
723
+ * Get current position history
724
+ */
725
+ getPositions: () => [...positions]
726
+ }
727
+ });
728
+ initialize();
729
+ const destroyHandler = () => {
730
+ cleanup();
731
+ };
732
+ instance.on("destroy", destroyHandler);
733
+ };
564
734
  var frequencyPlugin = (plugin, instance, config) => {
565
735
  plugin.ns("frequency");
566
736
  plugin.defaults({
@@ -688,7 +858,527 @@ var frequencyPlugin = (plugin, instance, config) => {
688
858
  });
689
859
  }
690
860
  };
861
+ function respectsDNT() {
862
+ if (typeof navigator === "undefined") return false;
863
+ return navigator.doNotTrack === "1" || navigator.msDoNotTrack === "1" || window.doNotTrack === "1";
864
+ }
865
+ function createVisitsEvent(isFirstVisit, totalVisits, sessionVisits, firstVisitTime, lastVisitTime, timestamp) {
866
+ return {
867
+ isFirstVisit,
868
+ totalVisits,
869
+ sessionVisits,
870
+ firstVisitTime,
871
+ lastVisitTime,
872
+ timestamp
873
+ };
874
+ }
875
+ var pageVisitsPlugin = (plugin, instance, config) => {
876
+ plugin.ns("pageVisits");
877
+ plugin.defaults({
878
+ pageVisits: {
879
+ enabled: true,
880
+ respectDNT: true,
881
+ sessionKey: "pageVisits:session",
882
+ totalKey: "pageVisits:total",
883
+ ttl: void 0,
884
+ autoIncrement: true
885
+ }
886
+ });
887
+ if (!instance.storage) {
888
+ console.warn("[PageVisits] Storage plugin not found, auto-loading...");
889
+ instance.use(storagePlugin);
890
+ }
891
+ const sdkInstance = instance;
892
+ let sessionCount = 0;
893
+ let totalCount = 0;
894
+ let firstVisitTime;
895
+ let lastVisitTime;
896
+ let isFirstVisitFlag = false;
897
+ let initialized = false;
898
+ function loadData() {
899
+ const sessionKey = config.get("pageVisits.sessionKey") ?? "pageVisits:session";
900
+ const totalKey = config.get("pageVisits.totalKey") ?? "pageVisits:total";
901
+ const storedSession = sdkInstance.storage.get(sessionKey, {
902
+ backend: "sessionStorage"
903
+ });
904
+ sessionCount = storedSession ?? 0;
905
+ const storedTotal = sdkInstance.storage.get(totalKey, {
906
+ backend: "localStorage"
907
+ });
908
+ if (storedTotal) {
909
+ totalCount = storedTotal.count ?? 0;
910
+ firstVisitTime = storedTotal.first;
911
+ lastVisitTime = storedTotal.last;
912
+ isFirstVisitFlag = false;
913
+ } else {
914
+ totalCount = 0;
915
+ firstVisitTime = void 0;
916
+ lastVisitTime = void 0;
917
+ isFirstVisitFlag = true;
918
+ }
919
+ }
920
+ function saveData() {
921
+ const sessionKey = config.get("pageVisits.sessionKey") ?? "pageVisits:session";
922
+ const totalKey = config.get("pageVisits.totalKey") ?? "pageVisits:total";
923
+ const ttl = config.get("pageVisits.ttl");
924
+ sdkInstance.storage.set(sessionKey, sessionCount, {
925
+ backend: "sessionStorage"
926
+ });
927
+ const totalData = {
928
+ count: totalCount,
929
+ first: firstVisitTime ?? Date.now(),
930
+ last: lastVisitTime ?? Date.now()
931
+ };
932
+ sdkInstance.storage.set(totalKey, totalData, {
933
+ backend: "localStorage",
934
+ ...ttl && { ttl }
935
+ });
936
+ }
937
+ function increment() {
938
+ if (!initialized) {
939
+ loadData();
940
+ initialized = true;
941
+ }
942
+ sessionCount += 1;
943
+ totalCount += 1;
944
+ const now = Date.now();
945
+ if (isFirstVisitFlag) {
946
+ firstVisitTime = now;
947
+ }
948
+ lastVisitTime = now;
949
+ saveData();
950
+ const event = createVisitsEvent(
951
+ isFirstVisitFlag,
952
+ totalCount,
953
+ sessionCount,
954
+ firstVisitTime,
955
+ lastVisitTime,
956
+ now
957
+ );
958
+ plugin.emit("pageVisits:incremented", event);
959
+ if (isFirstVisitFlag) {
960
+ isFirstVisitFlag = false;
961
+ }
962
+ }
963
+ function reset() {
964
+ const sessionKey = config.get("pageVisits.sessionKey") ?? "pageVisits:session";
965
+ const totalKey = config.get("pageVisits.totalKey") ?? "pageVisits:total";
966
+ sdkInstance.storage.remove(sessionKey, { backend: "sessionStorage" });
967
+ sdkInstance.storage.remove(totalKey, { backend: "localStorage" });
968
+ sessionCount = 0;
969
+ totalCount = 0;
970
+ firstVisitTime = void 0;
971
+ lastVisitTime = void 0;
972
+ isFirstVisitFlag = false;
973
+ initialized = false;
974
+ plugin.emit("pageVisits:reset");
975
+ }
976
+ function getState() {
977
+ return createVisitsEvent(
978
+ isFirstVisitFlag,
979
+ totalCount,
980
+ sessionCount,
981
+ firstVisitTime,
982
+ lastVisitTime,
983
+ Date.now()
984
+ );
985
+ }
986
+ function initialize() {
987
+ const enabled = config.get("pageVisits.enabled") ?? true;
988
+ const respectDNTConfig = config.get("pageVisits.respectDNT") ?? true;
989
+ const autoIncrement = config.get("pageVisits.autoIncrement") ?? true;
990
+ if (respectDNTConfig && respectsDNT()) {
991
+ plugin.emit("pageVisits:disabled", { reason: "dnt" });
992
+ return;
993
+ }
994
+ if (!enabled) {
995
+ plugin.emit("pageVisits:disabled", { reason: "config" });
996
+ return;
997
+ }
998
+ if (autoIncrement) {
999
+ increment();
1000
+ }
1001
+ }
1002
+ instance.on("sdk:ready", initialize);
1003
+ plugin.expose({
1004
+ pageVisits: {
1005
+ getTotalCount: () => totalCount,
1006
+ getSessionCount: () => sessionCount,
1007
+ isFirstVisit: () => isFirstVisitFlag,
1008
+ getFirstVisitTime: () => firstVisitTime,
1009
+ getLastVisitTime: () => lastVisitTime,
1010
+ increment,
1011
+ reset,
1012
+ getState
1013
+ }
1014
+ });
1015
+ };
1016
+
1017
+ // src/scroll-depth/scroll-depth.ts
1018
+ function detectDevice() {
1019
+ if (typeof window === "undefined") return "desktop";
1020
+ const ua = navigator.userAgent;
1021
+ const isMobile = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
1022
+ const isTablet = /iPad|Android(?!.*Mobile)/i.test(ua);
1023
+ const width = window.innerWidth;
1024
+ if (width < 768) return "mobile";
1025
+ if (width < 1024) return "tablet";
1026
+ if (isMobile) return "mobile";
1027
+ if (isTablet) return "tablet";
1028
+ return "desktop";
1029
+ }
1030
+ function throttle(func, wait) {
1031
+ let timeout = null;
1032
+ let previous = 0;
1033
+ return function throttled(...args) {
1034
+ const now = Date.now();
1035
+ const remaining = wait - (now - previous);
1036
+ if (remaining <= 0 || remaining > wait) {
1037
+ if (timeout) {
1038
+ clearTimeout(timeout);
1039
+ timeout = null;
1040
+ }
1041
+ previous = now;
1042
+ func(...args);
1043
+ } else if (!timeout) {
1044
+ timeout = setTimeout(() => {
1045
+ previous = Date.now();
1046
+ timeout = null;
1047
+ func(...args);
1048
+ }, remaining);
1049
+ }
1050
+ };
1051
+ }
1052
+ function calculateScrollPercent(includeViewportHeight) {
1053
+ if (typeof document === "undefined") return 0;
1054
+ const scrollingElement = document.scrollingElement || document.documentElement;
1055
+ const scrollTop = scrollingElement.scrollTop;
1056
+ const scrollHeight = scrollingElement.scrollHeight;
1057
+ const clientHeight = scrollingElement.clientHeight;
1058
+ if (scrollHeight <= clientHeight) {
1059
+ return 100;
1060
+ }
1061
+ if (includeViewportHeight) {
1062
+ return Math.min((scrollTop + clientHeight) / scrollHeight * 100, 100);
1063
+ }
1064
+ return Math.min(scrollTop / (scrollHeight - clientHeight) * 100, 100);
1065
+ }
1066
+ function calculateEngagementScore(velocity, fastScrollThreshold, directionChanges, timeScrollingUp, totalTime) {
1067
+ const velocityScore = Math.min(velocity / fastScrollThreshold * 50, 50);
1068
+ const directionScore = Math.min(directionChanges / 5 * 30, 30);
1069
+ const seekingScore = Math.min(timeScrollingUp / totalTime * 20, 20);
1070
+ return Math.max(0, 100 - (velocityScore + directionScore + seekingScore));
1071
+ }
1072
+ var scrollDepthPlugin = (plugin, instance, config) => {
1073
+ plugin.ns("experiences.scrollDepth");
1074
+ plugin.defaults({
1075
+ scrollDepth: {
1076
+ thresholds: [25, 50, 75, 100],
1077
+ throttle: 100,
1078
+ includeViewportHeight: true,
1079
+ recalculateOnResize: true,
1080
+ trackAdvancedMetrics: false,
1081
+ fastScrollVelocityThreshold: 3,
1082
+ disableOnMobile: false
1083
+ }
1084
+ });
1085
+ const scrollConfig = config.get("scrollDepth");
1086
+ if (!scrollConfig) return;
1087
+ const cfg = scrollConfig;
1088
+ const device = detectDevice();
1089
+ if (cfg.disableOnMobile && device === "mobile") {
1090
+ return;
1091
+ }
1092
+ let maxScrollPercent = 0;
1093
+ const triggeredThresholds = /* @__PURE__ */ new Set();
1094
+ const pageLoadTime = Date.now();
1095
+ let lastScrollPosition = 0;
1096
+ let lastScrollTime = Date.now();
1097
+ let lastScrollDirection = null;
1098
+ let directionChangesSinceLastThreshold = 0;
1099
+ let timeScrollingUp = 0;
1100
+ const thresholdTimes = /* @__PURE__ */ new Map();
1101
+ function handleScroll() {
1102
+ const currentPercent = calculateScrollPercent(cfg.includeViewportHeight ?? true);
1103
+ const now = Date.now();
1104
+ const scrollingElement = document.scrollingElement || document.documentElement;
1105
+ const currentPosition = scrollingElement.scrollTop;
1106
+ let velocity = 0;
1107
+ if (cfg.trackAdvancedMetrics) {
1108
+ const timeDelta = now - lastScrollTime;
1109
+ const positionDelta = currentPosition - lastScrollPosition;
1110
+ velocity = timeDelta > 0 ? Math.abs(positionDelta) / timeDelta : 0;
1111
+ const currentDirection = positionDelta > 0 ? "down" : positionDelta < 0 ? "up" : lastScrollDirection;
1112
+ if (currentDirection && lastScrollDirection && currentDirection !== lastScrollDirection) {
1113
+ directionChangesSinceLastThreshold++;
1114
+ }
1115
+ if (currentDirection === "up" && timeDelta > 0) {
1116
+ timeScrollingUp += timeDelta;
1117
+ }
1118
+ lastScrollDirection = currentDirection;
1119
+ lastScrollPosition = currentPosition;
1120
+ lastScrollTime = now;
1121
+ }
1122
+ maxScrollPercent = Math.max(maxScrollPercent, currentPercent);
1123
+ for (const threshold of cfg.thresholds || []) {
1124
+ if (currentPercent >= threshold && !triggeredThresholds.has(threshold)) {
1125
+ triggeredThresholds.add(threshold);
1126
+ if (cfg.trackAdvancedMetrics) {
1127
+ thresholdTimes.set(threshold, now - pageLoadTime);
1128
+ }
1129
+ const eventPayload = {
1130
+ triggered: true,
1131
+ timestamp: now,
1132
+ percent: Math.round(currentPercent * 100) / 100,
1133
+ maxPercent: Math.round(maxScrollPercent * 100) / 100,
1134
+ threshold,
1135
+ thresholdsCrossed: Array.from(triggeredThresholds).sort((a, b) => a - b),
1136
+ device
1137
+ };
1138
+ if (cfg.trackAdvancedMetrics) {
1139
+ const fastScrollThreshold = cfg.fastScrollVelocityThreshold || 3;
1140
+ const isFastScrolling = velocity > fastScrollThreshold;
1141
+ const engagementScore = calculateEngagementScore(
1142
+ velocity,
1143
+ fastScrollThreshold,
1144
+ directionChangesSinceLastThreshold,
1145
+ timeScrollingUp,
1146
+ now - pageLoadTime
1147
+ );
1148
+ eventPayload.advanced = {
1149
+ timeToThreshold: now - pageLoadTime,
1150
+ velocity: Math.round(velocity * 1e3) / 1e3,
1151
+ // Round to 3 decimals
1152
+ isFastScrolling,
1153
+ directionChanges: directionChangesSinceLastThreshold,
1154
+ timeScrollingUp,
1155
+ engagementScore: Math.round(engagementScore)
1156
+ };
1157
+ directionChangesSinceLastThreshold = 0;
1158
+ }
1159
+ instance.emit("trigger:scrollDepth", eventPayload);
1160
+ }
1161
+ }
1162
+ }
1163
+ const throttledScrollHandler = throttle(handleScroll, cfg.throttle || 100);
1164
+ const throttledResizeHandler = throttle(handleScroll, cfg.throttle || 100);
1165
+ function initialize() {
1166
+ if (typeof window === "undefined" || typeof document === "undefined") {
1167
+ return;
1168
+ }
1169
+ window.addEventListener("scroll", throttledScrollHandler, { passive: true });
1170
+ if (cfg.recalculateOnResize) {
1171
+ window.addEventListener("resize", throttledResizeHandler, { passive: true });
1172
+ }
1173
+ }
1174
+ function cleanup() {
1175
+ window.removeEventListener("scroll", throttledScrollHandler);
1176
+ window.removeEventListener("resize", throttledResizeHandler);
1177
+ }
1178
+ const destroyHandler = () => {
1179
+ cleanup();
1180
+ };
1181
+ instance.on("destroy", destroyHandler);
1182
+ plugin.expose({
1183
+ scrollDepth: {
1184
+ /**
1185
+ * Get the maximum scroll percentage reached during the session
1186
+ */
1187
+ getMaxPercent: () => maxScrollPercent,
1188
+ /**
1189
+ * Get the current scroll percentage
1190
+ */
1191
+ getCurrentPercent: () => calculateScrollPercent(cfg.includeViewportHeight ?? true),
1192
+ /**
1193
+ * Get all thresholds that have been crossed
1194
+ */
1195
+ getThresholdsCrossed: () => Array.from(triggeredThresholds).sort((a, b) => a - b),
1196
+ /**
1197
+ * Get the detected device type
1198
+ */
1199
+ getDevice: () => device,
1200
+ /**
1201
+ * Get advanced metrics (only available when trackAdvancedMetrics is enabled)
1202
+ */
1203
+ getAdvancedMetrics: () => {
1204
+ if (!cfg.trackAdvancedMetrics) return null;
1205
+ const now = Date.now();
1206
+ return {
1207
+ timeOnPage: now - pageLoadTime,
1208
+ directionChanges: directionChangesSinceLastThreshold,
1209
+ timeScrollingUp,
1210
+ thresholdTimes: Object.fromEntries(thresholdTimes)
1211
+ };
1212
+ },
1213
+ /**
1214
+ * Reset scroll depth tracking
1215
+ * Clears all triggered thresholds, max scroll, and advanced metrics
1216
+ */
1217
+ reset: () => {
1218
+ maxScrollPercent = 0;
1219
+ triggeredThresholds.clear();
1220
+ directionChangesSinceLastThreshold = 0;
1221
+ timeScrollingUp = 0;
1222
+ thresholdTimes.clear();
1223
+ lastScrollDirection = null;
1224
+ }
1225
+ }
1226
+ });
1227
+ if (typeof window !== "undefined") {
1228
+ setTimeout(initialize, 0);
1229
+ }
1230
+ return () => {
1231
+ cleanup();
1232
+ instance.off("destroy", destroyHandler);
1233
+ };
1234
+ };
1235
+
1236
+ // src/time-delay/time-delay.ts
1237
+ function calculateElapsed(startTime, pausedDuration) {
1238
+ return Date.now() - startTime - pausedDuration;
1239
+ }
1240
+ function isDocumentHidden() {
1241
+ if (typeof document === "undefined") return false;
1242
+ return document.hidden || false;
1243
+ }
1244
+ function createTimeDelayEvent(startTime, pausedDuration, wasPaused, visibilityChanges) {
1245
+ const timestamp = Date.now();
1246
+ const elapsed = timestamp - startTime;
1247
+ const activeElapsed = elapsed - pausedDuration;
1248
+ return {
1249
+ timestamp,
1250
+ elapsed,
1251
+ activeElapsed,
1252
+ wasPaused,
1253
+ visibilityChanges
1254
+ };
1255
+ }
1256
+ var timeDelayPlugin = (plugin, instance, config) => {
1257
+ plugin.ns("experiences.timeDelay");
1258
+ plugin.defaults({
1259
+ timeDelay: {
1260
+ delay: 0,
1261
+ pauseWhenHidden: true
1262
+ }
1263
+ });
1264
+ const timeDelayConfig = config.get("timeDelay");
1265
+ if (!timeDelayConfig) return;
1266
+ const delay = timeDelayConfig.delay ?? 0;
1267
+ const pauseWhenHidden = timeDelayConfig.pauseWhenHidden ?? true;
1268
+ if (delay <= 0) return;
1269
+ const startTime = Date.now();
1270
+ let triggered = false;
1271
+ let paused = false;
1272
+ let pausedDuration = 0;
1273
+ let lastPauseTime = 0;
1274
+ let visibilityChanges = 0;
1275
+ let timer = null;
1276
+ let visibilityListener = null;
1277
+ function trigger() {
1278
+ if (triggered) return;
1279
+ triggered = true;
1280
+ const eventPayload = createTimeDelayEvent(
1281
+ startTime,
1282
+ pausedDuration,
1283
+ visibilityChanges > 0,
1284
+ visibilityChanges
1285
+ );
1286
+ instance.emit("trigger:timeDelay", eventPayload);
1287
+ cleanup();
1288
+ }
1289
+ function scheduleTimer(remainingDelay) {
1290
+ if (timer) {
1291
+ clearTimeout(timer);
1292
+ }
1293
+ timer = setTimeout(() => {
1294
+ trigger();
1295
+ }, remainingDelay);
1296
+ }
1297
+ function handleVisibilityChange() {
1298
+ const hidden = isDocumentHidden();
1299
+ if (hidden && !paused) {
1300
+ paused = true;
1301
+ lastPauseTime = Date.now();
1302
+ visibilityChanges++;
1303
+ if (timer) {
1304
+ clearTimeout(timer);
1305
+ timer = null;
1306
+ }
1307
+ } else if (!hidden && paused) {
1308
+ paused = false;
1309
+ const pauseDuration = Date.now() - lastPauseTime;
1310
+ pausedDuration += pauseDuration;
1311
+ visibilityChanges++;
1312
+ const elapsed = calculateElapsed(startTime, pausedDuration);
1313
+ const remaining = delay - elapsed;
1314
+ if (remaining > 0) {
1315
+ scheduleTimer(remaining);
1316
+ } else {
1317
+ trigger();
1318
+ }
1319
+ }
1320
+ }
1321
+ function cleanup() {
1322
+ if (timer) {
1323
+ clearTimeout(timer);
1324
+ timer = null;
1325
+ }
1326
+ if (visibilityListener && typeof document !== "undefined") {
1327
+ document.removeEventListener("visibilitychange", visibilityListener);
1328
+ visibilityListener = null;
1329
+ }
1330
+ }
1331
+ function initialize() {
1332
+ if (pauseWhenHidden && isDocumentHidden()) {
1333
+ paused = true;
1334
+ lastPauseTime = Date.now();
1335
+ visibilityChanges++;
1336
+ } else {
1337
+ scheduleTimer(delay);
1338
+ }
1339
+ if (pauseWhenHidden && typeof document !== "undefined") {
1340
+ visibilityListener = handleVisibilityChange;
1341
+ document.addEventListener("visibilitychange", visibilityListener);
1342
+ }
1343
+ }
1344
+ plugin.expose({
1345
+ timeDelay: {
1346
+ getElapsed: () => {
1347
+ return Date.now() - startTime;
1348
+ },
1349
+ getActiveElapsed: () => {
1350
+ let currentPausedDuration = pausedDuration;
1351
+ if (paused) {
1352
+ currentPausedDuration += Date.now() - lastPauseTime;
1353
+ }
1354
+ return calculateElapsed(startTime, currentPausedDuration);
1355
+ },
1356
+ getRemaining: () => {
1357
+ if (triggered) return 0;
1358
+ const elapsed = calculateElapsed(startTime, pausedDuration);
1359
+ const remaining = delay - elapsed;
1360
+ return Math.max(0, remaining);
1361
+ },
1362
+ isPaused: () => paused,
1363
+ isTriggered: () => triggered,
1364
+ reset: () => {
1365
+ triggered = false;
1366
+ paused = false;
1367
+ pausedDuration = 0;
1368
+ lastPauseTime = 0;
1369
+ visibilityChanges = 0;
1370
+ cleanup();
1371
+ initialize();
1372
+ }
1373
+ }
1374
+ });
1375
+ initialize();
1376
+ const destroyHandler = () => {
1377
+ cleanup();
1378
+ };
1379
+ instance.on("destroy", destroyHandler);
1380
+ };
691
1381
 
692
- export { bannerPlugin, debugPlugin, frequencyPlugin };
1382
+ export { bannerPlugin, debugPlugin, exitIntentPlugin, frequencyPlugin, pageVisitsPlugin, scrollDepthPlugin, timeDelayPlugin };
693
1383
  //# sourceMappingURL=index.js.map
694
1384
  //# sourceMappingURL=index.js.map