nodebb-plugin-ezoic-infinite 1.6.86 → 1.6.87

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": "1.6.86",
3
+ "version": "1.6.87",
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
@@ -1,134 +1,138 @@
1
1
  (function () {
2
- 'use strict';
2
+ 'use strict';
3
3
 
4
- if (window.ezInfiniteInjected) return;
5
- window.ezInfiniteInjected = true;
4
+ if (window.ezInfiniteInjected) return;
5
+ window.ezInfiniteInjected = true;
6
6
 
7
- const WRAP_CLASS = 'nodebb-ezoic-wrap';
8
- const POOL_ID = 'nodebb-ezoic-placeholder-pool';
9
-
10
- let config = null;
11
- let isInternalChange = false;
12
- let ezIsLoaded = false;
13
- let lastEnableTime = 0;
14
-
15
- function getPool() {
16
- let p = document.getElementById(POOL_ID);
17
- if (!p) {
18
- p = document.createElement('div');
19
- p.id = POOL_ID;
20
- p.style.display = 'none';
21
- document.body.appendChild(p);
22
- }
23
- return p;
24
- }
7
+ const WRAP_CLASS = 'nodebb-ezoic-wrap';
8
+ const POOL_ID = 'nodebb-ezoic-placeholder-pool';
9
+
10
+ let config = null;
11
+ let isInternalChange = false;
12
+ let activePlaceholders = new Set();
25
13
 
26
- function callEzoic(pid) {
27
- if (typeof window.ezstandalone === 'undefined') return;
28
- const id = parseInt(pid, 10);
29
- if (isNaN(id)) return;
30
-
31
- window.ezstandalone.cmd.push(function() {
32
- window.ezstandalone.define(id);
33
-
34
- // Si pas encore activé globalement
35
- if (!window.ezstandalone.enabled) {
36
- window.ezstandalone.enable();
37
- window.ezstandalone.display();
38
- lastEnableTime = Date.now();
39
- } else {
40
- // SECURITÉ : On attend au moins 2 secondes après un 'enable'
41
- // avant d'autoriser un 'refresh' pour éviter l'erreur "same page"
42
- const timeSinceEnable = Date.now() - lastEnableTime;
43
- if (timeSinceEnable > 2000) {
44
- window.ezstandalone.refresh();
45
- } else {
46
- // Si on est trop proche du enable, on retente dans 2.5s
47
- setTimeout(function() {
48
- window.ezstandalone.refresh();
49
- }, 2500);
50
- }
51
- }
52
- });
14
+ function getPool() {
15
+ let p = document.getElementById(POOL_ID);
16
+ if (!p) {
17
+ p = document.createElement('div');
18
+ p.id = POOL_ID;
19
+ p.style.display = 'none';
20
+ document.body.appendChild(p);
53
21
  }
22
+ return p;
23
+ }
54
24
 
55
- function redistribute() {
56
- if (!config || config.excluded) return;
25
+ // Utilisation stricte de la file d'attente CMD du zip
26
+ function callEzoic(id) {
27
+ if (typeof window.ezstandalone === 'undefined') return;
28
+
29
+ window.ezstandalone.cmd.push(function() {
30
+ // Si l'ID est déjà affiché, on ne fait rien pour éviter le spam de refresh
31
+ if (activePlaceholders.has(id)) {
32
+ window.ezstandalone.refresh();
33
+ return;
34
+ }
57
35
 
58
- // Harmony NodeBB 4 - Sélecteurs Accueil et Topics
59
- const topicItems = document.querySelectorAll('[component="category/topic"]');
60
- const postItems = document.querySelectorAll('[component="post"]');
36
+ window.ezstandalone.define(id);
37
+ activePlaceholders.add(id);
61
38
 
62
- // Page d'accueil / Catégories
63
- if (topicItems.length > 0 && config.enableBetweenAds) {
64
- processItems(Array.from(topicItems), 'between', config.intervalPosts, config.showFirstTopicAd);
65
- }
39
+ if (!window.ezstandalone.enabled) {
40
+ window.ezstandalone.enable();
41
+ window.ezstandalone.display();
42
+ } else {
43
+ window.ezstandalone.refresh();
44
+ }
45
+ });
46
+ }
47
+
48
+ function redistribute() {
49
+ if (!config || config.excluded) return;
50
+
51
+ // Utilisation des sélecteurs exacts du zip V17 (Harmony/NodeBB 4)
52
+ const topicItems = document.querySelectorAll('li[component="category/topic"]');
53
+ const postItems = document.querySelectorAll('[component="post"][data-pid]');
54
+
55
+ // Injection pour la page d'accueil / catégories
56
+ if (topicItems.length > 0 && config.enableBetweenAds) {
57
+ inject(Array.from(topicItems), 'between', config.intervalPosts, config.showFirstTopicAd);
58
+ }
59
+
60
+ // Injection pour les messages (topics)
61
+ if (postItems.length > 0 && config.enableMessageAds) {
62
+ inject(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
63
+ }
64
+ }
65
+
66
+ function inject(items, kind, interval, showFirst) {
67
+ const int = parseInt(interval, 10) || 10;
68
+ items.forEach((item, index) => {
69
+ const pos = index + 1;
70
+ const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
71
+
72
+ const next = item.nextElementSibling;
73
+ if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
74
+ const pool = getPool();
75
+ const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
66
76
 
67
- // Topics (Messages)
68
- if (postItems.length > 0 && config.enableMessageAds) {
69
- processItems(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
77
+ if (available) {
78
+ isInternalChange = true;
79
+ // On insère l'élément comme dans le zip original
80
+ item.parentNode.insertBefore(available, item.nextSibling);
81
+
82
+ const pid = parseInt(available.getAttribute('data-placeholder-id'), 10);
83
+ callEzoic(pid);
84
+
85
+ setTimeout(() => { isInternalChange = false; }, 100);
70
86
  }
71
- }
87
+ }
88
+ });
89
+ }
72
90
 
73
- function processItems(items, kind, interval, showFirst) {
74
- const int = parseInt(interval, 10) || 10;
75
- items.forEach((item, index) => {
76
- const pos = index + 1;
77
- const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
78
-
79
- const next = item.nextElementSibling;
80
- if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
81
- const pool = getPool();
82
- const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
83
- if (available) {
84
- isInternalChange = true;
85
- item.parentNode.insertBefore(available, item.nextSibling);
86
- callEzoic(available.getAttribute('data-placeholder-id'));
87
- setTimeout(() => { isInternalChange = false; }, 100);
88
- }
91
+ function init() {
92
+ fetch('/api/plugins/ezoic-infinite/config')
93
+ .then(r => r.json())
94
+ .then(data => {
95
+ config = data;
96
+ const pool = getPool();
97
+
98
+ const setupPool = (raw, kind) => {
99
+ if (!raw) return;
100
+ raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
101
+ if (!document.querySelector(`[data-placeholder-id="${id}"]`)) {
102
+ const d = document.createElement('div');
103
+ d.className = WRAP_CLASS;
104
+ d.setAttribute('data-kind', kind);
105
+ d.setAttribute('data-placeholder-id', id);
106
+ // Structure HTML propre
107
+ d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}" class="ezoic-ad"></div>`;
108
+ pool.appendChild(d);
89
109
  }
90
- });
91
- }
110
+ });
111
+ };
92
112
 
93
- function init() {
94
- fetch('/api/plugins/ezoic-infinite/config')
95
- .then(r => r.json())
96
- .then(data => {
97
- config = data;
98
- const pool = getPool();
99
- const setup = (raw, kind) => {
100
- if (!raw) return;
101
- raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
102
- if (!document.querySelector(`[data-placeholder-id="${id}"]`)) {
103
- const d = document.createElement('div');
104
- d.className = WRAP_CLASS;
105
- d.setAttribute('data-kind', kind);
106
- d.setAttribute('data-placeholder-id', id);
107
- d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}" class="ezoic-ad"></div>`;
108
- pool.appendChild(d);
109
- }
110
- });
111
- };
112
- setup(config.placeholderIds, 'between');
113
- setup(config.messagePlaceholderIds, 'message');
114
-
115
- redistribute();
116
-
117
- const observer = new MutationObserver(() => {
118
- if (!isInternalChange) redistribute();
119
- });
120
- observer.observe(document.body, { childList: true, subtree: true });
121
- });
122
- }
113
+ setupPool(config.placeholderIds, 'between');
114
+ setupPool(config.messagePlaceholderIds, 'message');
115
+
116
+ redistribute();
123
117
 
124
- // Correction Page d'accueil : Forcer redistribution quand on finit de charger une page
125
- window.addEventListener('action:ajaxify.end', function() {
126
- setTimeout(redistribute, 800);
118
+ // Observer pour l'infinite scroll (reprise du zip)
119
+ const observer = new MutationObserver(() => {
120
+ if (!isInternalChange) redistribute();
121
+ });
122
+ observer.observe(document.body, { childList: true, subtree: true });
123
+ });
124
+ }
125
+
126
+ // Événements NodeBB pour gérer la navigation sans rechargement
127
+ if (window.jQuery) {
128
+ window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function() {
129
+ setTimeout(redistribute, 500);
127
130
  });
131
+ }
128
132
 
