1function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 2 3/** 4 * TB Prebid 5 * Version: 2.0 6 * Description: Uma implementação do Tecnoblog para o Prebid 7 * Author: Matheus Pratta <https://matheus.io> 8 */ 9(() => { 10 var _class, _temp, _class2, _temp2, _temp3, _temp4, _temp5; 11 12 // Stub vazio para quando a função não existe 13 const noop = () => {}; // Classe de Timer para ser usado pela API 14 15 16 const Timer = function Timer(callback, delay) { 17 let timerId, 18 start, 19 remaining = delay; 20 this.running = false; 21 this.finished = false; 22 23 let callbackHandle = () => { 24 this.running = false; 25 this.finished = true; 26 if (callback) callback(); 27 }; 28 29 this.pause = function () { 30 if (this.finished || !this.running) return; 31 window.clearTimeout(timerId); 32 remaining -= new Date() - start; 33 this.running = false; 34 }; 35 36 this.resume = function () { 37 if (this.finished || this.running) return; 38 start = new Date(); 39 window.clearTimeout(timerId); 40 timerId = window.setTimeout(callback, remaining); 41 this.running = true; 42 }; 43 44 this.increaseBy = function (amount) { 45 this.pause(); 46 47 if (!this.finished) { 48 this.remaining += amount; 49 } else { 50 this.remaining = amount; 51 this.finished = false; 52 } 53 54 this.resume(); 55 }; 56 57 this.resume(); 58 }; // Classe de Eventos para serem usados pela API 59 60 61 const Events = (_temp = _class = class Events { 62 // Onde ficaram todos hooks 63 // Adiciona um listener 64 static on(eventCode, callback) { 65 // Cria a array de eventos se necessário usando a função clear 66 if (!Events.fetch(eventCode)) Events.clear(eventCode); // Insere o listener 67 68 Events.__events[eventCode.toLowerCase()].push(callback); 69 } // Obtém os listeners de um evento 70 71 72 static fetch(eventCode, defaultValue) { 73 // Obtém o listener ou default 74 return Events.__events[eventCode.toLowerCase()] || defaultValue; 75 } // Limpa um listener 76 77 78 static clear(eventCode) { 79 // Esvazia toda a array do listener 80 Events.__events[eventCode.toLowerCase()] = []; 81 } // Dispara um listener 82 83 84 static trigger(eventCode) { 85 // Pega a array de eventos 86 const eventHandlers = Events.fetch(eventCode, []); // Faz o disparo em cada um dos handlers 87 88 eventHandlers.map(handler => (handler || noop).apply({ 89 eventCode: eventCode 90 }, Utils.argumentsToArray(arguments).slice(2))); 91 } // Dispara um listener e depois limpa o mesmo 92 93 94 static triggerAndClear(eventCode) { 95 // Faz o trigger 96 Events.trigger.apply(this, [eventCode].concat(Utils.argumentsToArray(arguments))); // Limpa o evento 97 98 Events.clear(eventCode); 99 } 100 101 }, _defineProperty(_class, "__events", {}), _temp); // Classe de Utilitários para serem usados pela API 102 103 const Utils = (_temp2 = _class2 = class Utils { 104 // Diz quanto tempo faz que o script está rodando 105 static getRunningTime() { 106 return (Date.now() - Utils.getRunningTimeStart) / 1000; 107 } // Converte um objeto de arguments para array 108 109 110 static argumentsToArray(args) { 111 let output = []; 112 113 for (let i = 0; i < args.length; i++) { 114 output.push(args[i]); 115 } 116 117 return output; 118 } // Detecta quando um determinado elemento terminou de carregar (enquanto baixa o HTML) 119 120 121 static trackElementLoading(element, callback) { 122 // Cria o código único do evento 123 const eventCode = `Utils.trackElementLoading::${element}`; // Insere o callback direto nos eventos 124 125 Events.on(eventCode, callback); // Testa se o elemento já existe 126 127 const elementExists = document.querySelector(element); // Se ele já existe, dá um trigger no evento e não executa mais nada 128 129 if (!!elementExists) { 130 Events.triggerAndClear(eventCode, elementExists); 131 return; 132 } // Caso não exista, a gente vai utilizar o MutationObserver. Primeiro passo é detectar e inicializar ele (caso não tenha sido): 133 134 135 if (window.MutationObserver) { 136 // Insere na lista de observações 137 if (!Utils.trackElementLoading.observerList) Utils.trackElementLoading.observerList = []; 138 Utils.trackElementLoading.observerList.push({ 139 selector: element, 140 event: eventCode 141 }); // Inicializa o observer 142 143 if (!Utils.trackElementLoading.observer) { 144 Utils.trackElementLoading.observer = new MutationObserver(mutations => { 145 // Processa as mutações 146 for (let iMutation = 0; iMutation < mutations.length; iMutation++) { 147 for (let iNode = 0; iNode < mutations[iMutation].addedNodes.length; iNode++) { 148 // Este é o node que foi inserido 149 let addedNode = mutations[iMutation].addedNodes[iNode]; // Passa por cada um dos elementos sendo observados e checa se ele bate 150 151 for (let iObserved = 0; iObserved < Utils.trackElementLoading.observerList.length; iObserved++) { 152 // Este é o seletor observado 153 let observedSelector = Utils.trackElementLoading.observerList[iObserved]; // Testa se ele bate com o node 154 155 if (observedSelector && addedNode.matches && addedNode.matches(observedSelector.selector)) { 156 // Bateu! \õ/ agora vamos remover da lista em andamento e chamar o evento 157 Utils.trackElementLoading.observerList[iObserved] = null; 158 delete Utils.trackElementLoading.observerList[iObserved]; 159 Events.triggerAndClear(observedSelector.event, addedNode); 160 break; 161 } 162 } 163 } 164 } // Depois que processou as mutações, atualiza a lista de elementos sendo acompanhados para remover os itens vazios (excluÃdos) 165 166 167 Utils.trackElementLoading.observerList = Utils.trackElementLoading.observerList.filter(observer => !!observer); // Caso o observer não tenha mais nada pra observar, desconectamos e limpamos ele 168 169 if (Utils.trackElementLoading.observerList.length == 0) { 170 Utils.trackElementLoading.observer.disconnect(); 171 Utils.trackElementLoading.observer = null; 172 delete Utils.trackElementLoading.observer; 173 } 174 }); // Começa a observar 175 176 this.trackElementLoading.observer.observe(document.documentElement, { 177 childList: true, 178 subtree: true 179 }); 180 } 181 } else { 182 // Coloca o trigger para quando o documento der ready. 183 document.addEventListener('ready', () => { 184 // Detecta se o elemento carregou 185 const elementLoaded = document.querySelector(element); // Dispara o evento 186 187 if (!!elementLoaded) Events.triggerAndClear(eventCode, elementLoaded); 188 }); 189 } 190 } 191 192 }, _defineProperty(_class2, "getRunningTimeStart", Date.now()), _temp2); // Eventos e debug 193 194 const Debug = function Debug() { 195 if (Debug.enabled) console.log.apply(console, [Debug.prefix, `[${Utils.getRunningTime().toFixed(3)}]`].concat(Utils.argumentsToArray(arguments))); 196 }; 197 198 Debug.info = function () { 199 if (Debug.enabled) console.info.apply(console, [Debug.prefix, `[${Utils.getRunningTime().toFixed(3)}]`].concat(Utils.argumentsToArray(arguments))); 200 }; 201 202 Debug.warn = function () { 203 console.warn.apply(console, [Debug.prefix, `[${Utils.getRunningTime().toFixed(3)}]`].concat(Utils.argumentsToArray(arguments))); 204 }; 205 206 Debug.error = function () { 207 console.error.apply(console, [Debug.prefix, `[${Utils.getRunningTime().toFixed(3)}]`].concat(Utils.argumentsToArray(arguments))); 208 }; 209 210 Debug.prefix = 'TB-Prebid'; 211 Debug.enabled = ~document.location.hash.indexOf('debug'); // Classe para acompanhar visibilidade do usuário 212 213 const ActivityTracker = new (_temp3 = class ActivityTracker { 214 // Lista de eventos que são considerados ações do usuário 215 // Inicializa o tracker de atividades 216 constructor() { 217 _defineProperty(this, "validActivityEvents", ['mousedown', 'mousemove', 'mouseup', 'scroll', 'touchstart', 'touchmove', 'touch', 'keydown', 'ke
217ypress', 'keyup']); 218 219 _defineProperty(this, "isCurrentlyActive", false); 220 221 _defineProperty(this, "inactivityTimeout", 6000); 222 223 _defineProperty(this, "handleActivitySignalIsProcessing", false); 224 225 _defineProperty(this, "handleActivitySignalInactiveTimeout", null); 226 227 // Faz hook na janela e no documento para processar os eventos de atividade 228 this.validActivityEvents.map(eventName => { 229 window.addEventListener(eventName, this.handleActivitySignal); 230 document.addEventListener(eventName, this.handleActivitySignal); 231 }); 232 } // Indica se está ativo ou não 233 234 235 handleActivitySignal() { 236 // Throttling 237 if (!!this.handleActivitySignalIsProcessing) return; 238 this.handleActivitySignalIsProcessing = true; // Em casos onde o usuário não estava ativo, emite um evento 239 240 if (!this.isCurrentlyActive) { 241 Events.trigger('useractive'); 242 } // Mantém o status em true 243 244 245 this.isCurrentlyActive = true; // Limpa o timeout atual de inatividade 246 247 if (!!this.handleActivitySignalInactiveTimeout) clearTimeout(this.handleActivitySignalInactiveTimeout); // Gera um novo timeout para inatividade 248 249 this.handleActivitySignalInactiveTimeout = setTimeout(() => { 250 this.isCurrentlyActive = false; 251 Events.trigger('userinactive'); 252 }, this.inactivityTimeout); // Ativa eventos novamente 253 254 setTimeout(() => { 255 this.handleActivitySignalIsProcessing = false; 256 }, 100); 257 } 258 259 }, _temp3)(); // Classe de Analytics da API 260 261 const TecnoblogAdAnalytics = class TecnoblogAdAnalytics { 262 static send(action, label, value) { 263 if (window.ga) { 264 // Argumentos padrão do GA 265 let args = ['send', 'event', 'Ads']; // Prepara argumentos custom 266 267 if (action) args.push(action); 268 if (label) args.push(label); 269 if (value) args.push(value); // Obrigar a ter mais de 3 args 270 271 if (args.length <= 3) { 272 // Exibe erro
273 Debug.error('AdAnalytics:', 'Argumentos insuficientes', args); 274 return; 275 } // Dispara 276 277 278 window.ga.apply(window.ga, args); 279 } 280 } 281 282 }; // Classe de AdSlot para ser usado pela API 283 284 const AdSlot = (_temp4 = class AdSlot { 285 // Inicializador do AdSlot 286 constructor(adManager, { 287 eventCode, 288 mediaQuery, 289 block, 290 code, 291 container, 292 sizes, 293 mediaTypes, 294 prebid, 295 options, 296 events 297 }) { 298 _defineProperty(this, "isSlotBiddable", false); 299 300 _defineProperty(this, "bidRefreshTimer", null); 301 302 _defineProperty(this, "bidRefreshShowedMessage", false); 303 304 _defineProperty(this, "impressionViewed", false); 305 306 _defineProperty(this, "viewablePercentage", 0.00); 307 308 // Manager do slot, para notificar quando o slot está pronto para bids, etc 309 this.manager = adManager; // Opções 310 311 this.options = Object.assign({ 312 unsafeFrame: true, 313 minViewabilityPercent: this.manager.options.viewabilityTracker.minViewabilityPercent, 314 timeoutRefresh: this.manager.options.viewabilityTracker.timeoutRefresh, 315 timeoutRefreshRetry: this.manager.options.viewabilityTracker.timeoutRefreshRetry 316 }, options); // Media Query condicional para exibir o Ad 317 318 this.mediaQuery = mediaQuery || 'screen'; // Bloco do anúncio (GPT) 319 320 this.block = block; // ID/Código do elemento do Ad 321 322 this.code = code; // Tipos de mÃdia 323 324 this.mediaTypes = mediaTypes; // Código de eventos 325 326 this.eventCode = eventCode; // Eventos 327 328 for (let event in events || {}) { 329 // Pega qual é o callback 330 let callback = events[event]; // Registra o evento 331 332 this.on(event, callback); 333 } // Container do Ad 334 335 336 this.container = container; // Tamanhos do Ad 337 338 this.sizes = sizes; // Definições do Prebid 339 340 this.prebid = prebid; // Definições de slot registradas 341 342 this.registeredWith = {}; // Se o media atual for adequado, já renderizamos o slot 343 344 if (this.isSlotAvailableOnCurrentMedia()) this.renderElement(); 345 } // Eventos 346 347 348 on(event, callback) { 349 Events.on(`${this.eventCode}::${event}`, callback); 350 } 351 352 trigger(event) { 353 Events.trigger.apply(this, [`${this.eventCode}::${event}`].concat(Utils.argumentsToArray(arguments))); 354 } 355 356 triggerAndClear(event) { 357 Events.triggerAndClear.apply(this, [`${this.eventCode}::${event}`].concat(Utils.argumentsToArray(arguments))); 358 } // Quando houver resize, testar se devemos ou não exibir o slot 359 360 361 handleWindowResize() { 362 // Nosso slot está disponÃvel no media atual? 363 if (this.isSlotAvailableOnCurrentMedia()) { 364 // Caso esteja, detecta se já foi renderizado. Caso tenha, deixamos visÃvel, caso não, renderizamos. 365 if (this.element) { 366 this.element.style.display = 'block'; 367 this.element.style.visibility = 'visible'; 368 this.manager.makeSlotBiddable(this); 369 } else { 370 this.renderElement(); 371 } 372 } else { 373 // Se o slot estiver renderizado, deixamos ele invisÃvel, caso contrário não fazemos nada 374 if (!!this.element) { 375 this.element.style.display = 'none'; 376 this.element.style.visibility = 'hidden'; 377 this.manager.makeSlotUnbiddable(this); 378 } 379 } 380 } // Detecta se o media atual é adequado 381 382 383 isSlotAvailableOnCurrentMedia() { 384 return window.matchMedia(this.mediaQuery).matches; 385 } // Renderiza o HTML do slot 386 387 388 renderElement() { 389 // Não renderiza duas vezes 390 if (!!this.element) return; // Pega o container do nosso slot 391 392 Utils.trackElementLoading(this.container, containerElement => { 393 // Cria a <div> do nosso slot 394 let element = document.createElement('div'); // Coloca o ID do slot 395 396 element.setAttribute('id', this.code); // Insere ele no container 397 398 containerElement.appendChild(element); // Define slot como renderizado 399 400 this.element = element; // Emite evento 401 402 this.triggerAndClear('rendered'); // Registra o slot no Prebid 403 404 if (this.prebid) this.registerWithPrebid(); // Registra o slot no GPT 405 406 this.registerWithGpt(); // Define o slot como biddable 407 408 this.manager.makeSlotBiddable(this); 409 }); 410 } // Indica se o slot está biddable 411 412 413 // Verifica se o slot pod receber anúncios 414 canReceiveAds() { 415 // Se o elemento não existe, retorna false 416 if (!this.element) return false; // Checa se o slot está biddable mas a impressão atual não foi vista 417 418 if (this.isSlotBiddable && !this.impressionViewed) return false; // Checa se ele está visÃvel 419 420 if ('hidden' == this.element.style.visibility) return false; // Checa se ele está visÃvel 421 422 if ('none' == this.element.style.display) return false; // Deu bom, retorna true 423 424 return true; 425 } // Registra o slot no Prebid 426 427 428 registerWithPrebid() { 429 // Coloca na fila do Prebid 430 this.manager.enqueuePrebid(() => { 431 // Não registra se não pode receber ads 432 if (!this.canReceiveAds()) return Debug.warn('Registro de slot no Prebid nao foi possivel. Elemento esta visivel?', this.code); // Não registra duas vezes! 433 434 if (this.registeredWith.prebid) return; // Debug! 435 436 Debug.info('Registrando slot no Prebid:', this.code); // Filtrar tamanhos para o Prebid 437 438 let prebidSizes = []; 439 440 for (let iSize = 0; iSize < this.sizes.length; iSize++) { 441 // Pega o tamanho 442 let size = this.sizes[iSize]; // Devemos remover ele? 443 444 if (!Array.isArray(size) || ~size.indexOf('fluid')) continue; // Inserir na array de tamanhos adaptada ao prebid 445 446 prebidSizes.push(size); 447 } // Cria a definição de Ad Unit para o Prebid 448 449 450 let adUnitDef = Object.assign({
451 code: this.code, 452 mediaTypes: this.mediaTypes ? this.mediaTypes : { 453 banner: { 454 sizes: prebidSizes 455 } 456 }, 457 bids: this.prebid && this.prebid.bids ? this.prebid.bids : [] 458 }, this.prebid ? this.prebid : {}); // Registra no Prebid 459 460 Debug.info('Prebid.addAdUnits', this.code); 461 pbjs.addAdUnits(adUnitDef); // Salva no registro 462 463 this.registeredWith.prebid = adUnitDef; 464 }); 465 } // Registra o slot no GPT 466 467 468 registerWithGpt() { 469 // Coloca na fila do GPT 470 this.manager.enqueueGpt(() => { 471 // Não registra se não pode receber ads 472 if (!this.canReceiveAds()) return Debug.warn('Registro de slot no GPT nao foi possivel. Elemento esta visivel?', this.code); // Não registra duas vezes! 473 474 if (this.registeredWith.gpt) return; // Debug! 475 476 Debug.info('Registrando slot no GPT:', this.code); // Cria o slot GPT 477 478 let gptSlot = window.googletag.defineSlot(this.block, this.sizes, this.code); // Se o slot estiver null, dá erro 479 480 if (null == gptSlot) return Debug.warn('GPT retornou slot como null:', this.code); // Safe frame 481 482 Debug.info('GPT.setForceSafeFrame:', !this.options.unsafeFrame); 483 gptSlot.setForceSafeFrame(!this.options.unsafeFrame); // Inclui serviço pubads 484 485 Debug.info('GPT.addService:', 'pubads()'); 486 gptSlot.addService(googletag.pubads()); // Targeting opcional 487 488 if (this.options.targeting) { 489 for (let target in this.options.targeting) { 490 Debug.info('GPT.setTargeting:', target, this.options.targeting[target]); 491 gptSlot.setTargeting(target, this.options.targeting[target]); 492 } 493 } // Faz o fetch via SRA 494 495 496 Debug.info('GPT.display:', this.code); 497 window.googletag.display(gptSlot); // Salva no registro 498 499 this.registeredWith.gpt = gptSlot; 500 }); 501 } // Limpa o slot para receber um novo anúncio 502 503 504 cleanup() { 505 // Limpa no HTML 506 let slotInnerElement = document.querySelector(this.code); 507 if (!!slotInnerElement) document.querySelector(this.code).innerHTML = ''; 508 } // Processa o refresh do slot 509 510 511 refresh() { 512 // Executa apenas quando a página terminar de carregar, para não dar render block 513 this.manager.enqueueWhenPageReady(() => { 514 // Debug 515 Debug.info('Preparando exibicao do slot:', this.code); // Marca como não visto 516 517 this.impressionViewed = false; // Faz o refresh no GPT 518 519 if (this.registeredWith.gpt) { 520 Debug.info('Exibindo slot usando GPT:', this.code); 521 this.manager.refreshGpt({ 522 code: this.code, 523 slot: this.registeredWith.gpt 524 }); 525 } 526 }); 527 } // Função que refaz o bid do slot 528 529 530 bidRefresh() { 531 // Checamos se a % visÃvel dele atende nosso mÃnimo 532 if (!this.isInView()) { 533 // Não estamos, então recriamos o timer um pouco mais pra frente e deixamos um log 534 if (!this.bidRefreshShowedMessage) { 535 Debug.info('Slot solicitou refresh mas nao atende porcentagem minima de visibilidade para refresh:', this.code); 536 this.bidRefreshShowedMessage = true; 537 } // Para e remove o timer de refresh 538 539 540 this.bidRefreshTimer.pause(); 541 delete this.bidRefreshTimer; // Cria um novo timer 542 543 if (!this.bidRefreshTimer) this.bidRefreshTimer = new Timer(() => { 544 this.bidRefresh(); 545 }, this.options.timeoutRefreshRetry); // Encerra 546 547 return; 548 } // Permite exibir a mensagem novamente 549 550 551 this.bidRefreshShowedMessage = false; // Para e remove o timer de refresh 552 553 this.bidRefreshTimer.pause(); 554 delete this.bidRefreshTimer; // Enfileramos o refresh para o próximo momento que o usuário e a janela estiverem ativos 555 556 this.manager.enqueueWhenVisibleAndActive(() => { 557 // Debug 558 Debug.info('Preparando slot para bid:', this.code); // Insere na fila de bids 559 560 this.manager.makeSlotBiddable(this, true); 561 }); // Debug 562 563 Debug.info('Slot inserido na fila de bid:', this.code); 564 } // Determina se a impressão do anúncio já computou viewability 565 566 567 computeViewability() { 568 // Marca o slot com impressão exibida 569 this.impressionViewed = true; 570 Debug.info('Impressao exibida:', this.code, this.impressionViewed); // Cria um timer para disparar o refresh do anúncio 571 572 if (!this.bidRefreshTimer) this.bidRefreshTimer = new Timer(() => { 573 this.bidRefresh(); 574 }, this.options.timeoutRefresh); // Emite evento de viewability 575 576 this.trigger('viewability', this); 577 } // Porcentagem do slot que está visÃvel 578 579 580 updateViewablePercentage(percentage) { 581 // Atualiza o valor 582 this.viewablePercentage = percentage; // Emite um evento 583 584 this.trigger('slotVisibilityChanged', this.isInView(), this.viewablePercentage); 585 } // Processa impressões perdidas (quando o anúncio buga e fica em branco) 586 587 588 handleMissedImpression(event) { 589 // Debug 590 Debug.warn(`Impressao perdida no slot '${this.code}':`, event); 591 Debug.warn(`Enfileirando slot '${this.code}' sem Prebid...`); // Enfileira para rodar quando der o tempo de refresh 592 593 if (!this.bidRefreshTimer) { 594 this.bidRefreshTimer = new Timer(() => { 595 // Garantir que a página está visÃvel e ativa 596 this.manager.enqueueWhenVisibleAndActive(() => { 597 //Debug 598 Debug.warn(`Executando bid do slot '${this.code}' sem Prebid...`); // Tenta limpar os targetings do Prebid e exibir diretamente da GPT 599 600 this.registeredWith.gpt.clearTargeting(); 601 googletag.pubads().refresh([this.registeredWith.gpt]); 602 }); 603 }, this.options.timeoutRefresh); 604 } 605 } // Processa mudanças de status do slot 606 607 608 handleStateChange(eventName, event) { 609 // Debug 610 Debug.info(`Evento '${eventName}' disparado no slot '${this.code}':`, event); 611 612 if (eventName == 'slotVisibilityChanged') { 613 // Loga no Google Analytics o que for de % visÃvel 614 TecnoblogAdAnalytics.send(`Slot Visibility Changed`, this.block || this.code, event.inViewPercentage); 615 } else { 616 // Loga no Google Analytics o que não for de % visÃvel 617 TecnoblogAdAnalytics.send(`GPT Event ${eventName}`, this.block || this.code); 618 } 619 } // Retorna se o slot está ou não visÃvel 620 621 622 isInView() { 623 return this.viewablePercentage >= this.options.minViewabilityPercent; 624 } 625 626 }, _temp4); // Classe principal da API 627 628 const TecnoblogAdBidding = (_temp5 = class TecnoblogAdBidding { 629 // Opções default 630 // Inicializador da API 631 constructor({ 632 options, 633 labels, 634 ads, 635 adsPerLabel 636 }) { 637 _defineProperty(this, "options", { 638 // Ajustes do Prebid 639 prebid: { 640 timeout: 1100 641 }, 642 // Ajustes do GPT 643 gpt: { 644 centering: true, 645 unsafeFrame: true, 646 lazyLoad: { 647 fetchMarginPercent: 70, 648 renderMarginPercent: 50, 649 mobileScaling: 1.7 650 } 651 } 652 }); 653 654 _defineProperty(this, "isPageVisible", true); 655 656 _defineProperty(this, "isUserActive", true); 657 658 _defineProperty(this, "queueOnVisibleAndActive", []); 659 660 _defineProperty(this, "handleQueueVisibleAndActiveIsProcessing", false); 661 662 _defineProperty(this, "biddableSlots", []); 663 664 _defineProperty(this, "makeSlotBiddableAutoBidTimeout", null); 665 666 _defineProperty(this, "queueOnPageReady", []); 667 668 _defineProperty(this, "isProcessingWhenPageReady", false); 669 670 _defineProperty(this, "bidCurrently", []); 671 672 _defineProperty(this, "refreshGptSlots", []); 673 674 _defineProperty(this, "refreshGptIsRefreshing", false); 675 676 // Opções básicas 677 this.options = Object.assign(this.options, options); // Debug das opções 678 679 Debug.info('Inicializando TB Prebid com as opcoes:', this.options); // Inicializa estrutura básica do Prebid 680 681 window.pbjs = window.pbjs || { 682 que: [] 683 }; // Inicializa estrutura básica do GPT 684 685 window.googletag = window.googletag || { 686 cmd: [] 687 }; // Coloca notificações de inicialização na fila do GPT e Prebid 688 689 this.enqueuePrebid(() => { 690 Debug.info(`Prebid inicializado. Versao: ${pbjs.version}`); 691 }); 692 this.enqueueGpt(() => { 693 Debug.info(`GPT inicializado. Versao: ${googletag.getVersion()}`); 694 }); // Inicialização do Prebid 695 696 this.enqueuePrebid(() => { 697 // Seta configuração básica do prebid 698 window.pbjs.setConfig({ 699 useBidCache: true, 700 bidderSequence: 'random', 701 priceGranularity: 'high', 702 enableSendAllBids: false, 703 bidderTimeout: this.options.prebid.timeout, 704 userSync: { 705 syncDelay: 5000, 706 iframeEnabled: true 707 }, 708 currency: { 709 adServerCurrency: 'USD' 710 } 711 }); // Seta multiplicadores de bidder (corrige CPM) 712 713 if (this.options.prebid.bidderCpmMultipliers) { 714 let bidderSettingsCurrent = window.pbjs.bidderSettings || {}; 715
716 for (let bidder in this.options.prebid.bidderCpmMultipliers) { 717 // Pegamos o valor do bidder 718 let bidderValue = this.options.prebid.bidderCpmMultipliers[bidder]; // Pegamos as opções deste bidder 719 720 let bidderSettings = bidderSettingsCurrent[bidder] || {}; // Configuramos o ajuste de CPM 721 722 bidderSettings.bidCpmAdjustment = bidCpm => { 723 // Calculamos novo valor 724 let newBidCpm = bidCpm * bidderValue; // Debug 725 726 Debug.info('Prebid.bicCpmAdjustment:', bidder, `$${bidCpm.toFixed(3)} => $${newBidCpm.toFixed(3)}`); // Retornamos o valor com o bid ajustado 727 728 return newBidCpm; 729 }; // Salvamos as novas opções deste bidder 730 731 732 bidderSettingsCurrent[bidder] = bidderSettings; 733 } // Atualizamos todas opções 734 735 736 window.pbjs.bidderSettings = bidderSettingsCurrent; 737 } 738 }); // Inicialização do GPT 739 740 this.enqueueGpt(() => { 741 // Desativa carregamento automático (conflita com Prebid) 742 window.googletag.pubads().disableInitialLoad(); // Ativa modo single request 743 744 window.googletag.pubads().enableSingleRequest(); // Ativa centralização 745 746 window.googletag.pubads().setCentering(this.options.gpt.centering); // Ativa safe-frame 747 748 window.googletag.pubads().setForceSafeFrame(!this.options.gpt.unsafeFrame); // Ativa lazy-loading 749 750 if (this.options.gpt.lazyLoad) window.googletag.pubads().enableLazyLoad(this.options.gpt.lazyLoad); // Ativa serviços 751 752 window.googletag.enableServices(); // Processa mudança de states de um slot (mais para debug) 753 754 window.googletag.pubads().addEventListener('slotVisibilityChanged', event => { 755 this.handleGptSlotState('slotVisibilityChanged', event); 756 }); 757 window.googletag.pubads().addEventListener('impressionViewable', event => { 758 this.handleGptSlotState('impressionViewable', event); 759 }); 760 window.googletag.pubads().addEventListener('slotRenderEnded', event => { 761 this.handleGptSlotState('slotRenderEnded', event); 762 }); 763 window.googletag.pubads().addEventListener('slotRequested', event => { 764 this.handleGptSlotState('slotRequested', event); 765 }); 766 window.googletag.pubads().addEventListener('slotOnload', event => { 767 this.handleGptSlotState('slotOnload', event); 768 }); // Registra eventos 769 770 window.googletag.pubads().addEventListener('impressionViewable', event => { 771 this.handleGptImpressionViewable(event); 772 }); 773 window.googletag.pubads().addEventListener('slotVisibilityChanged', event => { 774 this.handleGptSlotViewability(event); 775 }); 776 window.googletag.pubads().addEventListener('slotRenderEnded', event => { 777 this.handleGptSlotRendered(event); 778 }); 779 }); // Importa as labels 780 781 this.labels = labels; // Prepara onde os slots ficarão armazenados 782 783 this.adSlots = []; // Cria uma lista de ads, caso não tenha sido passada 784 785 ads = ads || []; // Caso os slots estejam organizados por label, convertemos de formato 786 787 if (!!adsPerLabel) { 788 // Loop em cada label 789 for (let adLabelCode in adsPerLabel) { 790 // Pega todos os ads 791 let adLabelAds = adsPerLabel[adLabelCode]; // Processa os ads 792 793 adLabelAds = adLabelAds.map(adSlotDef => { 794 adSlotDef.label = adLabelCode; 795 return adSlotDef; 796 }); // Coloca junto dos outros ads 797 798 ads = ads.concat(adLabelAds); 799 } 800 } // Processa os Ads 801 802 803 for (let iAdSlotDef = 0; iAdSlotDef < ads.length; iAdSlotDef++) { 804 // Importa a definição do slot 805 let adSlotDef = ads[iAdSlotDef]; // Define um tipo caso ele não possua 806 807 adSlotDef.type = adSlotDef.type || 'single'; // Cria o AdSlot de acordo com o tipo 808 809 switch (adSlotDef.type.toLowerCase()) { 810 case 'single': 811 // Apenas insere o slot 812 this.insertAdSlot(adSlotDef); 813 break; 814 815 case 'ab-bidding': 816 // Verifica se o A/B bidding está ativo 817 if (!this.options.abBidding) { 818 Debug.warn('A/B bidding nao esta ativo nas configuracoes.'); 819 break; 820 } // Pega os pesos do A/B bidding 821 822 823 let abWeights = this.options.abBidding.weights || {}; 824 let abValues = []; // Converte os pesos em valores 825 826 for (let abWeight in abWeights) { 827 // Só insere no sorteio se o valor estiver disponivel nas definições de slot 828 if (!adSlotDef.formats[abWeight]) continue; // Prepara o sorteio 829
830 for (let abWeightValue = 0; abWeightValue < abWeights[abWeight]; abWeightValue++) { 831 abValues.push(abWeight); 832 } 833 } // Caso nenhum peso for encontrado, retorna erro 834 835 836 if (abValues.length == 0) { 837 Debug.warn('Nenhum valor para A/B bidding foi encontrado:', Object.keys(adSlotDef.formats).join(', '), '<->', Object.keys(abWeights).join(', ')); 838 break; 839 } // Determina qual das definições de slot(s) vai ser usada 840 841 842 Debug.info('Determinando A/B bidding para os valores:', abValues.join(', ')); 843 let abWinner = abValues[Math.round(Math.random() * (abValues.length - 1))]; 844 let abWinnerFormat = adSlotDef.formats[abWinner]; 845 Debug.info('Utilizando A/B bidding com o vencedor:', abWinner); // Insere os slots 846 847 for (let iAbSlotDef = 0; iAbSlotDef < abWinnerFormat.length; iAbSlotDef++) { 848 // Cria as labels corretas, sem isso os ads desktop podem aparecer no mobile e vice-versa 849 if (adSlotDef.label) abWinnerFormat[iAbSlotDef].label = adSlotDef.label; // Insere o slot 850 851 this.insertAdSlot(abWinnerFormat[iAbSlotDef]); 852 } 853 854 break; 855 856 default: 857 Debug.warn('Tipo de slot invalido:', adSlotDef.type); 858 break; 859 } 860 } // Event handler pra quando o usuário ficar ativo e inativo 861 862 863 Events.on('useractive', () => { 864 this.isUserActive = true; 865 this.handleQueueVisibleAndActive(); 866 }); 867 Events.on('userinactive', () => { 868 this.isUserActive = false; 869 }); // Event handler para quando a janela perder foco ou ganhar foco 870 871 window.addEventListener('blur', () => { 872 this.isPageVisible = false; 873 }); 874 window.addEventListener('focus', () => { 875 this.isPageVisible = true; 876 }); // Event handler para detectar quando a página está pronta 877 878 window.addEventListener('readystatechange', this.handleWhenPageReady); // Event handler pra quando houver resize da janela passar para cada slot 879 880 // window.addEventListener('resize', () => { 881 // this.adSlots.map(adSlot => adSlot.handleWindowResize()); 882 // }); 883 } // Indica se a página está ou não visÃvel 884 885 886 // Determina se a página e o usuário estão ativos 887 isVisibleAndActive() { 888 // Retorna se a página está visÃvel e o usuário está ativo 889 return this.isPageVisible && this.isUserActive; 890 } // Fila de ações a serem executadas quando o usuário e a página estão ativos 891 892 893 // Enfilera ações nos slots quando a página e usuário estiverem ativos 894 enqueueWhenVisibleAndActive(fn) { 895 // Insere no final da fila 896 this.queueOnVisibleAndActive.push(fn); // Se já está ativo, causa um trigger no handler, apenas caso já estiver ativo 897 898 this.handleQueueVisibleAndActive(); 899 } // Processa ações nos slots quando a página e o usuário estiverem ativos 900 901 902 handleQueueVisibleAndActive() { 903 // Caso não estiver visÃvel e ativo, não faz nada 904 if (!this.isVisibleAndActive()) return; // Throttling 905 906 if (!!this.handleQueueVisibleAndActiveIsProcessing) return; 907 this.handleQueueVisibleAndActiveIsProcessing = true; // Processa a fila 908 909 let fn = null; 910 911 while (fn = this.queueOnVisibleAndActive.shift()) { 912 // Só processa funções 913 if ('function' == typeof fn) { 914 // Dispara função 915 fn(); 916 } 917 } // Throttling (fim) 918 919 920 this.handleQueueVisibleAndActiveIsProcessing = false; 921 } // Insere um único AdSlot baseado numa definição de slot, já convertendo a label do mesmo em uma Media Query 922 923 924 insertAdSlot(adSlotDef) { 925 // Checa se o slot depende de uma idade mÃnima ou máxima do post 926 if (!!adSlotDef.contentMinAge || !!adSlotDef.contentMaxAge) { 927 // Checa se a idade do conteúdo foi informada, se não for paramos aqui e retornamos um warning 928 if (!window.currentContentAge) return Debug.warn(`O slot ${adSlotDef.code} depende da variavel 'window.currentContentAge', que nao esta presente. Pulando slot...`); // Detecta se o slot está fora da faixa de tempo configurada, se estiver fora a
928penas registramos no log e ignoramos 929 930 if (!((adSlotDef.contentMinAge || 0) <= window.currentContentAge && window.currentContentAge <= (adSlotDef.contentMaxAge || Infinity))) return Debug.info(`O slot ${adSlotDef.code} nao sera inserido pois nao atende aos requisitos de idade do conteudo atual.`); 931 } // Cria a Media Query caso ela não tenha sido especificada 932 933 934 adSlotDef.mediaQuery = adSlotDef.mediaQuery || this.labels[adSlotDef.label]; // Cria o objeto do slot 935 936 let adSlot = new AdSlot(this, adSlotDef); // Debug 937 938 Debug.info(`Inserindo slot ${adSlotDef.code} na lista de AdSlots:`, adSlotDef, adSlot); // Insere ele na lista de slots 939 940 this.adSlots.push(adSlot); // Retorna ele 941 942 return adSlot; 943 } // Registra quais slots podem receber bids 944 945 946 makeSlotBiddable(slot, autoBid) { 947 this.makeSlotUnbiddable(slot); 948 slot.isSlotBiddable = true; 949 this.biddableSlots.push(slot); // Detecta se usaremos autobid 950 951 autoBid = autoBid || false; // Depois de um breve intervalo (caso entre mais algum slot nesse tempo) disparar os bids 952 953 if (true == autoBid) { 954 // Reseta o timeout de auto-bid 955 if (this.makeSlotBiddableAutoBidTimeout) clearTimeout(this.makeSlotBiddableAutoBidTimeout); // Seta um novo timeout 956 957 this.makeSlotBiddableAutoBidTimeout = setTimeout(() => { 958 this.bid(); 959 }, 50); 960 } 961 } // Indica que um determinado slot não pode receber bids 962 963 964 makeSlotUnbiddable(slot) { 965 slot.isSlotBiddable = false; 966 this.biddableSlots = this.biddableSlots.filter(biddableSlot => biddableSlot.code != slot.code); 967 } // Detecta se a página carregou 968 969 970 isPageReady() { 971 return document.readyState == 'complete' || document.readyState == 'interactive'; 972 } // Coloca na fila de processamento para quando a página estiver "ready" 973 974 975 enqueueWhenPageReady(fn) { 976 // Se a página estiver pronta, executamos direto 977 if (this.isPageReady()) fn(); // Senão, colocamos na fila 978 979 this.queueOnPageReady.push(fn); 980 } // Processa a fila 981 982 983 handleWhenPageReady() { 984 // Não executa se não estiver pronto 985 if (!this.isPageReady()) return; // Debounce 986 987 if (this.isProcessingWhenPageReady) return; 988 this.isProcessingWhenPageReady = true; // Debug 989 990 if (this.queueOnPageReady.length > 0) Debug.info(`Carregamento da pagina concluido. Status atual: ${document.readyState}. Processando ${this.queueOnPageReady.length} eventos...`); // Processa fila 991 992 let fn = null; 993 994 while (fn = this.queueOnPageReady.shift()) { 995 if ('function' == typeof fn) fn(); 996 } // Debounce (fim) 997 998 999 this.isProcessingWhenPageReady = false; 1000 } // Coloca na fila do Prebid 1001 1002 1003 enqueuePrebid(fn) { 1004 window.pbjs.que.push(fn); 1005 } // Coloca na fila do Prebid 1006 1007 1008 enqueueGpt(fn) { 1009 window.googletag.cmd.push(fn); 1010 } // Função que roda todos os bids 1011 1012 1013 bid() { 1014 // Evita que duas bids ocorram ao mesmo tempo 1015 if (this.bid.isBidding) return; // Marca como bid em andamento 1016 1017 this.bid.isBidding = true; // Salva os biddableSlots 1018 1019 let biddableSlots = this.biddableSlots; // Limpa os biddableSlots 1020 1021 this.biddableSlots.map(slot => { 1022 slot.isSlotBiddable = false; 1023 }); 1024 this.biddableSlots = []; // Registro do que vai ser bidado 1025 1026 this.bidCurrently = []; // Bids para slots que usam Prebid 1027 1028 this.enqueuePrebid(() => { 1029 // Filtra slots que recebem bids do Prebid 1030 let biddablePrebid = biddableSlots.filter(slot => !!slot.registeredWith.prebid); 1031 let biddablePrebidCodes = biddablePrebid.map(slot => slot.code); // Marca os slots sendo bidados 1032 1033 this.bidCurrently = this.bidCurrently.concat(biddablePrebid); // Roda a bid 1034 1035 Debug.info('Prebid.requestBids:', biddablePrebidCodes); 1036 window.pbjs.requestBids({ 1037 adUnitCodes: biddablePrebidCodes, 1038 bidsBackHandler: () => { 1039 this.handlePrebidBidsBack(); 1040 } 1041 }); 1042 }); // Bids para slots que não usam Prebid 1043 1044 this.enqueueGpt(() => { 1045 // Filtra slots que não recebem bids do Prebid 1046 let biddableNonPrebid = biddableSlots.filter(slot => !slot.registeredWith.prebid); 1047 let biddableNonPrebidSlots = biddableNonPrebid.map(slot => slot.registeredWith.gpt); 1048 let biddableNonPrebidCodes = biddableNonPrebid.map(slot => slot.code); // Debug 1049 1050 if (biddableNonPrebidSlots.length > 0) Debug.info('Processando slots GPT direto (sem Prebid)'); // Faz o refresh dos slots (exibindo os anúncios) 1051 1052 Debug.info('pubads().refresh:', biddableNonPrebidCodes.join(', ')); 1053 window.googletag.pubads().refresh(biddableNonPrebidSlots); 1054 }); 1055 } // Obtém o slot a partir de um ID 1056 1057 1058 getSlotFromId(elementId) { 1059 // Processa todos slots procurando nosso slot destino 1060 for (let iSlot = 0; iSlot < this.adSlots.length; iSlot++) { 1061 // Retorna ele diretamente se for encontrado 1062 if (this.adSlots[iSlot].code == elementId) return this.adSlots[iSlot]; 1063 } // Retorna null se não encontrar 1064 1065 1066 return null; 1067 } // Processa evento de quando a impressão do slot foi vista 1068 1069 1070 handleGptImpressionViewable(event) { 1071 // Computa impressão no slot
1072 this.getSlotFromId(event.slot.getSlotElementId()).computeViewability(); 1073 } // Processa evento de visiblidade do slot do GPT 1074 1075 1076 handleGptSlotViewability(event) { 1077 // Atualiza a visibilidade do slot 1078 this.getSlotFromId(event.slot.getSlotElementId()).updateViewablePercentage(event.inViewPercentage); 1079 } // Processa evento de render do GPT 1080 1081 1082 handleGptSlotRendered(event) { 1083 // Caso o anúncio vier vazio => PREJUÃZO AAAAAAAAAAAAAA 1084 if (event.isEmpty) { 1085 this.getSlotFromId(event.slot.getSlotElementId()).handleMissedImpression(event); 1086 } 1087 } // Processa TODOS eventos de um slot 1088 1089 1090 handleGptSlotState(eventName, event) { 1091 // Passa o evento para o slot 1092 this.getSlotFromId(event.slot.getSlotElementId()).handleStateChange(eventName, event); 1093 } // Processa resposta das bids do Prebid 1094 1095 1096 handlePrebidBidsBack() { 1097 // Agora que temos as bids simplesmente aguardamos a resposta do AdServer da Google 1098 Debug.info('Resposta de bids do Prebid recebida. Aguardando AdServer da Google...'); // Coloca na fila do GPT 1099 1100 this.enqueueGpt(() => { 1101 this.awaitForGoogleAdServer(); 1102 }); 1103 } // Parte final para exibir os anúncios 1104 1105 1106 awaitForGoogleAdServer() { 1107 // Se já inicializou está tudo ok. 1108 if (!!this.awaitForGoogleAdServer.initialized) return; // Verifica se o pubads já está disponÃvel 1109 1110 if (!(window.googletag && window.googletag.pubadsReady)) { 1111 // Ainda não está disponÃvel, aguardamos um pouco mais 1112 setTimeout(() => { 1113 this.awaitForGoogleAdServer(); 1114 }, 50); 1115 return; 1116 } // Neste ponto o pubads está disponÃvel, enfileramos no GPT para poder setar o targeting 1117 1118 1119 Debug.info('AdServer da Google pronto, prosseguindo!'); 1120 this.awaitForGoogleAdServer.initialized = true; // Filtra quais slots serão setados 1121 1122 let prebidAdUnitCodes = this.bidCurrently.map(slot => slot.code); // Define o targeting do Prebid 1123 1124 Debug.info('Prebid.setTargetingForGPTAsync:', prebidAdUnitCodes.join(', ')); 1125 window.pbjs.setTargetingForGPTAsync(prebidAdUnitCodes); // Força o refresh (exibição) dos slots 1126 1127 this.bidCurrently.map(slot => { 1128 slot.cleanup(); 1129 slot.refresh(); 1130 }); // Libera processamento das bids novamente 1131 1132 this.awaitForGoogleAdServer.initialized = false; 1133 this.bid.isBidding = false; 1134 } // Refresh GPT 1135 1136 1137 refreshGpt(slotData) { 1138 // Coloca o slot na fila 1139 this.refreshGptSlots.push(slotData); // Enfilera refresh 1140 1141 setTimeout(() => { 1142 // Debounce 1143 if (this.refreshGptIsRefreshing) return; 1144 this.refreshGptIsRefreshing = true; // Obtém os dados da fila e limpa ela 1145 1146 let refreshSlots = this.refreshGptSlots; 1147 this.refreshGptSlots = []; // Converte a fila em uma lista de slots 1148 1149 let refreshSlotsGpt = refreshSlots.map(slotData => slotData.slot); 1150 let refreshSlotsCodes = refreshSlots.map(slotData => slotData.code); // Debug 1151 1152 if (refreshSlotsGpt.length > 0) Debug.info('Processando refresh de slots...'); // Faz o refresh 1153 1154 if (refreshSlotsGpt.length > 0) { 1155 Debug.info('pubads().refresh:', refreshSlotsCodes); 1156 window.googletag.pubads().refresh(refreshSlotsGpt); 1157 } // Desativa debounce 1158 1159 1160 this.refreshGptIsRefreshing = false; 1161 }, 50); 1162 } 1163 1164 }, _temp5); // Registra a API na janela 1165 1166 window.TecnoblogAdBidding = TecnoblogAdBidding; 1167 window.TecnoblogAdBidding.Utils = Utils; 1168 window.TecnoblogAdBidding.AdSlot = AdSlot; 1169 window.TecnoblogAdBidding.Events = Events; 1170 window.TecnoblogAdBidding.ActivityTracker = ActivityTracker; 1171})(); 1172
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.