nodebb-plugin-ezoic-infinite 0.5.2 → 0.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "0.5.2",
3
+ "version": "0.5.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/plugin.json CHANGED
@@ -17,10 +17,10 @@
17
17
  "public": "public"
18
18
  },
19
19
  "acpScripts": [
20
- "./public/admin.js"
20
+ "public/admin.js"
21
21
  ],
22
22
  "scripts": [
23
- "./public/client.js"
23
+ "public/client.js"
24
24
  ],
25
25
  "templates": "public/templates"
26
26
  }
package/public/admin.js CHANGED
@@ -9,27 +9,18 @@
9
9
  require(['settings', 'alerts'], function (Settings, alerts) {
10
10
  Settings.load('ezoic-infinite', $form);
11
11
 
12
- function doSave(e) {
13
- if (e) e.preventDefault();
14
- const $btn = $('#save');
15
- $btn.prop('disabled', true);
12
+ $('#save').off('click.ezoicInfinite').on('click.ezoicInfinite', function (e) {
13
+ e.preventDefault();
16
14
 
17
- // Settings.save signature differs slightly across versions; keep compatible
18
15
  Settings.save('ezoic-infinite', $form, function () {
16
+ // Toast vert (NodeBB core)
19
17
  if (alerts && typeof alerts.success === 'function') {
20
18
  alerts.success('Enregistré');
21
19
  } else if (window.app && typeof window.app.alertSuccess === 'function') {
22
20
  window.app.alertSuccess('Enregistré');
23
21
  }
24
- $btn.prop('disabled', false);
25
22
  });
26
-
27
- // Fallback: re-enable even if callback not fired (mobile sometimes)
28
- setTimeout(function () { $btn.prop('disabled', false); }, 4000);
29
- }
30
-
31
- $form.off('submit.ezoicInfinite').on('submit.ezoicInfinite', doSave);
32
- $('#save').off('click.ezoicInfinite').on('click.ezoicInfinite', doSave);
23
+ });
33
24
  });
34
25
  }
35
26
 
package/public/client.js CHANGED
@@ -1,6 +1,8 @@
1
1
  /* globals ajaxify */
2
2
  'use strict';
3
3
 
4
+ window.ezoicInfiniteLoaded = true;
5
+
4
6
  let cachedConfig;
5
7
  let lastFetch = 0;
6
8
  let debounceTimer;
@@ -22,14 +24,30 @@ function parsePool(raw) {
22
24
  ));
23
25
  }
24
26
 
27
+ /**
28
+ * Harmony: the real post wrapper is usually [component="post"][data-pid]
29
+ * We must avoid counting nested nodes like component="post/parent" or other elements
30
+ * that can also carry data-pid in some setups.
31
+ */
25
32
  function getTopicPosts() {
26
- const $p = $('[component="post"]').not('.ezoic-ad-post');
27
- if ($p.length) return $p;
33
+ const $primary = $('[component="post"][data-pid]');
34
+ if ($primary.length) return $primary.not('.ezoic-ad-post');
35
+
36
+ // Fallback: top-level nodes with data-pid that contain the post content,
37
+ // excluding nodes nested inside another data-pid container.
38
+ const $top = $('[data-pid]').filter(function () {
39
+ const $el = $(this);
40
+ const hasContent = $el.find('[component="post/content"]').length > 0;
41
+ const nested = $el.parents('[data-pid]').length > 0;
42
+ return hasContent && !nested;
43
+ });
44
+ if ($top.length) return $top.not('.ezoic-ad-post');
45
+
28
46
  return $('.posts .post').not('.ezoic-ad-post');
29
47
  }
30
48
 
