nodebb-plugin-ezoic-infinite 0.9.9 → 0.9.11
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 +16 -79
- package/package.json +2 -5
- package/plugin.json +5 -2
- package/public/admin.js +2 -7
- package/public/client.js +304 -212
- package/public/style.css +5 -0
- package/public/templates/admin/plugins/ezoic-infinite.tpl +31 -35
package/library.js
CHANGED
|
@@ -1,96 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const meta = require.main.require('./src/meta');
|
|
4
|
-
const db = require.main.require('./src/database');
|
|
5
4
|
|
|
6
|
-
const
|
|
7
|
-
const plugin = {};
|
|
5
|
+
const Plugin = {};
|
|
8
6
|
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
if (Array.isArray(value)) return value;
|
|
12
|
-
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function parseBool(v, def = false) {
|
|
16
|
-
if (v === undefined || v === null || v === '') return def;
|
|
17
|
-
if (typeof v === 'boolean') return v;
|
|
18
|
-
const s = String(v).toLowerCase();
|
|
19
|
-
return s === '1' || s === 'true' || s === 'on' || s === 'yes';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function getAllGroups() {
|
|
23
|
-
const names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
24
|
-
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
25
|
-
const data = await groups.getGroupsData(filtered);
|
|
26
|
-
// Sort alphabetically for ACP usability
|
|
27
|
-
data.sort((a, b) => String(a.name).localeCompare(String(b.name), 'fr', { sensitivity: 'base' }));
|
|
28
|
-
return data;
|
|
29
|
-
}
|
|
30
|
-
async function getSettings() {
|
|
31
|
-
const s = await meta.settings.get(SETTINGS_KEY);
|
|
32
|
-
return {
|
|
33
|
-
// Between-post ads (simple blocks)
|
|
34
|
-
enableBetweenAds: parseBool(s.enableBetweenAds, true),
|
|
35
|
-
placeholderIds: (s.placeholderIds || '').trim(),
|
|
36
|
-
intervalPosts: Math.max(1, parseInt(s.intervalPosts, 10) || 6),
|
|
7
|
+
Plugin.init = async function (params) {
|
|
8
|
+
const { router, middleware } = params;
|
|
37
9
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
|
|
10
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
|
|
11
|
+
router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
|
|
12
|
+
};
|
|
42
13
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
14
|
+
async function renderAdmin(req, res) {
|
|
15
|
+
const settings = await meta.settings.get('ezoic-infinite');
|
|
46
16
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
17
|
+
res.render('admin/plugins/ezoic-infinite', {
|
|
18
|
+
title: 'Ezoic - Publicités Infinite Scroll',
|
|
19
|
+
settings,
|
|
20
|
+
});
|
|
51
21
|
}
|
|
52
22
|
|
|
53
|
-
|
|
23
|
+
Plugin.addAdminNavigation = async function (header) {
|
|
54
24
|
header.plugins = header.plugins || [];
|
|
55
25
|
header.plugins.push({
|
|
56
26
|
route: '/plugins/ezoic-infinite',
|
|
57
|
-
icon: 'fa-
|
|
58
|
-
name: 'Ezoic Infinite
|
|
27
|
+
icon: 'fa-bullhorn',
|
|
28
|
+
name: 'Ezoic Infinite',
|
|
59
29
|
});
|
|
60
30
|
return header;
|
|
61
31
|
};
|
|
62
32
|
|
|
63
|
-
|
|
64
|
-
async function render(req, res) {
|
|
65
|
-
const settings = await getSettings();
|
|
66
|
-
const allGroups = await getAllGroups();
|
|
67
|
-
|
|
68
|
-
res.render('admin/plugins/ezoic-infinite', {
|
|
69
|
-
title: 'Ezoic Infinite Ads',
|
|
70
|
-
...settings,
|
|
71
|
-
enableBetweenAds_checked: settings.enableBetweenAds ? 'checked' : '',
|
|
72
|
-
enableMessageAds_checked: settings.enableMessageAds ? 'checked' : '',
|
|
73
|
-
allGroups,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
78
|
-
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
79
|
-
|
|
80
|
-
router.get('/api/plugins/ezoic-infinite/config', middleware.buildHeader, async (req, res) => {
|
|
81
|
-
const settings = await getSettings();
|
|
82
|
-
const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
|
|
83
|
-
|
|
84
|
-
res.json({
|
|
85
|
-
excluded,
|
|
86
|
-
enableBetweenAds: settings.enableBetweenAds,
|
|
87
|
-
placeholderIds: settings.placeholderIds,
|
|
88
|
-
intervalPosts: settings.intervalPosts,
|
|
89
|
-
enableMessageAds: settings.enableMessageAds,
|
|
90
|
-
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
91
|
-
messageIntervalPosts: settings.messageIntervalPosts,
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
module.exports = plugin;
|
|
33
|
+
module.exports = Plugin;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-ezoic-infinite",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "Ezoic
|
|
3
|
+
"version": "0.9.11",
|
|
4
|
+
"description": "Injection de publicités Ezoic entre les topics et entre les messages avec infinite scroll (NodeBB 4.x).",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -11,9 +11,6 @@
|
|
|
11
11
|
"ads",
|
|
12
12
|
"infinite-scroll"
|
|
13
13
|
],
|
|
14
|
-
"engines": {
|
|
15
|
-
"node": ">=18"
|
|
16
|
-
},
|
|
17
14
|
"nbbpm": {
|
|
18
15
|
"compatibility": "^4.0.0"
|
|
19
16
|
}
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nodebb-plugin-ezoic-infinite",
|
|
3
|
-
"name": "
|
|
4
|
-
"description": "Ezoic ads with infinite scroll
|
|
3
|
+
"name": "Ezoic Infinite",
|
|
4
|
+
"description": "Ezoic ads injection with infinite scroll (topics list + topic posts)",
|
|
5
5
|
"library": "./library.js",
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
@@ -22,5 +22,8 @@
|
|
|
22
22
|
"scripts": [
|
|
23
23
|
"public/client.js"
|
|
24
24
|
],
|
|
25
|
+
"css": [
|
|
26
|
+
"public/style.css"
|
|
27
|
+
],
|
|
25
28
|
"templates": "public/templates"
|
|
26
29
|
}
|
package/public/admin.js
CHANGED
|
@@ -19,7 +19,7 @@ $(document).ready(function () {
|
|
|
19
19
|
form.find('[name="messageIntervalPosts"]').val(parseInt(data.messageIntervalPosts, 10) || 3);
|
|
20
20
|
form.find('[name="messagePlaceholderIds"]').val(data.messagePlaceholderIds || '');
|
|
21
21
|
|
|
22
|
-
// NodeBB 4.x
|
|
22
|
+
// Load groups dynamically (NodeBB 4.x)
|
|
23
23
|
socket.emit('groups.getGroups', {}, function (err2, groups) {
|
|
24
24
|
const $select = form.find('[name="excludedGroups"]');
|
|
25
25
|
$select.empty();
|
|
@@ -65,11 +65,6 @@ $(document).ready(function () {
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
$(
|
|
69
|
-
.on('click.ezoicInfiniteSave', '.ezoic-infinite-save', function (e) {
|
|
70
|
-
e.preventDefault();
|
|
71
|
-
save();
|
|
72
|
-
});
|
|
73
|
-
|
|
68
|
+
$('.ezoic-infinite-save').on('click', save);
|
|
74
69
|
load();
|
|
75
70
|
});
|
package/public/client.js
CHANGED
|
@@ -1,252 +1,344 @@
|
|
|
1
|
+
/* global $, ajaxify, app, socket */
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
window.ezoicInfiniteLoaded
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
4
|
+
(function () {
|
|
5
|
+
if (window.ezoicInfiniteLoaded) return;
|
|
6
|
+
window.ezoicInfiniteLoaded = true;
|
|
7
|
+
|
|
8
|
+
const SETTINGS_NS = 'ezoic-infinite';
|
|
9
|
+
|
|
10
|
+
let settings = null;
|
|
11
|
+
let pageKey = null;
|
|
12
|
+
|
|
13
|
+
// State per page
|
|
14
|
+
let usedTopic = new Set(); // ids currently in DOM (topic)
|
|
15
|
+
let usedCat = new Set(); // ids currently in DOM (category)
|
|
16
|
+
let fifoTopic = []; // [{id, afterNo}]
|
|
17
|
+
let fifoCat = []; // [{id, afterPos}]
|
|
18
|
+
|
|
19
|
+
// Refresh single-flight
|
|
20
|
+
let refreshInFlight = false;
|
|
21
|
+
let refreshQueued = false;
|
|
22
|
+
|
|
23
|
+
function parsePool(text) {
|
|
24
|
+
return String(text || '')
|
|
25
|
+
.split(/\r?\n/)
|
|
26
|
+
.map(s => s.trim())
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.map(s => parseInt(s, 10))
|
|
29
|
+
.filter(n => Number.isFinite(n) && n > 0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function userExcluded() {
|
|
33
|
+
try {
|
|
34
|
+
const raw = (settings && settings.excludedGroups) ? String(settings.excludedGroups) : '';
|
|
35
|
+
if (!raw) return false;
|
|
36
|
+
const excluded = raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
37
|
+
if (!excluded.length) return false;
|
|
38
|
+
const myGroups = (app.user && app.user.groups) ? app.user.groups : [];
|
|
39
|
+
return excluded.some(g => myGroups.includes(g));
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return false;
|
|
30
42
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
function parsePool(raw) {
|
|
36
|
-
if (!raw) return [];
|
|
37
|
-
return Array.from(new Set(
|
|
38
|
-
String(raw).split(/[\n,;\s]+/)
|
|
39
|
-
.map(x => parseInt(x, 10))
|
|
40
|
-
.filter(n => Number.isFinite(n) && n > 0)
|
|
41
|
-
));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function fetchConfig() {
|
|
45
|
-
if (cachedConfig && Date.now() - lastFetch < 10000) return cachedConfig;
|
|
46
|
-
const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
|
|
47
|
-
cachedConfig = await res.json();
|
|
48
|
-
lastFetch = Date.now();
|
|
49
|
-
return cachedConfig;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function isTopicPage() {
|
|
53
|
-
return $('[component="post/content"]').length > 0 || $('[component="post"][data-pid]').length > 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function isCategoryTopicListPage() {
|
|
57
|
-
return $('li[component="category/topic"]').length > 0;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getTopicPosts() {
|
|
61
|
-
const $primary = $('[component="post"][data-pid]');
|
|
62
|
-
if ($primary.length) return $primary.not('.ezoic-ad-post');
|
|
63
|
-
|
|
64
|
-
return $('[data-pid]').filter(function () {
|
|
65
|
-
const $el = $(this);
|
|
66
|
-
const hasContent = $el.find('[component="post/content"]').length > 0;
|
|
67
|
-
const nested = $el.parents('[data-pid]').length > 0;
|
|
68
|
-
return hasContent && !nested;
|
|
69
|
-
}).not('.ezoic-ad-post');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getCategoryTopicItems() {
|
|
73
|
-
return $('li[component="category/topic"]').not('.ezoic-ad-topic');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function tagName($el) {
|
|
77
|
-
return ($el && $el.length ? (($el.prop('tagName') || '').toUpperCase()) : '');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function makeWrapperLike($target, classes, innerHtml, attrs) {
|
|
81
|
-
const t = tagName($target);
|
|
82
|
-
const attrStr = attrs ? ' ' + attrs : '';
|
|
83
|
-
if (t === 'LI') {
|
|
84
|
-
return '<li class="' + classes + ' list-unstyled"' + attrStr + '>' + innerHtml + '</li>';
|
|
85
|
-
}
|
|
86
|
-
return '<div class="' + classes + '"' + attrStr + '>' + innerHtml + '</div>';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function pickNextId(pool) {
|
|
90
|
-
for (const id of pool) {
|
|
91
|
-
if (!usedIds.has(id)) return id;
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function callEzoic(ids) {
|
|
97
|
-
if (!ids || !ids.length) return;
|
|
98
|
-
|
|
99
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
100
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
101
|
-
|
|
102
|
-
const run = function () {
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getPageKey() {
|
|
103
46
|
try {
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
return
|
|
47
|
+
if (ajaxify && ajaxify.data) {
|
|
48
|
+
if (ajaxify.data.tid) return 'topic:' + ajaxify.data.tid;
|
|
49
|
+
if (ajaxify.data.cid) return 'cid:' + ajaxify.data.cid + ':' + window.location.pathname;
|
|
107
50
|
}
|
|
108
51
|
} catch (e) {}
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const index = slot * interval - 1;
|
|
134
|
-
const $target = $items.eq(index);
|
|
135
|
-
if (!$target.length) continue;
|
|
136
|
-
|
|
137
|
-
const id = pickNextId(pool);
|
|
138
|
-
if (!id) {
|
|
139
|
-
// pool exhausted: stop injecting further to avoid reusing ids and "jumping"
|
|
140
|
-
break;
|
|
52
|
+
return window.location.pathname;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isTopicPage() {
|
|
56
|
+
try { if (ajaxify && ajaxify.data && ajaxify.data.tid) return true; } catch (e) {}
|
|
57
|
+
return /^\/topic\//.test(window.location.pathname);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isCategoryTopicList() {
|
|
61
|
+
return $('li[component="category/topic"]').length > 0 && !isTopicPage();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function cleanupForNewPage() {
|
|
65
|
+
$('.ezoic-ad').remove();
|
|
66
|
+
usedTopic = new Set();
|
|
67
|
+
usedCat = new Set();
|
|
68
|
+
fifoTopic = [];
|
|
69
|
+
fifoCat = [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pickNextId(pool, usedSet) {
|
|
73
|
+
for (const id of pool) {
|
|
74
|
+
if (!usedSet.has(id)) return id;
|
|
141
75
|
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
142
78
|
|
|
143
|
-
|
|
144
|
-
|
|
79
|
+
function destroyPlaceholder(id) {
|
|
80
|
+
try {
|
|
81
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
82
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
83
|
+
if (typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
84
|
+
window.ezstandalone.destroyPlaceholders(id);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
window.ezstandalone.cmd.push(function () {
|
|
88
|
+
try { window.ezstandalone.destroyPlaceholders(id); } catch (e) {}
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {}
|
|
91
|
+
}
|
|
145
92
|
|
|
146
|
-
|
|
93
|
+
function ensureUniquePlaceholder(id) {
|
|
94
|
+
const existing = document.getElementById('ezoic-pub-ad-placeholder-' + id);
|
|
95
|
+
if (!existing) return;
|
|
147
96
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
97
|
+
const wrap = existing.closest('.ezoic-ad');
|
|
98
|
+
if (wrap) {
|
|
99
|
+
try { $(wrap).remove(); } catch (e) { wrap.remove(); }
|
|
100
|
+
} else {
|
|
101
|
+
existing.remove();
|
|
102
|
+
}
|
|
103
|
+
destroyPlaceholder(id);
|
|
151
104
|
}
|
|
152
105
|
|
|
153
|
-
|
|
154
|
-
|
|
106
|
+
// IMPORTANT: showAds is called ONLY for a single newly injected placeholder id (never a batch)
|
|
107
|
+
function callEzoicSingle(id) {
|
|
108
|
+
if (!id) return;
|
|
155
109
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
window.__ezoicLastSingle = window.__ezoicLastSingle || {};
|
|
112
|
+
const last = window.__ezoicLastSingle[id] || 0;
|
|
113
|
+
if (now - last < 800) return;
|
|
114
|
+
window.__ezoicLastSingle[id] = now;
|
|
160
115
|
|
|
161
|
-
|
|
116
|
+
try {
|
|
117
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
118
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
119
|
+
|
|
120
|
+
const run = function () {
|
|
121
|
+
try {
|
|
122
|
+
if (typeof window.ezstandalone.showAds === 'function') {
|
|
123
|
+
window.ezstandalone.showAds(id);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {}
|
|
127
|
+
return false;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
window.ezstandalone.cmd.push(function () { run(); });
|
|
131
|
+
|
|
132
|
+
// Retry a few times if ez loads late
|
|
133
|
+
let tries = 0;
|
|
134
|
+
const tick = function () {
|
|
135
|
+
tries++;
|
|
136
|
+
if (run() || tries >= 8) return;
|
|
137
|
+
setTimeout(tick, 800);
|
|
138
|
+
};
|
|
139
|
+
setTimeout(tick, 800);
|
|
140
|
+
} catch (e) {}
|
|
141
|
+
}
|
|
162
142
|
|
|
163
|
-
|
|
164
|
-
|
|
143
|
+
// Auto height: wrapper visible only when placeholder gets children
|
|
144
|
+
function setupAutoHeightOnce() {
|
|
145
|
+
if (window.__ezoicAutoHeight) return;
|
|
146
|
+
window.__ezoicAutoHeight = true;
|
|
165
147
|
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
148
|
+
const mark = function (wrap) {
|
|
149
|
+
if (!wrap) return;
|
|
150
|
+
const ph = wrap.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
|
|
151
|
+
if (ph && ph.children && ph.children.length) {
|
|
152
|
+
wrap.classList.add('ezoic-filled');
|
|
153
|
+
}
|
|
154
|
+
};
|
|
169
155
|
|
|
170
|
-
const
|
|
171
|
-
|
|
156
|
+
const scan = function () {
|
|
157
|
+
document.querySelectorAll('.ezoic-ad').forEach(mark);
|
|
158
|
+
};
|
|
172
159
|
|
|
173
|
-
|
|
174
|
-
|
|
160
|
+
scan();
|
|
161
|
+
setInterval(scan, 1000);
|
|
175
162
|
|
|
176
|
-
|
|
163
|
+
try {
|
|
164
|
+
const mo = new MutationObserver(scan);
|
|
165
|
+
mo.observe(document.body, { childList: true, subtree: true });
|
|
166
|
+
} catch (e) {}
|
|
167
|
+
}
|
|
177
168
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
169
|
+
function insertAfter($target, id, cls, afterVal) {
|
|
170
|
+
ensureUniquePlaceholder(id);
|
|
171
|
+
const wrap = $(
|
|
172
|
+
'<div class="ezoic-ad ' + cls + '" data-ezoic-id="' + id + '" data-ezoic-after="' + afterVal + '">' +
|
|
173
|
+
'<div class="ezoic-ad-inner"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>' +
|
|
174
|
+
'</div>'
|
|
175
|
+
);
|
|
176
|
+
$target.after(wrap);
|
|
177
|
+
return wrap;
|
|
181
178
|
}
|
|
182
179
|
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
function recycleTopic($posts) {
|
|
181
|
+
fifoTopic.sort((a, b) => a.afterNo - b.afterNo);
|
|
182
|
+
while (fifoTopic.length) {
|
|
183
|
+
const old = fifoTopic.shift();
|
|
184
|
+
const sel = '.ezoic-ad-topic[data-ezoic-id="' + old.id + '"][data-ezoic-after="' + old.afterNo + '"]';
|
|
185
|
+
const $el = $(sel);
|
|
186
|
+
if (!$el.length) continue;
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const $last = $posts.last();
|
|
190
|
+
if ($last.length && $el.prev().is($last)) {
|
|
191
|
+
fifoTopic.push(old);
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
} catch (e) {}
|
|
195
|
+
|
|
196
|
+
$el.remove();
|
|
197
|
+
usedTopic.delete(old.id);
|
|
198
|
+
destroyPlaceholder(old.id);
|
|
199
|
+
return old.id;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
185
203
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
204
|
+
function recycleCat($items) {
|
|
205
|
+
fifoCat.sort((a, b) => a.afterPos - b.afterPos);
|
|
206
|
+
while (fifoCat.length) {
|
|
207
|
+
const old = fifoCat.shift();
|
|
208
|
+
const sel = '.ezoic-ad-between[data-ezoic-id="' + old.id + '"][data-ezoic-after="' + old.afterPos + '"]';
|
|
209
|
+
const $el = $(sel);
|
|
210
|
+
if (!$el.length) continue;
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const $last = $items.last();
|
|
214
|
+
if ($last.length && $el.prev().is($last)) {
|
|
215
|
+
fifoCat.push(old);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
} catch (e) {}
|
|
219
|
+
|
|
220
|
+
$el.remove();
|
|
221
|
+
usedCat.delete(old.id);
|
|
222
|
+
destroyPlaceholder(old.id);
|
|
223
|
+
return old.id;
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
194
226
|
}
|
|
195
227
|
|
|
196
|
-
|
|
197
|
-
|
|
228
|
+
function injectInTopic() {
|
|
229
|
+
if (!(settings && (settings.enableMessageAds === true || settings.enableMessageAds === 'on'))) return;
|
|
230
|
+
|
|
231
|
+
const interval = parseInt(settings.messageIntervalPosts, 10) || 3;
|
|
232
|
+
const pool = parsePool(settings.messagePlaceholderIds);
|
|
233
|
+
if (!pool.length) return;
|
|
198
234
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (!cfg || cfg.excluded) return;
|
|
235
|
+
const $posts = $('[component="post"][data-pid]');
|
|
236
|
+
if (!$posts.length) return;
|
|
202
237
|
|
|
203
|
-
|
|
204
|
-
|
|
238
|
+
$posts.each(function (idx) {
|
|
239
|
+
const postNo = idx + 1;
|
|
240
|
+
if (postNo % interval !== 0) return;
|
|
241
|
+
if (idx === $posts.length - 1) return;
|
|
205
242
|
|
|
206
|
-
|
|
207
|
-
|
|
243
|
+
const $post = $(this);
|
|
244
|
+
const existing = $post.next('.ezoic-ad-topic');
|
|
245
|
+
if (existing.length) return;
|
|
208
246
|
|
|
209
|
-
|
|
210
|
-
|
|
247
|
+
let id = pickNextId(pool, usedTopic);
|
|
248
|
+
if (!id) {
|
|
249
|
+
id = recycleTopic($posts);
|
|
250
|
+
if (!id) return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
usedTopic.add(id);
|
|
254
|
+
fifoTopic.push({ id, afterNo: postNo });
|
|
255
|
+
|
|
256
|
+
insertAfter($post, id, 'ezoic-ad-topic', postNo);
|
|
257
|
+
callEzoicSingle(id);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
211
260
|
|
|
212
|
-
|
|
213
|
-
|
|
261
|
+
function injectInCategory() {
|
|
262
|
+
if (!(settings && (settings.enableBetweenAds === true || settings.enableBetweenAds === 'on'))) return;
|
|
214
263
|
|
|
215
|
-
|
|
264
|
+
const interval = parseInt(settings.intervalTopics, 10) || 6;
|
|
265
|
+
const pool = parsePool(settings.placeholderIds);
|
|
266
|
+
if (!pool.length) return;
|
|
216
267
|
|
|
217
|
-
const
|
|
268
|
+
const $items = $('li[component="category/topic"]');
|
|
269
|
+
if (!$items.length) return;
|
|
218
270
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
271
|
+
$items.each(function (idx) {
|
|
272
|
+
const pos = idx + 1;
|
|
273
|
+
if (pos % interval !== 0) return;
|
|
274
|
+
if (idx === $items.length - 1) return;
|
|
275
|
+
|
|
276
|
+
const $li = $(this);
|
|
277
|
+
const existing = $li.next('.ezoic-ad-between');
|
|
278
|
+
if (existing.length) return;
|
|
279
|
+
|
|
280
|
+
let id = pickNextId(pool, usedCat);
|
|
281
|
+
if (!id) {
|
|
282
|
+
id = recycleCat($items);
|
|
283
|
+
if (!id) return;
|
|
225
284
|
}
|
|
226
|
-
callEzoic(newIds);
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
285
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
286
|
+
usedCat.add(id);
|
|
287
|
+
fifoCat.push({ id, afterPos: pos });
|
|
288
|
+
|
|
289
|
+
insertAfter($li, id, 'ezoic-ad-between', pos);
|
|
290
|
+
callEzoicSingle(id);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function refresh() {
|
|
295
|
+
if (!settings) return;
|
|
296
|
+
if (userExcluded()) return;
|
|
297
|
+
|
|
298
|
+
if (refreshInFlight) { refreshQueued = true; return; }
|
|
299
|
+
refreshInFlight = true;
|
|
300
|
+
try {
|
|
301
|
+
const key = getPageKey();
|
|
302
|
+
if (pageKey !== key) {
|
|
303
|
+
pageKey = key;
|
|
304
|
+
cleanupForNewPage();
|
|
233
305
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
306
|
+
|
|
307
|
+
setupAutoHeightOnce();
|
|
308
|
+
|
|
309
|
+
if (isTopicPage()) injectInTopic();
|
|
310
|
+
else if (isCategoryTopicList()) injectInCategory();
|
|
311
|
+
} finally {
|
|
312
|
+
refreshInFlight = false;
|
|
313
|
+
if (refreshQueued) { refreshQueued = false; setTimeout(refresh, 50); }
|
|
241
314
|
}
|
|
242
315
|
}
|
|
243
|
-
}
|
|
244
316
|
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
317
|
+
function loadSettings(cb) {
|
|
318
|
+
socket.emit('admin.settings.get', { hash: SETTINGS_NS }, function (err, data) {
|
|
319
|
+
settings = data || {};
|
|
320
|
+
cb && cb();
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function boot() {
|
|
325
|
+
loadSettings(function () {
|
|
326
|
+
refresh();
|
|
327
|
+
setTimeout(refresh, 1200);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
$(document).ready(boot);
|
|
332
|
+
$(window).on('action:ajaxify.end', boot);
|
|
333
|
+
|
|
334
|
+
$(window).on('action:posts.loaded action:topic.loaded action:topics.loaded action:category.loaded', function () {
|
|
335
|
+
refresh();
|
|
336
|
+
setTimeout(refresh, 600);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
$(window).on('action:ajaxify.start', function () {
|
|
340
|
+
pageKey = null;
|
|
341
|
+
cleanupForNewPage();
|
|
342
|
+
});
|
|
249
343
|
|
|
250
|
-
|
|
251
|
-
$(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
|
|
252
|
-
setTimeout(debounceRefresh, 1800);
|
|
344
|
+
})();
|
package/public/style.css
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/* Le conteneur est caché tant qu'Ezoic n'a pas injecté de contenu */
|
|
2
|
+
.ezoic-ad{min-height:0 !important;height:auto !important;padding:0 !important;margin:0.5rem 0;}
|
|
3
|
+
.ezoic-ad:not(.ezoic-filled){display:none !important;}
|
|
4
|
+
.ezoic-ad .ezoic-ad-inner{padding:0;margin:0;}
|
|
5
|
+
.ezoic-ad .ezoic-ad-inner > div{margin:0;padding:0;}
|
|
@@ -1,59 +1,55 @@
|
|
|
1
1
|
<div class="acp-page-container">
|
|
2
|
-
<
|
|
2
|
+
<h1 class="mb-3">Ezoic - Publicités Infinite Scroll</h1>
|
|
3
3
|
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
|
|
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>
|
|
11
|
-
|
|
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>
|
|
4
|
+
<div class="alert alert-info">
|
|
5
|
+
Format placeholder : <code><div id="ezoic-pub-ad-placeholder-XXX"></div></code>
|
|
6
|
+
</div>
|
|
17
7
|
|
|
8
|
+
<form role="form" class="ezoic-infinite-settings">
|
|
18
9
|
<div class="mb-3">
|
|
19
|
-
<label class="form-label"
|
|
20
|
-
<
|
|
10
|
+
<label class="form-label">Groupes exclus (pas de pubs pour ces groupes)</label>
|
|
11
|
+
<select multiple class="form-select" name="excludedGroups"></select>
|
|
12
|
+
<div class="form-text">Maintenez Ctrl/Cmd pour sélectionner plusieurs groupes. Liste triée par ordre alphabétique.</div>
|
|
21
13
|
</div>
|
|
22
14
|
|
|
23
15
|
<hr/>
|
|
24
16
|
|
|
25
|
-
<
|
|
26
|
-
<p class="form-text">Insère un bloc qui ressemble à un post, toutes les N réponses (dans une page topic).</p>
|
|
17
|
+
<h3>Entre les topics dans une catégorie (liste des sujets)</h3>
|
|
27
18
|
|
|
28
|
-
<div class="form-check mb-
|
|
29
|
-
<input class="form-check-input" type="checkbox"
|
|
30
|
-
<label class="form-check-label"
|
|
19
|
+
<div class="form-check form-switch mb-2">
|
|
20
|
+
<input class="form-check-input" type="checkbox" name="enableBetweenAds">
|
|
21
|
+
<label class="form-check-label">Activer les pubs entre les topics</label>
|
|
31
22
|
</div>
|
|
32
23
|
|
|
33
24
|
<div class="mb-3">
|
|
34
|
-
<label class="form-label"
|
|
35
|
-
<
|
|
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>
|
|
25
|
+
<label class="form-label">Intervalle (insérer après chaque N topics)</label>
|
|
26
|
+
<input type="number" class="form-control" name="intervalTopics" min="1" step="1">
|
|
37
27
|
</div>
|
|
38
28
|
|
|
39
29
|
<div class="mb-3">
|
|
40
|
-
<label class="form-label"
|
|
41
|
-
<
|
|
30
|
+
<label class="form-label">Pool d'IDs de placeholder (un par ligne)</label>
|
|
31
|
+
<textarea class="form-control" name="placeholderIds" rows="6"></textarea>
|
|
42
32
|
</div>
|
|
43
33
|
|
|
44
34
|
<hr/>
|
|
45
35
|
|
|
46
|
-
<
|
|
36
|
+
<h3>Dans les topics (entre les messages)</h3>
|
|
37
|
+
|
|
38
|
+
<div class="form-check form-switch mb-2">
|
|
39
|
+
<input class="form-check-input" type="checkbox" name="enableMessageAds">
|
|
40
|
+
<label class="form-check-label">Activer les pubs entre les messages</label>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div class="mb-3">
|
|
44
|
+
<label class="form-label">Intervalle (insérer après chaque N messages)</label>
|
|
45
|
+
<input type="number" class="form-control" name="messageIntervalPosts" min="1" step="1">
|
|
46
|
+
</div>
|
|
47
|
+
|
|
47
48
|
<div class="mb-3">
|
|
48
|
-
<label class="form-label"
|
|
49
|
-
<
|
|
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>
|
|
49
|
+
<label class="form-label">Pool d'IDs de placeholder pour messages (un par ligne)</label>
|
|
50
|
+
<textarea class="form-control" name="messagePlaceholderIds" rows="6"></textarea>
|
|
55
51
|
</div>
|
|
56
52
|
|
|
57
|
-
<button
|
|
53
|
+
<button type="button" class="btn btn-primary ezoic-infinite-save">Enregistrer</button>
|
|
58
54
|
</form>
|
|
59
55
|
</div>
|