@websline/cms-view-utils 1.9.0 → 1.10.1
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/package.json +1 -1
- package/src/ads/createAdRunner.js +5 -3
- package/src/ads/selectAd.js +6 -2
- package/src/ads/triggers.js +32 -14
package/package.json
CHANGED
|
@@ -208,17 +208,19 @@ const createAdRunner = ({
|
|
|
208
208
|
|
|
209
209
|
/**
|
|
210
210
|
* Following the ad ends it too: a visitor who acted on it has seen what it
|
|
211
|
-
* had to say, and meeting it again on the next page reads as nagging.
|
|
211
|
+
* had to say, and meeting it again on the next page reads as nagging. A
|
|
212
|
+
* persistent ad stays until the visitor closes it.
|
|
212
213
|
*/
|
|
213
214
|
click(ad) {
|
|
214
215
|
if (!ad) return;
|
|
215
216
|
|
|
216
217
|
track(ad.uuid, "click");
|
|
217
|
-
release(ad);
|
|
218
|
+
if (!ad.persistent) release(ad);
|
|
218
219
|
},
|
|
219
220
|
|
|
221
|
+
/** An ad that is not closable has no close button, so nothing to dismiss. */
|
|
220
222
|
dismiss(ad) {
|
|
221
|
-
if (!ad) return;
|
|
223
|
+
if (!ad || ad.closable === false) return;
|
|
222
224
|
|
|
223
225
|
track(ad.uuid, "close");
|
|
224
226
|
release(ad);
|
package/src/ads/selectAd.js
CHANGED
|
@@ -98,8 +98,12 @@ const filterEligibleAds = ({
|
|
|
98
98
|
candidates.filter((ad) => {
|
|
99
99
|
if (!hasConsent(ad, isConsentGiven)) return false;
|
|
100
100
|
// Once per visit is the floor; a reload must not put it back on the screen.
|
|
101
|
-
|
|
102
|
-
if (
|
|
101
|
+
// A persistent ad has no such floor: it stays on every page until closed.
|
|
102
|
+
if (!ad.persistent && wasSeenThisVisit(ad, seenAt(ad.uuid))) return false;
|
|
103
|
+
// `closable: false` has no close button, so no dismissal can hold it back.
|
|
104
|
+
if (ad.closable !== false && isSuppressed(ad, dismissedAt(ad.uuid), now)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
103
107
|
|
|
104
108
|
const { cookieName, cookieValue, paramName, paramValue } = ad.conditions ?? {};
|
|
105
109
|
|
package/src/ads/triggers.js
CHANGED
|
@@ -39,16 +39,41 @@ const afterDelay = (seconds, fire, timers) => {
|
|
|
39
39
|
return () => timers.clearTimeout(id);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
/** How far the page can scroll at all; zero for a page that fits the window. */
|
|
43
|
+
const scrollableHeight = (target, doc) =>
|
|
44
|
+
Math.max((doc?.documentElement?.scrollHeight ?? 0) - (target.innerHeight ?? 0), 0);
|
|
45
|
+
|
|
42
46
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* bounce or a sideways scroll — and the ad would never appear for that visit.
|
|
47
|
+
* Fixed rather than set per ad, and in pixels rather than a share of the page: a
|
|
48
|
+
* share of a long page lies far down, so the ad would come late or not at all.
|
|
46
49
|
*/
|
|
47
|
-
const
|
|
50
|
+
const SCROLL_DISTANCE_PX = 300;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Keeps listening until the distance is reached: `once` would drop the listener
|
|
54
|
+
* on an overscroll bounce back at the top and lose the ad for that visit. A page
|
|
55
|
+
* shorter than the distance counts once scrolled to its end.
|
|
56
|
+
*/
|
|
57
|
+
const onScrolledPast = (fire, target, doc) => {
|
|
58
|
+
const reached = () => {
|
|
59
|
+
const scrollable = scrollableHeight(target, doc);
|
|
60
|
+
|
|
61
|
+
// A pixel of slack: at the very end, zoomed pages report a fractional scrollY.
|
|
62
|
+
return (
|
|
63
|
+
scrollable > 0 && target.scrollY >= Math.min(SCROLL_DISTANCE_PX, scrollable) - 1
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Already that far when the ad arrived — no further event may come.
|
|
68
|
+
if (reached()) {
|
|
69
|
+
fire();
|
|
70
|
+
return () => {};
|
|
71
|
+
}
|
|
72
|
+
|
|
48
73
|
const stop = () => target.removeEventListener("scroll", handler);
|
|
49
74
|
|
|
50
75
|
function handler() {
|
|
51
|
-
if (
|
|
76
|
+
if (!reached()) return;
|
|
52
77
|
|
|
53
78
|
stop();
|
|
54
79
|
fire();
|
|
@@ -120,14 +145,7 @@ const watchTrigger = (trigger, onFire, env = {}) => {
|
|
|
120
145
|
}
|
|
121
146
|
|
|
122
147
|
case "scroll":
|
|
123
|
-
|
|
124
|
-
// Already scrolled when the ad arrived — the event will not come again.
|
|
125
|
-
if (target.scrollY > 0) {
|
|
126
|
-
fire();
|
|
127
|
-
return never();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return onFirstScroll(fire, target);
|
|
148
|
+
return target ? onScrolledPast(fire, target, doc) : never();
|
|
131
149
|
|
|
132
150
|
case "exit_intent":
|
|
133
151
|
return doc ? onExitIntent(fire, doc) : never();
|
|
@@ -138,4 +156,4 @@ const watchTrigger = (trigger, onFire, env = {}) => {
|
|
|
138
156
|
}
|
|
139
157
|
};
|
|
140
158
|
|
|
141
|
-
export { countPageView, PAGE_VIEWS_KEY, watchTrigger };
|
|
159
|
+
export { countPageView, PAGE_VIEWS_KEY, SCROLL_DISTANCE_PX, watchTrigger };
|