1// /assets/nt-ads.js â Publicidad web con Google Ad Manager (GPT). MIG-2.15. 2// Codigo de la guia de implementacion v1.2 de NoticiasTrabajo (24/9/2026, apartado 4.2). 3// Se carga con async despues de gpt.js (GamLoader.astro). En la fase 3 se envolvera 4// aqui Prebid, por eso la definicion de slots queda en una unica funcion. 5// Cambios respecto al codigo de la guia: 6// - Los slots se definen tras DOMContentLoaded: con gpt.js en cache este script puede 7// ejecutarse antes de que el parser llegue a los huecos del body. 8// - El zocalo arranca oculto con visibility:hidden (CSS) y no con display:none: con 9// display:none el div no pasaba la comprobacion de visibilidad y el slot nunca se 10// definia. Mientras se ve, el body reserva su altura abajo para no tapar Taboola 11// ni el pie. 12// - Key-values, lazy load, colapso, centrado y SRA con setConfig en vez de los metodos 13// de pubads (setTargeting, enableLazyLoadâ¦), que GPT marca como obsoletos. 14// - Colapso solo sin relleno (ON_NO_FILL), no antes de pedir el anuncio: ver abajo. 15// La cabecera (nt-ad-top, nt-ad-top-m) no colapsa nunca, para no provocar CLS. 16// - La altura del zocalo solo se toma de size si es un array (puede llegar 'fluid'). 17// - Los mid solo piden los formatos de escritorio que caben en el ancho del hueco. 18window.googletag = window.googletag || { cmd: [] }; 19googletag.cmd.push(function () { 20 var NET = "/21760474234/noticiastrabajo.es/"; // red + unidad padre 21 var pubads = googletag.pubads(); 22 23 function defineSlots() { 24 // Metadatos del dataLayer: page_metadata trae page_type; section, content_type y page_version llegan en un push aparte sin event 25 var meta = {}; 26 (window.dataLayer || []).forEach(function (e) { 27 if ( 28 e && 29 (e.event === "page_metadata" || 30 e.page_type || 31 e.section || 32 e.content_type) 33 ) { 34 Object.assign(meta, e); 35 } 36 }); 37 38 // Size mappings: [ancho viewport, alto] => tamanos permitidos 39 var mapTop = googletag 40 .sizeMapping() 41 .addSize( 42 [1024, 0], 43 [ 44 [970, 250], 45 [970, 90], 46 [728, 90], 47 ], 48 ) 49 .addSize([768, 0], [[728, 90]]) 50 .addSize( 51 [0, 0], 52 [ 53 [320, 100], 54 [320, 50], 55 [300, 100], 56 ], 57 ) 58 .build(); 59 var mapMpu = googletag 60 .sizeMapping() 61 .addSize( 62 [1024, 0], 63 [ 64 [300, 600], 65 [300, 250], 66 ], 67 ) 68 .addSize([0, 0], []) 69 .build(); 70 var mapSide = googletag 71 .sizeMapping() 72 .addSize( 73 [1024, 0], 74 [ 75 [300, 600], 76 [300, 250], 77 ], 78 ) 79 .addSize([0, 0], []) 80 .build(); 81 var mapIntext = googletag 82 .sizeMapping() 83 .addSize([1024, 0], [[728, 90], [336, 280], [300, 250], "fluid"]) 84 .addSize([0, 0], [[300, 600], [336, 280], [300, 250], "fluid"]) // movil: AMP igual pero sin 336x280 85 .build(); 86 // intext-2 y 3: sin 300x600, que en estas posiciones paga menos que el 336x280 87 var mapIntextLow = googletag 88 .sizeMapping() 89 .addSize([1024, 0], [[728, 90], [336, 280], [300, 250], "fluid"]) 90 .addSize([0, 0], [[336, 280], [300, 250], "fluid"]) 91 .build(); 92 // Los mid dependen del ancho del hueco, no solo del viewport: en los listados de 93 // seccion la columna mide 628-836 px y un 970 empujaba el lateral fuera de pantalla. 94 // Si no cabe ningun formato de escritorio, se piden los de movil. 95 function mapMid(el) { 96 var width = el.clientWidth; 97 var mobile = [ 98 [336, 280], 99 [300, 250], 100 ]; 101 var desktop = [ 102 [970, 250], 103 [970, 90], 104 [728, 90], 105 ] 106 .filter(function (s) { 107 return s[0] <= width; 108 }) 109 .concat(mobile); 110 return googletag 111 .sizeMapping() 112 .addSize([1024, 0], desktop) 113 .addSize([0, 0], mobile) 114 .build(); 115 } 116 var mapTopM = googletag 117 .sizeMapping() 118 .addSize([1024, 0], []) 119 .addSize( 120 [0, 0], 121 [ 122 [336, 280], 123 [300, 250], 124 [320, 100], 125 [320, 50], 126 [300, 100], 127 ], 128 ) 129 .build(); 130 var mapSticky = googletag 131 .sizeMapping() 132 .addSize([1024, 0], []) // escritorio: fase 2 133 .addSize( 134 [0, 0], 135 [ 136 [320, 100], 137 [320, 50], 138 [300, 100], 139 ], 140 ) 141 .build(); 142 143 // La cabecera se pide nada mas cargar, a la vista del lector: si colapsara sin 144 // relleno, todo el contenido subiria de golpe (CLS de 0,12 a 0,46 medido en local). 145 // Estos huecos conservan su altura reservada aunque no haya anuncio. 146 var NEVER_COLLAPSE = { "nt-ad-top": true, "nt-ad-top-m": true }; 147 148 // Solo se define el slot si el div existe y es visible (display != none) 149 function define(unit, divId, sizes, mapping, pos) { 150 var el = document.getElementById(divId); 151 if ( 152 !el || 153 (el.offsetParent === null && getComputedStyle(el).position !== "fixed") 154 ) { 155 return; 156 } 157 var s = googletag.defineSlot(NET + unit, sizes, divId); 158 if (s) { 159 s.defineSizeMapping( 160 typeof mapping === "function" ? mapping(el) : mapping, 161 ).addService(pubads); 162 s.setConfig({ targeting: { pos: pos } }); 163 if (NEVER_COLLAPSE[divId]) { 164 s.setConfig({ collapseDiv: "DISABLED" }); 165 } 166 } 167 } 168 169 // Primer argumento: codigo de la unidad en Ad Manager (no el nombre). En
169web coincide con el id del div. 170 define( 171 "nt-ad-top", 172 "nt-ad-top", 173 [ 174 [970, 250], 175 [970, 90], 176 [728, 90], 177 [320, 100], 178 [320, 50], 179 [300, 100], 180 ], 181 mapTop, 182 "top", 183 ); 184 define( 185 "nt-ad-top-m", 186 "nt-ad-top-m", 187 [ 188 [336, 280], 189 [300, 250], 190 [320, 100], 191 [320, 50], 192 [300, 100], 193 ], 194 mapTopM, 195 "top_m", 196 ); 197 define( 198 "nt-ad-mpu-1", 199 "nt-ad-mpu-1", 200 [ 201 [300, 600], 202 [300, 250], 203 ], 204 mapMpu, 205 "mpu1", 206 ); 207 define( 208 "nt-ad-mpu-2", 209 "nt-ad-mpu-2", 210 [ 211 [300, 600], 212 [300, 250], 213 ], 214 mapMpu, 215 "mpu2", 216 ); 217 // Lateral de las listas verticales con publi de portada (uno por bloque) 218 document 219 .querySelectorAll('[id^="nt-ad-home-side-"]') 220 .forEach(function (el, i) { 221 define( 222 "nt-ad-home-side", 223 el.id, 224 [ 225 [300, 600], 226 [300, 250], 227 ], 228 mapSide, 229 "home_side" + (i + 1), 230 ); 231 }); 232 define( 233 "nt-ad-intext-1", 234 "nt-ad-intext-1", 235 [[728, 90], [300, 600], [336, 280], [300, 250], "fluid"], 236 mapIntext, 237 "intext1", 238 ); 239 define( 240 "nt-ad-intext-2", 241 "nt-ad-intext-2", 242 [[728, 90], [336, 280], [300, 250], "fluid"], 243 mapIntextLow, 244 "intext2", 245 ); 246 define( 247 "nt-ad-intext-3", 248 "nt-ad-intext-3", 249 [[728, 90], [336, 280], [300, 250], "fluid"], 250 mapIntextLow, 251 "intext3", 252 ); 253 define( 254 "nt-ad-mid-1", 255 "nt-ad-mid-1", 256 [ 257 [970, 250], 258 [970, 90], 259 [728, 90], 260 [336, 280], 261 [300, 250], 262 ], 263 mapMid, 264 "mid1", 265 ); 266 define( 267 "nt-ad-mid-2", 268 "nt-ad-mid-2", 269 [ 270 [970, 250], 271 [970, 90], 272 [728, 90], 273 [336, 280], 274 [300, 250], 275 ], 276 mapMid, 277 "mid2", 278 ); 279 define( 280 "nt-ad-sticky", 281 "nt-ad-sticky", 282 [ 283 [970, 90], 284 [728, 90], 285 [320, 100], 286 [320, 50], 287 [300, 100], 288 ], 289 mapSticky, 290 "sticky", 291 ); 292 293 // Key-values de pagina 294 googletag.setConfig({ 295 targeting: { 296 page_type: String(meta.page_type || "other"), 297 section: String(meta.section || ""), 298 content_type: String(meta.content_type || ""), 299 page_version: "canonical", 300 }, 301 }); 302 303 // Rendimiento. collapseDiv ON_NO_FILL: el hueco conserva su altura reservada hasta la 304 // respuesta y solo colapsa si no hay relleno (collapseEmptyDivs(true) de la guia lo 305 // colapsaba ANTES de pedir el anuncio y anulaba la reserva de espacio contra el CLS). 306 // threadYield ENABLED_ALL_SLOTS: GPT cede el hilo principal entre slots tambien con los 307 // que estan a la vista (por defecto solo con los de fuera). Mejora el INP: el clic en 308 // "Aceptar" del CMP dispara las peticiones que esperaban al consentimiento y bajo de 309 // 200-208 ms a 144 ms de mediana (movil emulado, CPU 4x, portada y articulo). 310 googletag.setConfig({ 311 lazyLoad: { 312 fetchMarginPercent: 100, 313 renderMarginPercent: 50, 314 mobileScaling: 2.0, 315 }, 316 collapseDiv: "ON_NO_FILL", 317 centering: true, 318 singleRequest: true, 319 threadYield: "ENABLED_ALL_SLOTS", 320 }); 321 322 // Zocalo: solo visible si hay relleno; ajusta su altura al tamano servido y reserva 323 // ese espacio al final del body. El boton de cierre (Layout.astro) lo quita del DOM. 324 // Con el refresco solo crece, nunca encoge: si tras un 320x100 llega un 320x50 el 325 // borde de arriba bajaria 50 px y contaria como layout shift (CLS) en cada refresco. 326 pubads.addEventListener("slotRenderEnded", function (ev) { 327 if (ev.slot.getSlotElementId() !== "nt-ad-sticky") { 328 return; 329 } 330 var wrap = document.getElementById("nt-sticky-wrap"); 331 if (!wrap) { 332 return; 333 } 334 if (ev.isEmpty) { 335 wrap.style.display = "none"; 336 document.body.style.paddingBottom = ""; 337 return; 338 } 339 var height = Math.max( 340 (Array.isArray(ev.size) && ev.size[1]) || 50, // size puede ser 'fluid' 341 parseInt(wrap.style.height, 10) || 0, 342 ); 343 wrap.style.height = height + "px"; 344 wrap.style.visibility = "visible"; 345 document.body.style.paddingBottom = (wrap.offsetHeight || height) + "px"; 346 }); 347 348 // Interstitial de Google (pantalla completa entre paginas). GPT devuelve null si la 349 // pagina no es apta (p. ej. escritorio sin soporte), y entonces no se define. 350 var inter = googletag.defineOutOfPageSlot( 351 NET + "nt-ad-interstitial", 352 googletag.enums.OutOfPageFormat.INTERSTITIAL, 353 ); 354 if (inter) { 355 inter 356 .addService(pubads) 357 .setConfig({ targeting: { pos: "interstitial" } }); 358 } 359 360 // Refresco del zocalo: 30 s despues de cada impresion visible, con la pestana visible 361 // (si esta oculta, espera a que vuelva) y como maximo 10 veces. El key-value refresh 362 // lleva la cuenta. No se refresca si el lector lo ha cerrado (el boton lo quita del DOM). 363 var NT_REFRESH_MS = 30000; 364 var NT_REFRESH_MAX = 10; 365 var stickyRefreshes = 0; 366 pubads.addEventListener("impressionViewable", function (ev) { 367 var s = ev.slot; 368 if ( 369 s.getSlotElementId() !== "nt-ad-sticky" || 370 stickyRefreshes >= NT_REFRESH_MAX 371 ) { 372 return; 373 } 374 function refresh() { 375 if (!document.getElementById("nt-ad-sticky")) { 376 return; 377 } 378 if (document.visibilityState !== "visible") { 379 document.addEventListener("visibilitychange", refresh, { 380 once: true, 381 }); 382 return; 383 } 384 stickyRefreshes++; 385 s.setConfig({ targeting: { refresh: String(stickyRefreshes) } }); 386 pubads.refresh([s]); 387 } 388 setTimeout(refresh, NT_REFRESH_MS); 389 }); 390 391 googletag.enableServices(); 392 393 // display() de todos los huecos presentes, interstitial incluido (no hace falta script 394 // inline junto a cada div). Se pasa el slot y no el id: el interstitial no tiene div.
395 pubads.getSlots().forEach(function (s) { 396 googletag.display(s); 397 }); 398 } 399 400 if (document.readyState === "loading") { 401 document.addEventListener("DOMContentLoaded", defineSlots); 402 } else { 403 defineSlots(); 404 } 405});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.