nodebb-plugin-ezoic-infinite 0.6.3 → 0.6.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/client.js +51 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Ezoic ads with infinite scroll using a pool of placeholder IDs",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -10,23 +10,23 @@ let debounceTimer;
10
10
  let inFlight = false;
11
11
  let rerunRequested = false;
12
12
 
13
- // Incremental state (prevents ads "jumping to the top")
13
+ // Per-page incremental state
14
14
  let pageKey = null;
15
- let injectedSlots = new Set(); // slotNumber per page
16
- let usedIds = new Set(); // ids currently injected per page
15
+ let injectedSlots = new Set(); // slot numbers already injected on this page
16
+ let usedIds = new Set(); // IDs currently present in DOM
17
+ let adsFIFO = []; // [{slot, id, selector}] in insertion order (oldest first)
17
18
 
18
19
  function resetPageState() {
19
20
  injectedSlots = new Set();
20
21
  usedIds = new Set();
22
+ adsFIFO = [];
21
23
  }
22
24
 
23
25
  function currentPageKey() {
24
- // Stable key per ajaxified page
25
26
  try {
26
27
  if (ajaxify && ajaxify.data) {
27
28
  if (ajaxify.data.tid) return 'topic:' + ajaxify.data.tid;
28
29
  if (ajaxify.data.cid) return 'category:' + ajaxify.data.cid;
29
- if (ajaxify.data.template) return 'tpl:' + ajaxify.data.template + ':' + (ajaxify.data.url || window.location.pathname);
30
30
  }
31
31
  } catch (e) {}
32
32
  return window.location.pathname;
@@ -86,6 +86,10 @@ function makeWrapperLike($target, classes, innerHtml, attrs) {
86
86
  return '<div class="' + classes + '"' + attrStr + '>' + innerHtml + '</div>';
87
87
  }
88
88
 
89
+ function cleanupWrappersOnNav() {
90
+ $('.ezoic-ad-post, .ezoic-ad-between, .ezoic-ad-topic').remove();
91
+ }
92
+
89
93
  function pickNextId(pool) {
90
94
  for (const id of pool) {
91
95
  if (!usedIds.has(id)) return id;
@@ -111,7 +115,7 @@ function callEzoic(ids) {
111
115
 
112
116
  window.ezstandalone.cmd.push(function () { run(); });
113
117
 
114
- // retry a few times (Ezoic can load late)
118
+ // Retry (Ezoic can load late)
115
119
  let tries = 0;
116
120
  const maxTries = 6;
117
121
  const timer = setInterval(function () {
@@ -120,6 +124,21 @@ function callEzoic(ids) {
120
124
  }, 800);
121
125
  }
122
126
 
127
+ function fifoEvictIfNeeded(poolSize) {
128
+ // Keep at most poolSize ads in DOM: remove oldest when we exceed pool capacity.
129
+ // This makes ads "follow" the scroll (new ads appear, oldest disappear) without jumping positions.
130
+ while (adsFIFO.length > poolSize) {
131
+ const old = adsFIFO.shift();
132
+ if (!old) break;
133
+
134
+ const $el = $(old.selector);
135
+ if ($el.length) $el.remove();
136
+
137
+ injectedSlots.delete(old.slot);
138
+ usedIds.delete(old.id);
139
+ }
140
+ }
141
+
123
142
  function injectBetweenIncremental($items, pool, interval, wrapperClass) {
124
143
  const total = $items.length;
125
144
  const maxSlot = Math.floor(total / interval);
@@ -136,18 +155,29 @@ function injectBetweenIncremental($items, pool, interval, wrapperClass) {
136
155
 
137
156
  const id = pickNextId(pool);
138
157
  if (!id) {
139
- // pool exhausted: stop injecting further to avoid reusing ids and "jumping"
158
+ // No free IDs right now; stop. Oldest will be evicted once new ads can be created.
140
159
  break;
141
160
  }
142
161
 
143
162
  const placeholder = '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>';
144
- const html = makeWrapperLike($target, wrapperClass, placeholder, 'data-ezoic-slot="' + slot + '" data-ezoic-id="' + id + '"');
163
+ const selector = '.ezoic-ad-topic[data-ezoic-slot="' + slot + '"][data-ezoic-id="' + id + '"]';
164
+ const html = makeWrapperLike(
165
+ $target,
166
+ wrapperClass,
167
+ placeholder,
168
+ 'data-ezoic-slot="' + slot + '" data-ezoic-id="' + id + '"'
169
+ );
145
170
 
146
171
  $target.after(html);
147
172
 
148
173
  injectedSlots.add(slot);
149
174
  usedIds.add(id);
175
+ adsFIFO.push({ slot: slot, id: id, selector: selector });
176
+
150
177
  newIds.push(id);
178
+
179
+ // Enforce capacity immediately (prevents runaway and keeps ads near viewport)
180
+ fifoEvictIfNeeded(pool.length);
151
181
  }
152
182
 
153
183
  return newIds;
@@ -171,26 +201,35 @@ function injectMessageIncremental($posts, pool, interval) {
171
201
  if (!id) break;
172
202
 
173
203
  const inner = '<div class="content"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
174
- const html = makeWrapperLike($target, 'post ezoic-ad-post', inner, 'data-ezoic-slot="' + slot + '" data-ezoic-id="' + id + '"');
204
+ const selector = '.ezoic-ad-post[data-ezoic-slot="' + slot + '"][data-ezoic-id="' + id + '"]';
205
+ const html = makeWrapperLike(
206
+ $target,
207
+ 'post ezoic-ad-post',
208
+ inner,
209
+ 'data-ezoic-slot="' + slot + '" data-ezoic-id="' + id + '"'
210
+ );
175
211
 
176
212
  $target.after(html);
177
213
 
178
214
  injectedSlots.add(slot);
179
215
  usedIds.add(id);
216
+ adsFIFO.push({ slot: slot, id: id, selector: selector });
217
+
180
218
  newIds.push(id);
219
+
220
+ fifoEvictIfNeeded(pool.length);
181
221
  }
182
222
 
183
223
  return newIds;
184
224
  }
185
225
 
186
226
  async function refreshAds() {
187
- // reset state when navigating (ajaxify)
227
+ // Reset state on navigation
188
228
  const key = currentPageKey();
189
229
  if (pageKey !== key) {
190
230
  pageKey = key;
191
231
  resetPageState();
192
- // also cleanup any injected wrappers that may have been left by browser bfcache
193
- $('.ezoic-ad-post, .ezoic-ad-between, .ezoic-ad-topic').remove();
232
+ cleanupWrappersOnNav();
194
233
  }
195
234
 
196
235
  if (inFlight) { rerunRequested = true; return; }