nodebb-plugin-ezoic-infinite 0.6.1 → 0.6.3
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 +140 -130
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
/* globals ajaxify */
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
3
|
+
/* globals ajaxify */
|
|
4
4
|
window.ezoicInfiniteLoaded = true;
|
|
5
5
|
|
|
6
6
|
let cachedConfig;
|
|
7
7
|
let lastFetch = 0;
|
|
8
8
|
let debounceTimer;
|
|
9
9
|
|
|
10
|
-
let lastSignature = null;
|
|
11
10
|
let inFlight = false;
|
|
12
11
|
let rerunRequested = false;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
// Incremental state (prevents ads "jumping to the top")
|
|
14
|
+
let pageKey = null;
|
|
15
|
+
let injectedSlots = new Set(); // slotNumber per page
|
|
16
|
+
let usedIds = new Set(); // ids currently injected per page
|
|
17
|
+
|
|
18
|
+
function resetPageState() {
|
|
19
|
+
injectedSlots = new Set();
|
|
20
|
+
usedIds = new Set();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function currentPageKey() {
|
|
24
|
+
// Stable key per ajaxified page
|
|
25
|
+
try {
|
|
26
|
+
if (ajaxify && ajaxify.data) {
|
|
27
|
+
if (ajaxify.data.tid) return 'topic:' + ajaxify.data.tid;
|
|
28
|
+
if (ajaxify.data.cid) return 'category:' + ajaxify.data.cid;
|
|
29
|
+
if (ajaxify.data.template) return 'tpl:' + ajaxify.data.template + ':' + (ajaxify.data.url || window.location.pathname);
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {}
|
|
32
|
+
return window.location.pathname;
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
function parsePool(raw) {
|
|
@@ -28,44 +41,35 @@ function parsePool(raw) {
|
|
|
28
41
|
));
|
|
29
42
|
}
|
|
30
43
|
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} catch (e) {}
|
|
38
|
-
return $('[component="post/content"]').length > 0;
|
|
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;
|
|
39
50
|
}
|
|
40
51
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
} catch (e) {}
|
|
52
|
+
function isTopicPage() {
|
|
53
|
+
return $('[component="post/content"]').length > 0 || $('[component="post"][data-pid]').length > 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isCategoryTopicListPage() {
|
|
47
57
|
return $('li[component="category/topic"]').length > 0;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
function getTopicPosts() {
|
|
51
|
-
if (!guessIsTopicPage()) return $();
|
|
52
|
-
|
|
53
|
-
// Harmony standard wrapper
|
|
54
61
|
const $primary = $('[component="post"][data-pid]');
|
|
55
62
|
if ($primary.length) return $primary.not('.ezoic-ad-post');
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
const $top = $('[data-pid]').filter(function () {
|
|
64
|
+
return $('[data-pid]').filter(function () {
|
|
59
65
|
const $el = $(this);
|
|
60
66
|
const hasContent = $el.find('[component="post/content"]').length > 0;
|
|
61
67
|
const nested = $el.parents('[data-pid]').length > 0;
|
|
62
68
|
return hasContent && !nested;
|
|
63
|
-
});
|
|
64
|
-
return $top.not('.ezoic-ad-post');
|
|
69
|
+
}).not('.ezoic-ad-post');
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
function getCategoryTopicItems() {
|
|
68
|
-
if (!guessIsCategoryPage()) return $();
|
|
69
73
|
return $('li[component="category/topic"]').not('.ezoic-ad-topic');
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -73,114 +77,122 @@ function tagName($el) {
|
|
|
73
77
|
return ($el && $el.length ? (($el.prop('tagName') || '').toUpperCase()) : '');
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
function
|
|
77
|
-
$('.ezoic-ad-post').remove();
|
|
78
|
-
$('.ezoic-ad-between').remove();
|
|
79
|
-
$('.ezoic-ad-topic').remove();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function computeWindowSlots(totalItems, interval, poolSize) {
|
|
83
|
-
const slots = Math.floor(totalItems / interval);
|
|
84
|
-
if (slots <= 0) return [];
|
|
85
|
-
const start = Math.max(1, slots - poolSize + 1);
|
|
86
|
-
const out = [];
|
|
87
|
-
for (let s = start; s <= slots; s++) out.push(s);
|
|
88
|
-
return out;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function makeWrapperLike($target, classes, innerHtml) {
|
|
80
|
+
function makeWrapperLike($target, classes, innerHtml, attrs) {
|
|
92
81
|
const t = tagName($target);
|
|
82
|
+
const attrStr = attrs ? ' ' + attrs : '';
|
|
93
83
|
if (t === 'LI') {
|
|
94
|
-
return '<li class="' + classes + ' list-unstyled"
|
|
84
|
+
return '<li class="' + classes + ' list-unstyled"' + attrStr + '>' + innerHtml + '</li>';
|
|
95
85
|
}
|
|
96
|
-
return '<div class="' + classes + '"
|
|
86
|
+
return '<div class="' + classes + '"' + attrStr + '>' + innerHtml + '</div>';
|
|
97
87
|
}
|
|
98
88
|
|
|
99
|
-
function
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
if (!slotsToRender.length) return [];
|
|
103
|
-
|
|
104
|
-
const activeIds = [];
|
|
105
|
-
for (let i = 0; i < slotsToRender.length; i++) {
|
|
106
|
-
const slotNumber = slotsToRender[i];
|
|
107
|
-
const id = ids[i];
|
|
108
|
-
const index = slotNumber * interval - 1;
|
|
109
|
-
const $target = $items.eq(index);
|
|
110
|
-
if (!$target.length) continue;
|
|
111
|
-
|
|
112
|
-
const html = makeWrapperLike($target, wrapperClass, '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>');
|
|
113
|
-
$target.after(html);
|
|
114
|
-
activeIds.push(id);
|
|
89
|
+
function pickNextId(pool) {
|
|
90
|
+
for (const id of pool) {
|
|
91
|
+
if (!usedIds.has(id)) return id;
|
|
115
92
|
}
|
|
116
|
-
return
|
|
93
|
+
return null;
|
|
117
94
|
}
|
|
118
95
|
|
|
119
|
-
function
|
|
120
|
-
const total = $posts.length;
|
|
121
|
-
const slotsToRender = computeWindowSlots(total, interval, ids.length);
|
|
122
|
-
if (!slotsToRender.length) return [];
|
|
123
|
-
|
|
124
|
-
const activeIds = [];
|
|
125
|
-
for (let i = 0; i < slotsToRender.length; i++) {
|
|
126
|
-
const slotNumber = slotsToRender[i];
|
|
127
|
-
const id = ids[i];
|
|
128
|
-
const index = slotNumber * interval - 1;
|
|
129
|
-
const $target = $posts.eq(index);
|
|
130
|
-
if (!$target.length) continue;
|
|
131
|
-
|
|
132
|
-
const inner = '<div class="content"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
133
|
-
const html = makeWrapperLike($target, 'post ezoic-ad-post', inner);
|
|
134
|
-
$target.after(html);
|
|
135
|
-
activeIds.push(id);
|
|
136
|
-
}
|
|
137
|
-
return activeIds;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function ezoicRender(ids) {
|
|
96
|
+
function callEzoic(ids) {
|
|
141
97
|
if (!ids || !ids.length) return;
|
|
142
98
|
|
|
143
99
|
window.ezstandalone = window.ezstandalone || {};
|
|
144
100
|
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
145
101
|
|
|
146
|
-
|
|
147
|
-
window.ezstandalone.cmd.push(function () {
|
|
148
|
-
try {
|
|
149
|
-
if (typeof window.ezstandalone.refreshAds === 'function') {
|
|
150
|
-
window.ezstandalone.refreshAds.apply(window.ezstandalone, ids);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
} catch (e) {}
|
|
154
|
-
|
|
102
|
+
const run = function () {
|
|
155
103
|
try {
|
|
156
104
|
if (typeof window.ezstandalone.showAds === 'function') {
|
|
157
105
|
window.ezstandalone.showAds.apply(window.ezstandalone, ids);
|
|
158
|
-
return;
|
|
106
|
+
return true;
|
|
159
107
|
}
|
|
160
108
|
} catch (e) {}
|
|
109
|
+
return false;
|
|
110
|
+
};
|
|
161
111
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
112
|
+
window.ezstandalone.cmd.push(function () { run(); });
|
|
113
|
+
|
|
114
|
+
// retry a few times (Ezoic can load late)
|
|
115
|
+
let tries = 0;
|
|
116
|
+
const maxTries = 6;
|
|
117
|
+
const timer = setInterval(function () {
|
|
118
|
+
tries++;
|
|
119
|
+
if (run() || tries >= maxTries) clearInterval(timer);
|
|
120
|
+
}, 800);
|
|
169
121
|
}
|
|
170
122
|
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
123
|
+
function injectBetweenIncremental($items, pool, interval, wrapperClass) {
|
|
124
|
+
const total = $items.length;
|
|
125
|
+
const maxSlot = Math.floor(total / interval);
|
|
126
|
+
if (maxSlot <= 0) return [];
|
|
127
|
+
|
|
128
|
+
const newIds = [];
|
|
129
|
+
|
|
130
|
+
for (let slot = 1; slot <= maxSlot; slot++) {
|
|
131
|
+
if (injectedSlots.has(slot)) continue;
|
|
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;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const placeholder = '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>';
|
|
144
|
+
const html = makeWrapperLike($target, wrapperClass, placeholder, 'data-ezoic-slot="' + slot + '" data-ezoic-id="' + id + '"');
|
|
145
|
+
|
|
146
|
+
$target.after(html);
|
|
147
|
+
|
|
148
|
+
injectedSlots.add(slot);
|
|
149
|
+
usedIds.add(id);
|
|
150
|
+
newIds.push(id);
|
|
175
151
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
152
|
+
|
|
153
|
+
return newIds;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function injectMessageIncremental($posts, pool, interval) {
|
|
157
|
+
const total = $posts.length;
|
|
158
|
+
const maxSlot = Math.floor(total / interval);
|
|
159
|
+
if (maxSlot <= 0) return [];
|
|
160
|
+
|
|
161
|
+
const newIds = [];
|
|
162
|
+
|
|
163
|
+
for (let slot = 1; slot <= maxSlot; slot++) {
|
|
164
|
+
if (injectedSlots.has(slot)) continue;
|
|
165
|
+
|
|
166
|
+
const index = slot * interval - 1;
|
|
167
|
+
const $target = $posts.eq(index);
|
|
168
|
+
if (!$target.length) continue;
|
|
169
|
+
|
|
170
|
+
const id = pickNextId(pool);
|
|
171
|
+
if (!id) break;
|
|
172
|
+
|
|
173
|
+
const inner = '<div class="content"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
174
|
+
const html = makeWrapperLike($target, 'post ezoic-ad-post', inner, 'data-ezoic-slot="' + slot + '" data-ezoic-id="' + id + '"');
|
|
175
|
+
|
|
176
|
+
$target.after(html);
|
|
177
|
+
|
|
178
|
+
injectedSlots.add(slot);
|
|
179
|
+
usedIds.add(id);
|
|
180
|
+
newIds.push(id);
|
|
179
181
|
}
|
|
180
|
-
|
|
182
|
+
|
|
183
|
+
return newIds;
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
async function refreshAds() {
|
|
187
|
+
// reset state when navigating (ajaxify)
|
|
188
|
+
const key = currentPageKey();
|
|
189
|
+
if (pageKey !== key) {
|
|
190
|
+
pageKey = key;
|
|
191
|
+
resetPageState();
|
|
192
|
+
// also cleanup any injected wrappers that may have been left by browser bfcache
|
|
193
|
+
$('.ezoic-ad-post, .ezoic-ad-between, .ezoic-ad-topic').remove();
|
|
194
|
+
}
|
|
195
|
+
|
|
184
196
|
if (inFlight) { rerunRequested = true; return; }
|
|
185
197
|
inFlight = true;
|
|
186
198
|
|
|
@@ -194,49 +206,47 @@ async function refreshAds() {
|
|
|
194
206
|
const messagePool = parsePool(cfg.messagePlaceholderIds);
|
|
195
207
|
const messageInterval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
|
|
196
208
|
|
|
197
|
-
const
|
|
198
|
-
const
|
|
209
|
+
const onTopic = isTopicPage();
|
|
210
|
+
const onCategory = !onTopic && isCategoryTopicListPage();
|
|
199
211
|
|
|
200
|
-
|
|
212
|
+
const $posts = onTopic ? getTopicPosts() : $();
|
|
213
|
+
const $topicItems = onCategory ? getCategoryTopicItems() : $();
|
|
201
214
|
|
|
202
|
-
|
|
203
|
-
if (signature === lastSignature) return;
|
|
204
|
-
lastSignature = signature;
|
|
205
|
-
|
|
206
|
-
removeExistingEzoicNodes();
|
|
215
|
+
if (!$posts.length && !$topicItems.length) return;
|
|
207
216
|
|
|
208
|
-
const
|
|
217
|
+
const newIds = [];
|
|
209
218
|
|
|
210
|
-
//
|
|
219
|
+
// Your rule:
|
|
220
|
+
// - Category topic list: BETWEEN only
|
|
221
|
+
// - Topic page: MESSAGE only
|
|
211
222
|
if ($topicItems.length) {
|
|
212
223
|
if (cfg.enableBetweenAds && betweenPool.length) {
|
|
213
|
-
|
|
224
|
+
newIds.push(...injectBetweenIncremental($topicItems, betweenPool, betweenInterval, 'ezoic-ad-topic'));
|
|
214
225
|
}
|
|
215
|
-
|
|
226
|
+
callEzoic(newIds);
|
|
216
227
|
return;
|
|
217
228
|
}
|
|
218
229
|
|
|
219
|
-
// Topic pages: MESSAGE only (as you requested)
|
|
220
230
|
if ($posts.length) {
|
|
221
231
|
if (cfg.enableMessageAds && messagePool.length) {
|
|
222
|
-
|
|
232
|
+
newIds.push(...injectMessageIncremental($posts, messagePool, messageInterval));
|
|
223
233
|
}
|
|
224
|
-
|
|
234
|
+
callEzoic(newIds);
|
|
225
235
|
}
|
|
226
236
|
} finally {
|
|
227
237
|
inFlight = false;
|
|
228
238
|
if (rerunRequested) {
|
|
229
239
|
rerunRequested = false;
|
|
230
|
-
setTimeout(refreshAds,
|
|
240
|
+
setTimeout(refreshAds, 120);
|
|
231
241
|
}
|
|
232
242
|
}
|
|
233
243
|
}
|
|
234
244
|
|
|
235
245
|
function debounceRefresh() {
|
|
236
246
|
clearTimeout(debounceTimer);
|
|
237
|
-
debounceTimer = setTimeout(refreshAds,
|
|
247
|
+
debounceTimer = setTimeout(refreshAds, 180);
|
|
238
248
|
}
|
|
239
249
|
|
|
240
250
|
$(document).ready(debounceRefresh);
|
|
241
251
|
$(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
|
|
242
|
-
setTimeout(debounceRefresh,
|
|
252
|
+
setTimeout(debounceRefresh, 1800);
|