nodebb-plugin-ezoic-infinite 0.6.0 → 0.6.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 +77 -58
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
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
@@ -1,24 +1,17 @@
1
- /* globals ajaxify */
2
1
  'use strict';
3
2
 
3
+ /* globals ajaxify */
4
4
  window.ezoicInfiniteLoaded = true;
5
5
 
6
6
  let cachedConfig;
7
7
  let lastFetch = 0;
8
- let debounceTimer;
9
8
 
9
+ let debounceTimer;
10
10
  let lastSignature = null;
11
+
11
12
  let inFlight = false;
12
13
  let rerunRequested = false;
13
14
 
14
- async function fetchConfig() {
15
- if (cachedConfig && Date.now() - lastFetch < 10000) return cachedConfig;
16
- const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
17
- cachedConfig = await res.json();
18
- lastFetch = Date.now();
19
- return cachedConfig;
20
- }
21
-
22
15
  function parsePool(raw) {
23
16
  if (!raw) return [];
24
17
  return Array.from(new Set(
@@ -28,18 +21,28 @@ function parsePool(raw) {
28
21
  ));
29
22
  }
30
23
 
31
- function isTopicTemplate() {
32
- return !!(ajaxify && ajaxify.data && (ajaxify.data.template === 'topic' || ajaxify.data.template === 'topicEvents'));
24
+ async function fetchConfig() {
25
+ if (cachedConfig && Date.now() - lastFetch < 10000) return cachedConfig;
26
+ const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
27
+ cachedConfig = await res.json();
28
+ lastFetch = Date.now();
29
+ return cachedConfig;
30
+ }
31
+
32
+ // Robust DOM-based detection (no dependency on ajaxify template timing)
33
+ function isTopicPage() {
34
+ return $('[component="post/content"]').length > 0 || $('[component="post"][data-pid]').length > 0;
33
35
  }
34
36
 
35
- function isCategoryTemplate() {
36
- return !!(ajaxify && ajaxify.data && ajaxify.data.template === 'category');
37
+ function isCategoryTopicListPage() {
38
+ return $('li[component="category/topic"]').length > 0;
37
39
  }
38
40
 
39
41
  function getTopicPosts() {
40
- if (!isTopicTemplate()) return $();
41
42
  const $primary = $('[component="post"][data-pid]');
42
43
  if ($primary.length) return $primary.not('.ezoic-ad-post');
44
+
45
+ // Fallback: top-level data-pid containing post/content
43
46
  return $('[data-pid]').filter(function () {
44
47
  const $el = $(this);
45
48
  const hasContent = $el.find('[component="post/content"]').length > 0;
@@ -49,7 +52,6 @@ function getTopicPosts() {
49
52
  }
50
53
 
51
54
  function getCategoryTopicItems() {
52
- if (!isCategoryTemplate()) return $();
53
55
  return $('li[component="category/topic"]').not('.ezoic-ad-topic');
54
56
  }
55
57
 
@@ -57,11 +59,12 @@ function tagName($el) {
57
59
  return ($el && $el.length ? (($el.prop('tagName') || '').toUpperCase()) : '');
58
60
  }
59
61
 
60
- function removeExistingEzoicNodes() {
61
- // Remove only the wrappers we created. Their inner placeholder divs go away with them.
62
- $('.ezoic-ad-post').remove();
63
- $('.ezoic-ad-between').remove();
64
- $('.ezoic-ad-topic').remove();
62
+ function makeWrapperLike($target, classes, innerHtml) {
63
+ const t = tagName($target);
64
+ if (t === 'LI') {
65
+ return '<li class="' + classes + ' list-unstyled" data-ezoic-ad="1">' + innerHtml + '</li>';
66
+ }
67
+ return '<div class="' + classes + '" data-ezoic-ad="1">' + innerHtml + '</div>';
65
68
  }
66
69
 
67
70
  function computeWindowSlots(totalItems, interval, poolSize) {
@@ -73,15 +76,13 @@ function computeWindowSlots(totalItems, interval, poolSize) {
73
76
  return out;
74
77
  }
75
78
 
76
- function makeWrapperLike($target, classes, innerHtml) {
77
- const t = tagName($target);
78
- if (t === 'LI') {
79
- return '<li class="' + classes + ' list-unstyled" data-ezoic-ad="1">' + innerHtml + '</li>';
80
- }
81
- return '<div class="' + classes + '" data-ezoic-ad="1">' + innerHtml + '</div>';
79
+ function removeOurWrappers() {
80
+ $('.ezoic-ad-post').remove();
81
+ $('.ezoic-ad-between').remove();
82
+ $('.ezoic-ad-topic').remove();
82
83
  }
83
84
 
84
- function insertBetweenGeneric($items, ids, interval, wrapperClass) {
85
+ function insertBetween($items, ids, interval, wrapperClass) {
85
86
  const total = $items.length;
86
87
  const slotsToRender = computeWindowSlots(total, interval, ids.length);
87
88
  if (!slotsToRender.length) return [];
@@ -101,7 +102,7 @@ function insertBetweenGeneric($items, ids, interval, wrapperClass) {
101
102
  return activeIds;
102
103
  }
103
104
 
104
- function insertAdMessagesBetweenReplies($posts, ids, interval) {
105
+ function insertMessageAds($posts, ids, interval) {
105
106
  const total = $posts.length;
106
107
  const slotsToRender = computeWindowSlots(total, interval, ids.length);
107
108
  if (!slotsToRender.length) return [];
@@ -122,31 +123,45 @@ function insertAdMessagesBetweenReplies($posts, ids, interval) {
122
123
  return activeIds;
123
124
  }
124
125
 
125
- function ezoicShow(ids) {
126
+ function signatureFor($posts, $topicItems) {
127
+ if ($posts.length) {
128
+ const lastPid = $posts.last().attr('data-pid') || '';
129
+ return 'topic:' + $posts.length + ':' + lastPid;
130
+ }
131
+ if ($topicItems.length) {
132
+ const lastTid = $topicItems.last().attr('data-tid') || '';
133
+ return 'category:' + $topicItems.length + ':' + lastTid;
134
+ }
135
+ return 'none';
136
+ }
137
+
138
+ // Call Ezoic when ready (retry a few times, non-destructive)
139
+ function callEzoic(ids) {
126
140
  if (!ids || !ids.length) return;
127
141
 
128
- // Ezoic supports showAds(101,102,103) and recommends a single call with all ids.
129
142
  window.ezstandalone = window.ezstandalone || {};
130
143
  window.ezstandalone.cmd = window.ezstandalone.cmd || [];
131
- window.ezstandalone.cmd.push(function () {
144
+
145
+ const run = function () {
132
146
  try {
133
147
  if (typeof window.ezstandalone.showAds === 'function') {
134
148
  window.ezstandalone.showAds.apply(window.ezstandalone, ids);
149
+ return true;
135
150
  }
136
151
  } catch (e) {}
137
- });
138
- }
152
+ return false;
153
+ };
139
154
 
140
- function computeSignature($posts, $topicItems) {
141
- if ($posts.length) {
142
- const lastPid = $posts.last().attr('data-pid') || '';
143
- return 'topic:' + $posts.length + ':' + lastPid;
144
- }
145
- if ($topicItems.length) {
146
- const lastTid = $topicItems.last().attr('data-tid') || '';
147
- return 'category:' + $topicItems.length + ':' + lastTid;
148
- }
149
- return 'none';
155
+ // Use cmd queue first (best if Ezoic loads later)
156
+ window.ezstandalone.cmd.push(function () { run(); });
157
+
158
+ // Also retry a few times in case cmd isn't flushed promptly
159
+ let tries = 0;
160
+ const maxTries = 6;
161
+ const timer = setInterval(function () {
162
+ tries++;
163
+ if (run() || tries >= maxTries) clearInterval(timer);
164
+ }, 800);
150
165
  }
151
166
 
152
167
  async function refreshAds() {
@@ -163,34 +178,38 @@ async function refreshAds() {
163
178
  const messagePool = parsePool(cfg.messagePlaceholderIds);
164
179
  const messageInterval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
165
180
 
166
- const $posts = getTopicPosts();
167
- const $topicItems = getCategoryTopicItems();
181
+ const onTopic = isTopicPage();
182
+ const onCategory = !onTopic && isCategoryTopicListPage();
183
+
184
+ const $posts = onTopic ? getTopicPosts() : $();
185
+ const $topicItems = onCategory ? getCategoryTopicItems() : $();
168
186
 
169
187
  if (!$posts.length && !$topicItems.length) return;
170
188
 
171
- const signature = computeSignature($posts, $topicItems);
172
- if (signature === lastSignature) return;
173
- lastSignature = signature;
189
+ const sig = signatureFor($posts, $topicItems);
190
+ if (sig === lastSignature) return;
191
+ lastSignature = sig;
174
192
 
175
- removeExistingEzoicNodes();
193
+ removeOurWrappers();
176
194
 
177
195
  const activeIds = [];
178
196
 
179
- // Category pages: BETWEEN only (between topics)
197
+ // Your rule:
198
+ // - Category topic list: BETWEEN only
199
+ // - Topic page: MESSAGE only
180
200
  if ($topicItems.length) {
181
201
  if (cfg.enableBetweenAds && betweenPool.length) {
182
- activeIds.push(...insertBetweenGeneric($topicItems, betweenPool, betweenInterval, 'ezoic-ad-topic'));
202
+ activeIds.push(...insertBetween($topicItems, betweenPool, betweenInterval, 'ezoic-ad-topic'));
183
203
  }
184
- ezoicShow(activeIds);
204
+ callEzoic(activeIds);
185
205
  return;
186
206
  }
187
207
 
188
- // Topic pages: MESSAGE only (between replies)
189
208
  if ($posts.length) {
190
209
  if (cfg.enableMessageAds && messagePool.length) {
191
- activeIds.push(...insertAdMessagesBetweenReplies($posts, messagePool, messageInterval));
210
+ activeIds.push(...insertMessageAds($posts, messagePool, messageInterval));
192
211
  }
193
- ezoicShow(activeIds);
212
+ callEzoic(activeIds);
194
213
  }
195
214
  } finally {
196
215
  inFlight = false;
@@ -203,9 +222,9 @@ async function refreshAds() {
203
222
 
204
223
  function debounceRefresh() {
205
224
  clearTimeout(debounceTimer);
206
- debounceTimer = setTimeout(refreshAds, 220);
225
+ debounceTimer = setTimeout(refreshAds, 200);
207
226
  }
208
227
 
209
228
  $(document).ready(debounceRefresh);
210
229
  $(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
211
- setTimeout(debounceRefresh, 2200);
230
+ setTimeout(debounceRefresh, 2000);