nodebb-plugin-ezoic-infinite 1.8.34 → 1.8.35
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 +108 -31
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
SHOW_RELEASE_MS: 700,
|
|
39
39
|
BATCH_FLUSH_MS: 80,
|
|
40
40
|
RECYCLE_DELAY_MS: 450,
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
// Limits
|
|
@@ -923,50 +923,122 @@
|
|
|
923
923
|
} catch (_) {}
|
|
924
924
|
}
|
|
925
925
|
|
|
926
|
-
// ── TCF
|
|
926
|
+
// ── TCF / CMP Protection ─────────────────────────────────────────────────
|
|
927
927
|
//
|
|
928
|
-
//
|
|
929
|
-
//
|
|
928
|
+
// Root cause of the CMP errors:
|
|
929
|
+
// "Cannot read properties of null (reading 'postMessage')"
|
|
930
|
+
// "Cannot set properties of null (setting 'addtlConsent')"
|
|
930
931
|
//
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
//
|
|
932
|
+
// The CMP (Gatekeeper Consent) communicates via postMessage on the
|
|
933
|
+
// __tcfapiLocator iframe's contentWindow. During NodeBB ajaxify navigation,
|
|
934
|
+
// jQuery's html() or empty() on the content area can cascade and remove
|
|
935
|
+
// iframes from <body>. The CMP then calls getTCData on a stale reference
|
|
936
|
+
// where contentWindow is null.
|
|
934
937
|
//
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
// iframe
|
|
938
|
-
|
|
939
|
-
|
|
938
|
+
// Strategy (3 layers):
|
|
939
|
+
//
|
|
940
|
+
// 1. PROTECT: Move the locator iframe into <head> where ajaxify never
|
|
941
|
+
// touches it. The TCF spec only requires the iframe to exist in the
|
|
942
|
+
// document with name="__tcfapiLocator" — it works from <head>.
|
|
943
|
+
//
|
|
944
|
+
// 2. GUARD: Wrap __tcfapi and __cmp with a safety layer that catches
|
|
945
|
+
// errors in the CMP's internal getTCData, preventing the uncaught
|
|
946
|
+
// TypeError from propagating.
|
|
947
|
+
//
|
|
948
|
+
// 3. RESTORE: MutationObserver on <body> childList (not subtree) to
|
|
949
|
+
// immediately re-create the locator if something still removes it.
|
|
940
950
|
|
|
941
951
|
function ensureTcfLocator() {
|
|
942
|
-
// Only needed if CMP APIs are present
|
|
943
952
|
if (!window.__tcfapi && !window.__cmp) return;
|
|
944
953
|
|
|
945
|
-
const
|
|
946
|
-
|
|
954
|
+
const LOCATOR_ID = '__tcfapiLocator';
|
|
955
|
+
|
|
956
|
+
// Create or relocate the locator iframe into <head> for protection
|
|
957
|
+
const ensureInHead = () => {
|
|
958
|
+
let existing = document.getElementById(LOCATOR_ID);
|
|
959
|
+
if (existing) {
|
|
960
|
+
// If it's in <body>, move it to <head> where ajaxify can't reach it
|
|
961
|
+
if (existing.parentElement !== document.head) {
|
|
962
|
+
try { document.head.appendChild(existing); } catch (_) {}
|
|
963
|
+
}
|
|
964
|
+
return existing;
|
|
965
|
+
}
|
|
966
|
+
// Create fresh
|
|
947
967
|
const f = document.createElement('iframe');
|
|
948
968
|
f.style.display = 'none';
|
|
949
|
-
f.id = f.name =
|
|
950
|
-
|
|
969
|
+
f.id = f.name = LOCATOR_ID;
|
|
970
|
+
try { document.head.appendChild(f); } catch (_) {
|
|
971
|
+
// Fallback to body if head insertion fails
|
|
972
|
+
(document.body || document.documentElement).appendChild(f);
|
|
973
|
+
}
|
|
974
|
+
return f;
|
|
951
975
|
};
|
|
952
976
|
|
|
953
|
-
|
|
977
|
+
ensureInHead();
|
|
978
|
+
|
|
979
|
+
// Layer 2: Guard the CMP API calls against null contentWindow
|
|
980
|
+
if (!window.__nbbCmpGuarded) {
|
|
981
|
+
window.__nbbCmpGuarded = true;
|
|
982
|
+
|
|
983
|
+
// Wrap __tcfapi
|
|
984
|
+
if (typeof window.__tcfapi === 'function') {
|
|
985
|
+
const origTcf = window.__tcfapi;
|
|
986
|
+
window.__tcfapi = function (cmd, version, cb, param) {
|
|
987
|
+
try {
|
|
988
|
+
return origTcf.call(this, cmd, version, function (...args) {
|
|
989
|
+
try { cb?.(...args); } catch (_) {}
|
|
990
|
+
}, param);
|
|
991
|
+
} catch (e) {
|
|
992
|
+
// If the error is the null postMessage/addtlConsent, swallow it
|
|
993
|
+
if (e?.message?.includes('null')) {
|
|
994
|
+
// Re-ensure locator exists, then retry once
|
|
995
|
+
ensureInHead();
|
|
996
|
+
try { return origTcf.call(this, cmd, version, cb, param); } catch (_) {}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
954
1001
|
|
|
955
|
-
|
|
1002
|
+
// Wrap __cmp (legacy CMP v1 API)
|
|
1003
|
+
if (typeof window.__cmp === 'function') {
|
|
1004
|
+
const origCmp = window.__cmp;
|
|
1005
|
+
window.__cmp = function (...args) {
|
|
1006
|
+
try {
|
|
1007
|
+
return origCmp.apply(this, args);
|
|
1008
|
+
} catch (e) {
|
|
1009
|
+
if (e?.message?.includes('null')) {
|
|
1010
|
+
ensureInHead();
|
|
1011
|
+
try { return origCmp.apply(this, args); } catch (_) {}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Layer 3: MutationObserver to immediately restore if removed
|
|
956
1019
|
if (!window.__nbbTcfObs) {
|
|
957
|
-
window.__nbbTcfObs = new MutationObserver(
|
|
958
|
-
//
|
|
959
|
-
if (document.getElementById(
|
|
960
|
-
//
|
|
961
|
-
|
|
962
|
-
_tcfDebounce = setTimeout(inject, TIMING.TCF_DEBOUNCE_MS);
|
|
963
|
-
});
|
|
964
|
-
// Observe only direct children of body (not deep subtree)
|
|
965
|
-
// since the locator iframe is a direct child
|
|
966
|
-
window.__nbbTcfObs.observe(document.body || document.documentElement, {
|
|
967
|
-
childList: true,
|
|
968
|
-
subtree: false,
|
|
1020
|
+
window.__nbbTcfObs = new MutationObserver(muts => {
|
|
1021
|
+
// Fast check: still in document?
|
|
1022
|
+
if (document.getElementById(LOCATOR_ID)) return;
|
|
1023
|
+
// Something removed it — restore immediately (no debounce)
|
|
1024
|
+
ensureInHead();
|
|
969
1025
|
});
|
|
1026
|
+
// Observe body direct children only (the most likely removal point)
|
|
1027
|
+
try {
|
|
1028
|
+
window.__nbbTcfObs.observe(document.body || document.documentElement, {
|
|
1029
|
+
childList: true,
|
|
1030
|
+
subtree: false,
|
|
1031
|
+
});
|
|
1032
|
+
} catch (_) {}
|
|
1033
|
+
// Also observe <head> in case something cleans it
|
|
1034
|
+
try {
|
|
1035
|
+
if (document.head) {
|
|
1036
|
+
window.__nbbTcfObs.observe(document.head, {
|
|
1037
|
+
childList: true,
|
|
1038
|
+
subtree: false,
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
} catch (_) {}
|
|
970
1042
|
}
|
|
971
1043
|
}
|
|
972
1044
|
|
|
@@ -985,6 +1057,8 @@
|
|
|
985
1057
|
'cannot call refresh on the same page',
|
|
986
1058
|
'no placeholders are currently defined in Refresh',
|
|
987
1059
|
'Debugger iframe already exists',
|
|
1060
|
+
'[CMP] Error in custom getTCData',
|
|
1061
|
+
'vignette: no interstitial API',
|
|
988
1062
|
];
|
|
989
1063
|
const PH_PATTERN = `with id ${PH_PREFIX}`;
|
|
990
1064
|
|
|
@@ -1050,6 +1124,9 @@
|
|
|
1050
1124
|
state.kind = null;
|
|
1051
1125
|
state.blockedUntil = 0;
|
|
1052
1126
|
|
|
1127
|
+
// Fix: CMP modal can leave aria-hidden="true" on <body> after navigation
|
|
1128
|
+
try { document.body.removeAttribute('aria-hidden'); } catch (_) {}
|
|
1129
|
+
|
|
1053
1130
|
muteConsole();
|
|
1054
1131
|
ensureTcfLocator();
|
|
1055
1132
|
warmNetwork();
|