nodebb-plugin-ezoic-infinite 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/client.js +88 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -27,6 +27,12 @@
27
27
  usedPosts: new Set(),
28
28
  usedCategories: new Set(),
29
29
 
30
+ // wrappers currently on page (FIFO for recycling)
31
+ liveBetween: [],
32
+ liveMessage: [],
33
+ liveCategory: [],
34
+ usedCategories: new Set(),
35
+
30
36
  lastShowById: new Map(),
31
37
  pendingById: new Set(),
32
38
 
@@ -103,6 +109,60 @@
103
109
  });
104
110
  }
105
111
 
112
+
113
+ function safeRect(el) {
114
+ try { return el.getBoundingClientRect(); } catch (e) { return null; }
115
+ }
116
+
117
+ function destroyPlaceholderIds(ids) {
118
+ if (!ids || !ids.length) return;
119
+ const call = () => {
120
+ try {
121
+ if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
122
+ window.ezstandalone.destroyPlaceholders(ids);
123
+ }
124
+ } catch (e) {}
125
+ };
126
+ try {
127
+ window.ezstandalone = window.ezstandalone || {};
128
+ window.ezstandalone.cmd = window.ezstandalone.cmd || [];
129
+ if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') call();
130
+ else window.ezstandalone.cmd.push(call);
131
+ } catch (e) {}
132
+ }
133
+
134
+ function getRecyclable(liveArr) {
135
+ const margin = 1800;
136
+ for (let i = 0; i < liveArr.length; i++) {
137
+ const entry = liveArr[i];
138
+ if (!entry || !entry.wrap || !entry.wrap.isConnected) { liveArr.splice(i, 1); i--; continue; }
139
+ const r = safeRect(entry.wrap);
140
+ if (r && r.bottom < -margin) {
141
+ liveArr.splice(i, 1);
142
+ return entry;
143
+ }
144
+ }
145
+ return null;
146
+ }
147
+
148
+ function moveWrapAfter(wrap, target, kindClass, afterPos) {
149
+ try {
150
+ wrap.className = `${WRAP_CLASS} ${kindClass}`;
151
+ wrap.setAttribute('data-ezoic-after', String(afterPos));
152
+ target.insertAdjacentElement('afterend', wrap);
153
+ return true;
154
+ } catch (e) {
155
+ return false;
156
+ }
157
+ }
158
+
159
+ function pickId(pool, liveArr) {
160
+ if (pool.length) return { id: pool.shift(), recycled: null };
161
+ const recycled = getRecyclable(liveArr);
162
+ if (recycled) return { id: recycled.id, recycled };
163
+ return { id: null, recycled: null };
164
+ }
165
+
106
166
  function buildWrap(id, kindClass, afterPos) {
107
167
  const wrap = document.createElement('div');
108
168
  wrap.className = `${WRAP_CLASS} ${kindClass}`;
@@ -132,16 +192,10 @@
132
192
  try {
133
193
  state.usedTopics.forEach((id) => ids.push(id));
134
194
  state.usedPosts.forEach((id) => ids.push(id));
135
- state.usedCategories.forEach((id) => ids.push(id));
195
+ state.usedCategories && state.usedCategories.forEach((id) => ids.push(id));
136
196
  } catch (e) {}
137
-
138
- if (!ids.length) return;
139
-
140
- const call = () => {
141
- try {
142
- if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
143
- window.ezstandalone.destroyPlaceholders(ids);
144
- }
197
+ destroyPlaceholderIds(ids);
198
+ }
145
199
  } catch (e) {}
146
200
  };
147
201
 
@@ -239,9 +293,9 @@
239
293
  })();
240
294
  }
241
295
 
242
- function nextId(kind) {
243
- const pool = (kind === 'between') ? state.poolTopics : (kind === 'message') ? state.poolPosts : state.poolCategories;
244
- if (pool.length) return pool.shift();
296
+ function nextId(pool) {
297
+ // backward compatible: the injector passes the pool array
298
+ if (Array.isArray(pool) && pool.length) return pool.shift();
245
299
  return null;
246
300
  }
247
301
 
@@ -298,13 +352,26 @@
298
352
 
299
353
  if (findWrap(kindClass, afterPos)) continue;
300
354
 
301
- const id = nextId(kindPool);
355
+ const liveArr = (kindClass === 'ezoic-ad-between') ? state.liveBetween
356
+ : (kindClass === 'ezoic-ad-message') ? state.liveMessage
357
+ : state.liveCategory;
358
+
359
+ const pick = pickId(kindPool, liveArr);
360
+ const id = pick.id;
302
361
  if (!id) break;
303
362
 
304
- usedSet.add(id);
305
- const wrap = insertAfter(el, id, kindClass, afterPos);
306
- if (!wrap) continue;
363
+ let wrap = null;
364
+ if (pick.recycled && pick.recycled.wrap) {
365
+ destroyPlaceholderIds([id]);
366
+ wrap = pick.recycled.wrap;
367
+ if (!moveWrapAfter(wrap, el, kindClass, afterPos)) continue;
368
+ } else {
369
+ usedSet.add(id);
370
+ wrap = insertAfter(el, id, kindClass, afterPos);
371
+ if (!wrap) continue;
372
+ }
307
373
 
374
+ liveArr.push({ id, wrap });
308
375
  callShowAdsWhenReady(id);
309
376
  inserted += 1;
310
377
  }
@@ -331,8 +398,13 @@
331
398
  state.poolTopics = [];
332
399
  state.poolPosts = [];
333
400
  state.poolCategories = [];
401
+ state.poolCategories = [];
334
402
  state.usedTopics.clear();
335
403
  state.usedPosts.clear();
404
+ state.usedCategories && state.usedCategories.clear();
405
+ state.liveBetween = [];
406
+ state.liveMessage = [];
407
+ state.liveCategory = [];
336
408
  state.usedCategories.clear();
337
409
 
338
410
  state.lastShowById = new Map();