31
- function isLi($el) {
32
- return ($el && $el.length && (($el.prop('tagName') || '').toUpperCase() === 'LI'));
49
+ function tagName($el) {
50
+ return ($el && $el.length ? (($el.prop('tagName') || '').toUpperCase()) : '');
33
51
  }
34
52
 
35
53
  function removePlaceholdersByPool(pool) {
@@ -42,42 +60,25 @@ function removeAdWrappers() {
42
60
  }
43
61
 
44
62
  function computeWindowSlots(totalItems, interval, poolSize) {
63
+ // totalItems posts -> number of ad slots at positions interval, 2*interval, ...
45
64
  const slots = Math.floor(totalItems / interval);
46
65
  if (slots <= 0) return [];
66
+
67
+ // IMPORTANT:
68
+ // We cannot display more than poolSize ads at once because Ezoic placeholder IDs
69
+ // must be unique on the page. If slots > poolSize, we only render the latest poolSize slots.
47
70
  const start = Math.max(1, slots - poolSize + 1);
48
71
  const out = [];
49
72
  for (let s = start; s <= slots; s++) out.push(s);
50
73
  return out;
51
74
  }
52
75
 
53
- function makeBetweenWrapper($targetPost, placeholderId) {
54
- if (isLi($targetPost)) {
55
- return (
56
- '<li class="ezoic-ad-between list-unstyled" data-ezoic-ad="1">' +
57
- '<div id="ezoic-pub-ad-placeholder-' + placeholderId + '"></div>' +
58
- '</li>'
59
- );
60
- }
61
- return '<div class="ezoic-ad-between" id="ezoic-pub-ad-placeholder-' + placeholderId + '"></div>';
62
- }
63
-
64
- function makeAdMessageWrapper($targetPost, placeholderId) {
65
- if (isLi($targetPost)) {
66
- return (
67
- '<li class="post ezoic-ad-post" data-ezoic-ad="1">' +
68
- '<div class="content">' +
69
- '<div id="ezoic-pub-ad-placeholder-' + placeholderId + '"></div>' +
70
- '</div>' +
71
- '</li>'
72
- );
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>';
73
80
  }
74
- return (
75
- '<div class="post ezoic-ad-post" data-ezoic-ad="1">' +
76
- '<div class="content">' +
77
- '<div id="ezoic-pub-ad-placeholder-' + placeholderId + '"></div>' +
78
- '</div>' +
79
- '</div>'
80
- );
81
+ return '<div class="' + classes + '" data-ezoic-ad="1">' + innerHtml + '</div>';
81
82
  }
82
83
 
83
84
  function insertBetweenPosts($posts, pool, interval) {
@@ -88,12 +89,13 @@ function insertBetweenPosts($posts, pool, interval) {
88
89
  const activeIds = [];
89
90
  for (let i = 0; i < slotsToRender.length; i++) {
90
91
  const slotNumber = slotsToRender[i];
91
- const id = pool[i];
92
- const index = slotNumber * interval - 1;
92
+ const id = pool[i]; // map latest slots to pool in order
93
+ const index = slotNumber * interval - 1; // 0-based index of the post after which we insert
93
94
  const $target = $posts.eq(index);
94
95
  if (!$target.length) continue;
95
96
 
96
- $target.after(makeBetweenWrapper($target, id));
97
+ const html = makeWrapperLike($target, 'ezoic-ad-between', '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>');
98
+ $target.after(html);
97
99
  activeIds.push(id);
98
100
  }
99
101
  return activeIds;
@@ -112,7 +114,9 @@ function insertAdMessagesBetweenReplies($posts, pool, interval) {
112
114
  const $target = $posts.eq(index);
113
115
  if (!$target.length) continue;
114
116
 
115
- $target.after(makeAdMessageWrapper($target, id));
117
+ const inner = '<div class="content"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
118
+ const html = makeWrapperLike($target, 'post ezoic-ad-post', inner);
119
+ $target.after(html);
116
120
  activeIds.push(id);
117
121
  }
118
122
  return activeIds;
@@ -120,11 +124,7 @@ function insertAdMessagesBetweenReplies($posts, pool, interval) {
120
124
 
121
125
  async function refreshAds() {
122
126
  let cfg;
123
- try {
124
- cfg = await fetchConfig();
125
- } catch (e) {
126
- return;
127
- }
127
+ try { cfg = await fetchConfig(); } catch (e) { return; }
128
128
  if (!cfg || cfg.excluded) return;
129
129
 
130
130
  const betweenPool = parsePool(cfg.placeholderIds);
@@ -136,6 +136,7 @@ async function refreshAds() {
136
136
  const $posts = getTopicPosts();
137
137
  if (!$posts.length) return;
138
138
 
139
+ // Clean first
139
140
  removeAdWrappers();
140
141
  removePlaceholdersByPool(betweenPool);
141
142
  removePlaceholdersByPool(messagePool);
@@ -150,19 +151,24 @@ async function refreshAds() {
150
151
  activeIds.push(...insertAdMessagesBetweenReplies($posts, messagePool, messageInterval));
151
152
  }
152
153
 
154
+ // Ezoic render
153
155
  if (activeIds.length && window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
154
156
  try { window.ezstandalone.destroyPlaceholders(); } catch (e) {}
155
157
  }
156
158
  activeIds.forEach(id => {
157
- try { window.ezstandalone && typeof window.ezstandalone.showAds === 'function' && window.ezstandalone.showAds(id); } catch (e) {}
159
+ try {
160
+ if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
161
+ window.ezstandalone.showAds(id);
162
+ }
163
+ } catch (e) {}
158
164
  });
159
165
  }
160
166
 
161
167
  function debounceRefresh() {
162
168
  clearTimeout(debounceTimer);
163
- debounceTimer = setTimeout(refreshAds, 120);
169
+ debounceTimer = setTimeout(refreshAds, 150);
164
170
  }
165
171
 
166
172
  $(document).ready(debounceRefresh);
167
173
  $(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
168
- setTimeout(debounceRefresh, 1200);
174
+ setTimeout(debounceRefresh, 1500);
@@ -54,6 +54,6 @@
54
54
  <p class="form-text">Si l’utilisateur appartient à un de ces groupes, aucune pub n’est injectée.</p>
55
55
  </div>
56
56
 
57
- <button id="save" type="submit" class="btn btn-primary">Enregistrer</button>
57
+ <button id="save" class="btn btn-primary">Enregistrer</button>
58
58
  </form>
59
59
  </div>