nodebb-plugin-ezoic-infinite 0.5.7 → 0.5.9

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 +69 -45
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
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
@@ -7,6 +7,10 @@ let cachedConfig;
7
7
  let lastFetch = 0;
8
8
  let debounceTimer;
9
9
 
10
+ let lastSignature = null;
11
+ let inFlight = false;
12
+ let rerunRequested = false;
13
+
10
14
  async function fetchConfig() {
11
15
  if (cachedConfig && Date.now() - lastFetch < 10000) return cachedConfig;
12
16
  const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
@@ -24,7 +28,16 @@ function parsePool(raw) {
24
28
  ));
25
29
  }
26
30
 
31
+ function isTopicTemplate() {
32
+ return !!(ajaxify && ajaxify.data && (ajaxify.data.template === 'topic' || ajaxify.data.template === 'topicEvents'));
33
+ }
34
+
35
+ function isCategoryTemplate() {
36
+ return !!(ajaxify && ajaxify.data && ajaxify.data.template === 'category');
37
+ }
38
+
27
39
  function getTopicPosts() {
40
+ if (!isTopicTemplate()) return $();
28
41
  const $primary = $('[component="post"][data-pid]');
29
42
  if ($primary.length) return $primary.not('.ezoic-ad-post');
30
43
 
@@ -40,6 +53,7 @@ function getTopicPosts() {
40
53
  }
41
54
 
42
55
  function getCategoryTopicItems() {
56
+ if (!isCategoryTemplate()) return $();
43
57
  return $('li[component="category/topic"]').not('.ezoic-ad-topic');
44
58
  }
45
59
 
@@ -47,14 +61,11 @@ function tagName($el) {
47
61
  return ($el && $el.length ? (($el.prop('tagName') || '').toUpperCase()) : '');
48
62
  }
49
63
 
50
- function removePlaceholdersByPool(pool) {
51
- pool.forEach(id => $('[id="ezoic-pub-ad-placeholder-' + id + '"]').remove());
52
- }
53
-
54
- function removeAdWrappers() {
64
+ function removeExistingEzoicNodes() {
55
65
  $('.ezoic-ad-post').remove();
56
66
  $('.ezoic-ad-between').remove();
57
67
  $('.ezoic-ad-topic').remove();
68
+ $('[id^="ezoic-pub-ad-placeholder-"]').remove();
58
69
  }
59
70
 
60
71
  function computeWindowSlots(totalItems, interval, poolSize) {
@@ -130,14 +141,11 @@ function uniqueConcat(a, b) {
130
141
  function ezoicCall(ids) {
131
142
  if (!ids || !ids.length) return;
132
143
 
133
- // Queue calls until Ezoic is ready (recommended pattern)
134
144
  window.ezstandalone = window.ezstandalone || {};
135
145
  window.ezstandalone.cmd = window.ezstandalone.cmd || [];
136
146
 
137
147
  window.ezstandalone.cmd.push(function () {
138
148
  try {
139
- // IMPORTANT: destroy only the placeholders we are about to reuse,
140
- // NOT all placeholders. Destroying too early/all can make ads disappear.
141
149
  if (typeof window.ezstandalone.destroyPlaceholders === 'function') {
142
150
  window.ezstandalone.destroyPlaceholders.apply(window.ezstandalone, ids);
143
151
  }
@@ -151,62 +159,78 @@ function ezoicCall(ids) {
151
159
  });
152
160
  }
153
161
 
154
- async function refreshAds() {
155
- let cfg;
156
- try { cfg = await fetchConfig(); } catch (e) { return; }
157
- if (!cfg || cfg.excluded) return;
162
+ function computeSignature($posts, $topicItems) {
163
+ if ($posts.length) {
164
+ const lastPid = $posts.last().attr('data-pid') || '';
165
+ return 'topic:' + $posts.length + ':' + lastPid;
166
+ }
167
+ if ($topicItems.length) {
168
+ const lastTid = $topicItems.last().attr('data-tid') || '';
169
+ return 'category:' + $topicItems.length + ':' + lastTid;
170
+ }
171
+ return 'none';
172
+ }
158
173
 
159
- const betweenPool = parsePool(cfg.placeholderIds);
160
- const betweenInterval = Math.max(1, parseInt(cfg.intervalPosts, 10) || 6);
174
+ async function refreshAds() {
175
+ if (inFlight) { rerunRequested = true; return; }
176
+ inFlight = true;
161
177
 
162
- const messagePool = parsePool(cfg.messagePlaceholderIds);
163
- const messageInterval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
178
+ try {
179
+ const cfg = await fetchConfig();
180
+ if (!cfg || cfg.excluded) return;
164
181
 
165
- const $posts = getTopicPosts();
166
- const $topicItems = getCategoryTopicItems();
182
+ const betweenPool = parsePool(cfg.placeholderIds);
183
+ const betweenInterval = Math.max(1, parseInt(cfg.intervalPosts, 10) || 6);
167
184
 
168
- if (!$posts.length && !$topicItems.length) return;
185
+ const messagePool = parsePool(cfg.messagePlaceholderIds);
186
+ const messageInterval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
169
187
 
170
- removeAdWrappers();
171
- removePlaceholdersByPool(uniqueConcat(betweenPool, messagePool));
188
+ const $posts = getTopicPosts();
189
+ const $topicItems = getCategoryTopicItems();
172
190
 
173
- const combinedUnique = uniqueConcat(betweenPool, messagePool);
191
+ if (!$posts.length && !$topicItems.length) return;
174
192
 
175
- const activeIds = [];
193
+ const signature = computeSignature($posts, $topicItems);
194
+ if (signature === lastSignature) return;
195
+ lastSignature = signature;
176
196
 
177
- // Category topic list page
178
- if (!$posts.length && $topicItems.length) {
179
- if (cfg.enableBetweenAds && betweenPool.length) {
180
- activeIds.push(...insertBetweenGeneric($topicItems, combinedUnique.slice(0, betweenPool.length), betweenInterval, 'ezoic-ad-topic'));
181
- }
182
- }
197
+ removeExistingEzoicNodes();
183
198
 
184
- // Topic page
185
- if ($posts.length) {
186
- let cursor = 0;
199
+ const combinedUnique = uniqueConcat(betweenPool, messagePool);
200
+ const activeIds = [];
187
201
 
188
- if (cfg.enableBetweenAds && betweenPool.length) {
189
- const idsForBetween = combinedUnique.slice(cursor, cursor + betweenPool.length);
190
- cursor += idsForBetween.length;
191
- activeIds.push(...insertBetweenGeneric($posts, idsForBetween, betweenInterval, 'ezoic-ad-between'));
202
+ // Category topic list page: use BETWEEN only
203
+ if ($topicItems.length) {
204
+ if (cfg.enableBetweenAds && betweenPool.length) {
205
+ activeIds.push(...insertBetweenGeneric($topicItems, combinedUnique.slice(0, betweenPool.length), betweenInterval, 'ezoic-ad-topic'));
206
+ }
207
+ ezoicCall(activeIds);
208
+ return;
192
209
  }
193
210
 
194
- if (cfg.enableMessageAds && messagePool.length) {
195
- const idsForMessage = combinedUnique.slice(cursor, cursor + messagePool.length);
196
- cursor += idsForMessage.length;
197
- activeIds.push(...insertAdMessagesBetweenReplies($posts, idsForMessage, messageInterval));
211
+ // Topic page: ONLY message ads (as requested)
212
+ if ($posts.length) {
213
+ if (cfg.enableMessageAds && messagePool.length) {
214
+ // Use messagePool slice from the combined unique set, but keep it stable
215
+ const idsForMessage = combinedUnique.slice(0, messagePool.length);
216
+ activeIds.push(...insertAdMessagesBetweenReplies($posts, idsForMessage, messageInterval));
217
+ }
218
+ ezoicCall(activeIds);
219
+ }
220
+ } finally {
221
+ inFlight = false;
222
+ if (rerunRequested) {
223
+ rerunRequested = false;
224
+ setTimeout(refreshAds, 50);
198
225
  }
199
226
  }
200
-
201
- // Ask Ezoic to (re)fill ONLY the placeholders we inserted.
202
- ezoicCall(activeIds);
203
227
  }
204
228
 
205
229
  function debounceRefresh() {
206
230
  clearTimeout(debounceTimer);
207
- debounceTimer = setTimeout(refreshAds, 180);
231
+ debounceTimer = setTimeout(refreshAds, 220);
208
232
  }
209
233
 
210
234
  $(document).ready(debounceRefresh);
211
235
  $(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
212
- setTimeout(debounceRefresh, 1800);
236
+ setTimeout(debounceRefresh, 2200);