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