Meta Description" name="description" />

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>GAM Native Preview</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
html, body { margin:0; padding:0; background:#f6f7fb; font:14px/1.4 system-ui, -apple-system, Segoe UI, Roboto, Arial, "Noto Sans"; color:#1c2230; }
.wrap { max-width:980px; margin:24px auto; padding:0 16px; }
h1 { margin:0 0 6px; font-size:18px; }
.meta { color:#6b7280; margin:0 0 12px; }
.frame { margin:16px 0; padding:16px; border:1px dashed #c2c8d0; border-radius:12px; background:#fff; }
.container { margin:0 auto; padding:0; background:#fff; }
#adreform-gpt-ad { display:block; margin:0; padding:0; border:0; box-sizing:content-box; background:#fff; }
.chips { display:flex; gap:8px; flex-wrap:wrap; margin-top:8px; }
.chip { background:#eef2ff; border:1px solid #dbe1ff; color:#334155; padding:2px 8px; border-radius:999px; font-size:12px; }
.log { background:#0f172a; color:#d1e7ff; font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:12px; padding:12px; border-radius:8px; max-height:300px; overflow:auto; white-space:pre-wrap; }
</style>
<script>
// ---------- helpers ----------
const qp = (k, d=null) => new URLSearchParams(location.search).get(k) ?? d;
const isNative = () => !/^(0|false)$/i.test(qp('native','1'));
const showLogs = /^(1|true)$/i.test(qp('logs','0'));
function parseSize() {
const s = qp('size'); let w = qp('w'), h = qp('h');
if (s && !w && !h) { const m = String(s).match(/^(\d+)\s*x\s*(\d+)$/i); if (m){ w=m[1]; h=m[2]; } }
w = parseInt(w||'300',10); h = parseInt(h||'250',10);
return [ (Number.isFinite(w)&&w>0?w:300), (Number.isFinite(h)&&h>0?h:250) ];
}
const containerWidthPx = () => { const v = parseInt(qp('cw')||'430',10); return Number.isFinite(v)&&v>0?v:430; };
const log = (m,o) => {
if (!showLogs) return;
const el=document.getElementById('logs');
const t=new Date().toISOString().split('T')[1].replace('Z','');
el.textContent += `[${t}] ${m} ${o?JSON.stringify(o):''}\n`;
el.scrollTop=el.scrollHeight;
console.log(m,o||'');
};
// Keep current state of IDs
const state = {
lineItemId: qp('lineItemId', ''),
creativeId: qp('creativeId', ''),
};
function fmt(v) { return v && String(v).trim() ? v : '—'; }
// Coerce iu into a usable path
function iuPath() {
const raw = qp('iu', '');
if (raw && raw.startsWith('/')) return raw;
if (raw && /^\d+$/.test(raw)) return `/${raw}/preview`;
return '/dummy/preview';
}
function header() {
const url = new URL(location.href);
const chips = [];
chips.push({k:'mode', v: isNative() ? 'native (fluid)' : `display ${parseSize().join('x')}`});
chips.push({k:'AdUnitPath', v: iuPath()});
// Actual IDs
chips.push({k:'lineItemId', v: fmt(state.lineItemId)});
chips.push({k:'creativeId', v: fmt(state.creativeId)});
// Flags
['google_preview','gdfp_req'].forEach(k=>{
const has = url.searchParams.has(k);
chips.push({k, v: has ? '✓' : '✗'});
});
const cw = isNative() ? containerWidthPx()+'px' : '-';
chips.push({k:'cw', v: cw});
document.getElementById('meta').innerHTML =
'<div class="chips">' +
chips.map(c=>`<span class="chip">${c.k}: <b>${c.v}</b></span>`).join(' ') +
'</div>';
}
// Apply layout before GPT
function layout() {
const slot = document.getElementById('adreform-gpt-ad');
const cont = document.getElementById('container');
slot.style.margin = slot.style.padding = slot.style.border = '0';
if (isNative()) {
cont.style.width = containerWidthPx() + 'px';
slot.style.width = ''; slot.style.height = '';
} else {
const [W,H] = parseSize();
cont.style.width = '';
slot.style.width = W + 'px';
slot.style.height = H + 'px';
}
}
// Capture last ad request URL
(function intercept() {
const open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(m,u){ try{ if(typeof u==='string' && u.includes('/gampad/ads')) window.__lastAdReq=u; }catch(e){} return open.apply(this, arguments); };
const _fetch = window.fetch;
window.fetch = function(){ try{ const u=arguments[0]; if(typeof u==='string' && u.includes('/gampad/ads')) window.__lastAdReq=u; }catch(e){} return _fetch.apply(this, arguments); };
})();
window.addEventListener('DOMContentLoaded', ()=>
{
layout();
header();
if (showLogs) document.getElementById('logs').style.display = '';
}
);
// ---------- GPT ----------
window.googletag = window.googletag || { cmd: [] };
(function(){ const s=document.createElement('script'); s.async=true; s.src='https://securepubads.g.doubleclick.net/tag/js/gpt.js'; (document.head||document.body).appendChild(s); })();
googletag.cmd.push(function () {
const ver = googletag.getVersion ? googletag.getVersion() : 'unknown';
log('GPT version', {ver});
const def = isNative() ? ['fluid'] : parseSize();
const iu = iuPath();
log('Using iu', {iu});
log('Slot definition', {def});
const slot = googletag.defineSlot(iu, def, 'adreform-gpt-ad').addService(googletag.pubads());
googletag.pubads().enableSingleRequest();
googletag.pubads().addEventListener('slotRequested', e => log('slotRequested', e));
googletag.pubads().addEventListener('slotResponseReceived', e => log('slotResponseReceived', e));
googletag.pubads().addEventListener('slotRenderEnded', e => {
log('slotRenderEnded', {
isEmpty: e.isEmpty, size: e.size, creativeId: e.creativeId, lineItemId: e.lineItemId
});
if (e.lineItemId) state.lineItemId = e.lineItemId;
if (e.creativeId) state.creativeId = e.creativeId;
header();
});
googletag.enableServices();
requestAnimationFrame(()=> googletag.display('adreform-gpt-ad'));
setTimeout(()=> log('Last ad request URL', {url: window.__lastAdReq || '(not captured)'}), 1200);
});
</script>
</head>
<body>
<div class="wrap">
<h1>GAM Native Preview</h1>
<p id="meta" class="meta"></p>
<div class="frame">
<div id="container" class="container">
<div id="adreform-gpt-ad" aria-label="GPT Ad Slot"></div>
</div>
</div>
<div id="logs" class="log" aria-live="polite" style="display: none;"></div>
</div>
</body>
</html>