nodebb-plugin-ezoic-infinite 1.4.68 → 1.4.69
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 +390 -768
- package/public/style.css +36 -5
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,828 +1,450 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
|
-
const $ = (typeof window.jQuery === 'function') ? window.jQuery : null;
|
|
4
|
-
const SELECTORS = {
|
|
5
|
-
topicItem: 'li[component="category/topic"]',
|
|
6
|
-
postItem: '[component="post"][data-pid]',
|
|
7
|
-
categoryItem: 'li[component="categories/category"]',
|
|
8
|
-
}, WRAP_CLASS = 'ezoic-ad';
|
|
9
|
-
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-', MAX_INSERTS_PER_RUN = 3;
|
|
10
|
-
|
|
11
|
-
const sessionDefinedIds = new Set();
|
|
12
|
-
|
|
13
|
-
const insertingIds = new Set(), state = {
|
|
14
|
-
pageKey: null,
|
|
15
|
-
cfg: null,
|
|
16
|
-
cfgPromise: null,
|
|
17
3
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
/**
|
|
5
|
+
* NodeBB + Ezoic infinite ad injector
|
|
6
|
+
* Production-ready refactor:
|
|
7
|
+
* - No blank space: wrappers hidden until filled, removed on timeout
|
|
8
|
+
* - Bounded work per tick + throttled MutationObserver
|
|
9
|
+
* - Clean ajaxify navigation handling
|
|
10
|
+
*/
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
const SELECTORS = {
|
|
13
|
+
topicItem: 'li[component="category/topic"]',
|
|
14
|
+
categoryItem: 'li[component="categories/category"]',
|
|
15
|
+
postItem: '[component="post"][data-pid]',
|
|
16
|
+
postContent: '[component="post/content"]',
|
|
17
|
+
};
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
definedIds: new Set(),
|
|
19
|
+
const CLASS_WRAP = 'ezoic-ad';
|
|
20
|
+
const PH_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
const MAX_INSERTS_PER_TICK = 3;
|
|
23
|
+
const FILL_TIMEOUT_MS = 3500;
|
|
24
|
+
const RECYCLE_MARGIN_PX = 600;
|
|
25
|
+
const SHOW_DEBOUNCE_MS = 500;
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
const state = {
|
|
28
|
+
pageKey: null,
|
|
29
|
+
cfg: null,
|
|
30
|
+
cfgPromise: null,
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
32
|
+
pools: { topics: [], categories: [], messages: [] },
|
|
33
|
+
live: { topics: [], categories: [], messages: [] },
|
|
40
34
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
out.push(n);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
35
|
+
scheduled: false,
|
|
36
|
+
lastRunAt: 0,
|
|
37
|
+
timeouts: new Set(),
|
|
38
|
+
observer: null,
|
|
39
|
+
lastShowById: new Map(),
|
|
40
|
+
};
|
|
52
41
|
|
|
53
|
-
function
|
|
54
|
-
if (!raw) return [];
|
|
55
|
-
const lines = String(raw).split(/\r?\n/).map(s => s.trim()).filter(Boolean);
|
|
56
|
-
return uniqInts(lines);
|
|
57
|
-
}
|
|
42
|
+
function now() { return Date.now(); }
|
|
58
43
|
|
|
59
44
|
function getPageKey() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
45
|
+
try {
|
|
46
|
+
const ax = window.ajaxify;
|
|
47
|
+
if (ax && ax.data) {
|
|
48
|
+
if (ax.data.tid) return `topic:${ax.data.tid}`;
|
|
49
|
+
if (ax.data.cid) return `cid:${ax.data.cid}:${window.location.pathname}`;
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {}
|
|
52
|
+
return window.location.pathname || '';
|
|
68
53
|
}
|
|
69
54
|
|
|
70
55
|
function getKind() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function safeRect(el) {
|
|
103
|
-
try { return el.getBoundingClientRect(); } catch (e) { return null; }
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function destroyPlaceholderIds(ids) {
|
|
107
|
-
if (!ids || !ids.length) return;
|
|
108
|
-
const filtered = ids.filter((id) => {
|
|
109
|
-
try { return sessionDefinedIds.has(id); } catch (e) { return true; }
|
|
110
|
-
});
|
|
111
|
-
if (!filtered.length) return;
|
|
112
|
-
|
|
113
|
-
const call = () => {
|
|
114
|
-
try {
|
|
115
|
-
if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
116
|
-
window.ezstandalone.destroyPlaceholders(filtered);
|
|
117
|
-
}
|
|
118
|
-
} catch (e) {}
|
|
119
|
-
|
|
120
|
-
// Recyclage: libérer IDs après 100ms
|
|
121
|
-
setTimeout(() => {
|
|
122
|
-
filtered.forEach(id => sessionDefinedIds.delete(id));
|
|
123
|
-
}, 100);
|
|
124
|
-
};
|
|
125
|
-
try {
|
|
126
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
127
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
128
|
-
if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') call();
|
|
129
|
-
else window.ezstandalone.cmd.push(call);
|
|
130
|
-
} catch (e) {}
|
|
131
|
-
|
|
132
|
-
// Recyclage: libérer IDs après 100ms
|
|
133
|
-
setTimeout(() => {
|
|
134
|
-
filtered.forEach(id => sessionDefinedIds.delete(id));
|
|
135
|
-
}, 100);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Nettoyer éléments Ezoic invisibles qui créent espace vertical
|
|
139
|
-
function cleanupInvisibleEzoicElements() {
|
|
140
|
-
try {
|
|
141
|
-
document.querySelectorAll('.ezoic-ad').forEach(wrapper => {
|
|
142
|
-
const ph = wrapper.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
|
|
143
|
-
if (!ph) return;
|
|
144
|
-
|
|
145
|
-
// Supprimer TOUS les éléments après le placeholder rempli
|
|
146
|
-
// qui créent de l'espace vertical
|
|
147
|
-
let found = false;
|
|
148
|
-
Array.from(wrapper.children).forEach(child => {
|
|
149
|
-
if (child === ph || child.contains(ph)) {
|
|
150
|
-
found = true;
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Si élément APRÈS le placeholder
|
|
155
|
-
if (found) {
|
|
156
|
-
const rect = child.getBoundingClientRect();
|
|
157
|
-
const computed = window.getComputedStyle(child);
|
|
158
|
-
|
|
159
|
-
// Supprimer si:
|
|
160
|
-
// 1. Height > 0 mais pas de texte/image visible
|
|
161
|
-
// 2. Ou opacity: 0
|
|
162
|
-
// 3. Ou visibility: hidden
|
|
163
|
-
const hasContent = child.textContent.trim().length > 0 ||
|
|
164
|
-
child.querySelector('img, iframe, video');
|
|
165
|
-
|
|
166
|
-
if (!hasContent || computed.opacity === '0' || computed.visibility === 'hidden') {
|
|
167
|
-
child.remove();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
} catch (e) {}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function cleanupEmptyWrappers() {
|
|
176
|
-
try {
|
|
177
|
-
document.querySelectorAll('.ezoic-ad').forEach(wrapper => {
|
|
178
|
-
const ph = wrapper.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
|
|
179
|
-
if (ph && ph.children.length === 0) {
|
|
180
|
-
// Placeholder vide après 3s = pub non chargée
|
|
181
|
-
setTimeout(() => {
|
|
182
|
-
if (ph.children.length === 0) {
|
|
183
|
-
wrapper.remove();
|
|
184
|
-
}
|
|
185
|
-
}, 1500);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
} catch (e) {}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function getRecyclable(liveArr) {
|
|
192
|
-
const margin = 600;
|
|
193
|
-
for (let i = 0; i < liveArr.length; i++) {
|
|
194
|
-
const entry = liveArr[i];
|
|
195
|
-
if (!entry || !entry.wrap || !entry.wrap.isConnected) { liveArr.splice(i, 1); i--; continue; }
|
|
196
|
-
const r = safeRect(entry.wrap);
|
|
197
|
-
if (r && r.bottom < -margin) {
|
|
198
|
-
liveArr.splice(i, 1);
|
|
199
|
-
return entry;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function pickId(pool, liveArr) {
|
|
206
|
-
if (pool.length) return { id: pool.shift(), recycled: null };
|
|
207
|
-
const recycled = getRecyclable(liveArr);
|
|
208
|
-
if (recycled) return { id: recycled.id, recycled };
|
|
209
|
-
return { id: null, recycled: null };
|
|
56
|
+
const p = window.location.pathname || '';
|
|
57
|
+
if (/^\/topic\//.test(p)) return 'topic';
|
|
58
|
+
if (/^\/category\//.test(p)) return 'categoryTopics';
|
|
59
|
+
if (p === '/' || /^\/categories/.test(p)) return 'categories';
|
|
60
|
+
|
|
61
|
+
if (document.querySelector(SELECTORS.categoryItem)) return 'categories';
|
|
62
|
+
if (document.querySelector(SELECTORS.postItem)) return 'topic';
|
|
63
|
+
if (document.querySelector(SELECTORS.topicItem)) return 'categoryTopics';
|
|
64
|
+
return 'other';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseIdPool(raw) {
|
|
68
|
+
if (!raw) return [];
|
|
69
|
+
const tokens = String(raw)
|
|
70
|
+
.split(/[\s,;]+/)
|
|
71
|
+
.map(s => s.trim())
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
|
|
74
|
+
const out = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const t of tokens) {
|
|
77
|
+
const n = parseInt(t, 10);
|
|
78
|
+
if (Number.isFinite(n) && n > 0 && !seen.has(n)) {
|
|
79
|
+
seen.add(n);
|
|
80
|
+
out.push(n);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
210
84
|
}
|
|
211
85
|
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
return orig.apply(ez, arguments);
|
|
307
|
-
};
|
|
308
|
-
} catch (e) {}
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
applyPatch();
|
|
312
|
-
if (!window.__nodebbEzoicPatched) {
|
|
313
|
-
try {
|
|
314
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
315
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
316
|
-
window.ezstandalone.cmd.push(applyPatch);
|
|
317
|
-
} catch (e) {}
|
|
318
|
-
}
|
|
86
|
+
async function fetchConfig() {
|
|
87
|
+
if (state.cfg) return state.cfg;
|
|
88
|
+
if (state.cfgPromise) return state.cfgPromise;
|
|
89
|
+
|
|
90
|
+
state.cfgPromise = fetch('/api/plugins/ezoic-infinite/config', {
|
|
91
|
+
credentials: 'same-origin',
|
|
92
|
+
headers: { 'Accept': 'application/json' },
|
|
93
|
+
})
|
|
94
|
+
.then(r => (r && r.ok ? r.json() : null))
|
|
95
|
+
.catch(() => null)
|
|
96
|
+
.then((cfg) => {
|
|
97
|
+
state.cfg = cfg || { excluded: true };
|
|
98
|
+
state.pools.topics = parseIdPool(state.cfg.placeholderIds);
|
|
99
|
+
state.pools.categories = parseIdPool(state.cfg.categoryPlaceholderIds);
|
|
100
|
+
state.pools.messages = parseIdPool(state.cfg.messagePlaceholderIds);
|
|
101
|
+
return state.cfg;
|
|
102
|
+
})
|
|
103
|
+
.finally(() => { state.cfgPromise = null; });
|
|
104
|
+
|
|
105
|
+
return state.cfgPromise;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function rect(el) {
|
|
109
|
+
try { return el.getBoundingClientRect(); } catch (e) { return null; }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function takeRecyclable(kind) {
|
|
113
|
+
const arr = state.live[kind];
|
|
114
|
+
for (let i = 0; i < arr.length; i++) {
|
|
115
|
+
const entry = arr[i];
|
|
116
|
+
if (!entry || !entry.wrap || !entry.wrap.isConnected) {
|
|
117
|
+
arr.splice(i, 1); i--; continue;
|
|
118
|
+
}
|
|
119
|
+
const r = rect(entry.wrap);
|
|
120
|
+
if (r && r.bottom < -RECYCLE_MARGIN_PX) {
|
|
121
|
+
arr.splice(i, 1);
|
|
122
|
+
return entry;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function pickId(kind) {
|
|
129
|
+
const pool = state.pools[kind];
|
|
130
|
+
if (pool && pool.length) return { id: pool.shift(), recycled: null };
|
|
131
|
+
const recycled = takeRecyclable(kind);
|
|
132
|
+
if (recycled) return { id: recycled.id, recycled };
|
|
133
|
+
return { id: null, recycled: null };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function buildWrap(kind, afterIndex, id) {
|
|
137
|
+
const wrap = document.createElement('div');
|
|
138
|
+
wrap.className = `${CLASS_WRAP} ${CLASS_WRAP}--${kind}`;
|
|
139
|
+
wrap.dataset.ezoicKind = kind;
|
|
140
|
+
wrap.dataset.ezoicAfter = String(afterIndex);
|
|
141
|
+
wrap.dataset.ezoicId = String(id);
|
|
142
|
+
|
|
143
|
+
const ph = document.createElement('div');
|
|
144
|
+
ph.id = `${PH_PREFIX}${id}`;
|
|
145
|
+
wrap.appendChild(ph);
|
|
146
|
+
|
|
147
|
+
return wrap;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function findWrap(kind, afterIndex) {
|
|
151
|
+
return document.querySelector(`.${CLASS_WRAP}[data-ezoic-kind="${kind}"][data-ezoic-after="${afterIndex}"]`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function resetWrapPlaceholder(wrap, id) {
|
|
155
|
+
if (!wrap) return;
|
|
156
|
+
if (wrap.__ezoicObs) {
|
|
157
|
+
try { wrap.__ezoicObs.disconnect(); } catch (e) {}
|
|
158
|
+
wrap.__ezoicObs = null;
|
|
159
|
+
}
|
|
160
|
+
wrap.removeAttribute('data-ezoic-filled');
|
|
161
|
+
wrap.dataset.ezoicId = String(id);
|
|
162
|
+
|
|
163
|
+
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
|
164
|
+
const ph = document.createElement('div');
|
|
165
|
+
ph.id = `${PH_PREFIX}${id}`;
|
|
166
|
+
wrap.appendChild(ph);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function forcePlaceholderAutoHeight(wrap, id) {
|
|
170
|
+
const ph = wrap && wrap.querySelector ? wrap.querySelector(`#${PH_PREFIX}${id}`) : null;
|
|
171
|
+
if (!ph) return;
|
|
172
|
+
ph.style.setProperty('height', 'auto', 'important');
|
|
173
|
+
ph.style.setProperty('min-height', '0px', 'important');
|
|
174
|
+
requestAnimationFrame(() => {
|
|
175
|
+
ph.style.setProperty('height', 'auto', 'important');
|
|
176
|
+
ph.style.setProperty('min-height', '0px', 'important');
|
|
177
|
+
});
|
|
319
178
|
}
|
|
320
179
|
|
|
321
180
|
function markFilled(wrap) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
181
|
+
if (!wrap || !wrap.isConnected) return;
|
|
182
|
+
if (wrap.getAttribute('data-ezoic-filled') === '1') return;
|
|
183
|
+
wrap.setAttribute('data-ezoic-filled', '1');
|
|
184
|
+
|
|
185
|
+
const id = parseInt(wrap.dataset.ezoicId, 10);
|
|
186
|
+
if (Number.isFinite(id) && id > 0) forcePlaceholderAutoHeight(wrap, id);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function removeWrap(wrap) {
|
|
190
|
+
if (!wrap) return;
|
|
191
|
+
if (wrap.__ezoicObs) {
|
|
192
|
+
try { wrap.__ezoicObs.disconnect(); } catch (e) {}
|
|
193
|
+
wrap.__ezoicObs = null;
|
|
194
|
+
}
|
|
195
|
+
try { wrap.remove(); } catch (e) {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function showEzoicAd(id) {
|
|
199
|
+
if (!id || id <= 0) return;
|
|
200
|
+
|
|
201
|
+
const last = state.lastShowById.get(id) || 0;
|
|
202
|
+
if (now() - last < SHOW_DEBOUNCE_MS) return;
|
|
203
|
+
state.lastShowById.set(id, now());
|
|
204
|
+
|
|
205
|
+
const call = () => {
|
|
206
|
+
try {
|
|
207
|
+
if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
|
|
208
|
+
window.ezstandalone.showAds(id);
|
|
209
|
+
}
|
|
210
|
+
} catch (e) {}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
215
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
216
|
+
if (typeof window.ezstandalone.showAds === 'function') call();
|
|
217
|
+
else window.ezstandalone.cmd.push(call);
|
|
218
|
+
} catch (e) {}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function observeFill(wrap, id) {
|
|
222
|
+
const ph = wrap.querySelector(`#${PH_PREFIX}${id}`);
|
|
223
|
+
if (!ph) return;
|
|
224
|
+
|
|
225
|
+
// immediate
|
|
226
|
+
if (ph.querySelector('iframe, ins, img') || ph.childElementCount > 0) {
|
|
227
|
+
markFilled(wrap);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
332
230
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const obs = new MutationObserver(() => {
|
|
344
|
-
if (ph.childNodes && ph.childNodes.length > 0) {
|
|
345
|
-
markFilled(wrap); // CRITIQUE: Afficher wrapper maintenant
|
|
346
|
-
try { sessionDefinedIds.add(id); } catch (e) {}
|
|
347
|
-
try { obs.disconnect(); } catch (e) {}
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
obs.observe(ph, { childList: true, subtree: true });
|
|
351
|
-
wrap.__ezoicFillObs = obs;
|
|
352
|
-
} catch (e) {}
|
|
353
|
-
}
|
|
231
|
+
const obs = new MutationObserver(() => {
|
|
232
|
+
if (!wrap.isConnected) return;
|
|
233
|
+
const hasCreative = !!ph.querySelector('iframe, ins, img');
|
|
234
|
+
const r = rect(ph);
|
|
235
|
+
if (hasCreative || (r && r.height > 20)) {
|
|
236
|
+
markFilled(wrap);
|
|
237
|
+
try { obs.disconnect(); } catch (e) {}
|
|
238
|
+
wrap.__ezoicObs = null;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
354
241
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
242
|
+
wrap.__ezoicObs = obs;
|
|
243
|
+
try { obs.observe(ph, { childList: true, subtree: true, attributes: true }); } catch (e) {}
|
|
244
|
+
}
|
|
358
245
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
246
|
+
function scheduleRemovalIfUnfilled(wrap, kind, id) {
|
|
247
|
+
const t = setTimeout(() => {
|
|
248
|
+
state.timeouts.delete(t);
|
|
249
|
+
if (!wrap || !wrap.isConnected) return;
|
|
250
|
+
if (wrap.getAttribute('data-ezoic-filled') === '1') return;
|
|
251
|
+
removeWrap(wrap);
|
|
252
|
+
if (state.pools[kind] && typeof id === 'number' && id > 0) state.pools[kind].push(id);
|
|
253
|
+
}, FILL_TIMEOUT_MS);
|
|
254
|
+
state.timeouts.add(t);
|
|
366
255
|
}
|
|
367
|
-
|
|
256
|
+
|
|
257
|
+
function insertAfter(target, wrap) {
|
|
258
|
+
if (!target || !wrap) return false;
|
|
259
|
+
const next = target.nextElementSibling;
|
|
260
|
+
if (next && next.classList && next.classList.contains(CLASS_WRAP)) return false;
|
|
261
|
+
try {
|
|
262
|
+
target.insertAdjacentElement('afterend', wrap);
|
|
263
|
+
return true;
|
|
264
|
+
} catch (e) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
368
267
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (sessionDefinedIds.has(id)) {
|
|
377
|
-
try {
|
|
378
|
-
destroyPlaceholderIds([id]);
|
|
379
|
-
sessionDefinedIds.delete(id);
|
|
380
|
-
} catch (e) {}
|
|
268
|
+
|
|
269
|
+
function computeAfterIndexes(count, interval, showFirst) {
|
|
270
|
+
const out = [];
|
|
271
|
+
const step = Math.max(1, parseInt(interval, 10) || 1);
|
|
272
|
+
const start = showFirst ? 1 : step;
|
|
273
|
+
for (let i = start; i < count; i += step) out.push(i);
|
|
274
|
+
return out;
|
|
381
275
|
}
|
|
382
276
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (now - last < 3500) return;
|
|
386
|
-
|
|
387
|
-
// Ajouter à la batch
|
|
388
|
-
pendingShowAdsIds.add(id);
|
|
389
|
-
|
|
390
|
-
clearTimeout(batchShowAdsTimer);
|
|
391
|
-
batchShowAdsTimer = setTimeout(() => {
|
|
392
|
-
if (pendingShowAdsIds.size === 0) return;
|
|
393
|
-
|
|
394
|
-
const idsArray = Array.from(pendingShowAdsIds);
|
|
395
|
-
pendingShowAdsIds.clear();
|
|
396
|
-
|
|
397
|
-
// Appeler showAds avec TOUS les IDs en une fois
|
|
398
|
-
try {
|
|
399
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
400
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
401
|
-
window.ezstandalone.cmd.push(function() {
|
|
402
|
-
if (typeof window.ezstandalone.showAds === 'function') {
|
|
403
|
-
// Appel batch: showAds(id1, id2, id3...)
|
|
404
|
-
window.ezstandalone.showAds(...idsArray);
|
|
405
|
-
// Tracker tous les IDs
|
|
406
|
-
idsArray.forEach(id => {
|
|
407
|
-
state.lastShowById.set(id, Date.now());
|
|
408
|
-
sessionDefinedIds.add(id);
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
} catch (e) {}
|
|
413
|
-
|
|
414
|
-
// CRITIQUE: Nettoyer éléments invisibles APRÈS que pubs soient chargées
|
|
415
|
-
setTimeout(() => {
|
|
416
|
-
cleanupInvisibleEzoicElements();
|
|
417
|
-
}, 800); // 1.5s pour laisser Ezoic charger
|
|
418
|
-
}, 100);
|
|
419
|
-
}
|
|
277
|
+
function runCategoryTopics(cfg) {
|
|
278
|
+
if (!cfg.enableBetweenAds) return;
|
|
420
279
|
|
|
421
|
-
|
|
422
|
-
|
|
280
|
+
const items = Array.from(document.querySelectorAll(SELECTORS.topicItem));
|
|
281
|
+
if (!items.length) return;
|
|
423
282
|
|
|
424
|
-
|
|
425
|
-
|
|
283
|
+
const after = computeAfterIndexes(items.length, cfg.intervalPosts || 6, !!cfg.showFirstTopicAd);
|
|
284
|
+
let inserts = 0;
|
|
426
285
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
286
|
+
for (const afterIndex of after) {
|
|
287
|
+
if (inserts >= MAX_INSERTS_PER_TICK) break;
|
|
288
|
+
const anchor = items[afterIndex - 1];
|
|
289
|
+
if (!anchor) continue;
|
|
290
|
+
if (findWrap('topics', afterIndex)) continue;
|
|
431
291
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
sessionDefinedIds.add(id);
|
|
435
|
-
return true;
|
|
436
|
-
}
|
|
437
|
-
} catch (e) {}
|
|
438
|
-
return false;
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
const startPageKey = state.pageKey;
|
|
442
|
-
let attempts = 0;
|
|
443
|
-
(function waitForPh() {
|
|
444
|
-
if (state.pageKey !== startPageKey) return;
|
|
445
|
-
if (state.pendingById.has(id)) return;
|
|
446
|
-
|
|
447
|
-
attempts += 1;
|
|
448
|
-
const el = document.getElementById(phId);
|
|
449
|
-
if (el && el.isConnected) {
|
|
450
|
-
|
|
451
|
-
// Si on arrive ici, soit visible, soit timeout
|
|
452
|
-
|
|
453
|
-
if (doCall()) {
|
|
454
|
-
state.pendingById.delete(id);
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (attempts < 100) {
|
|
461
|
-
const timeoutId = setTimeout(waitForPh, 50);
|
|
462
|
-
state.activeTimeouts.add(timeoutId);
|
|
463
|
-
}
|
|
464
|
-
})();
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
async function fetchConfig() {
|
|
468
|
-
if (state.cfg) return state.cfg;
|
|
469
|
-
if (state.cfgPromise) return state.cfgPromise;
|
|
470
|
-
|
|
471
|
-
state.cfgPromise = (async () => {
|
|
472
|
-
const MAX_TRIES = 3;
|
|
473
|
-
let delay = 800;
|
|
474
|
-
for (let attempt = 1; attempt <= MAX_TRIES; attempt++) {
|
|
475
|
-
try {
|
|
476
|
-
const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
|
|
477
|
-
if (res.ok) {
|
|
478
|
-
state.cfg = await res.json();
|
|
479
|
-
return state.cfg;
|
|
480
|
-
}
|
|
481
|
-
} catch (e) {}
|
|
482
|
-
if (attempt < MAX_TRIES) await new Promise(r => setTimeout(r, delay));
|
|
483
|
-
delay *= 2;
|
|
484
|
-
}
|
|
485
|
-
return null;
|
|
486
|
-
})();
|
|
292
|
+
const { id, recycled } = pickId('topics');
|
|
293
|
+
if (!id) break;
|
|
487
294
|
|
|
488
|
-
|
|
489
|
-
|
|
295
|
+
const wrap = recycled ? recycled.wrap : buildWrap('topics', afterIndex, id);
|
|
296
|
+
if (recycled) resetWrapPlaceholder(wrap, id);
|
|
490
297
|
|
|
491
|
-
|
|
492
|
-
if (state.poolTopics.length === 0) state.poolTopics = parsePool(cfg.placeholderIds);
|
|
493
|
-
if (state.poolPosts.length === 0) state.poolPosts = parsePool(cfg.messagePlaceholderIds);
|
|
494
|
-
if (state.poolCategories.length === 0) state.poolCategories = parsePool(cfg.categoryPlaceholderIds);
|
|
495
|
-
}
|
|
298
|
+
if (!insertAfter(anchor, wrap)) { state.pools.topics.push(id); continue; }
|
|
496
299
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
return Array.from(new Set(out)).sort((a, b) => a - b);
|
|
300
|
+
state.live.topics.push({ id, wrap });
|
|
301
|
+
observeFill(wrap, id);
|
|
302
|
+
scheduleRemovalIfUnfilled(wrap, 'topics', id);
|
|
303
|
+
if (wrap.querySelector(`#${PH_PREFIX}${id}`)) showEzoicAd(id);
|
|
304
|
+
inserts++;
|
|
305
|
+
}
|
|
505
306
|
}
|
|
506
307
|
|
|
507
|
-
function
|
|
508
|
-
|
|
509
|
-
const targets = computeTargets(items.length, interval, showFirst);
|
|
510
|
-
|
|
511
|
-
let inserted = 0;
|
|
512
|
-
for (const afterPos of targets) {
|
|
513
|
-
if (inserted >= MAX_INSERTS_PER_RUN) break;
|
|
514
|
-
|
|
515
|
-
const el = items[afterPos - 1];
|
|
516
|
-
if (!el || !el.isConnected) continue;
|
|
308
|
+
function runCategories(cfg) {
|
|
309
|
+
if (!cfg.enableCategoryAds) return;
|
|
517
310
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
311
|
+
const items = Array.from(document.querySelectorAll(SELECTORS.categoryItem));
|
|
312
|
+
if (!items.length) return;
|
|
521
313
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
if (prevWrap) continue;
|
|
314
|
+
const after = computeAfterIndexes(items.length, cfg.intervalCategories || 4, !!cfg.showFirstCategoryAd);
|
|
315
|
+
let inserts = 0;
|
|
525
316
|
|
|
526
|
-
|
|
317
|
+
for (const afterIndex of after) {
|
|
318
|
+
if (inserts >= MAX_INSERTS_PER_TICK) break;
|
|
319
|
+
const anchor = items[afterIndex - 1];
|
|
320
|
+
if (!anchor) continue;
|
|
321
|
+
if (findWrap('categories', afterIndex)) continue;
|
|
527
322
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (!id) break;
|
|
323
|
+
const { id, recycled } = pickId('categories');
|
|
324
|
+
if (!id) break;
|
|
531
325
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (sessionDefinedIds.has(id)) {
|
|
535
|
-
destroyPlaceholderIds([id]);
|
|
536
|
-
}
|
|
537
|
-
const oldWrap = pick.recycled.wrap;
|
|
538
|
-
try { if (oldWrap && oldWrap.__ezoicFillObs) { oldWrap.__ezoicFillObs.disconnect(); } } catch (e) {}
|
|
539
|
-
try { oldWrap && oldWrap.remove(); } catch (e) {}
|
|
540
|
-
wrap = insertAfter(el, id, kindClass, afterPos);
|
|
541
|
-
if (!wrap) continue;
|
|
542
|
-
setTimeout(() => {
|
|
543
|
-
callShowAdsWhenReady(id);
|
|
544
|
-
}, 50);
|
|
545
|
-
} else {
|
|
546
|
-
usedSet.add(id);
|
|
547
|
-
wrap = insertAfter(el, id, kindClass, afterPos);
|
|
548
|
-
if (!wrap) continue;
|
|
549
|
-
// Micro-délai pour laisser le DOM se synchroniser
|
|
550
|
-
// Appel immédiat au lieu de 10ms delay
|
|
551
|
-
callShowAdsWhenReady(id);
|
|
552
|
-
}
|
|
326
|
+
const wrap = recycled ? recycled.wrap : buildWrap('categories', afterIndex, id);
|
|
327
|
+
if (recycled) resetWrapPlaceholder(wrap, id);
|
|
553
328
|
|
|
554
|
-
|
|
555
|
-
if (wrap && (
|
|
556
|
-
(wrap.previousElementSibling && wrap.previousElementSibling.classList && wrap.previousElementSibling.classList.contains(WRAP_CLASS)) || (wrap.nextElementSibling && wrap.nextElementSibling.classList && wrap.nextElementSibling.classList.contains(WRAP_CLASS))
|
|
557
|
-
)) {
|
|
558
|
-
try { wrap.remove(); } catch (e) {}
|
|
559
|
-
if (!(pick.recycled && pick.recycled.wrap)) {
|
|
560
|
-
try { kindPool.unshift(id); } catch (e) {}
|
|
561
|
-
usedSet.delete(id);
|
|
562
|
-
}
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
inserted += 1;
|
|
566
|
-
}
|
|
567
|
-
return inserted;
|
|
568
|
-
}
|
|
329
|
+
if (!insertAfter(anchor, wrap)) { state.pools.categories.push(id); continue; }
|
|
569
330
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const ph = ad.querySelector && ad.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
|
|
577
|
-
if (ph) {
|
|
578
|
-
const id = parseInt(ph.id.replace(PLACEHOLDER_PREFIX, ''), 10);
|
|
579
|
-
if (Number.isFinite(id) && id > 0) {
|
|
580
|
-
// Détruire le placeholder si Ezoic l'a déjà défini
|
|
581
|
-
if (sessionDefinedIds.has(id)) {
|
|
582
|
-
destroyPlaceholderIds([id]);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
331
|
+
state.live.categories.push({ id, wrap });
|
|
332
|
+
observeFill(wrap, id);
|
|
333
|
+
scheduleRemovalIfUnfilled(wrap, 'categories', id);
|
|
334
|
+
if (wrap.querySelector(`#${PH_PREFIX}${id}`)) showEzoicAd(id);
|
|
335
|
+
inserts++;
|
|
336
|
+
}
|
|
585
337
|
}
|
|
586
|
-
ad.remove();
|
|
587
|
-
} catch (e) {}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function cleanup() {
|
|
593
|
-
destroyUsedPlaceholders();
|
|
594
|
-
|
|
595
|
-
document.querySelectorAll('.ezoic-ad').forEach(el => {
|
|
596
|
-
try { el.remove(); } catch (e) {}
|
|
597
|
-
});
|
|
598
338
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
state.cfgPromise = null;
|
|
339
|
+
function runMessageAds(cfg) {
|
|
340
|
+
if (!cfg.enableMessageAds) return;
|
|
602
341
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
state.usedTopics.clear();
|
|
607
|
-
state.usedPosts.clear();
|
|
608
|
-
state.usedCategories.clear();
|
|
609
|
-
state.lastShowById.clear();
|
|
610
|
-
state.pendingById.clear();
|
|
611
|
-
state.definedIds.clear();
|
|
342
|
+
const posts = Array.from(document.querySelectorAll(SELECTORS.postItem))
|
|
343
|
+
.filter(p => p && p.isConnected && p.querySelector(SELECTORS.postContent));
|
|
344
|
+
if (!posts.length) return;
|
|
612
345
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
});
|
|
616
|
-
state.activeTimeouts.clear();
|
|
346
|
+
const after = computeAfterIndexes(posts.length, cfg.messageIntervalPosts || 3, !!cfg.showFirstMessageAd);
|
|
347
|
+
let inserts = 0;
|
|
617
348
|
|
|
618
|
-
|
|
349
|
+
for (const afterIndex of after) {
|
|
350
|
+
if (inserts >= MAX_INSERTS_PER_TICK) break;
|
|
351
|
+
const anchor = posts[afterIndex - 1];
|
|
352
|
+
if (!anchor) continue;
|
|
353
|
+
if (findWrap('messages', afterIndex)) continue;
|
|
619
354
|
|
|
620
|
-
|
|
355
|
+
const { id, recycled } = pickId('messages');
|
|
356
|
+
if (!id) break;
|
|
621
357
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
358
|
+
const wrap = recycled ? recycled.wrap : buildWrap('messages', afterIndex, id);
|
|
359
|
+
wrap.classList.add('ezoic-ad--message');
|
|
360
|
+
if (recycled) resetWrapPlaceholder(wrap, id);
|
|
626
361
|
|
|
627
|
-
|
|
628
|
-
if (state.obs) return;
|
|
629
|
-
state.obs = new MutationObserver(() => scheduleRun('mutation'));
|
|
630
|
-
try { state.obs.observe(document.body, { childList: true, subtree: true }); } catch (e) {}
|
|
631
|
-
}
|
|
362
|
+
if (!insertAfter(anchor, wrap)) { state.pools.messages.push(id); continue; }
|
|
632
363
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
364
|
+
state.live.messages.push({ id, wrap });
|
|
365
|
+
observeFill(wrap, id);
|
|
366
|
+
scheduleRemovalIfUnfilled(wrap, 'messages', id);
|
|
367
|
+
if (wrap.querySelector(`#${PH_PREFIX}${id}`)) showEzoicAd(id);
|
|
368
|
+
inserts++;
|
|
369
|
+
}
|
|
636
370
|
}
|
|
637
371
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
if (!cfg || cfg.excluded) return;
|
|
372
|
+
function cleanupForNavigation() {
|
|
373
|
+
for (const t of state.timeouts) clearTimeout(t);
|
|
374
|
+
state.timeouts.clear();
|
|
642
375
|
|
|
643
|
-
|
|
376
|
+
if (state.observer) {
|
|
377
|
+
try { state.observer.disconnect(); } catch (e) {}
|
|
378
|
+
state.observer = null;
|
|
379
|
+
}
|
|
644
380
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (normalizeBool(cfg.enableMessageAds)) {
|
|
650
|
-
inserted = injectBetween('ezoic-ad-message', getPostContainers(),
|
|
651
|
-
Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3),
|
|
652
|
-
normalizeBool(cfg.showFirstMessageAd),
|
|
653
|
-
state.poolPosts,
|
|
654
|
-
state.usedPosts);
|
|
655
|
-
}
|
|
656
|
-
} else if (kind === 'categoryTopics') {
|
|
657
|
-
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
658
|
-
inserted = injectBetween('ezoic-ad-between', getTopicItems(),
|
|
659
|
-
Math.max(1, parseInt(cfg.intervalPosts, 10) || 6),
|
|
660
|
-
normalizeBool(cfg.showFirstTopicAd),
|
|
661
|
-
state.poolTopics,
|
|
662
|
-
state.usedTopics);
|
|
663
|
-
}
|
|
664
|
-
} else if (kind === 'categories') {
|
|
665
|
-
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
666
|
-
inserted = injectBetween('ezoic-ad-categories', getCategoryItems(),
|
|
667
|
-
Math.max(1, parseInt(cfg.intervalCategories, 10) || 4),
|
|
668
|
-
normalizeBool(cfg.showFirstCategoryAd),
|
|
669
|
-
state.poolCategories,
|
|
670
|
-
state.usedCategories);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
enforceNoAdjacentAds();
|
|
675
|
-
|
|
676
|
-
let count = 0;
|
|
677
|
-
if (kind === 'topic') count = getPostContainers().length;
|
|
678
|
-
else if (kind === 'categoryTopics') count = getTopicItems().length;
|
|
679
|
-
else if (kind === 'categories') count = getCategoryItems().length;
|
|
680
|
-
|
|
681
|
-
if (count === 0 && 0 < 25) {
|
|
682
|
-
setTimeout(arguments[0], 50);
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
if (inserted >= MAX_INSERTS_PER_RUN) {
|
|
687
|
-
setTimeout(arguments[0], 50);
|
|
688
|
-
} else if (inserted === 0 && count > 0) {
|
|
689
|
-
// Pool épuisé ou recyclage pas encore disponible.
|
|
690
|
-
if (state.poolWaitAttempts < 8) {
|
|
691
|
-
state.poolWaitAttempts += 1;
|
|
692
|
-
setTimeout(arguments[0], 50);
|
|
693
|
-
} else {
|
|
694
|
-
}
|
|
695
|
-
} else if (inserted > 0) {
|
|
696
|
-
}
|
|
381
|
+
state.live.topics = [];
|
|
382
|
+
state.live.categories = [];
|
|
383
|
+
state.live.messages = [];
|
|
384
|
+
state.lastShowById.clear();
|
|
697
385
|
}
|
|
698
386
|
|
|
699
387
|
function scheduleRun() {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
762
|
-
});
|
|
763
|
-
}, { passive: true });
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
function waitForContentThenRun() {
|
|
767
|
-
const MIN_WORDS = 250;
|
|
768
|
-
let attempts = 0;
|
|
769
|
-
const maxAttempts = 20; // 20 × 200ms = 4s max
|
|
770
|
-
|
|
771
|
-
(function check() {
|
|
772
|
-
attempts++;
|
|
773
|
-
|
|
774
|
-
// Compter les mots sur la page
|
|
775
|
-
const text = document.body.innerText || '';
|
|
776
|
-
const wordCount = text.split(/\s+/).filter(Boolean).length;
|
|
777
|
-
|
|
778
|
-
if (wordCount >= MIN_WORDS) {
|
|
779
|
-
// Assez de contenu → lancer l'insertion
|
|
780
|
-
scheduleRun();
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// Pas assez de contenu
|
|
785
|
-
if (attempts >= maxAttempts) {
|
|
786
|
-
// Timeout après 4s → tenter quand même
|
|
787
|
-
scheduleRun();
|
|
788
|
-
return;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
// Réessayer dans 200ms
|
|
792
|
-
setTimeout(check, 50);
|
|
793
|
-
})();
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
function waitForEzoicThenRun() {
|
|
797
|
-
let attempts = 0;
|
|
798
|
-
const maxAttempts = 50; // 50 × 200ms = 10s max
|
|
799
|
-
|
|
800
|
-
(function check() {
|
|
801
|
-
attempts++;
|
|
802
|
-
// Vérifier si Ezoic est chargé
|
|
803
|
-
if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
|
|
804
|
-
// Ezoic est prêt → lancer l'insertion
|
|
805
|
-
scheduleRun();
|
|
806
|
-
waitForContentThenRun();
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
|
-
// Ezoic pas encore prêt
|
|
810
|
-
if (attempts >= maxAttempts) {
|
|
811
|
-
// Tenter quand même
|
|
812
|
-
scheduleRun();
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
// Réessayer dans 200ms
|
|
816
|
-
setTimeout(check, 50);
|
|
817
|
-
})();
|
|
388
|
+
if (state.scheduled) return;
|
|
389
|
+
state.scheduled = true;
|
|
390
|
+
requestAnimationFrame(() => {
|
|
391
|
+
state.scheduled = false;
|
|
392
|
+
run().catch(() => {});
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function run() {
|
|
397
|
+
const pk = getPageKey();
|
|
398
|
+
if (pk !== state.pageKey) {
|
|
399
|
+
state.pageKey = pk;
|
|
400
|
+
cleanupForNavigation();
|
|
401
|
+
state.cfg = null;
|
|
402
|
+
state.cfgPromise = null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const cfg = await fetchConfig();
|
|
406
|
+
if (!cfg || cfg.excluded) return;
|
|
407
|
+
|
|
408
|
+
const kind = getKind();
|
|
409
|
+
if (kind === 'categoryTopics') runCategoryTopics(cfg);
|
|
410
|
+
if (kind === 'categories') runCategories(cfg);
|
|
411
|
+
if (kind === 'topic') runMessageAds(cfg);
|
|
412
|
+
|
|
413
|
+
// remove obvious empty spacer nodes injected by networks
|
|
414
|
+
try {
|
|
415
|
+
document.querySelectorAll(`.${CLASS_WRAP} > div:empty`).forEach(n => n.remove());
|
|
416
|
+
} catch (e) {}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function start() {
|
|
420
|
+
scheduleRun();
|
|
421
|
+
|
|
422
|
+
state.observer = new MutationObserver(() => {
|
|
423
|
+
const t = now();
|
|
424
|
+
if (t - state.lastRunAt < 200) return;
|
|
425
|
+
state.lastRunAt = t;
|
|
426
|
+
scheduleRun();
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
try { state.observer.observe(document.body, { childList: true, subtree: true }); } catch (e) {}
|
|
430
|
+
|
|
431
|
+
// NodeBB ajaxify hook
|
|
432
|
+
if (window.jQuery && window.jQuery(window).on) {
|
|
433
|
+
window.jQuery(window).on('action:ajaxify.end', () => {
|
|
434
|
+
state.pageKey = null;
|
|
435
|
+
scheduleRun();
|
|
436
|
+
});
|
|
437
|
+
} else {
|
|
438
|
+
window.addEventListener('popstate', () => {
|
|
439
|
+
state.pageKey = null;
|
|
440
|
+
scheduleRun();
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (document.readyState === 'loading') {
|
|
446
|
+
document.addEventListener('DOMContentLoaded', start);
|
|
447
|
+
} else {
|
|
448
|
+
start();
|
|
818
449
|
}
|
|
819
|
-
|
|
820
|
-
cleanup();
|
|
821
|
-
bind();
|
|
822
|
-
bindScroll();
|
|
823
|
-
ensureObserver();
|
|
824
|
-
state.pageKey = getPageKey();
|
|
825
|
-
|
|
826
|
-
// Attendre que Ezoic soit chargé avant d'insérer
|
|
827
|
-
waitForEzoicThenRun();
|
|
828
|
-
})();
|
|
450
|
+
})();
|
package/public/style.css
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Ezoic Infinite Ads
|
|
3
|
+
- Wrappers are hidden until ad is detected as filled (data-ezoic-filled="1").
|
|
4
|
+
- Prevent reserved heights/margins creating blank space under creatives.
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
.ezoic-ad {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
8
|
+
display: none;
|
|
9
|
+
width: 100%;
|
|
10
|
+
clear: both;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
5
13
|
}
|
|
6
14
|
|
|
7
|
-
.ezoic-ad
|
|
15
|
+
.ezoic-ad[data-ezoic-filled="1"] {
|
|
16
|
+
display: block;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Do not let the placeholder reserve fixed height */
|
|
20
|
+
.ezoic-ad > [id^="ezoic-pub-ad-placeholder-"] {
|
|
21
|
+
height: auto !important;
|
|
22
|
+
min-height: 0 !important;
|
|
8
23
|
margin: 0 !important;
|
|
9
24
|
padding: 0 !important;
|
|
10
|
-
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Avoid baseline gaps under iframes/ins */
|
|
28
|
+
.ezoic-ad iframe,
|
|
29
|
+
.ezoic-ad ins {
|
|
30
|
+
display: block !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Remove empty spacer divs that can appear after injection */
|
|
34
|
+
.ezoic-ad > div:empty {
|
|
35
|
+
display: none !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Optional: message-style ad spacing (looks nicer between posts) */
|
|
39
|
+
.ezoic-ad--message[data-ezoic-filled="1"] {
|
|
40
|
+
margin: 0.75rem 0;
|
|
41
|
+
}
|