nodebb-plugin-ezoic-infinite 0.6.1 → 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 +75 -87
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "0.6.1",
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,44 +21,37 @@ function parsePool(raw) {
28
21
  ));
29
22
  }
30
23
 
31
- function guessIsTopicPage() {
32
- // Prefer ajaxify template, fallback to DOM presence
33
- try {
34
- if (ajaxify && ajaxify.data && ajaxify.data.template) {
35
- return ['topic', 'topicEvents'].includes(ajaxify.data.template);
36
- }
37
- } catch (e) {}
38
- return $('[component="post/content"]').length > 0;
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;
39
30
  }
40
31
 
41
- function guessIsCategoryPage() {
42
- try {
43
- if (ajaxify && ajaxify.data && ajaxify.data.template) {
44
- return ajaxify.data.template === 'category';
45
- }
46
- } catch (e) {}
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;
35
+ }
36
+
37
+ function isCategoryTopicListPage() {
47
38
  return $('li[component="category/topic"]').length > 0;
48
39
  }
49
40
 
50
41
  function getTopicPosts() {
51
- if (!guessIsTopicPage()) return $();
52
-
53
- // Harmony standard wrapper
54
42
  const $primary = $('[component="post"][data-pid]');
55
43
  if ($primary.length) return $primary.not('.ezoic-ad-post');
56
44
 
57
- // Fallback: top-level data-pid that contains post content and isn't nested
58
- const $top = $('[data-pid]').filter(function () {
45
+ // Fallback: top-level data-pid containing post/content
46
+ return $('[data-pid]').filter(function () {
59
47
  const $el = $(this);
60
48
  const hasContent = $el.find('[component="post/content"]').length > 0;
61
49
  const nested = $el.parents('[data-pid]').length > 0;
62
50
  return hasContent && !nested;
63
- });
64
- return $top.not('.ezoic-ad-post');
51
+ }).not('.ezoic-ad-post');
65
52
  }
66
53
 
67
54
  function getCategoryTopicItems() {
68
- if (!guessIsCategoryPage()) return $();
69
55
  return $('li[component="category/topic"]').not('.ezoic-ad-topic');
70
56
  }
71
57
 
@@ -73,10 +59,12 @@ function tagName($el) {
73
59
  return ($el && $el.length ? (($el.prop('tagName') || '').toUpperCase()) : '');
74
60
  }
75
61
 
