nodebb-plugin-ezoic-infinite 1.0.3 → 1.0.6
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 +79 -29
- package/package.json +12 -2
- package/plugin.json +6 -6
- package/public/admin.js +24 -30
- package/public/client.js +256 -3
- package/public/style.css +3 -0
- package/public/templates/admin/plugins/ezoic-infinite.tpl +46 -65
package/library.js
CHANGED
|
@@ -1,50 +1,100 @@
|
|
|
1
|
-
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
4
3
|
const meta = require.main.require('./src/meta');
|
|
5
4
|
const groups = require.main.require('./src/groups');
|
|
5
|
+
const db = require.main.require('./src/database');
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
Plugin.init = async ({ router, middleware }) => {
|
|
10
|
-
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
|
|
11
|
-
router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
|
|
12
|
-
};
|
|
7
|
+
const SETTINGS_KEY = 'ezoic-infinite';
|
|
8
|
+
const plugin = {};
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
function normalizeExcludedGroups(value) {
|
|
11
|
+
if (!value) return [];
|
|
12
|
+
if (Array.isArray(value)) return value;
|
|
13
|
+
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
14
|
+
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
function parseBool(v, def = false) {
|
|
17
|
+
if (v === undefined || v === null || v === '') return def;
|
|
18
|
+
if (typeof v === 'boolean') return v;
|
|
19
|
+
const s = String(v).toLowerCase();
|
|
20
|
+
return s === '1' || s === 'true' || s === 'on' || s === 'yes';
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
groupList = groupNames.map(name => ({ name }));
|
|
23
|
+
async function getAllGroups() {
|
|
24
|
+
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
25
|
+
if (!names || !names.length) {
|
|
26
|
+
names = await db.getSortedSetRange('groups:visible:createtime', 0, -1);
|
|
27
27
|
}
|
|
28
|
+
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
29
|
+
const data = await groups.getGroupsData(filtered);
|
|
30
|
+
// Sort alphabetically for ACP usability
|
|
31
|
+
data.sort((a, b) => String(a.name).localeCompare(String(b.name), 'fr', { sensitivity: 'base' }));
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
async function getSettings() {
|
|
35
|
+
const s = await meta.settings.get(SETTINGS_KEY);
|
|
36
|
+
return {
|
|
37
|
+
// Between-post ads (simple blocks)
|
|
38
|
+
enableBetweenAds: parseBool(s.enableBetweenAds, true),
|
|
39
|
+
placeholderIds: (s.placeholderIds || '').trim(),
|
|
40
|
+
intervalPosts: Math.max(1, parseInt(s.intervalPosts, 10) || 6),
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
// "Ad message" between replies (looks like a post)
|
|
43
|
+
enableMessageAds: parseBool(s.enableMessageAds, false),
|
|
44
|
+
messagePlaceholderIds: (s.messagePlaceholderIds || '').trim(),
|
|
45
|
+
messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
|
|
46
|
+
|
|
47
|
+
excludedGroups: normalizeExcludedGroups(s.excludedGroups),
|
|
48
|
+
};
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
async function isUserExcluded(uid, excludedGroups) {
|
|
52
|
+
if (!uid || !excludedGroups.length) return false;
|
|
53
|
+
const userGroups = await groups.getUserGroups([uid]);
|
|
54
|
+
return (userGroups[0] || []).some(g => excludedGroups.includes(g.name));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
plugin.addAdminNavigation = async (header) => {
|
|
36
58
|
header.plugins = header.plugins || [];
|
|
37
59
|
header.plugins.push({
|
|
38
60
|
route: '/plugins/ezoic-infinite',
|
|
39
|
-
icon: 'fa-
|
|
40
|
-
name: 'Ezoic Infinite'
|
|
61
|
+
icon: 'fa-ad',
|
|
62
|
+
name: 'Ezoic Infinite Ads'
|
|
41
63
|
});
|
|
42
64
|
return header;
|
|
43
65
|
};
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
67
|
+
plugin.init = async ({ router, middleware }) => {
|
|
68
|
+
async function render(req, res) {
|
|
69
|
+
const settings = await getSettings();
|
|
70
|
+
const allGroups = await getAllGroups();
|
|
71
|
+
|
|
72
|
+
res.render('admin/plugins/ezoic-infinite', {
|
|
73
|
+
title: 'Ezoic Infinite Ads',
|
|
74
|
+
...settings,
|
|
75
|
+
enableBetweenAds_checked: settings.enableBetweenAds ? 'checked' : '',
|
|
76
|
+
enableMessageAds_checked: settings.enableMessageAds ? 'checked' : '',
|
|
77
|
+
allGroups,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
82
|
+
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
83
|
+
|
|
84
|
+
router.get('/api/plugins/ezoic-infinite/config', middleware.buildHeader, async (req, res) => {
|
|
85
|
+
const settings = await getSettings();
|
|
86
|
+
const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
|
|
87
|
+
|
|
88
|
+
res.json({
|
|
89
|
+
excluded,
|
|
90
|
+
enableBetweenAds: settings.enableBetweenAds,
|
|
91
|
+
placeholderIds: settings.placeholderIds,
|
|
92
|
+
intervalPosts: settings.intervalPosts,
|
|
93
|
+
enableMessageAds: settings.enableMessageAds,
|
|
94
|
+
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
95
|
+
messageIntervalPosts: settings.messageIntervalPosts,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
48
98
|
};
|
|
49
99
|
|
|
50
|
-
module.exports =
|
|
100
|
+
module.exports = plugin;
|
package/package.json
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-ezoic-infinite",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Ezoic
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Ezoic ads with infinite scroll using a pool of placeholder IDs",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"nodebb",
|
|
9
|
+
"nodebb-plugin",
|
|
10
|
+
"ezoic",
|
|
11
|
+
"ads",
|
|
12
|
+
"infinite-scroll"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
7
17
|
"nbbpm": {
|
|
8
18
|
"compatibility": "^4.0.0"
|
|
9
19
|
}
|
package/plugin.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nodebb-plugin-ezoic-infinite",
|
|
3
|
-
"name": "Ezoic Infinite",
|
|
3
|
+
"name": "NodeBB Ezoic Infinite Ads",
|
|
4
|
+
"description": "Ezoic ads with infinite scroll using a pool of placeholder IDs",
|
|
4
5
|
"library": "./library.js",
|
|
5
6
|
"hooks": [
|
|
6
7
|
{
|
|
@@ -10,10 +11,6 @@
|
|
|
10
11
|
{
|
|
11
12
|
"hook": "filter:admin.header.build",
|
|
12
13
|
"method": "addAdminNavigation"
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"hook": "filter:config.get",
|
|
16
|
-
"method": "addConfig"
|
|
17
14
|
}
|
|
18
15
|
],
|
|
19
16
|
"staticDirs": {
|
|
@@ -25,5 +22,8 @@
|
|
|
25
22
|
"scripts": [
|
|
26
23
|
"public/client.js"
|
|
27
24
|
],
|
|
28
|
-
"templates": "public/templates"
|
|
25
|
+
"templates": "public/templates",
|
|
26
|
+
"css": [
|
|
27
|
+
"public/style.css"
|
|
28
|
+
]
|
|
29
29
|
}
|
package/public/admin.js
CHANGED
|
@@ -1,35 +1,29 @@
|
|
|
1
|
+
/* globals ajaxify */
|
|
2
|
+
'use strict';
|
|
1
3
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
(function () {
|
|
5
|
+
function init() {
|
|
6
|
+
const $form = $('.ezoic-infinite-settings');
|
|
7
|
+
if (!$form.length) return;
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Object.keys(data).forEach(k => {
|
|
10
|
-
const el = form.find('[name="' + k + '"]');
|
|
11
|
-
if (!el.length) return;
|
|
12
|
-
if (el.attr('type') === 'checkbox') el.prop('checked', data[k]);
|
|
13
|
-
else el.val(data[k]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
if (data.excludedGroups) {
|
|
17
|
-
data.excludedGroups.split(',').forEach(g =>
|
|
18
|
-
form.find('option[value="' + g + '"]').prop('selected', true)
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
9
|
+
require(['settings', 'alerts'], function (Settings, alerts) {
|
|
10
|
+
Settings.load('ezoic-infinite', $form);
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
form.serializeArray().forEach(o => values[o.name] = o.value);
|
|
26
|
-
values.enableBetweenAds = form.find('[name=enableBetweenAds]').is(':checked');
|
|
27
|
-
values.enableMessageAds = form.find('[name=enableMessageAds]').is(':checked');
|
|
28
|
-
values.excludedGroups = (form.find('[name=excludedGroups]').val() || []).join(',');
|
|
12
|
+
$('#save').off('click.ezoicInfinite').on('click.ezoicInfinite', function (e) {
|
|
13
|
+
e.preventDefault();
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
Settings.save('ezoic-infinite', $form, function () {
|
|
16
|
+
// Toast vert (NodeBB core)
|
|
17
|
+
if (alerts && typeof alerts.success === 'function') {
|
|
18
|
+
alerts.success('Enregistré');
|
|
19
|
+
} else if (window.app && typeof window.app.alertSuccess === 'function') {
|
|
20
|
+
window.app.alertSuccess('Enregistré');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
33
24
|
});
|
|
34
|
-
}
|
|
35
|
-
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
$(document).ready(init);
|
|
28
|
+
$(window).on('action:ajaxify.end', init);
|
|
29
|
+
})();
|
package/public/client.js
CHANGED
|
@@ -1,6 +1,259 @@
|
|
|
1
|
+
'use strict';
|
|
1
2
|
|
|
2
|
-
/*
|
|
3
|
+
/* globals ajaxify */
|
|
3
4
|
(function () {
|
|
4
|
-
if (
|
|
5
|
-
|
|
5
|
+
if (window.ezoicInfiniteLoaded) return;
|
|
6
|
+
window.ezoicInfiniteLoaded = true;
|
|
7
|
+
|
|
8
|
+
let cachedConfig;
|
|
9
|
+
let lastFetch = 0;
|
|
10
|
+
let debounceTimer;
|
|
11
|
+
|
|
12
|
+
let inFlight = false;
|
|
13
|
+
let rerunRequested = false;
|
|
14
|
+
|
|
15
|
+
// per page state
|
|
16
|
+
let pageKey = null;
|
|
17
|
+
|
|
18
|
+
// separate pools/state
|
|
19
|
+
let usedBetween = new Set(); // between topics list
|
|
20
|
+
let usedMessage = new Set(); // between replies
|
|
21
|
+
let fifoBetween = [];
|
|
22
|
+
let fifoMessage = [];
|
|
23
|
+
|
|
24
|
+
function getPageKey() {
|
|
25
|
+
try {
|
|
26
|
+
if (ajaxify && ajaxify.data) {
|
|
27
|
+
if (ajaxify.data.tid) return 'topic:' + ajaxify.data.tid;
|
|
28
|
+
if (ajaxify.data.cid) return 'cid:' + ajaxify.data.cid + ':' + window.location.pathname;
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
return window.location.pathname;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isTopicPage() {
|
|
35
|
+
try { return !!(ajaxify && ajaxify.data && ajaxify.data.tid); } catch (e) {}
|
|
36
|
+
return /^\/topic\//.test(window.location.pathname);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isCategoryTopicList() {
|
|
40
|
+
return document.querySelectorAll('li[component="category/topic"]').length > 0 && !isTopicPage();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parsePool(raw) {
|
|
44
|
+
if (!raw) return [];
|
|
45
|
+
// accept newline, comma, space, semicolon
|
|
46
|
+
const arr = String(raw).split(/[\n,;\s]+/)
|
|
47
|
+
.map(x => parseInt(x, 10))
|
|
48
|
+
.filter(n => Number.isFinite(n) && n > 0);
|
|
49
|
+
// unique while preserving order
|
|
50
|
+
return Array.from(new Set(arr));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function fetchConfig() {
|
|
54
|
+
if (cachedConfig && Date.now() - lastFetch < 10000) return cachedConfig;
|
|
55
|
+
const base = (window.config && window.config.relative_path) ? window.config.relative_path : '';
|
|
56
|
+
const res = await fetch(base + '/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
|
|
57
|
+
const json = await res.json();
|
|
58
|
+
cachedConfig = json;
|
|
59
|
+
lastFetch = Date.now();
|
|
60
|
+
return json;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function cleanupForNewPage() {
|
|
64
|
+
document.querySelectorAll('.ezoic-ad').forEach(el => el.remove());
|
|
65
|
+
usedBetween = new Set();
|
|
66
|
+
usedMessage = new Set();
|
|
67
|
+
fifoBetween = [];
|
|
68
|
+
fifoMessage = [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function destroyPlaceholder(id) {
|
|
72
|
+
try {
|
|
73
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
74
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
75
|
+
if (typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
76
|
+
window.ezstandalone.destroyPlaceholders(id);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
window.ezstandalone.cmd.push(function () {
|
|
80
|
+
try { window.ezstandalone.destroyPlaceholders(id); } catch (e) {}
|
|
81
|
+
});
|
|
82
|
+
} catch (e) {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ensureUniquePlaceholder(id) {
|
|
86
|
+
const existing = document.getElementById('ezoic-pub-ad-placeholder-' + id);
|
|
87
|
+
if (!existing) return;
|
|
88
|
+
const wrap = existing.closest('.ezoic-ad');
|
|
89
|
+
if (wrap) wrap.remove();
|
|
90
|
+
else existing.remove();
|
|
91
|
+
destroyPlaceholder(id);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function callShowAdsSingle(id) {
|
|
95
|
+
if (!id) return;
|
|
96
|
+
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
window.__ezoicLastSingle = window.__ezoicLastSingle || {};
|
|
99
|
+
const last = window.__ezoicLastSingle[id] || 0;
|
|
100
|
+
if (now - last < 1200) return;
|
|
101
|
+
window.__ezoicLastSingle[id] = now;
|
|
102
|
+
|
|
103
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
104
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
105
|
+
|
|
106
|
+
const run = function () {
|
|
107
|
+
try {
|
|
108
|
+
if (typeof window.ezstandalone.showAds === 'function') {
|
|
109
|
+
window.ezstandalone.showAds(id);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
window.ezstandalone.cmd.push(function () { run(); });
|
|
117
|
+
|
|
118
|
+
let tries = 0;
|
|
119
|
+
(function tick() {
|
|
120
|
+
tries++;
|
|
121
|
+
if (run() || tries >= 8) return;
|
|
122
|
+
setTimeout(tick, 800);
|
|
123
|
+
})();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function pickNextId(pool, usedSet) {
|
|
127
|
+
for (const id of pool) if (!usedSet.has(id)) return id;
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function recycle(fifo, usedSet, selector) {
|
|
132
|
+
fifo.sort((a, b) => a.after - b.after);
|
|
133
|
+
while (fifo.length) {
|
|
134
|
+
const old = fifo.shift();
|
|
135
|
+
const el = document.querySelector(selector(old));
|
|
136
|
+
if (!el) continue;
|
|
137
|
+
el.remove();
|
|
138
|
+
usedSet.delete(old.id);
|
|
139
|
+
destroyPlaceholder(old.id);
|
|
140
|
+
return old.id;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function insertAfter(targetEl, id, cls, afterVal) {
|
|
146
|
+
ensureUniquePlaceholder(id);
|
|
147
|
+
const wrap = document.createElement('div');
|
|
148
|
+
wrap.className = 'ezoic-ad ' + cls;
|
|
149
|
+
wrap.setAttribute('data-ezoic-id', String(id));
|
|
150
|
+
wrap.setAttribute('data-ezoic-after', String(afterVal));
|
|
151
|
+
wrap.innerHTML = '<div class="ezoic-ad-inner"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
152
|
+
targetEl.insertAdjacentElement('afterend', wrap);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function injectBetweenTopics(cfg) {
|
|
156
|
+
if (!cfg.enableBetweenAds) return;
|
|
157
|
+
const interval = Math.max(1, parseInt(cfg.intervalPosts, 10) || 6);
|
|
158
|
+
const pool = parsePool(cfg.placeholderIds);
|
|
159
|
+
if (!pool.length) return;
|
|
160
|
+
|
|
161
|
+
const items = Array.from(document.querySelectorAll('li[component="category/topic"]'));
|
|
162
|
+
if (!items.length) return;
|
|
163
|
+
|
|
164
|
+
items.forEach((li, idx) => {
|
|
165
|
+
const pos = idx + 1;
|
|
166
|
+
if (pos % interval !== 0) return;
|
|
167
|
+
if (idx === items.length - 1) return;
|
|
168
|
+
|
|
169
|
+
const next = li.nextElementSibling;
|
|
170
|
+
if (next && next.classList && next.classList.contains('ezoic-ad-between')) return;
|
|
171
|
+
|
|
172
|
+
let id = pickNextId(pool, usedBetween);
|
|
173
|
+
if (!id) {
|
|
174
|
+
id = recycle(fifoBetween, usedBetween, (old) => '.ezoic-ad-between[data-ezoic-id="' + old.id + '"][data-ezoic-after="' + old.after + '"]');
|
|
175
|
+
if (!id) return; // pool empty and nothing recyclable => stop
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
usedBetween.add(id);
|
|
179
|
+
fifoBetween.push({ id, after: pos });
|
|
180
|
+
|
|
181
|
+
insertAfter(li, id, 'ezoic-ad-between', pos);
|
|
182
|
+
callShowAdsSingle(id);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function injectBetweenMessages(cfg) {
|
|
187
|
+
if (!cfg.enableMessageAds) return;
|
|
188
|
+
const interval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
|
|
189
|
+
const pool = parsePool(cfg.messagePlaceholderIds);
|
|
190
|
+
if (!pool.length) return;
|
|
191
|
+
|
|
192
|
+
const posts = Array.from(document.querySelectorAll('[component="post"][data-pid]'));
|
|
193
|
+
if (!posts.length) return;
|
|
194
|
+
|
|
195
|
+
posts.forEach((post, idx) => {
|
|
196
|
+
const no = idx + 1;
|
|
197
|
+
if (no % interval !== 0) return;
|
|
198
|
+
if (idx === posts.length - 1) return;
|
|
199
|
+
|
|
200
|
+
const next = post.nextElementSibling;
|
|
201
|
+
if (next && next.classList && next.classList.contains('ezoic-ad-message')) return;
|
|
202
|
+
|
|
203
|
+
let id = pickNextId(pool, usedMessage);
|
|
204
|
+
if (!id) {
|
|
205
|
+
id = recycle(fifoMessage, usedMessage, (old) => '.ezoic-ad-message[data-ezoic-id="' + old.id + '"][data-ezoic-after="' + old.after + '"]');
|
|
206
|
+
if (!id) return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
usedMessage.add(id);
|
|
210
|
+
fifoMessage.push({ id, after: no });
|
|
211
|
+
|
|
212
|
+
insertAfter(post, id, 'ezoic-ad-message', no);
|
|
213
|
+
callShowAdsSingle(id);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function run() {
|
|
218
|
+
if (inFlight) { rerunRequested = true; return; }
|
|
219
|
+
inFlight = true;
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
const key = getPageKey();
|
|
223
|
+
if (pageKey !== key) {
|
|
224
|
+
pageKey = key;
|
|
225
|
+
cleanupForNewPage();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const cfg = await fetchConfig();
|
|
229
|
+
if (!cfg || cfg.excluded) return;
|
|
230
|
+
|
|
231
|
+
if (isTopicPage()) injectBetweenMessages(cfg);
|
|
232
|
+
else if (isCategoryTopicList()) injectBetweenTopics(cfg);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
// silent
|
|
235
|
+
} finally {
|
|
236
|
+
inFlight = false;
|
|
237
|
+
if (rerunRequested) {
|
|
238
|
+
rerunRequested = false;
|
|
239
|
+
setTimeout(run, 50);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function scheduleRun() {
|
|
245
|
+
clearTimeout(debounceTimer);
|
|
246
|
+
debounceTimer = setTimeout(run, 150);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
250
|
+
run();
|
|
251
|
+
setTimeout(run, 1200);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
window.addEventListener('action:ajaxify.end', scheduleRun);
|
|
255
|
+
window.addEventListener('action:posts.loaded', scheduleRun);
|
|
256
|
+
window.addEventListener('action:topic.loaded', scheduleRun);
|
|
257
|
+
window.addEventListener('action:topics.loaded', scheduleRun);
|
|
258
|
+
window.addEventListener('action:category.loaded', scheduleRun);
|
|
6
259
|
})();
|
package/public/style.css
ADDED
|
@@ -1,78 +1,59 @@
|
|
|
1
|
-
<div class="
|
|
2
|
-
<
|
|
3
|
-
<div class="card">
|
|
4
|
-
<div class="card-header">
|
|
5
|
-
<strong>Ezoic Infinite</strong>
|
|
6
|
-
</div>
|
|
7
|
-
<div class="card-body">
|
|
8
|
-
<form class="ezoic-infinite-settings">
|
|
9
|
-
<div class="mb-3">
|
|
10
|
-
<label class="form-label">Groupes exclus</label>
|
|
11
|
-
<select multiple class="form-control" name="excludedGroups" size="8">
|
|
12
|
-
<!-- BEGIN groups -->
|
|
13
|
-
<option value="{groups.name}">{groups.name}</option>
|
|
14
|
-
<!-- END groups -->
|
|
15
|
-
</select>
|
|
16
|
-
<p class="form-text">Aucune publicité ne sera affichée pour ces groupes.</p>
|
|
17
|
-
</div>
|
|
1
|
+
<div class="acp-page-container">
|
|
2
|
+
<h2>Ezoic - Publicités Infinite Scroll Ads</h2>
|
|
18
3
|
|
|
19
|
-
|
|
4
|
+
<form class="ezoic-infinite-settings" role="form">
|
|
5
|
+
<h4 class="mt-3">Pubs entre les posts (bloc simple)</h4>
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<label class="form-check-label" for="enableBetweenAds">Activer</label>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<div class="mb-3">
|
|
29
|
-
<label class="form-label">Intervalle (après chaque N topics)</label>
|
|
30
|
-
<input type="number" class="form-control" name="intervalTopics" min="1" step="1">
|
|
31
|
-
</div>
|
|
7
|
+
<div class="form-check mb-3">
|
|
8
|
+
<input class="form-check-input" type="checkbox" id="enableBetweenAds" name="enableBetweenAds" {enableBetweenAds_checked}>
|
|
9
|
+
<label class="form-check-label" for="enableBetweenAds">Activer les pubs entre les posts</label>
|
|
10
|
+
</div>
|
|
32
11
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
12
|
+
<div class="mb-3">
|
|
13
|
+
<label class="form-label" for="placeholderIds">Pool d’IDs Ezoic (entre posts)</label>
|
|
14
|
+
<textarea id="placeholderIds" name="placeholderIds" class="form-control" rows="4">{placeholderIds}</textarea>
|
|
15
|
+
<p class="form-text">Un ID par ligne (ou séparé par virgules/espaces). Le nombre d’IDs = nombre max de pubs simultanées.</p>
|
|
16
|
+
</div>
|
|
37
17
|
|
|
38
|
-
|
|
18
|
+
<div class="mb-3">
|
|
19
|
+
<label class="form-label" for="intervalPosts">Afficher une pub tous les N posts</label>
|
|
20
|
+
<input type="number" id="intervalPosts" name="intervalPosts" class="form-control" value="{intervalPosts}" min="1">
|
|
21
|
+
</div>
|
|
39
22
|
|
|
40
|
-
|
|
23
|
+
<hr/>
|
|
41
24
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<label class="form-check-label" for="enableMessageAds">Activer</label>
|
|
45
|
-
</div>
|
|
25
|
+
<h4 class="mt-3">Pubs “message” entre les réponses</h4>
|
|
26
|
+
<p class="form-text">Insère un bloc qui ressemble à un post, toutes les N réponses (dans une page topic).</p>
|
|
46
27
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
28
|
+
<div class="form-check mb-3">
|
|
29
|
+
<input class="form-check-input" type="checkbox" id="enableMessageAds" name="enableMessageAds" {enableMessageAds_checked}>
|
|
30
|
+
<label class="form-check-label" for="enableMessageAds">Activer les pubs “message”</label>
|
|
31
|
+
</div>
|
|
51
32
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
<div class="mb-3">
|
|
34
|
+
<label class="form-label" for="messagePlaceholderIds">Pool d’IDs Ezoic (message)</label>
|
|
35
|
+
<textarea id="messagePlaceholderIds" name="messagePlaceholderIds" class="form-control" rows="4">{messagePlaceholderIds}</textarea>
|
|
36
|
+
<p class="form-text">Pool séparé recommandé pour éviter la réutilisation d’IDs. IMPORTANT : ne réutilise pas les mêmes IDs dans les deux pools.</p>
|
|
37
|
+
</div>
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</form>
|
|
61
|
-
</div>
|
|
39
|
+
<div class="mb-3">
|
|
40
|
+
<label class="form-label" for="messageIntervalPosts">Afficher un “message pub” tous les N messages</label>
|
|
41
|
+
<input type="number" id="messageIntervalPosts" name="messageIntervalPosts" class="form-control" value="{messageIntervalPosts}" min="1">
|
|
62
42
|
</div>
|
|
63
|
-
</div>
|
|
64
43
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
</
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
44
|
+
<hr/>
|
|
45
|
+
|
|
46
|
+
<h4 class="mt-3">Exclusions</h4>
|
|
47
|
+
<div class="mb-3">
|
|
48
|
+
<label class="form-label" for="excludedGroups">Groupes exclus</label>
|
|
49
|
+
<select id="excludedGroups" name="excludedGroups" class="form-select" multiple>
|
|
50
|
+
<!-- BEGIN allGroups -->
|
|
51
|
+
<option value="{allGroups.name}">{allGroups.name}</option>
|
|
52
|
+
<!-- END allGroups -->
|
|
53
|
+
</select>
|
|
54
|
+
<p class="form-text">Si l’utilisateur appartient à un de ces groupes, aucune pub n’est injectée.</p>
|
|
76
55
|
</div>
|
|
77
|
-
|
|
56
|
+
|
|
57
|
+
<button id="save" class="btn btn-primary">Enregistrer</button>
|
|
58
|
+
</form>
|
|
78
59
|
</div>
|