oglasi in CMP nazaj na stanje pred 28. 8. 2026 (Discover padec)
- index.html: Googlovo CMP sporocilo spet zaduseno (controlledMessagingFunction + skrivanje fc-*), kot do 28. 8. - adsense.tsx in mobile-sticky-ad.tsx: stara verzija (stare enote 4154017639, 7672178246, 4593001335, 3854634730, 2398082838; in-article okvir se izrise sele ob polnem oglasu; sticky 320x50) - clanek: vrhnji pas nad naslovnico, brez display oglasa pred besedilom, najvec dva in-article oglasa - naslovnica: postavitev ostane, GridBannerAd na stari enoti 4154017639 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ViFFkiQmVM2yhrd6pU1UBQ
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
a31797a807
commit
bc275e272f
@@ -35,6 +35,32 @@
|
|||||||
<link rel="apple-touch-icon" href="/favicon.png" />
|
<link rel="apple-touch-icon" href="/favicon.png" />
|
||||||
<link rel="shortcut icon" href="/favicon.png" />
|
<link rel="shortcut icon" href="/favicon.png" />
|
||||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4465464714854276" crossorigin="anonymous"></script>
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4465464714854276" crossorigin="anonymous"></script>
|
||||||
|
<script>
|
||||||
|
window.googlefc = window.googlefc || {};
|
||||||
|
window.googlefc.ccpa = window.googlefc.ccpa || {};
|
||||||
|
window.googlefc.callbackQueue = window.googlefc.callbackQueue || [];
|
||||||
|
window.googlefc.controlledMessagingFunction = function(message) { message.proceed(false); };
|
||||||
|
(function hideGoogleCMP() {
|
||||||
|
var style = document.createElement('style');
|
||||||
|
style.textContent = '.fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,div[id^="fc-"],div[class^="fc-"]{display:none!important;visibility:hidden!important}';
|
||||||
|
document.head.appendChild(style);
|
||||||
|
var observer = new MutationObserver(function(mutations) {
|
||||||
|
mutations.forEach(function(m) {
|
||||||
|
m.addedNodes.forEach(function(node) {
|
||||||
|
if (node.nodeType === 1) {
|
||||||
|
var id = node.id || '';
|
||||||
|
var cls = node.className || '';
|
||||||
|
if (id.indexOf('fc-') === 0 || (typeof cls === 'string' && cls.indexOf('fc-') >= 0)) {
|
||||||
|
node.style.display = 'none';
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
observer.observe(document.documentElement, { childList: true, subtree: true });
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Fira+Code:wght@300..700&family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Oxanium:wght@200..800&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100..900;1,100..900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Architects+Daughter&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Fira+Code:wght@300..700&family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Lora:ital,wght@0,400..700;1,400..700&family=Merriweather:ital,opsz,wght@0,18..144,300..900;1,18..144,300..900&family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Outfit:wght@100..900&family=Oxanium:wght@200..800&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100..900;1,100..900&family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&family=Space+Grotesk:wght@300..700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||||
|
|||||||
+104
-229
@@ -11,7 +11,6 @@ interface AdSenseProps {
|
|||||||
style?: Record<string, string>;
|
style?: Record<string, string>;
|
||||||
layout?: string;
|
layout?: string;
|
||||||
layoutKey?: string;
|
layoutKey?: string;
|
||||||
eager?: boolean;
|
|
||||||
onAdStatus?: (filled: boolean) => void;
|
onAdStatus?: (filled: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,74 +22,12 @@ export default function AdSense({
|
|||||||
style,
|
style,
|
||||||
layout,
|
layout,
|
||||||
layoutKey,
|
layoutKey,
|
||||||
// Privzeto zahtevamo takoj. Odlozena zahteva dvigne vidnost, a znizza stevilo
|
|
||||||
// prikazov pri enotah globlje na strani - in prav te (in-article, multiplex)
|
|
||||||
// nosijo prihodek. Lazy pot ostaja na voljo prek eager={false}, a je nikjer
|
|
||||||
// ne uporabljamo, dokler ucinka ne moremo izmeriti v AdSense.
|
|
||||||
eager = true,
|
|
||||||
onAdStatus,
|
onAdStatus,
|
||||||
}: AdSenseProps) {
|
}: AdSenseProps) {
|
||||||
const adRef = useRef<HTMLModElement>(null);
|
const adRef = useRef<HTMLModElement>(null);
|
||||||
const pushed = useRef(false);
|
const pushed = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pushed.current) return;
|
|
||||||
const ins = adRef.current;
|
|
||||||
|
|
||||||
let io: IntersectionObserver | null = null;
|
|
||||||
let obs: MutationObserver | null = null;
|
|
||||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
let decided = false;
|
|
||||||
|
|
||||||
// Google postavi data-ad-status na "filled", "unfilled" ali "unfill-optimized".
|
|
||||||
// Ce statusa ni, se zanesemo na dejansko visino. Odlocimo se enkrat.
|
|
||||||
// AdSense odzivni enoti vnaprej doloci visino po sirini zaslona. Ce je
|
|
||||||
// postrezena kreativa nizja, ostane pod njo prazen rezerviran pas znotraj
|
|
||||||
// okvirja. Visino <ins> zato poravnamo na dejansko visino oglasnega okvirja.
|
|
||||||
// Nic se ne odreze - visina je natanko taksna kot oglas.
|
|
||||||
let fitTimers: ReturnType<typeof setTimeout>[] = [];
|
|
||||||
const fitToCreative = () => {
|
|
||||||
// Enota s trdno visino v style (fiksne Display enote) se ne prilagaja
|
|
||||||
// kreativi, sicer nam Google 300x400 razpotegne na 300x600.
|
|
||||||
if (style && typeof (style as any).height === "string") return;
|
|
||||||
const f = ins?.querySelector("iframe") as HTMLIFrameElement | null;
|
|
||||||
if (!ins || !f) return;
|
|
||||||
const h = f.getBoundingClientRect().height;
|
|
||||||
if (h > 10 && Math.abs(ins.getBoundingClientRect().height - h) > 2) {
|
|
||||||
ins.style.height = `${Math.ceil(h)}px`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const scheduleFit = () => {
|
|
||||||
fitTimers.forEach(clearTimeout);
|
|
||||||
fitTimers = [0, 300, 1000, 2500].map((d) => setTimeout(fitToCreative, d));
|
|
||||||
};
|
|
||||||
|
|
||||||
const decide = (filled: boolean) => {
|
|
||||||
if (decided) return;
|
|
||||||
decided = true;
|
|
||||||
obs?.disconnect();
|
|
||||||
if (timer) clearTimeout(timer);
|
|
||||||
if (filled) scheduleFit();
|
|
||||||
onAdStatus?.(filled);
|
|
||||||
};
|
|
||||||
// Visina samega <ins> ni dokaz - nekatere enote imajo rezerviran prostor.
|
|
||||||
// Napolnjen oglas ima notri okvir (iframe) z dejansko visino.
|
|
||||||
const hasFrame = () => {
|
|
||||||
const f = ins?.querySelector("iframe");
|
|
||||||
return !!f && (f as HTMLIFrameElement).clientHeight > 10;
|
|
||||||
};
|
|
||||||
const check = () => {
|
|
||||||
const st = ins?.getAttribute("data-ad-status") || "";
|
|
||||||
if (st === "filled") return decide(true);
|
|
||||||
if (st.startsWith("unfill")) return decide(false);
|
|
||||||
if (hasFrame()) return decide(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Oglas zahtevamo sele, ko se enota priblizuje vidnemu polju (200 px prej).
|
|
||||||
// Enote globoko pod pregibom so se prej zahtevale takoj ob nalaganju strani in
|
|
||||||
// vecine uporabnik ni nikoli videl - nizka vidnost pomeni nizjo ceno.
|
|
||||||
// Odlocitev o praznosti steje sele od zahteve naprej, ne od mounta.
|
|
||||||
const push = () => {
|
|
||||||
if (pushed.current) return;
|
if (pushed.current) return;
|
||||||
try {
|
try {
|
||||||
const adsbygoogle = (window as any).adsbygoogle || [];
|
const adsbygoogle = (window as any).adsbygoogle || [];
|
||||||
@@ -98,37 +35,38 @@ export default function AdSense({
|
|||||||
pushed.current = true;
|
pushed.current = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!onAdStatus) return;
|
||||||
|
const ins = adRef.current;
|
||||||
if (!ins) return;
|
if (!ins) return;
|
||||||
// Opazujemo tudi pri enotah brez onAdStatus - potrebujemo jih za poravnavo
|
|
||||||
// visine na kreativo.
|
// Google postavi data-ad-status na "filled", "unfilled" ali "unfill-optimized".
|
||||||
obs = new MutationObserver(() => { check(); fitToCreative(); });
|
// Ce statusa ni, se zanesemo na dejansko visino. Odlocimo se enkrat.
|
||||||
|
let decided = false;
|
||||||
|
const decide = (filled: boolean) => {
|
||||||
|
if (decided) return;
|
||||||
|
decided = true;
|
||||||
|
obs.disconnect();
|
||||||
|
clearTimeout(timer);
|
||||||
|
onAdStatus(filled);
|
||||||
|
};
|
||||||
|
// Visina samega <ins> ni dokaz - nekatere enote imajo rezerviran prostor.
|
||||||
|
// Napolnjen oglas ima notri okvir (iframe) z dejansko visino.
|
||||||
|
const hasFrame = () => {
|
||||||
|
const f = ins.querySelector("iframe");
|
||||||
|
return !!f && (f as HTMLIFrameElement).clientHeight > 10;
|
||||||
|
};
|
||||||
|
const check = () => {
|
||||||
|
const st = ins.getAttribute("data-ad-status") || "";
|
||||||
|
if (st === "filled") return decide(true);
|
||||||
|
if (st.startsWith("unfill")) return decide(false);
|
||||||
|
if (hasFrame()) return decide(true);
|
||||||
|
};
|
||||||
|
const obs = new MutationObserver(check);
|
||||||
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
|
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
|
||||||
timer = setTimeout(() => decide(hasFrame()), 3000);
|
const timer = setTimeout(() => decide(hasFrame()), 3000);
|
||||||
window.addEventListener("resize", scheduleFit);
|
|
||||||
check();
|
check();
|
||||||
scheduleFit();
|
return () => { obs.disconnect(); clearTimeout(timer); };
|
||||||
};
|
|
||||||
|
|
||||||
if (eager || !ins || typeof IntersectionObserver === "undefined") {
|
|
||||||
push();
|
|
||||||
} else {
|
|
||||||
io = new IntersectionObserver((entries) => {
|
|
||||||
if (entries.some((e) => e.isIntersecting)) {
|
|
||||||
io?.disconnect();
|
|
||||||
io = null;
|
|
||||||
push();
|
|
||||||
}
|
|
||||||
}, { rootMargin: "200px 0px" });
|
|
||||||
io.observe(ins);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
io?.disconnect();
|
|
||||||
obs?.disconnect();
|
|
||||||
if (timer) clearTimeout(timer);
|
|
||||||
fitTimers.forEach(clearTimeout);
|
|
||||||
window.removeEventListener("resize", scheduleFit);
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -139,8 +77,8 @@ export default function AdSense({
|
|||||||
style={style || { display: "block" }}
|
style={style || { display: "block" }}
|
||||||
data-ad-client="ca-pub-4465464714854276"
|
data-ad-client="ca-pub-4465464714854276"
|
||||||
data-ad-slot={slot}
|
data-ad-slot={slot}
|
||||||
{...(format ? { "data-ad-format": format } : {})}
|
data-ad-format={format}
|
||||||
data-full-width-responsive={responsive ? "true" : "false"}
|
data-full-width-responsive={responsive ? "true" : undefined}
|
||||||
{...(layout ? { "data-ad-layout": layout } : {})}
|
{...(layout ? { "data-ad-layout": layout } : {})}
|
||||||
{...(layoutKey ? { "data-ad-layout-key": layoutKey } : {})}
|
{...(layoutKey ? { "data-ad-layout-key": layoutKey } : {})}
|
||||||
/>
|
/>
|
||||||
@@ -150,54 +88,20 @@ export default function AdSense({
|
|||||||
|
|
||||||
let adSeedCounter = 1;
|
let adSeedCounter = 1;
|
||||||
|
|
||||||
// Vodoravni pas med vrsticami mreze. Oglas v celici je vrstico raztegnil na
|
export function ArticleCardAd() {
|
||||||
// svojo visino (546 px proti 399 px kartice) in pod izvlecki je ostalo prazno.
|
const seed = useMemo(() => adSeedCounter++, []);
|
||||||
// Pas cez vso sirino na visino kartic ne vpliva in ima boljso zapolnjenost.
|
|
||||||
export function GridBannerAd() {
|
|
||||||
const [filled, setFilled] = useState<boolean | null>(null);
|
const [filled, setFilled] = useState<boolean | null>(null);
|
||||||
|
if (filled === false) return null;
|
||||||
return (
|
return (
|
||||||
// Pas cez vso sirino je bil visok 248 do 280 px in je bralca prekinil bolj,
|
<ArtistPatternBg className="rounded-md border border-card-border h-full bg-card" seed={seed}>
|
||||||
// kot je prinesel. Zozimo ga na 728 px: pri tej sirini Google servira
|
|
||||||
// leaderboard 728x90, na telefonu pa 320x100. Rezanja ne uporabljamo,
|
|
||||||
// ker je obrezan oglas krsitev pravil.
|
|
||||||
<div className="my-2 mx-auto w-full max-w-[728px] min-h-[110px]" data-testid="ad-grid-banner">
|
|
||||||
<div className="text-[9px] text-muted-foreground/40 text-center pb-1 uppercase tracking-widest">
|
|
||||||
Anzeige
|
|
||||||
</div>
|
|
||||||
{/* FOLX 2026 - Leaderboard */}
|
|
||||||
<AdSense
|
<AdSense
|
||||||
slot="2757497569"
|
slot="3854634730"
|
||||||
format="auto"
|
format="fluid"
|
||||||
responsive={true}
|
layoutKey="-6r+cy-10+8a-3"
|
||||||
style={{ display: "block" }}
|
style={{ display: "block" }}
|
||||||
onAdStatus={setFilled}
|
onAdStatus={setFilled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</ArtistPatternBg>
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArticleCardAd() {
|
|
||||||
const [filled, setFilled] = useState<boolean | null>(null);
|
|
||||||
if (filled === false) return null;
|
|
||||||
// 31. 8. 2026: oglas mora biti enak kartici, torej cela celica in ne nizji
|
|
||||||
// blok z luknjo pod njim. Okvir je zato h-full in prevzame visino vrstice,
|
|
||||||
// oglas pa je v njem centriran. Enota 9916590834 je fiksna 300x400: fiksne
|
|
||||||
// mere Googlova skripta ne prevaja, zato se ne razteguje kot responsive.
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="h-full w-full flex items-center justify-center rounded-md border border-card-border bg-card overflow-hidden"
|
|
||||||
data-testid="ad-card"
|
|
||||||
>
|
|
||||||
{/* FOLX 2026 - Grid, fiksna Display enota 300x600. Fiksna mera pomeni,
|
|
||||||
da Googlova skripta visine ne prevaja; stara 9916590834 je bila
|
|
||||||
Responsiv in se je razteznila na 548 px. */}
|
|
||||||
<AdSense
|
|
||||||
slot="4126524750"
|
|
||||||
responsive={false}
|
|
||||||
style={{ display: "inline-block", width: "300px", height: "250px" }}
|
|
||||||
onAdStatus={setFilled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,71 +119,36 @@ export function TopBannerAd() {
|
|||||||
{shown && (
|
{shown && (
|
||||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
|
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
|
||||||
)}
|
)}
|
||||||
{/* format="auto" + full-width-responsive: Google dobi celo sirino in
|
|
||||||
izbere kreativo, ki jo zapolni. Pri "horizontal" s trdo visino je
|
|
||||||
vracal 320x50 in okrog je ostal prazen rob. */}
|
|
||||||
{/* FOLX 2026 - Leaderboard */}
|
|
||||||
<AdSense
|
<AdSense
|
||||||
slot="2757497569"
|
slot="4154017639"
|
||||||
format="auto"
|
format="horizontal"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{ display: "block" }}
|
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
|
||||||
responsive={false}
|
responsive={false}
|
||||||
eager
|
|
||||||
onAdStatus={setFilled}
|
onAdStatus={setFilled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navaden odzivni display v toku besedila. Za razliko od fluid in-article enote
|
|
||||||
// dobi Google celotno sirino in lahko postrezhe visoko kreativo (336x280, 300x600),
|
|
||||||
// torej en cel oglas, mimo katerega bralec odskrola - namesto vec drobnih.
|
|
||||||
export function InContentDisplayAd() {
|
|
||||||
const [filled, setFilled] = useState<boolean | null>(null);
|
|
||||||
if (filled === false) return null;
|
|
||||||
const shown = filled === true;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
|
|
||||||
data-testid="ad-in-content-display"
|
|
||||||
>
|
|
||||||
{shown && (
|
|
||||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
|
||||||
)}
|
|
||||||
{/* Sirina 728 px: pri njej Google servira leaderboard 728x90, na telefonu
|
|
||||||
320x100. Pri polni sirini clanka je servirala velik nativni oglas
|
|
||||||
visok 250 px in bralcu je zakrila zacetek besedila. */}
|
|
||||||
<div className="mx-auto w-full max-w-[728px]">
|
|
||||||
<AdSense
|
|
||||||
slot="2757497569"
|
|
||||||
format="auto"
|
|
||||||
responsive={true}
|
|
||||||
style={{ display: "block" }}
|
|
||||||
onAdStatus={setFilled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InArticleAd() {
|
export function InArticleAd() {
|
||||||
// Prava AdSense In-article enota (fluid + data-ad-layout="in-article").
|
// Prava AdSense In-article enota (fluid + data-ad-layout="in-article").
|
||||||
// Kadar oglasa ni, se okvir in rezerviran prostor ne izriseta.
|
// Kadar oglasa ni, se okvir in rezerviran prostor ne izriseta.
|
||||||
const [filled, setFilled] = useState<boolean | null>(null);
|
const [filled, setFilled] = useState<boolean | null>(null);
|
||||||
// 1. 9. 2026: prej se je okvir dodal sele po nalozitvi oglasa in stran je
|
if (filled === false) return null;
|
||||||
// poskocila - meritev je pokazala CLS 0,10 na clanku in 0,15 na naslovnici,
|
const shown = filled === true;
|
||||||
// Search Console pa 225 naslovov cez prag. Zdaj je prostor rezerviran
|
|
||||||
// vnaprej in se po nalozitvi nic ne premakne.
|
|
||||||
return (
|
return (
|
||||||
<div className="my-6 rounded-md overflow-hidden bg-card border border-card-border min-h-[280px]">
|
<div
|
||||||
|
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
|
||||||
|
style={shown ? { minHeight: "260px" } : undefined}
|
||||||
|
>
|
||||||
|
{shown && (
|
||||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||||
{/* FOLX 2026 - In-Article */}
|
)}
|
||||||
<AdSense
|
<AdSense
|
||||||
slot="6313599193"
|
slot="7672178246"
|
||||||
format="fluid"
|
format="fluid"
|
||||||
layout="in-article"
|
layout="in-article"
|
||||||
responsive={false}
|
|
||||||
style={{ display: "block", textAlign: "center" }}
|
style={{ display: "block", textAlign: "center" }}
|
||||||
onAdStatus={setFilled}
|
onAdStatus={setFilled}
|
||||||
/>
|
/>
|
||||||
@@ -294,60 +163,39 @@ export function MultiplexAd() {
|
|||||||
return (
|
return (
|
||||||
<div className={shown ? "bg-card rounded-md border border-card-border overflow-hidden" : ""}>
|
<div className={shown ? "bg-card rounded-md border border-card-border overflow-hidden" : ""}>
|
||||||
<AdSense
|
<AdSense
|
||||||
slot="8995602853"
|
slot="4593001335"
|
||||||
format="autorelaxed"
|
format="autorelaxed"
|
||||||
responsive={false}
|
|
||||||
style={{ display: "block" }}
|
style={{ display: "block" }}
|
||||||
className="w-full"
|
className={shown ? "w-full min-h-[250px]" : "w-full"}
|
||||||
onAdStatus={setFilled}
|
onAdStatus={setFilled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oba stolpca ostaneta (nic se ne odvzame), a se prilagodita sirini zaslona:
|
|
||||||
// 160x600 od 1536 px, 300x600 od 1700 px naprej, kjer je prostora dovolj.
|
|
||||||
// Sirino dolocz CSS, AdSense jo prebere ob zahtevi. Dodan onAdStatus, da prazna
|
|
||||||
// enota ne pusti luknje - prej je ni imela.
|
|
||||||
function SideRail({ side, offset }: { side: "left" | "right"; offset: number }) {
|
|
||||||
const [filled, setFilled] = useState<boolean | null>(null);
|
|
||||||
if (filled === false) return null;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none"
|
|
||||||
style={side === "left"
|
|
||||||
? { left: `calc(50% - ${offset}px - var(--rail-w))` }
|
|
||||||
: { right: `calc(50% - ${offset}px - var(--rail-w))` }}
|
|
||||||
data-testid={`ad-${side}-sidebar`}
|
|
||||||
>
|
|
||||||
<div className="pointer-events-auto w-full">
|
|
||||||
{/* Trdna mera 160x600 (skyscraper). Prej je bil pas na sirokih zaslonih
|
|
||||||
300 px in z responsive vertical si je Google visino racunal sam,
|
|
||||||
zato se je raztegoval cez pol zaslona. */}
|
|
||||||
{/* FOLX 2026 - Skyscraper, fiksna Display enota 160x600. */}
|
|
||||||
<AdSense
|
|
||||||
slot="4349780836"
|
|
||||||
format=""
|
|
||||||
responsive={false}
|
|
||||||
style={{ display: "inline-block", width: "160px", height: "600px" }}
|
|
||||||
onAdStatus={setFilled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PageSideAds({ contentHalfWidth = 640 }: { contentHalfWidth?: number }) {
|
export function PageSideAds({ contentHalfWidth = 640 }: { contentHalfWidth?: number }) {
|
||||||
// 31. 8. 2026: pasova sta bila prilepljena na rob zaslona, ker je bila
|
const gap = 10;
|
||||||
// spremenljivka --rail-w nad 1700 px se vedno 300, enota pa je zdaj fiksna
|
|
||||||
// 160 px - odmik se je racunal s 140 px prevelike sirine. Zdaj je povsod
|
|
||||||
// 160, razmik do vsebine pa majhen, da sta pasova tik ob clanku.
|
|
||||||
const gap = 8;
|
|
||||||
const offset = contentHalfWidth + gap;
|
const offset = contentHalfWidth + gap;
|
||||||
return (
|
return (
|
||||||
<div className="relative page-side-rails">
|
<div className="relative">
|
||||||
<SideRail side="left" offset={offset} />
|
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ left: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-left-sidebar">
|
||||||
<SideRail side="right" offset={offset} />
|
<div className="pointer-events-auto">
|
||||||
|
<AdSense
|
||||||
|
slot="2398082838"
|
||||||
|
format="vertical"
|
||||||
|
style={{ display: "block", width: "160px", height: "600px" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ right: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-right-sidebar">
|
||||||
|
<div className="pointer-events-auto">
|
||||||
|
<AdSense
|
||||||
|
slot="2398082838"
|
||||||
|
format="vertical"
|
||||||
|
style={{ display: "block", width: "160px", height: "600px" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -358,14 +206,41 @@ export function SidebarAd() {
|
|||||||
return (
|
return (
|
||||||
<div className={`sticky top-4 ${visible ? "" : "hidden"}`}>
|
<div className={`sticky top-4 ${visible ? "" : "hidden"}`}>
|
||||||
<div className="bg-card rounded-md border border-card-border p-4 mt-4">
|
<div className="bg-card rounded-md border border-card-border p-4 mt-4">
|
||||||
{/* FOLX 2026 - Sidebar, fiksna Display enota 300x600. */}
|
|
||||||
<AdSense
|
<AdSense
|
||||||
slot="9953566507"
|
slot="2398082838"
|
||||||
responsive={false}
|
format="auto"
|
||||||
style={{ display: "inline-block", width: "300px", height: "600px" }}
|
|
||||||
onAdStatus={(filled) => setVisible(filled)}
|
onAdStatus={(filled) => setVisible(filled)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vodoravni pas med vrsticami mreze na naslovnici. 4. 9. 2026: nazaj na staro
|
||||||
|
// enoto 4154017639 (horizontal), enako kot vrhnji pas pred 28. 8.
|
||||||
|
export function GridBannerAd() {
|
||||||
|
const [filled, setFilled] = useState<boolean | null>(null);
|
||||||
|
if (filled === false) return null;
|
||||||
|
const shown = filled === true;
|
||||||
|
return (
|
||||||
|
<div className="my-2 mx-auto w-full max-w-[728px]" data-testid="ad-grid-banner">
|
||||||
|
<div
|
||||||
|
className={shown
|
||||||
|
? "rounded-md overflow-hidden bg-card border border-card-border flex flex-col items-center"
|
||||||
|
: "flex flex-col items-center"}
|
||||||
|
>
|
||||||
|
{shown && (
|
||||||
|
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest w-full">Anzeige</div>
|
||||||
|
)}
|
||||||
|
<AdSense
|
||||||
|
slot="4154017639"
|
||||||
|
format="horizontal"
|
||||||
|
className="w-full"
|
||||||
|
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
|
||||||
|
responsive={false}
|
||||||
|
onAdStatus={setFilled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ const DISMISS_KEY = "folx-sticky-ad-dismissed";
|
|||||||
export default function MobileStickyAd() {
|
export default function MobileStickyAd() {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [filled, setFilled] = useState<boolean | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMobile) return;
|
if (!isMobile) return;
|
||||||
@@ -24,13 +23,11 @@ export default function MobileStickyAd() {
|
|||||||
sessionStorage.setItem(DISMISS_KEY, "1");
|
sessionStorage.setItem(DISMISS_KEY, "1");
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isMobile || !visible || filled === false) return null;
|
if (!isMobile || !visible) return null;
|
||||||
const shown = filled === true;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-[100] animate-in slide-in-from-bottom duration-300" data-testid="mobile-sticky-ad">
|
<div className="fixed bottom-0 left-0 right-0 z-[100] animate-in slide-in-from-bottom duration-300" data-testid="mobile-sticky-ad">
|
||||||
<div className={shown ? "relative bg-card border-t border-card-border shadow-2xl" : "relative"}>
|
<div className="relative bg-card border-t border-card-border shadow-2xl">
|
||||||
{shown && (
|
|
||||||
<button
|
<button
|
||||||
onClick={close}
|
onClick={close}
|
||||||
className="absolute -top-8 right-2 w-7 h-7 flex items-center justify-center rounded-full bg-card border border-card-border shadow-lg text-muted-foreground hover:text-foreground transition-colors"
|
className="absolute -top-8 right-2 w-7 h-7 flex items-center justify-center rounded-full bg-card border border-card-border shadow-lg text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -39,22 +36,13 @@ export default function MobileStickyAd() {
|
|||||||
>
|
>
|
||||||
<X className="w-4 h-4" />
|
<X className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
{shown && (
|
|
||||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||||
)}
|
<div className="flex justify-center pb-1">
|
||||||
{/* Vodoravni format brez full-width-responsive: enota se drzi sirine
|
|
||||||
svojega vsebnika in da 320x50 ali 320x100. Z vklopljenim
|
|
||||||
full-width-responsive je Google vracal 390x390 kvadrat. */}
|
|
||||||
<div className="w-full pb-1">
|
|
||||||
<AdSense
|
<AdSense
|
||||||
slot="4154017639"
|
slot="4154017639"
|
||||||
format="horizontal"
|
format="horizontal"
|
||||||
className="w-full"
|
style={{ display: "inline-block", width: "320px", height: "50px" }}
|
||||||
style={{ display: "block" }}
|
|
||||||
responsive={false}
|
responsive={false}
|
||||||
eager
|
|
||||||
onAdStatus={setFilled}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Footer from "@/components/footer";
|
import Footer from "@/components/footer";
|
||||||
import { InArticleAd, InContentDisplayAd, PageSideAds, MultiplexAd } from "@/components/adsense";
|
import { InArticleAd, PageSideAds, MultiplexAd, TopBannerAd } from "@/components/adsense";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
import ShareButtons from "@/components/share-buttons";
|
import ShareButtons from "@/components/share-buttons";
|
||||||
@@ -344,8 +344,7 @@ export default function ArticlePage() {
|
|||||||
<Header />
|
<Header />
|
||||||
<PageSideAds contentHalfWidth={480} />
|
<PageSideAds contentHalfWidth={480} />
|
||||||
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
<main className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||||
{/* Zgornjega pasu na clanku namenoma ni: potiskal je naslovnico in naslov
|
<TopBannerAd />
|
||||||
pod pregib, placan pa je najslabse. Prvi oglas pride sele v besedilu. */}
|
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<Button variant="ghost" size="sm" className="mb-3 gap-2" data-testid="button-back">
|
<Button variant="ghost" size="sm" className="mb-3 gap-2" data-testid="button-back">
|
||||||
<ArrowLeft className="w-4 h-4" />
|
<ArrowLeft className="w-4 h-4" />
|
||||||
@@ -407,10 +406,6 @@ export default function ArticlePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enota "Folx News - In-Article" (4154017639) je bila prej vrhnji pas nad
|
|
||||||
naslovnico. Ostane na clanku, a pod naslovom in tik pred besedilom:
|
|
||||||
isti prikazi, boljse mesto, naslov ne pade pod pregib. */}
|
|
||||||
<InContentDisplayAd />
|
|
||||||
|
|
||||||
{(() => {
|
{(() => {
|
||||||
const sanitized = sanitizeContent(articleBody || article.content);
|
const sanitized = sanitizeContent(articleBody || article.content);
|
||||||
@@ -435,13 +430,8 @@ export default function ArticlePage() {
|
|||||||
// Vsi so fluid in-article enota 7672178246 (najvecji zasluzkar).
|
// Vsi so fluid in-article enota 7672178246 (najvecji zasluzkar).
|
||||||
|
|
||||||
const last = segments.length - 1;
|
const last = segments.length - 1;
|
||||||
const extra = Math.min(3, Math.max(0, Math.floor((blocks.length - 10) / 5)));
|
// Najvec dva oglasa v besedilu: po prvem odstavku in na sredini (stanje pred 28. 8. 2026).
|
||||||
const adCount = last <= 0 ? 0 : (last === 1 ? 1 : Math.min(2 + extra, last));
|
const AD_AFTER = last <= 0 ? [] : (last === 1 ? [0] : [0, Math.floor(last / 2)]);
|
||||||
const AD_AFTER: number[] = [];
|
|
||||||
for (let i = 0; i < adCount; i++) {
|
|
||||||
const pos = i === 0 ? 0 : Math.round((i * last) / adCount);
|
|
||||||
if (pos < last && !AD_AFTER.includes(pos)) AD_AFTER.push(pos);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div ref={(el) => {
|
<div ref={(el) => {
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
@@ -462,9 +452,6 @@ export default function ArticlePage() {
|
|||||||
dangerouslySetInnerHTML={{ __html: html }}
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
data-testid={idx === 0 ? "article-content" : `article-content-${idx}`}
|
data-testid={idx === 0 ? "article-content" : `article-content-${idx}`}
|
||||||
/>
|
/>
|
||||||
{/* VSA mesta v besedilu so 7672178246 - to je enota z najvecjim
|
|
||||||
prihodkom (98,93 EUR v 7 dneh). Prej sta bili tam dve, zdaj
|
|
||||||
dve do pet. Enota 4154017639 ima svoje mesto pred besedilom. */}
|
|
||||||
{AD_AFTER.includes(idx) && <InArticleAd />}
|
{AD_AFTER.includes(idx) && <InArticleAd />}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user