76
- function removeExistingEzoicNodes() {
77
- $('.ezoic-ad-post').remove();
78
- $('.ezoic-ad-between').remove();
79
- $('.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>';
80
68
  }
81
69
 
82
70
  function computeWindowSlots(totalItems, interval, poolSize) {
@@ -88,15 +76,13 @@ function computeWindowSlots(totalItems, interval, poolSize) {
88
76
  return out;
89
77
  }
90
78
 
91
- function makeWrapperLike($target, classes, innerHtml) {
92
- const t = tagName($target);
93
- if (t === 'LI') {
94
- return '<li class="' + classes + ' list-unstyled" data-ezoic-ad="1">' + innerHtml + '</li>';
95
- }
96
- 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();
97
83
  }
98
84
 
99
- function insertBetweenGeneric($items, ids, interval, wrapperClass) {
85
+ function insertBetween($items, ids, interval, wrapperClass) {
100
86
  const total = $items.length;
101
87
  const slotsToRender = computeWindowSlots(total, interval, ids.length);
102
88
  if (!slotsToRender.length) return [];
@@ -116,7 +102,7 @@ function insertBetweenGeneric($items, ids, interval, wrapperClass) {
116
102
  return activeIds;
117
103
  }
118
104
 
119
- function insertAdMessagesBetweenReplies($posts, ids, interval) {
105
+ function insertMessageAds($posts, ids, interval) {
120
106
  const total = $posts.length;
121
107
  const slotsToRender = computeWindowSlots(total, interval, ids.length);
122
108
  if (!slotsToRender.length) return [];
@@ -137,47 +123,45 @@ function insertAdMessagesBetweenReplies($posts, ids, interval) {
137
123
  return activeIds;
138
124
  }
139
125
 
140
- function ezoicRender(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) {
141
140
  if (!ids || !ids.length) return;
142
141
 
143
142
  window.ezstandalone = window.ezstandalone || {};
144
143
  window.ezstandalone.cmd = window.ezstandalone.cmd || [];
145
144
 
146
- // Ezoic doc: showAds(101,102,103) within cmd.push for dynamic content citeturn0search0turn0search6
147
- window.ezstandalone.cmd.push(function () {
148
- try {
149
- if (typeof window.ezstandalone.refreshAds === 'function') {
150
- window.ezstandalone.refreshAds.apply(window.ezstandalone, ids);
151
- return;
152
- }
153
- } catch (e) {}
154
-
145
+ const run = function () {
155
146
  try {
156
147
  if (typeof window.ezstandalone.showAds === 'function') {
157
148
  window.ezstandalone.showAds.apply(window.ezstandalone, ids);
158
- return;
149
+ return true;
159
150
  }
160
151
  } catch (e) {}
152
+ return false;
153
+ };
161
154
 
162
- // Last resort: call showAds() with no args = "all placeholders" citeturn0search0turn0search6
163
- try {
164
- if (typeof window.ezstandalone.showAds === 'function') {
165
- window.ezstandalone.showAds();
166
- }
167
- } catch (e) {}
168
- });
169
- }
155
+ // Use cmd queue first (best if Ezoic loads later)
156
+ window.ezstandalone.cmd.push(function () { run(); });
170
157
 
171
- function computeSignature($posts, $topicItems) {
172
- if ($posts.length) {
173
- const lastPid = $posts.last().attr('data-pid') || '';
174
- return 'topic:' + $posts.length + ':' + lastPid;
175
- }
176
- if ($topicItems.length) {
177
- const lastTid = $topicItems.last().attr('data-tid') || '';
178
- return 'category:' + $topicItems.length + ':' + lastTid;
179
- }
180
- return 'none';
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);
181
165
  }
182
166
 
183
167
  async function refreshAds() {
@@ -194,34 +178,38 @@ async function refreshAds() {
194
178
  const messagePool = parsePool(cfg.messagePlaceholderIds);
195
179
  const messageInterval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
196
180
 
197
- const $posts = getTopicPosts();
198
- const $topicItems = getCategoryTopicItems();
181
+ const onTopic = isTopicPage();
182
+ const onCategory = !onTopic && isCategoryTopicListPage();
183
+
184
+ const $posts = onTopic ? getTopicPosts() : $();
185
+ const $topicItems = onCategory ? getCategoryTopicItems() : $();
199
186
 
200
187
  if (!$posts.length && !$topicItems.length) return;
201
188
 
202
- const signature = computeSignature($posts, $topicItems);
203
- if (signature === lastSignature) return;
204
- lastSignature = signature;
189
+ const sig = signatureFor($posts, $topicItems);
190
+ if (sig === lastSignature) return;
191
+ lastSignature = sig;
205
192
 
206
- removeExistingEzoicNodes();
193
+ removeOurWrappers();
207
194
 
208
195
  const activeIds = [];
209
196
 
210
- // Category pages: BETWEEN only
197
+ // Your rule:
198
+ // - Category topic list: BETWEEN only
199
+ // - Topic page: MESSAGE only
211
200
  if ($topicItems.length) {
212
201
  if (cfg.enableBetweenAds && betweenPool.length) {
213
- activeIds.push(...insertBetweenGeneric($topicItems, betweenPool, betweenInterval, 'ezoic-ad-topic'));
202
+ activeIds.push(...insertBetween($topicItems, betweenPool, betweenInterval, 'ezoic-ad-topic'));
214
203
  }
215
- ezoicRender(activeIds);
204
+ callEzoic(activeIds);
216
205
  return;
217
206
  }
218
207
 
219
- // Topic pages: MESSAGE only (as you requested)
220
208
  if ($posts.length) {
221
209
  if (cfg.enableMessageAds && messagePool.length) {
222
- activeIds.push(...insertAdMessagesBetweenReplies($posts, messagePool, messageInterval));
210
+ activeIds.push(...insertMessageAds($posts, messagePool, messageInterval));
223
211
  }
224
- ezoicRender(activeIds);
212
+ callEzoic(activeIds);
225
213
  }
226
214
  } finally {
227
215
  inFlight = false;
@@ -234,9 +222,9 @@ async function refreshAds() {
234
222
 
235
223
  function debounceRefresh() {
236
224
  clearTimeout(debounceTimer);
237
- debounceTimer = setTimeout(refreshAds, 220);
225
+ debounceTimer = setTimeout(refreshAds, 200);
238
226
  }
239
227
 
240
228
  $(document).ready(debounceRefresh);
241
229
  $(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
242
- setTimeout(debounceRefresh, 2200);
230
+ setTimeout(debounceRefresh, 2000);