nodebb-plugin-ezoic-infinite 1.5.62 → 1.5.64
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 +94 -18
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -234,22 +234,40 @@
|
|
|
234
234
|
window.__nodebbEzoicPatched = true;
|
|
235
235
|
const orig = ez.showAds;
|
|
236
236
|
|
|
237
|
+
// Important: preserve the original calling convention.
|
|
238
|
+
// Some Ezoic builds expect an array; calling one-by-one can lead to
|
|
239
|
+
// repeated define attempts and "Placeholder Id ... already been defined".
|
|
237
240
|
ez.showAds = function (...args) {
|
|
238
241
|
if (isBlocked()) return;
|
|
239
242
|
|
|
243
|
+
const now = Date.now();
|
|
240
244
|
let ids = [];
|
|
241
|
-
|
|
245
|
+
const isArrayCall = (args.length === 1 && Array.isArray(args[0]));
|
|
246
|
+
if (isArrayCall) ids = args[0];
|
|
242
247
|
else ids = args;
|
|
243
248
|
|
|
249
|
+
const filtered = [];
|
|
244
250
|
const seen = new Set();
|
|
245
251
|
for (const v of ids) {
|
|
246
252
|
const id = parseInt(v, 10);
|
|
247
253
|
if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
|
|
248
254
|
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
249
255
|
if (!ph || !ph.isConnected) continue;
|
|
256
|
+
|
|
257
|
+
// Extra throttle to avoid rapid duplicate defines during ajaxify churn
|
|
258
|
+
const last = state.lastShowById.get(id) || 0;
|
|
259
|
+
if (now - last < 650) continue;
|
|
260
|
+
state.lastShowById.set(id, now);
|
|
261
|
+
|
|
250
262
|
seen.add(id);
|
|
251
|
-
|
|
263
|
+
filtered.push(id);
|
|
252
264
|
}
|
|
265
|
+
|
|
266
|
+
if (!filtered.length) return;
|
|
267
|
+
try {
|
|
268
|
+
if (isArrayCall) orig.call(ez, filtered);
|
|
269
|
+
else orig.apply(ez, filtered);
|
|
270
|
+
} catch (e) {}
|
|
253
271
|
};
|
|
254
272
|
} catch (e) {}
|
|
255
273
|
};
|
|
@@ -329,12 +347,12 @@ function withInternalDomChange(fn) {
|
|
|
329
347
|
}
|
|
330
348
|
|
|
331
349
|
function safeDestroyById(id) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
350
|
+
// IMPORTANT:
|
|
351
|
+
// Do NOT call ez.destroyPlaceholders here.
|
|
352
|
+
// In NodeBB ajaxify/infinite-scroll flows, Ezoic can be mid-refresh.
|
|
353
|
+
// Destroy calls can create churn, reduce fill, and generate "does not exist" spam.
|
|
354
|
+
// We only remove our wrapper; Ezoic manages slot lifecycle.
|
|
355
|
+
return;
|
|
338
356
|
}
|
|
339
357
|
|
|
340
358
|
function pruneOrphanWraps(kindClass, items) {
|
|
@@ -347,11 +365,21 @@ function withInternalDomChange(fn) {
|
|
|
347
365
|
// NodeBB can insert separators/spacers; accept an anchor within a few previous siblings
|
|
348
366
|
let ok = false;
|
|
349
367
|
let prev = wrap.previousElementSibling;
|
|
350
|
-
for (let i = 0; i <
|
|
368
|
+
for (let i = 0; i < 8 && prev; i++) {
|
|
351
369
|
if (itemSet.has(prev)) { ok = true; break; }
|
|
352
370
|
prev = prev.previousElementSibling;
|
|
353
371
|
}
|
|
354
372
|
|
|
373
|
+
// If it is already filled (iframe/ins/img), be conservative and keep it.
|
|
374
|
+
// Prevents ads "disappearing too fast" during ajaxify churn / minor rerenders.
|
|
375
|
+
if (!ok) {
|
|
376
|
+
try {
|
|
377
|
+
const ph = wrap.querySelector && wrap.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
|
|
378
|
+
const filled = !!(ph && ph.querySelector && ph.querySelector('iframe, ins, img, .ez-ad, .ezoic-ad'));
|
|
379
|
+
if (filled) ok = true;
|
|
380
|
+
} catch (e) {}
|
|
381
|
+
}
|
|
382
|
+
|
|
355
383
|
if (!ok) {
|
|
356
384
|
const id = getWrapIdFromWrap(wrap);
|
|
357
385
|
withInternalDomChange(() => {
|
|
@@ -368,6 +396,28 @@ function withInternalDomChange(fn) {
|
|
|
368
396
|
return removed;
|
|
369
397
|
}
|
|
370
398
|
|
|
399
|
+
function declusterWraps(kindClass) {
|
|
400
|
+
try {
|
|
401
|
+
const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
|
|
402
|
+
if (wraps.length < 2) return;
|
|
403
|
+
for (let i = 1; i < wraps.length; i++) {
|
|
404
|
+
const w = wraps[i];
|
|
405
|
+
if (!w || !w.isConnected) continue;
|
|
406
|
+
// If previous siblings contain another wrap within 2 hops, remove this one.
|
|
407
|
+
let prev = w.previousElementSibling;
|
|
408
|
+
let hops = 0;
|
|
409
|
+
while (prev && hops < 3) {
|
|
410
|
+
if (prev.classList && prev.classList.contains(WRAP_CLASS)) {
|
|
411
|
+
withInternalDomChange(() => { try { w.remove(); } catch (e) {} });
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
prev = prev.previousElementSibling;
|
|
415
|
+
hops++;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
} catch (e) {}
|
|
419
|
+
}
|
|
420
|
+
|
|
371
421
|
function refreshEmptyState(id) {
|
|
372
422
|
// After Ezoic has had a moment to fill the placeholder, toggle the CSS class.
|
|
373
423
|
window.setTimeout(() => {
|
|
@@ -383,17 +433,25 @@ function withInternalDomChange(fn) {
|
|
|
383
433
|
}, 3500);
|
|
384
434
|
}
|
|
385
435
|
|
|
386
|
-
function buildWrap(id, kindClass, afterPos) {
|
|
436
|
+
function buildWrap(id, kindClass, afterPos, existingPlaceholder) {
|
|
387
437
|
const wrap = document.createElement('div');
|
|
388
438
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
389
439
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
390
440
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
391
441
|
wrap.style.width = '100%';
|
|
392
442
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
443
|
+
if (existingPlaceholder && existingPlaceholder.nodeType === 1) {
|
|
444
|
+
try {
|
|
445
|
+
existingPlaceholder.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
446
|
+
existingPlaceholder.setAttribute('data-ezoic-id', String(id));
|
|
447
|
+
} catch (e) {}
|
|
448
|
+
wrap.appendChild(existingPlaceholder);
|
|
449
|
+
} else {
|
|
450
|
+
const ph = document.createElement('div');
|
|
451
|
+
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
452
|
+
ph.setAttribute('data-ezoic-id', String(id));
|
|
453
|
+
wrap.appendChild(ph);
|
|
454
|
+
}
|
|
397
455
|
|
|
398
456
|
return wrap;
|
|
399
457
|
}
|
|
@@ -407,12 +465,27 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
407
465
|
if (findWrap(kindClass, afterPos)) return null;
|
|
408
466
|
if (insertingIds.has(id)) return null;
|
|
409
467
|
|
|
410
|
-
const existingPh = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
411
|
-
if (existingPh && existingPh.isConnected) return null;
|
|
412
|
-
|
|
413
468
|
insertingIds.add(id);
|
|
414
469
|
try {
|
|
415
|
-
const
|
|
470
|
+
const existingPh = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
471
|
+
|
|
472
|
+
// CRITICAL: never create a second element with the same id, even briefly.
|
|
473
|
+
// That can trigger "Placeholder Id ... already been defined" during load.
|
|
474
|
+
// If an existing placeholder already exists, move it into the new wrapper
|
|
475
|
+
// before inserting the wrapper into the DOM.
|
|
476
|
+
let moved = null;
|
|
477
|
+
if (existingPh && existingPh.isConnected) {
|
|
478
|
+
moved = existingPh;
|
|
479
|
+
// If it was inside one of our wrappers, drop that empty wrapper.
|
|
480
|
+
try {
|
|
481
|
+
const oldWrap = moved.closest && moved.closest(`.${WRAP_CLASS}`);
|
|
482
|
+
if (oldWrap && oldWrap.parentNode) {
|
|
483
|
+
withInternalDomChange(() => { try { oldWrap.remove(); } catch (e) {} });
|
|
484
|
+
}
|
|
485
|
+
} catch (e) {}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const wrap = buildWrap(id, kindClass, afterPos, moved);
|
|
416
489
|
target.insertAdjacentElement('afterend', wrap);
|
|
417
490
|
return wrap;
|
|
418
491
|
} finally {
|
|
@@ -741,6 +814,7 @@ function startShow(id) {
|
|
|
741
814
|
state.allPosts,
|
|
742
815
|
'curPosts'
|
|
743
816
|
);
|
|
817
|
+
declusterWraps('ezoic-ad-message');
|
|
744
818
|
}
|
|
745
819
|
} else if (kind === 'categoryTopics') {
|
|
746
820
|
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
@@ -754,6 +828,7 @@ function startShow(id) {
|
|
|
754
828
|
state.allTopics,
|
|
755
829
|
'curTopics'
|
|
756
830
|
);
|
|
831
|
+
declusterWraps('ezoic-ad-between');
|
|
757
832
|
}
|
|
758
833
|
} else if (kind === 'categories') {
|
|
759
834
|
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
@@ -767,6 +842,7 @@ function startShow(id) {
|
|
|
767
842
|
state.allCategories,
|
|
768
843
|
'curCategories'
|
|
769
844
|
);
|
|
845
|
+
declusterWraps('ezoic-ad-categories');
|
|
770
846
|
}
|
|
771
847
|
}
|
|
772
848
|
}
|