nodebb-plugin-ezoic-infinite 1.6.53 → 1.6.54
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 +1 -1
- package/public/client.js +60 -106
- package/public/style.css +12 -0
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,68 +1,27 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
// --- 1. GESTION DU SCROLL ---
|
|
5
|
-
let lastScrollY = 0;
|
|
6
|
-
let scrollDir = 1;
|
|
7
|
-
try {
|
|
8
|
-
lastScrollY = window.scrollY || 0;
|
|
9
|
-
window.addEventListener('scroll', () => {
|
|
10
|
-
const y = window.scrollY || 0;
|
|
11
|
-
const d = y - lastScrollY;
|
|
12
|
-
if (Math.abs(d) > 4) {
|
|
13
|
-
scrollDir = d > 0 ? 1 : -1;
|
|
14
|
-
lastScrollY = y;
|
|
15
|
-
}
|
|
16
|
-
}, { passive: true });
|
|
17
|
-
} catch (e) {}
|
|
18
|
-
|
|
19
4
|
const WRAP_CLASS = 'nodebb-ezoic-wrap';
|
|
20
|
-
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
21
5
|
const PINNED_ATTR = 'data-ezoic-pinned';
|
|
6
|
+
const PROCESSED_ATTR = 'data-ezoic-seen'; // Nouveau : marqueur de sujet
|
|
7
|
+
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
22
8
|
|
|
23
9
|
let config = null;
|
|
24
10
|
let usedIds = new Set();
|
|
25
11
|
let scheduleTimer = null;
|
|
26
12
|
|
|
27
|
-
// --- 2. SÉLECTEURS DE THÈMES (Harmony/Persona) ---
|
|
28
13
|
function getTopicList() {
|
|
29
|
-
return document.querySelector('
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getPostList() {
|
|
33
|
-
return document.querySelector('[component="topic"], [component="post/list"]');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// --- 3. FIX ANTI-PILEUP / ANTI-REMONTÉE ---
|
|
37
|
-
function pruneOrphans(ul) {
|
|
38
|
-
if (!ul) return;
|
|
39
|
-
const wraps = ul.querySelectorAll('.' + WRAP_CLASS);
|
|
40
|
-
wraps.forEach(wrap => {
|
|
41
|
-
// Si c'est la pub après le 1er sujet, on la force à rester collée au 1er LI
|
|
42
|
-
if (wrap.getAttribute(PINNED_ATTR) === 'true') {
|
|
43
|
-
const firstLi = ul.querySelector('li[data-tid], li:first-child');
|
|
44
|
-
if (firstLi && wrap.previousElementSibling !== firstLi) {
|
|
45
|
-
firstLi.after(wrap);
|
|
46
|
-
}
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
// Si on remonte, on supprime les pubs qui perdent leur "ancre" (le LI précédent)
|
|
50
|
-
// NodeBB détruit les LI du haut pour la performance, on doit suivre le mouvement
|
|
51
|
-
if (scrollDir === -1 && (!wrap.previousElementSibling || wrap.previousElementSibling.tagName !== 'LI')) {
|
|
52
|
-
wrap.remove();
|
|
53
|
-
}
|
|
54
|
-
});
|
|
14
|
+
return document.querySelector('[component="topic/list"], [component="category"], [component="categories"], .topic-list');
|
|
55
15
|
}
|
|
56
16
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const ids = poolStr.split(/[\s,]+/).filter(Boolean);
|
|
17
|
+
function getNextId(pool) {
|
|
18
|
+
if (!pool) return null;
|
|
19
|
+
const ids = pool.split(/[\s,]+/).filter(Boolean);
|
|
61
20
|
for (const id of ids) {
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
usedIds.add(
|
|
65
|
-
return
|
|
21
|
+
const n = id.replace(/[^\d]/g, '');
|
|
22
|
+
if (n && !usedIds.has(n)) {
|
|
23
|
+
usedIds.add(n);
|
|
24
|
+
return n;
|
|
66
25
|
}
|
|
67
26
|
}
|
|
68
27
|
return null;
|
|
@@ -79,92 +38,87 @@
|
|
|
79
38
|
} else {
|
|
80
39
|
window.ezstandalone.refresh();
|
|
81
40
|
}
|
|
82
|
-
} catch (e) { console.warn('[ezoic]
|
|
41
|
+
} catch (e) { console.warn('[ezoic] err', e); }
|
|
83
42
|
}
|
|
84
43
|
}
|
|
85
44
|
|
|
86
|
-
// --- 5. INJECTION ---
|
|
87
45
|
function inject() {
|
|
88
46
|
if (!config || config.excluded) return;
|
|
89
|
-
|
|
90
|
-
// A. Gestion des SUJETS (Liste de catégories/topics)
|
|
91
47
|
const ul = getTopicList();
|
|
92
|
-
if (ul
|
|
93
|
-
pruneOrphans(ul);
|
|
94
|
-
const items = Array.from(ul.children).filter(c => c.tagName === 'LI' && !c.classList.contains(WRAP_CLASS));
|
|
95
|
-
|
|
96
|
-
// Pub 1 (Pinned)
|
|
97
|
-
if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
|
|
98
|
-
insertAd(items[0], getNextId(config.placeholderIds), true);
|
|
99
|
-
}
|
|
48
|
+
if (!ul) return;
|
|
100
49
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
50
|
+
// 1. On récupère tous les LI qui ne sont pas des pubs
|
|
51
|
+
const items = Array.from(ul.children).filter(c => c.tagName === 'LI' && !c.classList.contains(WRAP_CLASS));
|
|
52
|
+
if (!items.length) return;
|
|
53
|
+
|
|
54
|
+
// 2. PUB N°1 (Après le 1er sujet)
|
|
55
|
+
if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
|
|
56
|
+
items[0].setAttribute(PROCESSED_ATTR, 'true');
|
|
57
|
+
insertAd(items[0], getNextId(config.placeholderIds), true);
|
|
112
58
|
}
|
|
113
59
|
|
|
114
|
-
//
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
60
|
+
// 3. PUBS SUIVANTES (Intervalle)
|
|
61
|
+
const interval = parseInt(config.intervalPosts, 10) || 5;
|
|
62
|
+
|
|
63
|
+
// On ne traite que les éléments qui n'ont pas encore été "vus" par le script
|
|
64
|
+
items.forEach((li, idx) => {
|
|
65
|
+
const positionAbsolue = idx + 1;
|
|
66
|
+
|
|
67
|
+
// Si on tombe sur un multiple de l'intervalle ET que le LI n'a pas déjà été marqué
|
|
68
|
+
if (positionAbsolue > 1 && positionAbsolue % interval === 0) {
|
|
69
|
+
// Vérification anti-doublon : est-ce que ce LI a déjà une pub juste après lui ?
|
|
70
|
+
const hasAdAfter = li.nextElementSibling && li.nextElementSibling.classList.contains(WRAP_CLASS);
|
|
71
|
+
|
|
72
|
+
if (!hasAdAfter) {
|
|
73
|
+
insertAd(li, getNextId(config.placeholderIds), false);
|
|
74
|
+
}
|
|
120
75
|
}
|
|
121
|
-
|
|
122
|
-
}
|
|
76
|
+
});
|
|
123
77
|
}
|
|
124
78
|
|
|
125
|
-
function insertAd(target, id, isPinned
|
|
126
|
-
if (!
|
|
79
|
+
function insertAd(target, id, isPinned) {
|
|
80
|
+
if (!id || !target) return;
|
|
81
|
+
|
|
127
82
|
const wrap = document.createElement('div');
|
|
128
|
-
wrap.className =
|
|
83
|
+
wrap.className = WRAP_CLASS + ' ezoic-ad-between';
|
|
129
84
|
if (isPinned) wrap.setAttribute(PINNED_ATTR, 'true');
|
|
130
85
|
|
|
131
86
|
const placeholder = document.createElement('div');
|
|
132
87
|
placeholder.id = PLACEHOLDER_PREFIX + id;
|
|
133
88
|
wrap.appendChild(placeholder);
|
|
134
89
|
|
|
90
|
+
// Insertion physique
|
|
135
91
|
target.after(wrap);
|
|
136
|
-
|
|
92
|
+
|
|
93
|
+
// On force un petit délai pour laisser Ezoic voir le nouveau div dans le DOM
|
|
94
|
+
setTimeout(() => callEzoic(id), 50);
|
|
137
95
|
}
|
|
138
96
|
|
|
139
|
-
|
|
140
|
-
async function fetchConfig() {
|
|
97
|
+
async function init() {
|
|
141
98
|
try {
|
|
142
99
|
const res = await fetch('/api/plugins/ezoic-infinite/config');
|
|
143
100
|
config = await res.json();
|
|
144
|
-
} catch (e) {}
|
|
145
|
-
}
|
|
146
101
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
scheduleTimer = setTimeout(inject, 200);
|
|
150
|
-
}
|
|
102
|
+
// Premier passage
|
|
103
|
+
inject();
|
|
151
104
|
|
|
152
|
-
|
|
153
|
-
fetchConfig().then(() => {
|
|
154
|
-
schedule();
|
|
105
|
+
// Observation du DOM (pour le scroll infini)
|
|
155
106
|
const ul = getTopicList();
|
|
156
107
|
if (ul && typeof MutationObserver !== 'undefined') {
|
|
157
|
-
new MutationObserver((
|
|
158
|
-
if (
|
|
159
|
-
|
|
108
|
+
const mo = new MutationObserver(() => {
|
|
109
|
+
if (scheduleTimer) clearTimeout(scheduleTimer);
|
|
110
|
+
scheduleTimer = setTimeout(inject, 300);
|
|
111
|
+
});
|
|
112
|
+
mo.observe(ul, { childList: true });
|
|
160
113
|
}
|
|
161
|
-
});
|
|
162
114
|
|
|
163
|
-
|
|
164
|
-
window.jQuery
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
115
|
+
// Hooks NodeBB
|
|
116
|
+
if (window.jQuery) {
|
|
117
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', () => {
|
|
118
|
+
setTimeout(inject, 500);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
} catch (e) { console.error('[ezoic] init fail', e); }
|
|
168
122
|
}
|
|
169
123
|
|
|
170
124
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
package/public/style.css
CHANGED
|
@@ -86,3 +86,15 @@ li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
|
|
|
86
86
|
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
|
|
87
87
|
/* ===== /V17 ===== */
|
|
88
88
|
|
|
89
|
+
.nodebb-ezoic-wrap {
|
|
90
|
+
display: block;
|
|
91
|
+
width: 100%;
|
|
92
|
+
clear: both;
|
|
93
|
+
margin: 20px 0 !important; /* Ajoute un peu d'espace pour le visuel */
|
|
94
|
+
min-height: 50px; /* Évite que le bloc ne soit écrasé à 0px */
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Si la pub est vide, on garde un petit espace pour éviter le saut au scroll up */
|
|
98
|
+
.nodebb-ezoic-wrap:empty {
|
|
99
|
+
min-height: 1px;
|
|
100
|
+
}
|