nodebb-plugin-ezoic-infinite 1.8.19 → 1.8.21
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 +150 -104
- package/package.json +1 -1
- package/public/admin.js +32 -16
- package/public/client.js +168 -107
package/library.js
CHANGED
|
@@ -1,159 +1,205 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const meta
|
|
3
|
+
const meta = require.main.require('./src/meta');
|
|
4
4
|
const groups = require.main.require('./src/groups');
|
|
5
|
-
const db
|
|
5
|
+
const db = require.main.require('./src/database');
|
|
6
6
|
|
|
7
7
|
const SETTINGS_KEY = 'ezoic-infinite';
|
|
8
|
-
const
|
|
8
|
+
const SETTINGS_TTL_MS = 30_000;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const EZOIC_SCRIPTS = `<script data-cfasync="false" src="https://cmp.gatekeeperconsent.com/min.js"></script>
|
|
11
|
+
<script data-cfasync="false" src="https://the.gatekeeperconsent.com/cmp.min.js"></script>
|
|
12
|
+
<script async src="//www.ezojs.com/ezoic/sa.min.js"></script>
|
|
13
|
+
<script>
|
|
14
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
15
|
+
ezstandalone.cmd = ezstandalone.cmd || [];
|
|
16
|
+
</script>`;
|
|
17
|
+
|
|
18
|
+
const DEFAULTS = Object.freeze({
|
|
19
|
+
enableBetweenAds: true,
|
|
20
|
+
showFirstTopicAd: false,
|
|
21
|
+
placeholderIds: '',
|
|
22
|
+
intervalPosts: 6,
|
|
23
|
+
enableCategoryAds: false,
|
|
24
|
+
showFirstCategoryAd: false,
|
|
25
|
+
categoryPlaceholderIds: '',
|
|
26
|
+
intervalCategories: 4,
|
|
27
|
+
enableMessageAds: false,
|
|
28
|
+
showFirstMessageAd: false,
|
|
29
|
+
messagePlaceholderIds: '',
|
|
30
|
+
messageIntervalPosts: 3,
|
|
31
|
+
excludedGroups: [],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const CONFIG_FIELDS = Object.freeze([
|
|
35
|
+
'enableBetweenAds', 'showFirstTopicAd', 'placeholderIds', 'intervalPosts',
|
|
36
|
+
'enableCategoryAds', 'showFirstCategoryAd', 'categoryPlaceholderIds', 'intervalCategories',
|
|
37
|
+
'enableMessageAds', 'showFirstMessageAd', 'messagePlaceholderIds', 'messageIntervalPosts',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const plugin = {
|
|
41
|
+
_settingsCache: null,
|
|
42
|
+
_settingsCacheAt: 0,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function toBool(value, fallback = false) {
|
|
46
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
47
|
+
if (typeof value === 'boolean') return value;
|
|
48
|
+
switch (String(value).trim().toLowerCase()) {
|
|
49
|
+
case '1':
|
|
50
|
+
case 'true':
|
|
51
|
+
case 'on':
|
|
52
|
+
case 'yes':
|
|
53
|
+
return true;
|
|
54
|
+
default:
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toPositiveInt(value, fallback) {
|
|
60
|
+
const parsed = Number.parseInt(value, 10);
|
|
61
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toStringTrim(value) {
|
|
65
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
66
|
+
}
|
|
11
67
|
|
|
12
68
|
function normalizeExcludedGroups(value) {
|
|
13
69
|
if (!value) return [];
|
|
14
|
-
if (Array.isArray(value))
|
|
15
|
-
|
|
16
|
-
const s = String(value).trim();
|
|
17
|
-
if (s.startsWith('[')) {
|
|
18
|
-
try { const parsed = JSON.parse(s); if (Array.isArray(parsed)) return parsed.map(String).filter(Boolean); } catch (_) {}
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return value.map(String).map(v => v.trim()).filter(Boolean);
|
|
19
72
|
}
|
|
20
|
-
|
|
21
|
-
|
|
73
|
+
|
|
74
|
+
const raw = String(value).trim();
|
|
75
|
+
if (!raw) return [];
|
|
76
|
+
|
|
77
|
+
if (raw.startsWith('[')) {
|
|
78
|
+
try {
|
|
79
|
+
const parsed = JSON.parse(raw);
|
|
80
|
+
if (Array.isArray(parsed)) {
|
|
81
|
+
return parsed.map(String).map(v => v.trim()).filter(Boolean);
|
|
82
|
+
}
|
|
83
|
+
} catch {}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return raw.split(',').map(v => v.trim()).filter(Boolean);
|
|
22
87
|
}
|
|
23
88
|
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
89
|
+
function buildSettings(raw = {}) {
|
|
90
|
+
return {
|
|
91
|
+
enableBetweenAds: toBool(raw.enableBetweenAds, DEFAULTS.enableBetweenAds),
|
|
92
|
+
showFirstTopicAd: toBool(raw.showFirstTopicAd, DEFAULTS.showFirstTopicAd),
|
|
93
|
+
placeholderIds: toStringTrim(raw.placeholderIds),
|
|
94
|
+
intervalPosts: toPositiveInt(raw.intervalPosts, DEFAULTS.intervalPosts),
|
|
95
|
+
enableCategoryAds: toBool(raw.enableCategoryAds, DEFAULTS.enableCategoryAds),
|
|
96
|
+
showFirstCategoryAd: toBool(raw.showFirstCategoryAd, DEFAULTS.showFirstCategoryAd),
|
|
97
|
+
categoryPlaceholderIds: toStringTrim(raw.categoryPlaceholderIds),
|
|
98
|
+
intervalCategories: toPositiveInt(raw.intervalCategories, DEFAULTS.intervalCategories),
|
|
99
|
+
enableMessageAds: toBool(raw.enableMessageAds, DEFAULTS.enableMessageAds),
|
|
100
|
+
showFirstMessageAd: toBool(raw.showFirstMessageAd, DEFAULTS.showFirstMessageAd),
|
|
101
|
+
messagePlaceholderIds: toStringTrim(raw.messagePlaceholderIds),
|
|
102
|
+
messageIntervalPosts: toPositiveInt(raw.messageIntervalPosts, DEFAULTS.messageIntervalPosts),
|
|
103
|
+
excludedGroups: normalizeExcludedGroups(raw.excludedGroups),
|
|
104
|
+
};
|
|
29
105
|
}
|
|
30
106
|
|
|
31
|
-
async function
|
|
107
|
+
async function listNonPrivilegeGroups() {
|
|
32
108
|
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
33
|
-
if (!names || !names.length) {
|
|
109
|
+
if (!Array.isArray(names) || !names.length) {
|
|
34
110
|
names = await db.getSortedSetRange('groups:visible:createtime', 0, -1);
|
|
35
111
|
}
|
|
36
|
-
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
37
|
-
const data = await groups.getGroupsData(filtered);
|
|
38
|
-
const valid = data.filter(g => g && g.name);
|
|
39
|
-
valid.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
|
|
40
|
-
return valid;
|
|
41
|
-
}
|
|
42
112
|
|
|
43
|
-
|
|
113
|
+
const publicNames = (names || []).filter(name => !groups.isPrivilegeGroup(name));
|
|
114
|
+
const groupData = await groups.getGroupsData(publicNames);
|
|
44
115
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
116
|
+
return (groupData || [])
|
|
117
|
+
.filter(group => group && group.name)
|
|
118
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
|
|
119
|
+
}
|
|
48
120
|
|
|
49
121
|
async function getSettings() {
|
|
50
122
|
const now = Date.now();
|
|
51
|
-
if (_settingsCache && (now - _settingsCacheAt) <
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
_settingsCache = {
|
|
55
|
-
enableBetweenAds: parseBool(s.enableBetweenAds, true),
|
|
56
|
-
showFirstTopicAd: parseBool(s.showFirstTopicAd, false),
|
|
57
|
-
placeholderIds: (s.placeholderIds || '').trim(),
|
|
58
|
-
intervalPosts: Math.max(1, parseInt(s.intervalPosts, 10) || 6),
|
|
59
|
-
enableCategoryAds: parseBool(s.enableCategoryAds, false),
|
|
60
|
-
showFirstCategoryAd: parseBool(s.showFirstCategoryAd, false),
|
|
61
|
-
categoryPlaceholderIds: (s.categoryPlaceholderIds || '').trim(),
|
|
62
|
-
intervalCategories: Math.max(1, parseInt(s.intervalCategories, 10) || 4),
|
|
63
|
-
enableMessageAds: parseBool(s.enableMessageAds, false),
|
|
64
|
-
showFirstMessageAd: parseBool(s.showFirstMessageAd, false),
|
|
65
|
-
messagePlaceholderIds: (s.messagePlaceholderIds || '').trim(),
|
|
66
|
-
messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
|
|
67
|
-
excludedGroups: normalizeExcludedGroups(s.excludedGroups),
|
|
68
|
-
};
|
|
69
|
-
return _settingsCache;
|
|
70
|
-
}
|
|
123
|
+
if (plugin._settingsCache && (now - plugin._settingsCacheAt) < SETTINGS_TTL_MS) {
|
|
124
|
+
return plugin._settingsCache;
|
|
125
|
+
}
|
|
71
126
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
127
|
+
const raw = await meta.settings.get(SETTINGS_KEY);
|
|
128
|
+
const settings = buildSettings(raw);
|
|
129
|
+
|
|
130
|
+
plugin._settingsCache = settings;
|
|
131
|
+
plugin._settingsCacheAt = now;
|
|
132
|
+
return settings;
|
|
76
133
|
}
|
|
77
134
|
|
|
78
|
-
|
|
135
|
+
async function isUserExcluded(uid, excludedGroups) {
|
|
136
|
+
if (!uid || !Array.isArray(excludedGroups) || !excludedGroups.length) return false;
|
|
79
137
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<script>
|
|
84
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
85
|
-
ezstandalone.cmd = ezstandalone.cmd || [];
|
|
86
|
-
</script>`;
|
|
138
|
+
const userGroups = await groups.getUserGroups([uid]);
|
|
139
|
+
const names = (userGroups && userGroups[0]) || [];
|
|
140
|
+
const excludedSet = new Set(excludedGroups);
|
|
87
141
|
|
|
88
|
-
|
|
142
|
+
return names.some(group => group && excludedSet.has(group.name));
|
|
143
|
+
}
|
|
89
144
|
|
|
90
|
-
plugin.onSettingsSet = function (data) {
|
|
91
|
-
if (data && data.hash === SETTINGS_KEY)
|
|
145
|
+
plugin.onSettingsSet = function onSettingsSet(data) {
|
|
146
|
+
if (data && data.hash === SETTINGS_KEY) {
|
|
147
|
+
plugin._settingsCache = null;
|
|
148
|
+
plugin._settingsCacheAt = 0;
|
|
149
|
+
}
|
|
92
150
|
};
|
|
93
151
|
|
|
94
|
-
plugin.addAdminNavigation = async (header)
|
|
152
|
+
plugin.addAdminNavigation = async function addAdminNavigation(header) {
|
|
95
153
|
header.plugins = header.plugins || [];
|
|
96
|
-
header.plugins.push({
|
|
154
|
+
header.plugins.push({
|
|
155
|
+
route: '/plugins/ezoic-infinite',
|
|
156
|
+
icon: 'fa-ad',
|
|
157
|
+
name: 'Ezoic Infinite Ads',
|
|
158
|
+
});
|
|
97
159
|
return header;
|
|
98
160
|
};
|
|
99
161
|
|
|
100
|
-
|
|
101
|
-
* Injecte les scripts Ezoic dans le <head> via templateData.customHTML.
|
|
102
|
-
*
|
|
103
|
-
* NodeBB v4 / thème Harmony : header.tpl contient {{customHTML}} dans le <head>
|
|
104
|
-
* (render.js ligne 232 : templateValues.customHTML = meta.config.customHTML).
|
|
105
|
-
* Le hook filter:middleware.renderHeader reçoit templateData = headerFooterData
|
|
106
|
-
* et est rendu via req.app.renderAsync('header', hookReturn.templateData).
|
|
107
|
-
* On préfixe customHTML pour que nos scripts passent AVANT le customHTML admin,
|
|
108
|
-
* tout en préservant ce dernier.
|
|
109
|
-
*/
|
|
110
|
-
plugin.injectEzoicHead = async (data) => {
|
|
162
|
+
plugin.injectEzoicHead = async function injectEzoicHead(data) {
|
|
111
163
|
try {
|
|
112
164
|
const settings = await getSettings();
|
|
113
|
-
const uid
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// Préfixer : nos scripts d'abord, puis le customHTML existant de l'admin
|
|
117
|
-
data.templateData.customHTML = EZOIC_SCRIPTS + (data.templateData.customHTML || '');
|
|
165
|
+
const uid = data?.req?.uid || 0;
|
|
166
|
+
if (await isUserExcluded(uid, settings.excludedGroups)) {
|
|
167
|
+
return data;
|
|
118
168
|
}
|
|
119
|
-
|
|
169
|
+
|
|
170
|
+
const templateData = data.templateData || (data.templateData = {});
|
|
171
|
+
templateData.customHTML = `${EZOIC_SCRIPTS}${templateData.customHTML || ''}`;
|
|
172
|
+
} catch {}
|
|
173
|
+
|
|
120
174
|
return data;
|
|
121
175
|
};
|
|
122
176
|
|
|
123
|
-
plugin.init = async ({ router, middleware })
|
|
124
|
-
async function
|
|
125
|
-
const settings
|
|
126
|
-
|
|
177
|
+
plugin.init = async function init({ router, middleware }) {
|
|
178
|
+
async function renderAdmin(req, res) {
|
|
179
|
+
const [settings, allGroups] = await Promise.all([
|
|
180
|
+
getSettings(),
|
|
181
|
+
listNonPrivilegeGroups(),
|
|
182
|
+
]);
|
|
183
|
+
|
|
127
184
|
res.render('admin/plugins/ezoic-infinite', {
|
|
128
185
|
title: 'Ezoic Infinite Ads',
|
|
129
186
|
...settings,
|
|
130
187
|
enableBetweenAds_checked: settings.enableBetweenAds ? 'checked' : '',
|
|
131
|
-
enableMessageAds_checked:
|
|
188
|
+
enableMessageAds_checked: settings.enableMessageAds ? 'checked' : '',
|
|
132
189
|
allGroups,
|
|
133
190
|
});
|
|
134
191
|
}
|
|
135
192
|
|
|
136
|
-
router.get('/admin/plugins/ezoic-infinite',
|
|
137
|
-
router.get('/api/admin/plugins/ezoic-infinite',
|
|
193
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
|
|
194
|
+
router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
|
|
138
195
|
|
|
139
196
|
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
140
197
|
const settings = await getSettings();
|
|
141
198
|
const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
placeholderIds: settings.placeholderIds,
|
|
147
|
-
intervalPosts: settings.intervalPosts,
|
|
148
|
-
enableCategoryAds: settings.enableCategoryAds,
|
|
149
|
-
showFirstCategoryAd: settings.showFirstCategoryAd,
|
|
150
|
-
categoryPlaceholderIds: settings.categoryPlaceholderIds,
|
|
151
|
-
intervalCategories: settings.intervalCategories,
|
|
152
|
-
enableMessageAds: settings.enableMessageAds,
|
|
153
|
-
showFirstMessageAd: settings.showFirstMessageAd,
|
|
154
|
-
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
155
|
-
messageIntervalPosts: settings.messageIntervalPosts,
|
|
156
|
-
});
|
|
199
|
+
|
|
200
|
+
const payload = { excluded };
|
|
201
|
+
for (const key of CONFIG_FIELDS) payload[key] = settings[key];
|
|
202
|
+
res.json(payload);
|
|
157
203
|
});
|
|
158
204
|
};
|
|
159
205
|
|
package/package.json
CHANGED
package/public/admin.js
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
/* globals ajaxify */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
(function () {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
(function initAdminEzoicSettings() {
|
|
5
|
+
const FORM_SELECTOR = '.ezoic-infinite-settings';
|
|
6
|
+
const SAVE_SELECTOR = '#save';
|
|
7
|
+
const SETTINGS_KEY = 'ezoic-infinite';
|
|
8
|
+
const EVENT_NS = '.ezoicInfinite';
|
|
9
|
+
|
|
10
|
+
function showSaved(alerts) {
|
|
11
|
+
if (alerts && typeof alerts.success === 'function') {
|
|
12
|
+
alerts.success('[[admin/settings:saved]]');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (window.app && typeof window.app.alertSuccess === 'function') {
|
|
16
|
+
window.app.alertSuccess('[[admin/settings:saved]]');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
8
19
|
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
function bind(Settings, alerts, $form) {
|
|
21
|
+
Settings.load(SETTINGS_KEY, $form);
|
|
11
22
|
|
|
12
|
-
|
|
23
|
+
$(SAVE_SELECTOR)
|
|
24
|
+
.off(`click${EVENT_NS}`)
|
|
25
|
+
.on(`click${EVENT_NS}`, function onSave(e) {
|
|
13
26
|
e.preventDefault();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (alerts && typeof alerts.success === 'function') {
|
|
17
|
-
alerts.success('[[admin/settings:saved]]');
|
|
18
|
-
} else if (window.app && typeof window.app.alertSuccess === 'function') {
|
|
19
|
-
window.app.alertSuccess('[[admin/settings:saved]]');
|
|
20
|
-
}
|
|
27
|
+
Settings.save(SETTINGS_KEY, $form, function onSaved() {
|
|
28
|
+
showSaved(alerts);
|
|
21
29
|
});
|
|
22
30
|
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function boot() {
|
|
34
|
+
const $form = $(FORM_SELECTOR);
|
|
35
|
+
if (!$form.length) return;
|
|
36
|
+
|
|
37
|
+
require(['settings', 'alerts'], function onModules(Settings, alerts) {
|
|
38
|
+
bind(Settings, alerts, $form);
|
|
23
39
|
});
|
|
24
40
|
}
|
|
25
41
|
|
|
26
|
-
$(document).ready(
|
|
27
|
-
$(window).on('action:ajaxify.end',
|
|
42
|
+
$(document).ready(boot);
|
|
43
|
+
$(window).on('action:ajaxify.end', boot);
|
|
28
44
|
})();
|
package/public/client.js
CHANGED
|
@@ -1,70 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NodeBB Ezoic Infinite Ads — client.js v36
|
|
3
|
-
*
|
|
4
|
-
* Historique des corrections majeures
|
|
5
|
-
* ────────────────────────────────────
|
|
6
|
-
* v18 Ancrage stable par data-pid / data-index au lieu d'ordinalMap fragile.
|
|
7
|
-
*
|
|
8
|
-
* v19 Intervalle global basé sur l'ordinal absolu (data-index), pas sur
|
|
9
|
-
* la position dans le batch courant.
|
|
10
|
-
*
|
|
11
|
-
* v20 Table KIND : anchorAttr / ordinalAttr / baseTag par kindClass.
|
|
12
|
-
* Fix fatal catégories : data-cid au lieu de data-index inexistant.
|
|
13
|
-
* IO fixe (une instance, jamais recréée).
|
|
14
|
-
* Fix TCF locator : MutationObserver recrée l'iframe si ajaxify la retire.
|
|
15
|
-
*
|
|
16
|
-
* v25 Fix scroll-up / virtualisation NodeBB : decluster + grace period.
|
|
17
|
-
*
|
|
18
|
-
* v26 Suppression définitive du recyclage d'id (causait réinjection en haut).
|
|
19
|
-
*
|
|
20
|
-
* v27 pruneOrphans supprimé (faux-orphelins sur virtualisation NodeBB posts).
|
|
21
|
-
*
|
|
22
|
-
* v28 decluster supprimé. Wraps persistants pendant la session.
|
|
23
|
-
*
|
|
24
|
-
* v32 Retour anchorAttr = data-index pour ezoic-ad-between.
|
|
25
|
-
* data-tid peut être absent → clés invalides → wraps empilés.
|
|
26
|
-
* pruneOrphansBetween réactivé uniquement pour topics de catégorie :
|
|
27
|
-
* – NodeBB NE virtualise PAS les topics dans une liste de catégorie,
|
|
28
|
-
* les ancres (data-index) restent en DOM → prune safe et nécessaire
|
|
29
|
-
* pour éviter l'empilement après scroll long.
|
|
30
|
-
* – Toujours désactivé pour les posts : NodeBB virtualise les posts
|
|
31
|
-
* hors-viewport → faux-orphelins → bug réinjection en haut.
|
|
32
|
-
*
|
|
33
|
-
* v34 moveDistantWrap — voir v38.
|
|
34
|
-
*
|
|
35
|
-
* v50 Suppression de bindLoginCheck() : NodeBB fait un rechargement complet
|
|
36
|
-
* après login — filter:middleware.renderHeader re-évalue l'exclusion au
|
|
37
|
-
* rechargement. Redondant depuis le fix normalizeExcludedGroups (v49).
|
|
38
|
-
*
|
|
39
|
-
* v43 Seuil de recyclage abaissé à -vh + unobserve avant recyclage.
|
|
40
|
-
*
|
|
41
|
-
* v42 Seuil -(IO_MARGIN + vh) (trop strict, peu de wraps éligibles).
|
|
42
|
-
*
|
|
43
|
-
* v41 Seuil -1vh (trop permissif sur mobile, ignorait IO_MARGIN).
|
|
44
|
-
*
|
|
45
|
-
* v40 Recyclage slots via destroyPlaceholders+define+displayMore avec délais.
|
|
46
|
-
* Séquence : destroy → 300ms → define → 300ms → displayMore.
|
|
47
|
-
* Testé manuellement : fonctionne. displayMore = API Ezoic infinite scroll.
|
|
48
|
-
*
|
|
49
|
-
* v38 Pool épuisé = fin de quota Ezoic par page-view. ez.refresh() interdit
|
|
50
|
-
* sur la même page que ez.enable() — supprimé. moveDistantWrap supprimé :
|
|
51
|
-
* déplacer un wrap "already defined" ne re-sert aucune pub. Pool épuisé →
|
|
52
|
-
* break propre dans injectBetween. muteConsole : ajout warnings refresh.
|
|
53
|
-
*
|
|
54
|
-
* v36 Optimisations chemin critique (scroll → injectBetween) :
|
|
55
|
-
* – S.wrapByKey Map<anchorKey,wrap> : findWrap() passe de querySelector
|
|
56
|
-
* sur tout le doc à un lookup O(1). Mis à jour dans insertAfter,
|
|
57
|
-
* dropWrap et cleanup.
|
|
58
|
-
* – wrapIsLive allégé : pour les voisins immédiats on vérifie les
|
|
59
|
-
* attributs du nœud lui-même sans querySelector global.
|
|
60
|
-
* – MutationObserver : matches() vérifié avant querySelector() pour
|
|
61
|
-
* court-circuiter les sous-arbres entiers ajoutés par NodeBB.
|
|
62
|
-
*
|
|
63
|
-
* v35 Revue complète prod-ready :
|
|
64
|
-
* – initPools protégé contre ré-initialisation inutile (S.poolsReady).
|
|
65
|
-
* – muteConsole élargit à "No valid placeholders for loadMore".
|
|
66
|
-
* – Commentaires et historique nettoyés.
|
|
67
|
-
*/
|
|
68
1
|
(function nbbEzoicInfinite() {
|
|
69
2
|
'use strict';
|
|
70
3
|
|
|
@@ -89,6 +22,11 @@
|
|
|
89
22
|
const MAX_DESTROY_BATCH = 4; // ids max par destroyPlaceholders(...ids)
|
|
90
23
|
const DESTROY_FLUSH_MS = 30; // micro-buffer destroy pour lisser les rafales
|
|
91
24
|
const BURST_COOLDOWN_MS = 100; // délai min entre deux déclenchements de burst
|
|
25
|
+
const CLEANUP_GRACE_MS = 3_500; // délai mini avant cleanup d'un wrap candidat
|
|
26
|
+
const SPECIAL_GRACE_MS = 30_000; // délai allongé pour sticky/fixed/adhesion
|
|
27
|
+
const RECENT_WRAP_ACTIVITY_MS = 5_000; // protège un wrap récemment muté/rafraîchi
|
|
28
|
+
const VIEWPORT_BUFFER_DESKTOP = 500;
|
|
29
|
+
const VIEWPORT_BUFFER_MOBILE = 250;
|
|
92
30
|
|
|
93
31
|
// Marges IO larges et fixes — observer créé une seule fois au boot
|
|
94
32
|
const IO_MARGIN_DESKTOP = '2500px 0px 2500px 0px';
|
|
@@ -99,6 +37,9 @@
|
|
|
99
37
|
topic: 'li[component="category/topic"]',
|
|
100
38
|
category: 'li[component="categories/category"]',
|
|
101
39
|
};
|
|
40
|
+
const WRAP_SEL = `.${WRAP_CLASS}`;
|
|
41
|
+
const FILL_SEL = 'iframe, ins, img, video, [data-google-container-id]';
|
|
42
|
+
const CONTENT_SEL_LIST = [SEL.post, SEL.topic, SEL.category];
|
|
102
43
|
|
|
103
44
|
/**
|
|
104
45
|
* Table KIND — source de vérité par kindClass.
|
|
@@ -140,6 +81,8 @@
|
|
|
140
81
|
wrapByKey: new Map(), // anchorKey → wrap DOM node
|
|
141
82
|
ezActiveIds: new Set(), // ids actifs côté plugin (wrap présent / récemment show)
|
|
142
83
|
ezShownSinceDestroy: new Set(), // ids déjà show depuis le dernier destroy Ezoic
|
|
84
|
+
wrapActivityAt: new Map(), // key/id -> ts activité DOM récente
|
|
85
|
+
lastWrapHeightByClass: new Map(),
|
|
143
86
|
scrollDir: 1, // 1=bas, -1=haut
|
|
144
87
|
scrollSpeed: 0, // px/s approx (EMA)
|
|
145
88
|
lastScrollY: 0,
|
|
@@ -157,7 +100,103 @@
|
|
|
157
100
|
const isBlocked = () => ts() < blockedUntil;
|
|
158
101
|
const isMobile = () => { try { return window.innerWidth < 768; } catch (_) { return false; } };
|
|
159
102
|
const normBool = v => v === true || v === 'true' || v === 1 || v === '1' || v === 'on';
|
|
160
|
-
const isFilled = n => !!(n?.querySelector?.(
|
|
103
|
+
const isFilled = n => !!(n?.querySelector?.(FILL_SEL));
|
|
104
|
+
|
|
105
|
+
function viewportBufferPx() {
|
|
106
|
+
return isMobile() ? VIEWPORT_BUFFER_MOBILE : VIEWPORT_BUFFER_DESKTOP;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function textSig(el) {
|
|
110
|
+
if (!(el instanceof Element)) return '';
|
|
111
|
+
const parts = [el.id || '', String(el.className || ''), el.getAttribute?.('name') || ''];
|
|
112
|
+
try {
|
|
113
|
+
for (const a of ['data-google-query-id', 'data-google-container-id', 'data-slot', 'data-ad-slot']) {
|
|
114
|
+
const v = el.getAttribute?.(a);
|
|
115
|
+
if (v) parts.push(v);
|
|
116
|
+
}
|
|
117
|
+
} catch (_) {}
|
|
118
|
+
return parts.join(' ').toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isSpecialSlotLike(el) {
|
|
122
|
+
const sig = textSig(el);
|
|
123
|
+
return /adhesion|interstitial|anchor|sticky|outofpage/.test(sig);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hasSpecialSlotMarkers(root) {
|
|
127
|
+
if (!(root instanceof Element)) return false;
|
|
128
|
+
if (isSpecialSlotLike(root)) return true;
|
|
129
|
+
try {
|
|
130
|
+
const nodes = root.querySelectorAll('[id],[class],[name],[data-slot],[data-ad-slot]');
|
|
131
|
+
for (const n of nodes) if (isSpecialSlotLike(n)) return true;
|
|
132
|
+
} catch (_) {}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hasFixedLikeNode(root, maxScan = 24) {
|
|
137
|
+
if (!(root instanceof Element)) return false;
|
|
138
|
+
const q = [root];
|
|
139
|
+
let seen = 0;
|
|
140
|
+
while (q.length && seen < maxScan) {
|
|
141
|
+
const n = q.shift();
|
|
142
|
+
seen++;
|
|
143
|
+
try {
|
|
144
|
+
const cs = window.getComputedStyle(n);
|
|
145
|
+
if (cs.position === 'fixed' || cs.position === 'sticky') return true;
|
|
146
|
+
} catch (_) {}
|
|
147
|
+
for (const c of n.children || []) q.push(c);
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function markWrapActivity(wrapOrId) {
|
|
153
|
+
const t = ts();
|
|
154
|
+
try {
|
|
155
|
+
if (wrapOrId instanceof Element) {
|
|
156
|
+
const key = wrapOrId.getAttribute(A_ANCHOR);
|
|
157
|
+
const id = parseInt(wrapOrId.getAttribute(A_WRAPID), 10);
|
|
158
|
+
if (key) S.wrapActivityAt.set(`k:${key}`, t);
|
|
159
|
+
if (Number.isFinite(id)) S.wrapActivityAt.set(`i:${id}`, t);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const id = parseInt(wrapOrId, 10);
|
|
163
|
+
if (Number.isFinite(id)) S.wrapActivityAt.set(`i:${id}`, t);
|
|
164
|
+
} catch (_) {}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function wrapRecentActivity(w) {
|
|
168
|
+
try {
|
|
169
|
+
const key = w?.getAttribute?.(A_ANCHOR);
|
|
170
|
+
const id = parseInt(w?.getAttribute?.(A_WRAPID), 10);
|
|
171
|
+
const t1 = key ? (S.wrapActivityAt.get(`k:${key}`) || 0) : 0;
|
|
172
|
+
const t2 = Number.isFinite(id) ? (S.wrapActivityAt.get(`i:${id}`) || 0) : 0;
|
|
173
|
+
const t3 = parseInt(w?.getAttribute?.(A_SHOWN) || '0', 10) || 0;
|
|
174
|
+
return (ts() - Math.max(t1, t2, t3)) < RECENT_WRAP_ACTIVITY_MS;
|
|
175
|
+
} catch (_) { return false; }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function wrapCleanupGraceMs(w) {
|
|
179
|
+
return (hasSpecialSlotMarkers(w) || hasFixedLikeNode(w)) ? SPECIAL_GRACE_MS : CLEANUP_GRACE_MS;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function wrapNearViewport(w) {
|
|
183
|
+
try {
|
|
184
|
+
const r = w.getBoundingClientRect();
|
|
185
|
+
const b = viewportBufferPx();
|
|
186
|
+
const vh = window.innerHeight || 800;
|
|
187
|
+
return r.bottom > -b && r.top < vh + b;
|
|
188
|
+
} catch (_) { return true; }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function rememberWrapHeight(w) {
|
|
192
|
+
try {
|
|
193
|
+
if (!(w instanceof Element)) return;
|
|
194
|
+
const klass = [...(w.classList || [])].find(c => c.startsWith('ezoic-ad-'));
|
|
195
|
+
if (!klass) return;
|
|
196
|
+
const h = Math.round(w.getBoundingClientRect().height || 0);
|
|
197
|
+
if (h >= 40) S.lastWrapHeightByClass.set(klass, h);
|
|
198
|
+
} catch (_) {}
|
|
199
|
+
}
|
|
161
200
|
|
|
162
201
|
function healFalseEmpty(root = document) {
|
|
163
202
|
try {
|
|
@@ -196,7 +235,7 @@
|
|
|
196
235
|
requestAnimationFrame(() => {
|
|
197
236
|
S.sweepQueued = false;
|
|
198
237
|
sweepDeadWraps();
|
|
199
|
-
|
|
238
|
+
healFalseEmpty();
|
|
200
239
|
});
|
|
201
240
|
}
|
|
202
241
|
|
|
@@ -264,16 +303,25 @@ function destroyBeforeReuse(ids) {
|
|
|
264
303
|
if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
|
|
265
304
|
seen.add(id);
|
|
266
305
|
out.push(id);
|
|
306
|
+
try {
|
|
307
|
+
const wrap = phEl(id)?.closest?.(WRAP_SEL) || null;
|
|
308
|
+
if (wrap && (wrapRecentActivity(wrap) || hasSpecialSlotMarkers(wrap) || hasFixedLikeNode(wrap))) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
} catch (_) {}
|
|
267
312
|
if (S.ezShownSinceDestroy.has(id)) toDestroy.push(id);
|
|
268
313
|
}
|
|
269
314
|
if (toDestroy.length) {
|
|
270
|
-
try {
|
|
315
|
+
try {
|
|
316
|
+
const ez = window.ezstandalone;
|
|
317
|
+
const run = () => { try { ez?.destroyPlaceholders?.(toDestroy); } catch (_) {} };
|
|
318
|
+
try { (typeof ez?.cmd?.push === 'function') ? ez.cmd.push(run) : run(); } catch (_) {}
|
|
319
|
+
} catch (_) {}
|
|
271
320
|
for (const id of toDestroy) S.ezShownSinceDestroy.delete(id);
|
|
272
321
|
}
|
|
273
322
|
return out;
|
|
274
323
|
}
|
|
275
324
|
|
|
276
|
-
|
|
277
325
|
// ── Config ─────────────────────────────────────────────────────────────────
|
|
278
326
|
|
|
279
327
|
async function fetchConfig() {
|
|
@@ -430,8 +478,10 @@ function destroyBeforeReuse(ids) {
|
|
|
430
478
|
function sweepDeadWraps() {
|
|
431
479
|
// NodeBB peut retirer des nœuds sans passer par dropWrap() (virtualisation / rerender).
|
|
432
480
|
// On libère alors les IDs/keys fantômes pour éviter l'épuisement du pool.
|
|
433
|
-
|
|
481
|
+
let changed = false;
|
|
482
|
+
for (const [key, wrap] of S.wrapByKey) {
|
|
434
483
|
if (wrap?.isConnected) continue;
|
|
484
|
+
changed = true;
|
|
435
485
|
S.wrapByKey.delete(key);
|
|
436
486
|
const id = parseInt(wrap?.getAttribute?.(A_WRAPID), 10);
|
|
437
487
|
if (Number.isFinite(id)) {
|
|
@@ -441,7 +491,7 @@ function destroyBeforeReuse(ids) {
|
|
|
441
491
|
S.ezActiveIds.delete(id);
|
|
442
492
|
}
|
|
443
493
|
}
|
|
444
|
-
if (S.pending.length) {
|
|
494
|
+
if (changed && S.pending.length) {
|
|
445
495
|
S.pending = S.pending.filter(id => S.pendingSet.has(id));
|
|
446
496
|
}
|
|
447
497
|
}
|
|
@@ -471,6 +521,8 @@ function recycleAndMove(klass, targetEl, newKey) {
|
|
|
471
521
|
|
|
472
522
|
for (const wrap of S.wrapByKey.values()) {
|
|
473
523
|
if (!wrap?.isConnected || !wrap.classList?.contains(WRAP_CLASS) || !wrap.classList.contains(klass)) continue;
|
|
524
|
+
if (wrapRecentActivity(wrap)) continue;
|
|
525
|
+
if (hasSpecialSlotMarkers(wrap) || hasFixedLikeNode(wrap)) continue;
|
|
474
526
|
try {
|
|
475
527
|
const rect = wrap.getBoundingClientRect();
|
|
476
528
|
const isAbove = rect.bottom <= farAbove;
|
|
@@ -505,6 +557,7 @@ function recycleAndMove(klass, targetEl, newKey) {
|
|
|
505
557
|
const oldKey = best.getAttribute(A_ANCHOR);
|
|
506
558
|
try { const ph = best.querySelector(`#${PH_PREFIX}${id}`); if (ph) S.io?.unobserve(ph); } catch (_) {}
|
|
507
559
|
mutate(() => {
|
|
560
|
+
rememberWrapHeight(best);
|
|
508
561
|
best.setAttribute(A_ANCHOR, newKey);
|
|
509
562
|
best.setAttribute(A_CREATED, String(ts()));
|
|
510
563
|
best.setAttribute(A_SHOWN, '0');
|
|
@@ -531,7 +584,6 @@ function recycleAndMove(klass, targetEl, newKey) {
|
|
|
531
584
|
return { id, wrap: best };
|
|
532
585
|
}
|
|
533
586
|
|
|
534
|
-
|
|
535
587
|
// ── Wraps DOM — création / suppression ────────────────────────────────────
|
|
536
588
|
|
|
537
589
|
function makeWrap(id, klass, key) {
|
|
@@ -542,6 +594,8 @@ function recycleAndMove(klass, targetEl, newKey) {
|
|
|
542
594
|
w.setAttribute(A_CREATED, String(ts()));
|
|
543
595
|
w.setAttribute(A_SHOWN, '0');
|
|
544
596
|
w.style.cssText = 'width:100%;display:block;';
|
|
597
|
+
const cachedH = S.lastWrapHeightByClass.get(klass);
|
|
598
|
+
if (Number.isFinite(cachedH) && cachedH > 0) w.style.minHeight = `${cachedH}px`;
|
|
545
599
|
const ph = document.createElement('div');
|
|
546
600
|
ph.id = `${PH_PREFIX}${id}`;
|
|
547
601
|
ph.setAttribute('data-ezoic-id', String(id));
|
|
@@ -563,11 +617,14 @@ function recycleAndMove(klass, targetEl, newKey) {
|
|
|
563
617
|
|
|
564
618
|
function dropWrap(w) {
|
|
565
619
|
try {
|
|
620
|
+
rememberWrapHeight(w);
|
|
566
621
|
const ph = w.querySelector(`[id^="${PH_PREFIX}"]`);
|
|
567
622
|
if (ph instanceof Element) S.io?.unobserve(ph);
|
|
568
623
|
const id = parseInt(w.getAttribute(A_WRAPID), 10);
|
|
569
624
|
if (Number.isFinite(id)) { S.ezActiveIds.delete(id); S.mountedIds.delete(id); }
|
|
570
625
|
const key = w.getAttribute(A_ANCHOR);
|
|
626
|
+
if (key) S.wrapActivityAt.delete(`k:${key}`);
|
|
627
|
+
if (Number.isFinite(id)) S.wrapActivityAt.delete(`i:${id}`);
|
|
571
628
|
if (key && S.wrapByKey.get(key) === w) S.wrapByKey.delete(key);
|
|
572
629
|
w.remove();
|
|
573
630
|
} catch (_) {}
|
|
@@ -592,9 +649,10 @@ function recycleAndMove(klass, targetEl, newKey) {
|
|
|
592
649
|
const klass = 'ezoic-ad-between';
|
|
593
650
|
const cfg = KIND[klass];
|
|
594
651
|
|
|
595
|
-
document.querySelectorAll(
|
|
652
|
+
document.querySelectorAll(`${WRAP_SEL}.${klass}`).forEach(w => {
|
|
596
653
|
const created = parseInt(w.getAttribute(A_CREATED) || '0', 10);
|
|
597
|
-
if (ts() - created < MIN_PRUNE_AGE_MS) return;
|
|
654
|
+
if (ts() - created < Math.max(MIN_PRUNE_AGE_MS, wrapCleanupGraceMs(w))) return;
|
|
655
|
+
if (wrapRecentActivity(w) || wrapNearViewport(w)) return;
|
|
598
656
|
|
|
599
657
|
const key = w.getAttribute(A_ANCHOR) ?? '';
|
|
600
658
|
const sid = key.slice(klass.length + 1); // après "ezoic-ad-between:"
|
|
@@ -751,7 +809,7 @@ function startShowBatch(ids) {
|
|
|
751
809
|
if (!canShowPlaceholderId(id, t)) continue;
|
|
752
810
|
|
|
753
811
|
S.lastShow.set(id, t);
|
|
754
|
-
try { ph.closest?.(
|
|
812
|
+
try { const wrap = ph.closest?.(WRAP_SEL); wrap?.setAttribute(A_SHOWN, String(t)); if (wrap) markWrapActivity(wrap); } catch (_) {}
|
|
755
813
|
valid.push(id);
|
|
756
814
|
}
|
|
757
815
|
|
|
@@ -774,7 +832,6 @@ function startShowBatch(ids) {
|
|
|
774
832
|
});
|
|
775
833
|
}
|
|
776
834
|
|
|
777
|
-
|
|
778
835
|
// ── Patch Ezoic showAds ────────────────────────────────────────────────────
|
|
779
836
|
//
|
|
780
837
|
// Intercepte ez.showAds() pour :
|
|
@@ -899,7 +956,7 @@ function startShowBatch(ids) {
|
|
|
899
956
|
|
|
900
957
|
function cleanup() {
|
|
901
958
|
blockedUntil = ts() + 1500;
|
|
902
|
-
mutate(() => document.querySelectorAll(
|
|
959
|
+
mutate(() => document.querySelectorAll(WRAP_SEL).forEach(dropWrap));
|
|
903
960
|
S.cfg = null;
|
|
904
961
|
S.poolsReady = false;
|
|
905
962
|
S.pools = { topics: [], posts: [], categories: [] };
|
|
@@ -909,6 +966,7 @@ function startShowBatch(ids) {
|
|
|
909
966
|
S.wrapByKey.clear();
|
|
910
967
|
S.ezActiveIds.clear();
|
|
911
968
|
S.ezShownSinceDestroy.clear();
|
|
969
|
+
S.wrapActivityAt.clear();
|
|
912
970
|
S.inflight = 0;
|
|
913
971
|
S.pending = [];
|
|
914
972
|
S.pendingSet.clear();
|
|
@@ -924,28 +982,51 @@ function startShowBatch(ids) {
|
|
|
924
982
|
S.lastScrollTs = 0;
|
|
925
983
|
}
|
|
926
984
|
|
|
985
|
+
function nodeMatchesAny(node, selectors) {
|
|
986
|
+
if (!(node instanceof Element)) return false;
|
|
987
|
+
for (const sel of selectors) {
|
|
988
|
+
try { if (node.matches(sel)) return true; } catch (_) {}
|
|
989
|
+
}
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function nodeContainsAny(node, selectors) {
|
|
994
|
+
if (!(node instanceof Element)) return false;
|
|
995
|
+
for (const sel of selectors) {
|
|
996
|
+
try { if (node.querySelector(sel)) return true; } catch (_) {}
|
|
997
|
+
}
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
|
|
927
1001
|
// ── MutationObserver ───────────────────────────────────────────────────────
|
|
928
1002
|
|
|
929
1003
|
function ensureDomObserver() {
|
|
930
1004
|
if (S.domObs) return;
|
|
931
|
-
const allSel = [SEL.post, SEL.topic, SEL.category];
|
|
932
1005
|
S.domObs = new MutationObserver(muts => {
|
|
933
1006
|
if (S.mutGuard > 0 || isBlocked()) return;
|
|
934
1007
|
for (const m of muts) {
|
|
935
1008
|
let sawWrapRemoval = false;
|
|
936
1009
|
for (const n of m.removedNodes) {
|
|
937
1010
|
if (n.nodeType !== 1) continue;
|
|
938
|
-
if ((n
|
|
1011
|
+
if (nodeMatchesAny(n, [WRAP_SEL]) || nodeContainsAny(n, [WRAP_SEL])) {
|
|
939
1012
|
sawWrapRemoval = true;
|
|
1013
|
+
try {
|
|
1014
|
+
if (n instanceof Element && n.classList?.contains(WRAP_CLASS)) markWrapActivity(n);
|
|
1015
|
+
else if (n instanceof Element) { const inner = n.querySelector?.(WRAP_SEL); if (inner) markWrapActivity(inner); }
|
|
1016
|
+
} catch (_) {}
|
|
940
1017
|
}
|
|
941
1018
|
}
|
|
942
1019
|
if (sawWrapRemoval) queueSweepDeadWraps();
|
|
943
1020
|
for (const n of m.addedNodes) {
|
|
944
1021
|
if (n.nodeType !== 1) continue;
|
|
945
1022
|
try { healFalseEmpty(n); } catch (_) {}
|
|
1023
|
+
try {
|
|
1024
|
+
const w = (n instanceof Element && (n.classList?.contains(WRAP_CLASS) ? n : n.closest?.(WRAP_SEL))) || null;
|
|
1025
|
+
if (w) markWrapActivity(w);
|
|
1026
|
+
else if (n instanceof Element) { const inner = n.querySelector?.(WRAP_SEL); if (inner) markWrapActivity(inner); }
|
|
1027
|
+
} catch (_) {}
|
|
946
1028
|
// matches() d'abord (O(1)), querySelector() seulement si nécessaire
|
|
947
|
-
if (
|
|
948
|
-
allSel.some(s => { try { return !!n.querySelector(s); } catch(_){return false;} })) {
|
|
1029
|
+
if (nodeMatchesAny(n, CONTENT_SEL_LIST) || nodeContainsAny(n, CONTENT_SEL_LIST)) {
|
|
949
1030
|
requestBurst(); return;
|
|
950
1031
|
}
|
|
951
1032
|
}
|
|
@@ -956,27 +1037,6 @@ function startShowBatch(ids) {
|
|
|
956
1037
|
|
|
957
1038
|
// ── Utilitaires ────────────────────────────────────────────────────────────
|
|
958
1039
|
|
|
959
|
-
function muteConsole() {
|
|
960
|
-
if (window.__nbbEzMuted) return;
|
|
961
|
-
window.__nbbEzMuted = true;
|
|
962
|
-
const MUTED = [
|
|
963
|
-
'[EzoicAds JS]: Placeholder Id',
|
|
964
|
-
'No valid placeholders for loadMore',
|
|
965
|
-
'cannot call refresh on the same page',
|
|
966
|
-
'no placeholders are currently defined in Refresh',
|
|
967
|
-
'Debugger iframe already exists',
|
|
968
|
-
`with id ${PH_PREFIX}`,
|
|
969
|
-
];
|
|
970
|
-
for (const m of ['log', 'info', 'warn', 'error']) {
|
|
971
|
-
const orig = console[m];
|
|
972
|
-
if (typeof orig !== 'function') continue;
|
|
973
|
-
console[m] = function (...a) {
|
|
974
|
-
if (typeof a[0] === 'string' && MUTED.some(p => a[0].includes(p))) return;
|
|
975
|
-
orig.apply(console, a);
|
|
976
|
-
};
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
1040
|
function ensureTcfLocator() {
|
|
981
1041
|
try {
|
|
982
1042
|
if (!window.__tcfapi && !window.__cmp) return;
|
|
@@ -998,6 +1058,7 @@ function startShowBatch(ids) {
|
|
|
998
1058
|
function warmNetwork() {
|
|
999
1059
|
const head = document.head;
|
|
1000
1060
|
if (!head) return;
|
|
1061
|
+
const frag = document.createDocumentFragment();
|
|
1001
1062
|
for (const [rel, href, cors] of [
|
|
1002
1063
|
['preconnect', 'https://g.ezoic.net', true ],
|
|
1003
1064
|
['preconnect', 'https://go.ezoic.net', true ],
|
|
@@ -1012,8 +1073,9 @@ function startShowBatch(ids) {
|
|
|
1012
1073
|
const l = document.createElement('link');
|
|
1013
1074
|
l.rel = rel; l.href = href;
|
|
1014
1075
|
if (cors) l.crossOrigin = 'anonymous';
|
|
1015
|
-
|
|
1076
|
+
frag.appendChild(l);
|
|
1016
1077
|
}
|
|
1078
|
+
head.appendChild(frag);
|
|
1017
1079
|
}
|
|
1018
1080
|
|
|
1019
1081
|
// ── Bindings ───────────────────────────────────────────────────────────────
|
|
@@ -1027,7 +1089,7 @@ function startShowBatch(ids) {
|
|
|
1027
1089
|
$(window).on('action:ajaxify.end.nbbEzoic', () => {
|
|
1028
1090
|
S.pageKey = pageKey();
|
|
1029
1091
|
blockedUntil = 0;
|
|
1030
|
-
|
|
1092
|
+
ensureTcfLocator(); warmNetwork();
|
|
1031
1093
|
patchShowAds(); getIO(); ensureDomObserver(); sweepDeadWraps(); requestBurst();
|
|
1032
1094
|
});
|
|
1033
1095
|
|
|
@@ -1075,7 +1137,6 @@ function startShowBatch(ids) {
|
|
|
1075
1137
|
// ── Boot ───────────────────────────────────────────────────────────────────
|
|
1076
1138
|
|
|
1077
1139
|
S.pageKey = pageKey();
|
|
1078
|
-
muteConsole();
|
|
1079
1140
|
ensureTcfLocator();
|
|
1080
1141
|
warmNetwork();
|
|
1081
1142
|
patchShowAds();
|