nodebb-plugin-ezoic-infinite 1.4.96 → 1.4.98
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/library.js +1 -1
- package/package.json +1 -1
- package/public/client.js +523 -859
- package/public/style.css +21 -7
package/public/client.js
CHANGED
|
@@ -1,940 +1,604 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
|
+
|
|
4
|
+
// NodeBB client context
|
|
3
5
|
const $ = (typeof window.jQuery === 'function') ? window.jQuery : null;
|
|
6
|
+
|
|
7
|
+
const WRAP_CLASS = 'ezoic-ad';
|
|
8
|
+
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
9
|
+
|
|
10
|
+
// Insert at most N ads per run to keep the UI smooth on infinite scroll
|
|
11
|
+
const MAX_INSERTS_PER_RUN = 3;
|
|
12
|
+
|
|
13
|
+
// Preload before viewport (tune if you want even earlier)
|
|
14
|
+
const PRELOAD_ROOT_MARGIN = '1200px 0px';
|
|
15
|
+
|
|
4
16
|
const SELECTORS = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// FLAG GLOBAL: Bloquer Ezoic pendant navigation
|
|
17
|
+
topicItem: 'li[component="category/topic"]',
|
|
18
|
+
postItem: '[component="post"][data-pid]',
|
|
19
|
+
categoryItem: 'li[component="categories/category"]',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Hard block during navigation to avoid “placeholder does not exist” spam
|
|
12
23
|
let EZOIC_BLOCKED = false;
|
|
13
|
-
|
|
14
|
-
// DEBUG: Vérifier que le plugin charge
|
|
15
24
|
|
|
16
|
-
const
|
|
25
|
+
const state = {
|
|
26
|
+
pageKey: null,
|
|
27
|
+
cfg: null,
|
|
28
|
+
|
|
29
|
+
poolTopics: [],
|
|
30
|
+
poolPosts: [],
|
|
31
|
+
poolCategories: [],
|
|
17
32
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
cfgPromise: null,
|
|
33
|
+
usedTopics: new Set(),
|
|
34
|
+
usedPosts: new Set(),
|
|
35
|
+
usedCategories: new Set(),
|
|
22
36
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
poolCategories: [],
|
|
37
|
+
// throttle per placeholder id
|
|
38
|
+
lastShowById: new Map(),
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
// observers / schedulers
|
|
41
|
+
domObs: null,
|
|
42
|
+
io: null,
|
|
43
|
+
runQueued: false,
|
|
30
44
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
// hero
|
|
46
|
+
heroDoneForPage: false,
|
|
47
|
+
};
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
const sessionDefinedIds = new Set();
|
|
50
|
+
const insertingIds = new Set();
|
|
37
51
|
|
|
38
|
-
|
|
39
|
-
activeTimeouts: new Set(),
|
|
40
|
-
lastScrollRun: 0, };
|
|
52
|
+
// ---------- small utils ----------
|
|
41
53
|
|
|
42
54
|
function normalizeBool(v) {
|
|
43
|
-
|
|
55
|
+
return v === true || v === 'true' || v === 1 || v === '1' || v === 'on';
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
function uniqInts(lines) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
const out = [];
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const v of lines) {
|
|
62
|
+
const n = parseInt(v, 10);
|
|
63
|
+
if (Number.isFinite(n) && n > 0 && !seen.has(n)) {
|
|
64
|
+
seen.add(n);
|
|
65
|
+
out.push(n);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
function parsePool(raw) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
if (!raw) return [];
|
|
73
|
+
const lines = String(raw)
|
|
74
|
+
.split(/\r?\n/)
|
|
75
|
+
.map(s => s.trim())
|
|
76
|
+
.filter(Boolean);
|
|
77
|
+
return uniqInts(lines);
|
|
62
78
|
}
|
|
63
79
|
|
|
64
80
|
function getPageKey() {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
try {
|
|
82
|
+
const ax = window.ajaxify;
|
|
83
|
+
if (ax && ax.data) {
|
|
84
|
+
if (ax.data.tid) return `topic:${ax.data.tid}`;
|
|
85
|
+
if (ax.data.cid) return `cid:${ax.data.cid}:${window.location.pathname}`;
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {}
|
|
88
|
+
return window.location.pathname;
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
function getKind() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
const p = window.location.pathname || '';
|
|
93
|
+
if (/^\/topic\//.test(p)) return 'topic';
|
|
94
|
+
if (/^\/category\//.test(p)) return 'categoryTopics';
|
|
95
|
+
if (p === '/' || /^\/categories/.test(p)) return 'categories';
|
|
96
|
+
|
|
97
|
+
// fallback by DOM
|
|
98
|
+
if (document.querySelector(SELECTORS.categoryItem)) return 'categories';
|
|
99
|
+
if (document.querySelector(SELECTORS.postItem)) return 'topic';
|
|
100
|
+
if (document.querySelector(SELECTORS.topicItem)) return 'categoryTopics';
|
|
101
|
+
return 'other';
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
function getTopicItems() {
|
|
88
|
-
|
|
105
|
+
return Array.from(document.querySelectorAll(SELECTORS.topicItem));
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
function getCategoryItems() {
|
|
92
|
-
|
|
109
|
+
return Array.from(document.querySelectorAll(SELECTORS.categoryItem));
|
|
93
110
|
}
|
|
94
111
|
|
|
95
112
|
function getPostContainers() {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// Lancer cleanup périodique toutes les 2s
|
|
189
|
-
setInterval(() => {
|
|
190
|
-
try { cleanupInvisibleEzoicElements(); } catch (e) {}
|
|
191
|
-
}, 2000);
|
|
192
|
-
|
|
193
|
-
function cleanupEmptyWrappers() {
|
|
194
|
-
try {
|
|
195
|
-
document.querySelectorAll('.ezoic-ad').forEach(wrapper => {
|
|
196
|
-
const ph = wrapper.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
|
|
197
|
-
if (ph && ph.children.length === 0) {
|
|
198
|
-
// Placeholder vide après 3s = pub non chargée
|
|
199
|
-
setTimeout(() => {
|
|
200
|
-
if (ph.children.length === 0) {
|
|
201
|
-
wrapper.remove();
|
|
202
|
-
}
|
|
203
|
-
}, 1500);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
} catch (e) {}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function getRecyclable(liveArr) {
|
|
210
|
-
const margin = 600;
|
|
211
|
-
for (let i = 0; i < liveArr.length; i++) {
|
|
212
|
-
const entry = liveArr[i];
|
|
213
|
-
if (!entry || !entry.wrap || !entry.wrap.isConnected) { liveArr.splice(i, 1); i--; continue; }
|
|
214
|
-
const r = safeRect(entry.wrap);
|
|
215
|
-
if (r && r.bottom < -margin) {
|
|
216
|
-
liveArr.splice(i, 1);
|
|
217
|
-
return entry;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return null;
|
|
113
|
+
const nodes = Array.from(document.querySelectorAll(SELECTORS.postItem));
|
|
114
|
+
return nodes.filter((el) => {
|
|
115
|
+
if (!el || !el.isConnected) return false;
|
|
116
|
+
if (!el.querySelector('[component="post/content"]')) return false;
|
|
117
|
+
const parentPost = el.parentElement && el.parentElement.closest('[component="post"][data-pid]');
|
|
118
|
+
if (parentPost && parentPost !== el) return false;
|
|
119
|
+
if (el.getAttribute('component') === 'post/parent') return false;
|
|
120
|
+
return true;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------- warm-up & patching ----------
|
|
125
|
+
|
|
126
|
+
const _warmLinksDone = new Set();
|
|
127
|
+
function warmUpNetwork() {
|
|
128
|
+
try {
|
|
129
|
+
const head = document.head || document.getElementsByTagName('head')[0];
|
|
130
|
+
if (!head) return;
|
|
131
|
+
const links = [
|
|
132
|
+
['preconnect', 'https://g.ezoic.net', true],
|
|
133
|
+
['dns-prefetch', 'https://g.ezoic.net', false],
|
|
134
|
+
['preconnect', 'https://go.ezoic.net', true],
|
|
135
|
+
['dns-prefetch', 'https://go.ezoic.net', false],
|
|
136
|
+
];
|
|
137
|
+
for (const [rel, href, cors] of links) {
|
|
138
|
+
const key = `${rel}|${href}`;
|
|
139
|
+
if (_warmLinksDone.has(key)) continue;
|
|
140
|
+
_warmLinksDone.add(key);
|
|
141
|
+
const link = document.createElement('link');
|
|
142
|
+
link.rel = rel;
|
|
143
|
+
link.href = href;
|
|
144
|
+
if (cors) link.crossOrigin = 'anonymous';
|
|
145
|
+
head.appendChild(link);
|
|
146
|
+
}
|
|
147
|
+
} catch (e) {}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Patch showAds to avoid warnings when a placeholder disappears (infinite scroll, ajaxify)
|
|
151
|
+
function patchShowAds() {
|
|
152
|
+
const applyPatch = () => {
|
|
153
|
+
try {
|
|
154
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
155
|
+
const ez = window.ezstandalone;
|
|
156
|
+
if (window.__nodebbEzoicPatched) return;
|
|
157
|
+
if (typeof ez.showAds !== 'function') return;
|
|
158
|
+
|
|
159
|
+
window.__nodebbEzoicPatched = true;
|
|
160
|
+
const orig = ez.showAds;
|
|
161
|
+
|
|
162
|
+
ez.showAds = function (...args) {
|
|
163
|
+
if (EZOIC_BLOCKED) return;
|
|
164
|
+
|
|
165
|
+
let ids = [];
|
|
166
|
+
if (args.length === 1 && Array.isArray(args[0])) ids = args[0];
|
|
167
|
+
else ids = args;
|
|
168
|
+
|
|
169
|
+
const seen = new Set();
|
|
170
|
+
for (const v of ids) {
|
|
171
|
+
const id = parseInt(v, 10);
|
|
172
|
+
if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
|
|
173
|
+
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
174
|
+
if (!ph || !ph.isConnected) continue;
|
|
175
|
+
seen.add(id);
|
|
176
|
+
try { orig.call(ez, id); } catch (e) {}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
} catch (e) {}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
applyPatch();
|
|
183
|
+
if (!window.__nodebbEzoicPatched) {
|
|
184
|
+
try {
|
|
185
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
186
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
187
|
+
window.ezstandalone.cmd.push(applyPatch);
|
|
188
|
+
} catch (e) {}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------- config & pools ----------
|
|
193
|
+
|
|
194
|
+
async function fetchConfigOnce() {
|
|
195
|
+
if (state.cfg) return state.cfg;
|
|
196
|
+
try {
|
|
197
|
+
const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
|
|
198
|
+
if (!res.ok) return null;
|
|
199
|
+
state.cfg = await res.json();
|
|
200
|
+
return state.cfg;
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
221
204
|
}
|
|
222
205
|
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
206
|
+
function initPools(cfg) {
|
|
207
|
+
if (!cfg) return;
|
|
208
|
+
if (state.poolTopics.length === 0) state.poolTopics = parsePool(cfg.placeholderIds);
|
|
209
|
+
if (state.poolPosts.length === 0) state.poolPosts = parsePool(cfg.messagePlaceholderIds);
|
|
210
|
+
if (state.poolCategories.length === 0) state.poolCategories = parsePool(cfg.categoryPlaceholderIds);
|
|
228
211
|
}
|
|
229
212
|
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
if (!wrap) return;
|
|
233
|
-
try { wrap.removeAttribute('data-ezoic-filled'); } catch (e) {}
|
|
234
|
-
if (wrap.__ezoicFillObs) { wrap.__ezoicFillObs.disconnect(); wrap.__ezoicFillObs = null; }
|
|
235
|
-
const old = wrap.querySelector && wrap.querySelector(`#${PLACEHOLDER_PREFIX}${id}`);
|
|
236
|
-
if (old) old.remove();
|
|
237
|
-
// Remove any leftover markup inside wrapper
|
|
238
|
-
wrap.querySelectorAll && wrap.querySelectorAll('iframe, ins').forEach(n => n.remove());
|
|
239
|
-
const ph = document.createElement('div');
|
|
240
|
-
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
241
|
-
wrap.appendChild(ph);
|
|
242
|
-
} catch (e) {}
|
|
243
|
-
}
|
|
213
|
+
// ---------- insertion primitives ----------
|
|
244
214
|
|
|
245
215
|
function isAdjacentAd(target) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
216
|
+
if (!target) return false;
|
|
217
|
+
const next = target.nextElementSibling;
|
|
218
|
+
if (next && next.classList && next.classList.contains(WRAP_CLASS)) return true;
|
|
219
|
+
const prev = target.previousElementSibling;
|
|
220
|
+
if (prev && prev.classList && prev.classList.contains(WRAP_CLASS)) return true;
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildWrap(target, id, kindClass, afterPos) {
|
|
225
|
+
const tag = (target && target.tagName === 'LI') ? 'li' : 'div';
|
|
226
|
+
const wrap = document.createElement(tag);
|
|
227
|
+
if (tag === 'li') {
|
|
228
|
+
wrap.style.listStyle = 'none';
|
|
229
|
+
// preserve common NodeBB list styling
|
|
230
|
+
if (target && target.classList && target.classList.contains('list-group-item')) wrap.classList.add('list-group-item');
|
|
231
|
+
}
|
|
232
|
+
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
233
|
+
if (wrap.tagName === 'LI') {
|
|
234
|
+
wrap.setAttribute('role', 'presentation');
|
|
235
|
+
wrap.setAttribute('aria-hidden', 'true');
|
|
236
|
+
}
|
|
237
|
+
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
238
|
+
wrap.style.width = '100%';
|
|
239
|
+
|
|
240
|
+
const ph = document.createElement('div');
|
|
241
|
+
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
242
|
+
ph.setAttribute('data-ezoic-id', String(id));
|
|
243
|
+
wrap.appendChild(ph);
|
|
244
|
+
|
|
245
|
+
return wrap;
|
|
269
246
|
}
|
|
270
247
|
|
|
271
248
|
function findWrap(kindClass, afterPos) {
|
|
272
|
-
|
|
249
|
+
return document.querySelector(`.${WRAP_CLASS}.${kindClass}[data-ezoic-after="${afterPos}"]`);
|
|
273
250
|
}
|
|
274
251
|
|
|
275
252
|
function insertAfter(target, id, kindClass, afterPos) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function isWrapMarkedFilled(wrap) {
|
|
376
|
-
try { return wrap && wrap.getAttribute && wrap.getAttribute('data-ezoic-filled') === '1'; } catch (e) { return false; }
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function attachFillObserver(wrap, id) {
|
|
380
|
-
try {
|
|
381
|
-
const ph = wrap && wrap.querySelector && wrap.querySelector(`#${PLACEHOLDER_PREFIX}${id}`);
|
|
382
|
-
if (!ph) return;
|
|
383
|
-
// Already filled?
|
|
384
|
-
if (ph.childNodes && ph.childNodes.length > 0) {
|
|
385
|
-
markFilled(wrap); // Afficher wrapper
|
|
386
|
-
sessionDefinedIds.add(id);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
const obs = new MutationObserver(() => {
|
|
390
|
-
if (ph.childNodes && ph.childNodes.length > 0) {
|
|
391
|
-
markFilled(wrap); // CRITIQUE: Afficher wrapper maintenant
|
|
392
|
-
try { sessionDefinedIds.add(id); } catch (e) {}
|
|
393
|
-
try { obs.disconnect(); } catch (e) {}
|
|
394
|
-
}
|
|
395
|
-
});
|
|
396
|
-
obs.observe(ph, { childList: true, subtree: true });
|
|
397
|
-
wrap.__ezoicFillObs = obs;
|
|
398
|
-
} catch (e) {}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function isPlaceholderFilled(id) {
|
|
402
|
-
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
403
|
-
if (!ph || !ph.isConnected) return false;
|
|
404
|
-
|
|
405
|
-
const wrap = ph.parentElement;
|
|
406
|
-
if (wrap && isWrapMarkedFilled(wrap)) return true;
|
|
407
|
-
|
|
408
|
-
const filled = !!(ph.childNodes && ph.childNodes.length > 0);
|
|
409
|
-
if (filled) {
|
|
410
|
-
try { state.definedIds && state.definedIds.add(id); sessionDefinedIds.add(id); } catch (e) {}
|
|
411
|
-
try { markFilled(wrap); } catch (e) {}
|
|
412
|
-
}
|
|
413
|
-
return filled;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
let batchShowAdsTimer = null;
|
|
417
|
-
const pendingShowAdsIds = new Set();
|
|
418
|
-
|
|
419
|
-
function scheduleShowAdsBatch(id) {
|
|
420
|
-
if (!id) return;
|
|
421
|
-
|
|
422
|
-
if (sessionDefinedIds.has(id)) {
|
|
423
|
-
try {
|
|
424
|
-
destroyPlaceholderIds([id]);
|
|
425
|
-
sessionDefinedIds.delete(id);
|
|
426
|
-
} catch (e) {}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Throttle: ne pas rappeler le même ID trop vite
|
|
430
|
-
const now = Date.now(), last = state.lastShowById.get(id) || 0;
|
|
431
|
-
if (now - last < 3500) return;
|
|
432
|
-
|
|
433
|
-
// Ajouter à la batch
|
|
434
|
-
pendingShowAdsIds.add(id);
|
|
435
|
-
|
|
436
|
-
clearTimeout(batchShowAdsTimer);
|
|
437
|
-
batchShowAdsTimer = setTimeout(() => {
|
|
438
|
-
// CRITIQUE: Vérifier que nous sommes toujours sur la même page
|
|
439
|
-
const currentPageKey = getPageKey();
|
|
440
|
-
if (state.pageKey && currentPageKey !== state.pageKey) {
|
|
441
|
-
pendingShowAdsIds.clear();
|
|
442
|
-
return; // Page a changé, annuler
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (pendingShowAdsIds.size === 0) return;
|
|
446
|
-
|
|
447
|
-
const idsArray = Array.from(pendingShowAdsIds);
|
|
448
|
-
pendingShowAdsIds.clear();
|
|
449
|
-
|
|
450
|
-
// CRITIQUE: Vérifier que placeholders existent encore
|
|
451
|
-
const validIds = idsArray.filter(id => {
|
|
452
|
-
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
453
|
-
return ph && ph.isConnected;
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
if (validIds.length === 0) return;
|
|
457
|
-
|
|
458
|
-
// Appeler showAds avec TOUS les IDs en une fois
|
|
459
|
-
try {
|
|
460
|
-
// CRITIQUE: Re-patcher AVANT chaque appel pour être sûr
|
|
461
|
-
patchShowAds();
|
|
462
|
-
|
|
463
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
464
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
465
|
-
window.ezstandalone.cmd.push(function() {
|
|
466
|
-
if (typeof window.ezstandalone.showAds === 'function') {
|
|
467
|
-
// Appel batch: showAds(id1, id2, id3...)
|
|
468
|
-
window.ezstandalone.showAds(...validIds);
|
|
469
|
-
// Tracker tous les IDs
|
|
470
|
-
validIds.forEach(id => {
|
|
471
|
-
state.lastShowById.set(id, Date.now());
|
|
472
|
-
sessionDefinedIds.add(id);
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
} catch (e) {}
|
|
477
|
-
|
|
478
|
-
// CRITIQUE: Nettoyer éléments invisibles APRÈS que pubs soient chargées
|
|
479
|
-
setTimeout(() => {
|
|
480
|
-
cleanupInvisibleEzoicElements();
|
|
481
|
-
}, 1500); // 1.5s pour laisser Ezoic charger
|
|
482
|
-
}, 100);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
function callShowAdsWhenReady(id) {
|
|
486
|
-
if (!id) return;
|
|
487
|
-
|
|
488
|
-
const now = Date.now(), last = state.lastShowById.get(id) || 0;
|
|
489
|
-
if (now - last < 3500) return;
|
|
490
|
-
|
|
491
|
-
const phId = `${PLACEHOLDER_PREFIX}${id}`, doCall = () => {
|
|
492
|
-
try {
|
|
493
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
494
|
-
if (typeof window.ezstandalone.showAds === 'function') {
|
|
495
|
-
|
|
496
|
-
state.lastShowById.set(id, Date.now());
|
|
497
|
-
window.ezstandalone.showAds(id);
|
|
498
|
-
sessionDefinedIds.add(id);
|
|
499
|
-
return true;
|
|
500
|
-
}
|
|
501
|
-
} catch (e) {}
|
|
502
|
-
return false;
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
const startPageKey = state.pageKey;
|
|
506
|
-
let attempts = 0;
|
|
507
|
-
(function waitForPh() {
|
|
508
|
-
if (state.pageKey !== startPageKey) return;
|
|
509
|
-
if (state.pendingById.has(id)) return;
|
|
510
|
-
|
|
511
|
-
attempts += 1;
|
|
512
|
-
const el = document.getElementById(phId);
|
|
513
|
-
if (el && el.isConnected) {
|
|
514
|
-
|
|
515
|
-
// Si on arrive ici, soit visible, soit timeout
|
|
516
|
-
|
|
517
|
-
if (doCall()) {
|
|
518
|
-
state.pendingById.delete(id);
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (attempts < 100) {
|
|
525
|
-
const timeoutId = setTimeout(waitForPh, 50);
|
|
526
|
-
state.activeTimeouts.add(timeoutId);
|
|
527
|
-
}
|
|
528
|
-
})();
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
async function fetchConfig() {
|
|
532
|
-
if (state.cfg) return state.cfg;
|
|
533
|
-
if (state.cfgPromise) return state.cfgPromise;
|
|
534
|
-
|
|
535
|
-
state.cfgPromise = (async () => {
|
|
536
|
-
const MAX_TRIES = 3;
|
|
537
|
-
let delay = 800;
|
|
538
|
-
for (let attempt = 1; attempt <= MAX_TRIES; attempt++) {
|
|
539
|
-
try {
|
|
540
|
-
const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
|
|
541
|
-
if (res.ok) {
|
|
542
|
-
state.cfg = await res.json();
|
|
543
|
-
return state.cfg;
|
|
544
|
-
}
|
|
545
|
-
} catch (e) {}
|
|
546
|
-
if (attempt < MAX_TRIES) await new Promise(r => setTimeout(r, delay));
|
|
547
|
-
delay *= 2;
|
|
548
|
-
}
|
|
549
|
-
return null;
|
|
550
|
-
})();
|
|
551
|
-
|
|
552
|
-
try { return await state.cfgPromise; } finally { state.cfgPromise = null; }
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function initPools(cfg) {
|
|
556
|
-
if (state.poolTopics.length === 0) state.poolTopics = parsePool(cfg.placeholderIds);
|
|
557
|
-
if (state.poolPosts.length === 0) state.poolPosts = parsePool(cfg.messagePlaceholderIds);
|
|
558
|
-
if (state.poolCategories.length === 0) state.poolCategories = parsePool(cfg.categoryPlaceholderIds);
|
|
559
|
-
}
|
|
253
|
+
if (!target || !target.insertAdjacentElement) return null;
|
|
254
|
+
if (findWrap(kindClass, afterPos)) return null;
|
|
255
|
+
if (insertingIds.has(id)) return null;
|
|
256
|
+
|
|
257
|
+
const existingPh = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
258
|
+
if (existingPh && existingPh.isConnected) return null;
|
|
259
|
+
|
|
260
|
+
insertingIds.add(id);
|
|
261
|
+
try {
|
|
262
|
+
const wrap = buildWrap(target, id, kindClass, afterPos);
|
|
263
|
+
target.insertAdjacentElement('afterend', wrap);
|
|
264
|
+
return wrap;
|
|
265
|
+
} finally {
|
|
266
|
+
insertingIds.delete(id);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function pickId(pool) {
|
|
271
|
+
return pool.length ? pool.shift() : null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function showAd(id) {
|
|
275
|
+
if (!id || EZOIC_BLOCKED) return;
|
|
276
|
+
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
const last = state.lastShowById.get(id) || 0;
|
|
279
|
+
if (now - last < 1500) return; // basic throttle
|
|
280
|
+
|
|
281
|
+
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
282
|
+
if (!ph || !ph.isConnected) return;
|
|
283
|
+
|
|
284
|
+
state.lastShowById.set(id, now);
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
288
|
+
const ez = window.ezstandalone;
|
|
289
|
+
|
|
290
|
+
// Fast path
|
|
291
|
+
if (typeof ez.showAds === 'function') {
|
|
292
|
+
ez.showAds(id);
|
|
293
|
+
sessionDefinedIds.add(id);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Queue once for when Ezoic is ready
|
|
298
|
+
ez.cmd = ez.cmd || [];
|
|
299
|
+
if (!ph.__ezoicQueued) {
|
|
300
|
+
ph.__ezoicQueued = true;
|
|
301
|
+
ez.cmd.push(() => {
|
|
302
|
+
try {
|
|
303
|
+
if (EZOIC_BLOCKED) return;
|
|
304
|
+
const el = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
305
|
+
if (!el || !el.isConnected) return;
|
|
306
|
+
window.ezstandalone.showAds(id);
|
|
307
|
+
sessionDefinedIds.add(id);
|
|
308
|
+
} catch (e) {}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
} catch (e) {}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ---------- preload / above-the-fold ----------
|
|
315
|
+
|
|
316
|
+
function ensurePreloadObserver() {
|
|
317
|
+
if (state.io) return state.io;
|
|
318
|
+
try {
|
|
319
|
+
state.io = new IntersectionObserver((entries) => {
|
|
320
|
+
for (const ent of entries) {
|
|
321
|
+
if (!ent.isIntersecting) continue;
|
|
322
|
+
const el = ent.target;
|
|
323
|
+
try { state.io && state.io.unobserve(el); } catch (e) {}
|
|
324
|
+
|
|
325
|
+
const idAttr = el && el.getAttribute && el.getAttribute('data-ezoic-id');
|
|
326
|
+
const id = parseInt(idAttr, 10);
|
|
327
|
+
if (Number.isFinite(id) && id > 0) showAd(id);
|
|
328
|
+
}
|
|
329
|
+
}, { root: null, rootMargin: PRELOAD_ROOT_MARGIN, threshold: 0.01 });
|
|
330
|
+
} catch (e) {
|
|
331
|
+
state.io = null;
|
|
332
|
+
}
|
|
333
|
+
return state.io;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function observePlaceholder(id) {
|
|
337
|
+
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
338
|
+
if (!ph || !ph.isConnected) return;
|
|
339
|
+
const io = ensurePreloadObserver();
|
|
340
|
+
try { io && io.observe(ph); } catch (e) {}
|
|
341
|
+
|
|
342
|
+
// If already above fold, fire immediately
|
|
343
|
+
try {
|
|
344
|
+
const r = ph.getBoundingClientRect();
|
|
345
|
+
if (r.top < window.innerHeight * 1.5 && r.bottom > -200) showAd(id);
|
|
346
|
+
} catch (e) {}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ---------- insertion logic ----------
|
|
560
350
|
|
|
561
351
|
function computeTargets(count, interval, showFirst) {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
352
|
+
const out = [];
|
|
353
|
+
if (count <= 0) return out;
|
|
354
|
+
if (showFirst) out.push(1);
|
|
355
|
+
for (let i = 1; i <= count; i++) {
|
|
356
|
+
if (i % interval === 0) out.push(i);
|
|
357
|
+
}
|
|
358
|
+
return Array.from(new Set(out)).sort((a, b) => a - b);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function injectBetween(kindClass, items, interval, showFirst, pool, usedSet) {
|
|
362
|
+
if (!items.length) return 0;
|
|
363
|
+
|
|
364
|
+
const targets = computeTargets(items.length, interval, showFirst);
|
|
365
|
+
let inserted = 0;
|
|
366
|
+
|
|
367
|
+
for (const afterPos of targets) {
|
|
368
|
+
if (inserted >= MAX_INSERTS_PER_RUN) break;
|
|
369
|
+
|
|
370
|
+
const el = items[afterPos - 1];
|
|
371
|
+
if (!el || !el.isConnected) continue;
|
|
372
|
+
if (isAdjacentAd(el)) continue;
|
|
373
|
+
if (findWrap(kindClass, afterPos)) continue;
|
|
374
|
+
|
|
375
|
+
const id = pickId(pool);
|
|
376
|
+
if (!id) break;
|
|
377
|
+
|
|
378
|
+
usedSet.add(id);
|
|
379
|
+
const wrap = insertAfter(el, id, kindClass, afterPos);
|
|
380
|
+
if (!wrap) {
|
|
381
|
+
usedSet.delete(id);
|
|
382
|
+
pool.unshift(id);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
observePlaceholder(id);
|
|
387
|
+
inserted += 1;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return inserted;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function insertHeroAdEarly() {
|
|
394
|
+
if (state.heroDoneForPage) return;
|
|
395
|
+
const cfg = await fetchConfigOnce();
|
|
396
|
+
if (!cfg || cfg.excluded) return;
|
|
397
|
+
|
|
398
|
+
initPools(cfg);
|
|
399
|
+
|
|
400
|
+
const kind = getKind();
|
|
401
|
+
let items = [];
|
|
402
|
+
let pool = null;
|
|
403
|
+
let usedSet = null;
|
|
404
|
+
let kindClass = '';
|
|
405
|
+
|
|
406
|
+
if (kind === 'topic' && normalizeBool(cfg.enableMessageAds)) {
|
|
407
|
+
items = getPostContainers();
|
|
408
|
+
pool = state.poolPosts;
|
|
409
|
+
usedSet = state.usedPosts;
|
|
410
|
+
kindClass = 'ezoic-ad-message';
|
|
411
|
+
} else if (kind === 'categoryTopics' && normalizeBool(cfg.enableBetweenAds)) {
|
|
412
|
+
items = getTopicItems();
|
|
413
|
+
pool = state.poolTopics;
|
|
414
|
+
usedSet = state.usedTopics;
|
|
415
|
+
kindClass = 'ezoic-ad-between';
|
|
416
|
+
} else if (kind === 'categories' && normalizeBool(cfg.enableCategoryAds)) {
|
|
417
|
+
items = getCategoryItems();
|
|
418
|
+
pool = state.poolCategories;
|
|
419
|
+
usedSet = state.usedCategories;
|
|
420
|
+
kindClass = 'ezoic-ad-categories';
|
|
421
|
+
} else {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (!items.length) return;
|
|
426
|
+
|
|
427
|
+
// Insert after the very first item (above-the-fold)
|
|
428
|
+
const afterPos = 1;
|
|
429
|
+
const el = items[afterPos - 1];
|
|
430
|
+
if (!el || !el.isConnected) return;
|
|
431
|
+
if (isAdjacentAd(el)) return;
|
|
432
|
+
if (findWrap(kindClass, afterPos)) { state.heroDoneForPage = true; return; }
|
|
433
|
+
|
|
434
|
+
const id = pickId(pool);
|
|
435
|
+
if (!id) return;
|
|
436
|
+
|
|
437
|
+
usedSet.add(id);
|
|
438
|
+
const wrap = insertAfter(el, id, kindClass, afterPos);
|
|
439
|
+
if (!wrap) {
|
|
440
|
+
usedSet.delete(id);
|
|
441
|
+
pool.unshift(id);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
state.heroDoneForPage = true;
|
|
446
|
+
observePlaceholder(id);
|
|
569
447
|
}
|
|
570
448
|
|
|
571
|
-
function
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
449
|
+
async function runCore() {
|
|
450
|
+
if (EZOIC_BLOCKED) return;
|
|
451
|
+
|
|
452
|
+
patchShowAds();
|
|
453
|
+
|
|
454
|
+
const cfg = await fetchConfigOnce();
|
|
455
|
+
if (!cfg || cfg.excluded) return;
|
|
456
|
+
initPools(cfg);
|
|
457
|
+
|
|
458
|
+
const kind = getKind();
|
|
459
|
+
|
|
460
|
+
if (kind === 'topic') {
|
|
461
|
+
if (normalizeBool(cfg.enableMessageAds)) {
|
|
462
|
+
injectBetween(
|
|
463
|
+
'ezoic-ad-message',
|
|
464
|
+
getPostContainers(),
|
|
465
|
+
Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3),
|
|
466
|
+
normalizeBool(cfg.showFirstMessageAd),
|
|
467
|
+
state.poolPosts,
|
|
468
|
+
state.usedPosts
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
} else if (kind === 'categoryTopics') {
|
|
472
|
+
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
473
|
+
injectBetween(
|
|
474
|
+
'ezoic-ad-between',
|
|
475
|
+
getTopicItems(),
|
|
476
|
+
Math.max(1, parseInt(cfg.intervalPosts, 10) || 6),
|
|
477
|
+
normalizeBool(cfg.showFirstTopicAd),
|
|
478
|
+
state.poolTopics,
|
|
479
|
+
state.usedTopics
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
} else if (kind === 'categories') {
|
|
483
|
+
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
484
|
+
injectBetween(
|
|
485
|
+
'ezoic-ad-categories',
|
|
486
|
+
getCategoryItems(),
|
|
487
|
+
Math.max(1, parseInt(cfg.intervalCategories, 10) || 4),
|
|
488
|
+
normalizeBool(cfg.showFirstCategoryAd),
|
|
489
|
+
state.poolCategories,
|
|
490
|
+
state.usedCategories
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
616
494
|
}
|
|
617
495
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
inserted += 1;
|
|
630
|
-
}
|
|
631
|
-
return inserted;
|
|
496
|
+
function scheduleRun() {
|
|
497
|
+
if (state.runQueued) return;
|
|
498
|
+
state.runQueued = true;
|
|
499
|
+
window.requestAnimationFrame(() => {
|
|
500
|
+
state.runQueued = false;
|
|
501
|
+
const pk = getPageKey();
|
|
502
|
+
if (state.pageKey && pk !== state.pageKey) return;
|
|
503
|
+
runCore().catch(() => {});
|
|
504
|
+
});
|
|
632
505
|
}
|
|
633
506
|
|
|
634
|
-
|
|
635
|
-
const ads = Array.from(document.querySelectorAll(`.${WRAP_CLASS}`));
|
|
636
|
-
for (let i = 0; i < ads.length; i++) {
|
|
637
|
-
const ad = ads[i], prev = ad.previousElementSibling;
|
|
638
|
-
if (prev && prev.classList && prev.classList.contains(WRAP_CLASS)) {
|
|
639
|
-
try {
|
|
640
|
-
const ph = ad.querySelector && ad.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
|
|
641
|
-
if (ph) {
|
|
642
|
-
const id = parseInt(ph.id.replace(PLACEHOLDER_PREFIX, ''), 10);
|
|
643
|
-
if (Number.isFinite(id) && id > 0) {
|
|
644
|
-
// Détruire le placeholder si Ezoic l'a déjà défini
|
|
645
|
-
if (sessionDefinedIds.has(id)) {
|
|
646
|
-
destroyPlaceholderIds([id]);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
ad.remove();
|
|
651
|
-
} catch (e) {}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
}
|
|
507
|
+
// ---------- observers / lifecycle ----------
|
|
655
508
|
|
|
656
509
|
function cleanup() {
|
|
657
|
-
|
|
658
|
-
// CRITIQUE: BLOQUER Ezoic immédiatement
|
|
659
|
-
EZOIC_BLOCKED = true;
|
|
660
|
-
|
|
661
|
-
// Détruire TOUS les placeholders Ezoic AVANT de supprimer DOM
|
|
662
|
-
const allWrappers = document.querySelectorAll('.ezoic-ad');
|
|
663
|
-
const allIds = [];
|
|
664
|
-
allWrappers.forEach(wrapper => {
|
|
665
|
-
const ph = wrapper.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
|
|
666
|
-
if (ph) {
|
|
667
|
-
const match = ph.id.match(/\d+/);
|
|
668
|
-
if (match) allIds.push(parseInt(match[0]));
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
// CRITIQUE: Vider COMPLÈTEMENT sessionDefinedIds
|
|
673
|
-
// Pour éviter que d'anciens IDs soient encore en mémoire
|
|
674
|
-
|
|
675
|
-
// CRITIQUE: Détruire AUSSI tous les IDs tracés dans state
|
|
676
|
-
// Pour annuler les anciens IDs qu'Ezoic a en mémoire
|
|
677
|
-
const trackedIds = [
|
|
678
|
-
...Array.from(state.usedTopics),
|
|
679
|
-
...Array.from(state.usedPosts),
|
|
680
|
-
...Array.from(state.usedCategories)
|
|
681
|
-
];
|
|
682
|
-
|
|
683
|
-
const allIdsToDestroy = [...new Set([...allIds, ...trackedIds, ...Array.from(sessionDefinedIds)])];
|
|
684
|
-
sessionDefinedIds.clear(); // ✅ VIDER TOUT
|
|
685
|
-
|
|
686
|
-
if (allIdsToDestroy.length > 0) {
|
|
687
|
-
destroyPlaceholderIds(allIdsToDestroy);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// Annuler batch showAds en attente
|
|
691
|
-
pendingShowAdsIds.clear();
|
|
692
|
-
clearTimeout(batchShowAdsTimer);
|
|
510
|
+
EZOIC_BLOCKED = true;
|
|
693
511
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
512
|
+
// remove all wrappers
|
|
513
|
+
try {
|
|
514
|
+
document.querySelectorAll(`.${WRAP_CLASS}`).forEach((el) => {
|
|
515
|
+
try { el.remove(); } catch (e) {}
|
|
516
|
+
});
|
|
517
|
+
} catch (e) {}
|
|
698
518
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
state.lastShowById.clear();
|
|
710
|
-
state.pendingById.clear();
|
|
711
|
-
state.definedIds.clear();
|
|
712
|
-
|
|
713
|
-
state.activeTimeouts.forEach(id => {
|
|
714
|
-
try { clearTimeout(id); } catch (e) {}
|
|
715
|
-
});
|
|
716
|
-
state.activeTimeouts.clear();
|
|
717
|
-
|
|
718
|
-
state.pendingById.clear();
|
|
719
|
-
|
|
720
|
-
if (state.obs) { state.obs.disconnect(); state.obs = null; }
|
|
721
|
-
|
|
722
|
-
state.scheduled = false;
|
|
723
|
-
clearTimeout(state.timer);
|
|
724
|
-
state.timer = null;
|
|
725
|
-
}
|
|
519
|
+
// reset state
|
|
520
|
+
state.cfg = null;
|
|
521
|
+
state.poolTopics = [];
|
|
522
|
+
state.poolPosts = [];
|
|
523
|
+
state.poolCategories = [];
|
|
524
|
+
state.usedTopics.clear();
|
|
525
|
+
state.usedPosts.clear();
|
|
526
|
+
state.usedCategories.clear();
|
|
527
|
+
state.lastShowById.clear();
|
|
528
|
+
state.heroDoneForPage = false;
|
|
726
529
|
|
|
727
|
-
|
|
728
|
-
if (state.obs) return;
|
|
729
|
-
state.obs = new MutationObserver(() => scheduleRun('mutation'));
|
|
730
|
-
try { state.obs.observe(document.body, { childList: true, subtree: true }); } catch (e) {}
|
|
731
|
-
}
|
|
530
|
+
sessionDefinedIds.clear();
|
|
732
531
|
|
|
733
|
-
|
|
734
|
-
// CRITIQUE: Ne rien insérer si navigation en cours
|
|
735
|
-
if (EZOIC_BLOCKED) {
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
if (!state.canShowAds) {
|
|
740
|
-
return;
|
|
532
|
+
// keep observers alive (MutationObserver will re-trigger after navigation)
|
|
741
533
|
}
|
|
742
534
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
let inserted = 0;
|
|
752
|
-
|
|
753
|
-
if (kind === 'topic') {
|
|
754
|
-
if (normalizeBool(cfg.enableMessageAds)) {
|
|
755
|
-
inserted = injectBetween('ezoic-ad-message', getPostContainers(),
|
|
756
|
-
Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3),
|
|
757
|
-
normalizeBool(cfg.showFirstMessageAd),
|
|
758
|
-
state.poolPosts,
|
|
759
|
-
state.usedPosts);
|
|
760
|
-
}
|
|
761
|
-
} else if (kind === 'categoryTopics') {
|
|
762
|
-
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
763
|
-
inserted = injectBetween('ezoic-ad-between', getTopicItems(),
|
|
764
|
-
Math.max(1, parseInt(cfg.intervalPosts, 10) || 6),
|
|
765
|
-
normalizeBool(cfg.showFirstTopicAd),
|
|
766
|
-
state.poolTopics,
|
|
767
|
-
state.usedTopics);
|
|
768
|
-
}
|
|
769
|
-
} else if (kind === 'categories') {
|
|
770
|
-
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
771
|
-
inserted = injectBetween('ezoic-ad-categories', getCategoryItems(),
|
|
772
|
-
Math.max(1, parseInt(cfg.intervalCategories, 10) || 4),
|
|
773
|
-
normalizeBool(cfg.showFirstCategoryAd),
|
|
774
|
-
state.poolCategories,
|
|
775
|
-
state.usedCategories);
|
|
535
|
+
function ensureDomObserver() {
|
|
536
|
+
if (state.domObs) return;
|
|
537
|
+
state.domObs = new MutationObserver(() => {
|
|
538
|
+
if (!EZOIC_BLOCKED) scheduleRun();
|
|
539
|
+
});
|
|
540
|
+
try {
|
|
541
|
+
state.domObs.observe(document.body, { childList: true, subtree: true });
|
|
542
|
+
} catch (e) {}
|
|
776
543
|
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
enforceNoAdjacentAds();
|
|
780
544
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
else if (kind === 'categoryTopics') count = getTopicItems().length;
|
|
784
|
-
else if (kind === 'categories') count = getCategoryItems().length;
|
|
545
|
+
function bindNodeBB() {
|
|
546
|
+
if (!$) return;
|
|
785
547
|
|
|
786
|
-
|
|
787
|
-
setTimeout(arguments[0], 50);
|
|
788
|
-
return;
|
|
789
|
-
}
|
|
548
|
+
$(window).off('.ezoicInfinite');
|
|
790
549
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
// Pool épuisé ou recyclage pas encore disponible.
|
|
795
|
-
if (state.poolWaitAttempts < 8) {
|
|
796
|
-
state.poolWaitAttempts += 1;
|
|
797
|
-
setTimeout(arguments[0], 50);
|
|
798
|
-
} else {
|
|
799
|
-
}
|
|
800
|
-
} else if (inserted > 0) {
|
|
801
|
-
}
|
|
802
|
-
}
|
|
550
|
+
$(window).on('action:ajaxify.start.ezoicInfinite', () => {
|
|
551
|
+
cleanup();
|
|
552
|
+
});
|
|
803
553
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
clearTimeout(state.timer);
|
|
809
|
-
state.timer = setTimeout(() => {
|
|
810
|
-
state.scheduled = false;
|
|
811
|
-
const pk = getPageKey();
|
|
812
|
-
if (state.pageKey && pk !== state.pageKey) return;
|
|
813
|
-
runCore().catch(() => {});
|
|
814
|
-
}, 50);
|
|
815
|
-
}
|
|
554
|
+
$(window).on('action:ajaxify.end.ezoicInfinite', () => {
|
|
555
|
+
state.pageKey = getPageKey();
|
|
556
|
+
EZOIC_BLOCKED = false;
|
|
816
557
|
|
|
817
|
-
|
|
818
|
-
|
|
558
|
+
warmUpNetwork();
|
|
559
|
+
patchShowAds();
|
|
560
|
+
ensurePreloadObserver();
|
|
561
|
+
ensureDomObserver();
|
|
819
562
|
|
|
820
|
-
|
|
563
|
+
// Ultra-fast above-the-fold first
|
|
564
|
+
insertHeroAdEarly().catch(() => {});
|
|
821
565
|
|
|
822
|
-
|
|
566
|
+
// Then normal insertion
|
|
567
|
+
scheduleRun();
|
|
568
|
+
});
|
|
823
569
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
ensureObserver();
|
|
831
|
-
|
|
832
|
-
state.canShowAds = true;
|
|
833
|
-
|
|
834
|
-
// CRITIQUE: Relancer insertion maintenant que navigation est terminée
|
|
835
|
-
scheduleRun();
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
$(window).on('action:category.loaded.ezoicInfinite', () => {
|
|
839
|
-
ensureObserver();
|
|
840
|
-
waitForContentThenRun();
|
|
841
|
-
});
|
|
842
|
-
$(window).on('action:topics.loaded.ezoicInfinite', () => {
|
|
843
|
-
ensureObserver();
|
|
844
|
-
waitForContentThenRun();
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
$(window).on('action:topic.loaded.ezoicInfinite', () => {
|
|
848
|
-
ensureObserver();
|
|
849
|
-
waitForContentThenRun();
|
|
850
|
-
});
|
|
851
|
-
|
|
852
|
-
$(window).on('action:posts.loaded.ezoicInfinite', () => {
|
|
853
|
-
ensureObserver();
|
|
854
|
-
// posts.loaded = infinite scroll
|
|
855
|
-
waitForContentThenRun();
|
|
856
|
-
});
|
|
570
|
+
// Infinite scroll / partial updates
|
|
571
|
+
$(window).on('action:posts.loaded.ezoicInfinite action:topics.loaded.ezoicInfinite action:category.loaded.ezoicInfinite action:topic.loaded.ezoicInfinite', () => {
|
|
572
|
+
if (EZOIC_BLOCKED) return;
|
|
573
|
+
scheduleRun();
|
|
574
|
+
});
|
|
857
575
|
}
|
|
858
576
|
|
|
859
577
|
function bindScroll() {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
const now = Date.now();
|
|
870
|
-
if (!state.lastScrollRun || now - state.lastScrollRun > 2000) {
|
|
871
|
-
state.lastScrollRun = now;
|
|
872
|
-
scheduleRun();
|
|
873
|
-
}
|
|
874
|
-
});
|
|
875
|
-
}, { passive: true });
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function waitForContentThenRun() {
|
|
879
|
-
const MIN_WORDS = 250;
|
|
880
|
-
let attempts = 0;
|
|
881
|
-
const maxAttempts = 20; // 20 × 200ms = 4s max
|
|
882
|
-
|
|
883
|
-
(function check() {
|
|
884
|
-
attempts++;
|
|
885
|
-
|
|
886
|
-
// Compter les mots sur la page
|
|
887
|
-
const text = document.body.innerText || '';
|
|
888
|
-
const wordCount = text.split(/\s+/).filter(Boolean).length;
|
|
889
|
-
|
|
890
|
-
if (wordCount >= MIN_WORDS) {
|
|
891
|
-
// Assez de contenu → lancer l'insertion
|
|
892
|
-
scheduleRun();
|
|
893
|
-
return;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// Pas assez de contenu
|
|
897
|
-
if (attempts >= maxAttempts) {
|
|
898
|
-
// Timeout après 4s → tenter quand même
|
|
899
|
-
scheduleRun();
|
|
900
|
-
return;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Réessayer dans 200ms
|
|
904
|
-
setTimeout(check, 50);
|
|
905
|
-
})();
|
|
578
|
+
let ticking = false;
|
|
579
|
+
window.addEventListener('scroll', () => {
|
|
580
|
+
if (ticking) return;
|
|
581
|
+
ticking = true;
|
|
582
|
+
window.requestAnimationFrame(() => {
|
|
583
|
+
ticking = false;
|
|
584
|
+
if (!EZOIC_BLOCKED) scheduleRun();
|
|
585
|
+
});
|
|
586
|
+
}, { passive: true });
|
|
906
587
|
}
|
|
907
588
|
|
|
908
|
-
|
|
909
|
-
let attempts = 0;
|
|
910
|
-
const maxAttempts = 50; // 50 × 200ms = 10s max
|
|
589
|
+
// ---------- boot ----------
|
|
911
590
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
scheduleRun();
|
|
918
|
-
waitForContentThenRun();
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
// Ezoic pas encore prêt
|
|
922
|
-
if (attempts >= maxAttempts) {
|
|
923
|
-
// Tenter quand même
|
|
924
|
-
scheduleRun();
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
// Réessayer dans 200ms
|
|
928
|
-
setTimeout(check, 50);
|
|
929
|
-
})();
|
|
930
|
-
}
|
|
591
|
+
state.pageKey = getPageKey();
|
|
592
|
+
warmUpNetwork();
|
|
593
|
+
patchShowAds();
|
|
594
|
+
ensurePreloadObserver();
|
|
595
|
+
ensureDomObserver();
|
|
931
596
|
|
|
932
|
-
|
|
933
|
-
bind();
|
|
597
|
+
bindNodeBB();
|
|
934
598
|
bindScroll();
|
|
935
|
-
ensureObserver();
|
|
936
|
-
state.pageKey = getPageKey();
|
|
937
599
|
|
|
938
|
-
//
|
|
939
|
-
|
|
940
|
-
|
|
600
|
+
// First paint: try hero + run
|
|
601
|
+
EZOIC_BLOCKED = false;
|
|
602
|
+
insertHeroAdEarly().catch(() => {});
|
|
603
|
+
scheduleRun();
|
|
604
|
+
})();
|