129
- if (document.readyState === 'loading') {
130
- document.addEventListener('DOMContentLoaded', init);
131
- } else {
132
- init();
133
- }
133
+ if (document.readyState === 'loading') {
134
+ document.addEventListener('DOMContentLoaded', init);
135
+ } else {
136
+ init();
137
+ }
134
138
  })();
package/public/style.css CHANGED
@@ -1,31 +1,20 @@
1
- /* Container principal */
2
1
  .nodebb-ezoic-wrap {
3
- display: block !important;
4
- width: 100% !important;
5
- min-width: 100% !important;
6
- margin: 20px 0 !important;
7
- min-height: 250px;
8
- clear: both;
9
- float: none !important;
2
+ display: block !important;
3
+ width: 100% !important;
4
+ margin: 30px 0 !important;
5
+ min-height: 250px;
6
+ clear: both;
10
7
  }
11
8
 
12
- /* Forcer l'affichage dans la liste des catégories/topics (Accueil) */
13
- [component="category/topic"] {
14
- margin-bottom: 0 !important;
9
+ /* Fix pour les listes de l'accueil Harmony */
10
+ li[component="category/topic"] + .nodebb-ezoic-wrap {
11
+ list-style: none;
12
+ padding: 15px;
13
+ background: rgba(0,0,0,0.02);
14
+ border-radius: 8px;
15
15
  }
16
16
 
17
- .nodebb-ezoic-wrap[data-kind="between"] {
18
- background: transparent;
19
- border-top: 1px solid rgba(0,0,0,0.05);
20
- padding: 20px 0;
21
- }
22
-
23
- /* Cache si vraiment aucun contenu injecté après 5s */
17
+ /* Éviter les trous si la pub ne charge pas */
24
18
  .nodebb-ezoic-wrap:empty {
25
- min-height: 1px;
26
- }
27
-
28
- /* Empêche les doublons */
29
- .nodebb-ezoic-wrap + .nodebb-ezoic-wrap {
30
- display: none !important;
19
+ min-height: 1px;
31
20
  }