nodebb-plugin-ezoic-infinite 1.5.63 → 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 +87 -22
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
|
};
|
|
@@ -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,24 +465,28 @@ 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
|
-
|
|
412
468
|
insertingIds.add(id);
|
|
413
469
|
try {
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
420
|
-
|
|
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.
|
|
421
480
|
try {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
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) {}
|
|
427
486
|
}
|
|
487
|
+
|
|
488
|
+
const wrap = buildWrap(id, kindClass, afterPos, moved);
|
|
489
|
+
target.insertAdjacentElement('afterend', wrap);
|
|
428
490
|
return wrap;
|
|
429
491
|
} finally {
|
|
430
492
|
insertingIds.delete(id);
|
|
@@ -752,6 +814,7 @@ function startShow(id) {
|
|
|
752
814
|
state.allPosts,
|
|
753
815
|
'curPosts'
|
|
754
816
|
);
|
|
817
|
+
declusterWraps('ezoic-ad-message');
|
|
755
818
|
}
|
|
756
819
|
} else if (kind === 'categoryTopics') {
|
|
757
820
|
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
@@ -765,6 +828,7 @@ function startShow(id) {
|
|
|
765
828
|
state.allTopics,
|
|
766
829
|
'curTopics'
|
|
767
830
|
);
|
|
831
|
+
declusterWraps('ezoic-ad-between');
|
|
768
832
|
}
|
|
769
833
|
} else if (kind === 'categories') {
|
|
770
834
|
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
@@ -778,6 +842,7 @@ function startShow(id) {
|
|
|
778
842
|
state.allCategories,
|
|
779
843
|
'curCategories'
|
|
780
844
|
);
|
|
845
|
+
declusterWraps('ezoic-ad-categories');
|
|
781
846
|
}
|
|
782
847
|
}
|
|
783
848
|
}
|