Regulace podle NIS2: Kyberzákon odešel do vlády se 101 rozpory, jen dva jsou ale ministerské

9. 1. 2024
Doba čtení: 5 minut

Sdílet

Autor: Depositphotos
Politické rozhodnutí bude muset padnout hlavně o bezpečnosti dodavatelského řetězce a definici kritické části aktiv. V nich se pohledy úřadů rozcházejí.

Více než dvě tisícovky úprav od verze předložené do meziresortního připomínkového řízení zaznamenal návrh nového kybernetického zákona. Národní úřad pro kybernetickou a informační bezpečnost (NÚKIB) ho stihl poslat do Legislativní rady vlády (LRV) poslední pracovní den před Vánocemi. Částečně jde o změny kosmetické, zpřesňující, v řadě aspektů jde ale o zásah mnohem hlubší, který má vliv na platnost závěrů uvedených v některých předchozích dílech našeho seriálu Regulace podle NIS2. NÚKIB už dříve slíbil, že mechanismu prověřování dodavatelského řetězce se úpravy vyhnou, a tento svůj slib splnil.

NÚKIB i přes rozpory s financemi, dopravou a ČTÚ poslal kybernetický zákon do legislativní rady Přečtěte si také:

NÚKIB i přes rozpory s financemi, dopravou a ČTÚ poslal kybernetický zákon do legislativní rady

My se detailnímu rozboru provedených změn budeme věnovat v jednom z lednových dílů seriálu NIS2. Kdo by si chtěl dopředu udělat podobnou analýzu jako my, bude potřebovat původní materiál, se kterým NÚKIB šel do připomínkového řízení, a jeho upravenou verzi pro LRV.

Pro dnešek si ale vystačíme s jiným dokumentem, a to finální tabulkou vypořádání připomínek. Ta nabobtnala na úctyhodných 768 stran, protože zachycuje i následnou komunikaci NÚKIBu s připomínkovými místy poté, co bylo připomínkové řízení skončeno.

Neakceptováno – závěr k více než třetině připomínek

Ve 331 případech (z celkových 864 uplatněných) NÚKIB tyto připomínky nakonec neakceptoval. U 230 případů se úřadu podařilo připomínkové místo o své pravdě přesvědčit a daný subjekt byl s vypořádáním spokojený, u 101 připomínek ale trvá rozpor. 

Tady je pak nejpodstatnější, kdo je tím, u koho rozpor přetrvává, resp. zdali se jedná o ministerstvo, jehož čelný představitel bude za několik týdnů na vládě o materiálu spolurozhodovat. NÚKIB si dobře uvědomil, že příliš mnoho rozporů s těmito důležitými připomínkovými místy by mohlo reálně ohrozit osud celého zákona, a tak se snažil hrany co nejvíce obrousit. A skutečně se mu to až na dvě výjimky podařilo.

První, pod číslem 456, se týká mechanismu prověřování dodavatelského řetězce. Jde o připomínku uplatněnou Ministerstvem dopravy, které protestuje proti tomu, na jak široký okruh dodavatelů úprava dopadne, a že buď prodraží veřejné zakázky, anebo dokonce některé tendry vůbec nebudou moci být zrealizovány, když na trhu nebude dostatek vhodných dodavatelů, z nichž by bylo možné vybírat. Doprava tak volá po diskusi a zodpovědném zhodnocení ekonomických dopadů, bez čehož „v žádném případě nemůže rezort dopravy souhlasit s tím, aby byl návrh předložen k projednání vládě“. 

NÚKIB reagoval tím, že „oslovil mnoho subjektů v rámci dotazníkového šetření“ a využil maximum informací pro to, aby posoudil možné dopady, s tím, že do budoucna uvítá debatu na téma dopadů regulace. To však ministerské úředníky na nábřeží Ludvíka Svobody neuspokojilo. „Od pana ministra nemáme mandát z této připomínky ustoupit,“ napsali NÚKIBu.

Neakceptováno a neakceptováno. NÚKIB rozeslal 600 stran vypořádání připomínek ke kyberzákonu Přečtěte si také:

Neakceptováno a neakceptováno. NÚKIB rozeslal 600 stran vypořádání připomínek ke kyberzákonu

Druhý vládní rozpor má číslo 614 a předcházela mu připomínka od Ministerstva financí cílící na nejednoznačně formulovanou definici kritické části stanoveného aktiva. Finance tak požadují, aby vymezení kritických částí bylo definováno přesněji, například provázáním jejich nedostupnosti s nedostupností celé strategicky významné služby. Tedy aby šlo o situaci, kdy ohrožení této kritické části stanoveného aktiva bude mít přímý vliv na službu jako celek. 

Na to NÚKIB reagoval tvrzením, že jednoznačnějším vymezením kritické části by porušil obecnost normy. Odmítl také navázat mechanismus bezpečnosti dodavatelského řetězce pouze na bezpečnost služby. „Cílem mechanismu, i celého zákona je zajištění bezpečnosti informací, tedy důvěrnosti, integrity a dostupnosti informací a dat,“ namítl NÚKIB, přičemž ministerstvo si na připomínkách trvá.

Resort financí přitom uplatnil 55 dalších námitek, brojících například proti tomu, aby v době hledání úspor byl státní rozpočet navyšován o výdaje na zajištění kybernetické bezpečnosti nebo že by mělo dojít k nárůstu tabulkových míst NÚKIBu k pokrytí potřebné agendy. Podle informací Lupy od zdroje obeznámeného s průběhem jednání bylo právě to největší třecí plochou mezi předkladatelem zákona a strážci rozpočtu a čekalo se, že k tomuto rozporu bude nutné přijmout politické rozhodnutí na vládě. 

NÚKIB ale zřejmě v tomto požadavku ustoupil, i když v tabulce vypořádání se k připomínce č. 605 omezil na lakonické konstatování, že „materiál byl na základě dohody s připomínkovým místem upraven a připomínka je vypořádána“, přičemž finance s vypořádáním souhlasí. Obdobně si pak resort Zbyňka Stanjury uhájil omezení prolomení povinnosti daňové mlčenlivosti. NÚKIBu tak budou informace potřebné pro jeho činnost poskytovány pouze ze strany orgánů Finanční a Celní správy.

Je tak evidentní, že od subjektů, jejichž zástupci na vládě budou o návrhu zákona rozhodovat, zůstaly rozpory svým významem – troufnu si tvrdit – marginální. Pokud přihlédneme k tomu, že NÚKIB dopředu avizoval, že prověřování dodavatelského řetězce je pro něj otázkou principu, ze které neustoupí, aby zákon pro něj měl ještě smysl, pak je-li toto jediná otázka, u které bude potřeba bojovat proti ministerstvům a rozhodnout o ní politicky, lze to považovat za úspěch.

Samosprávy a operátory čeká boj až ve sněmovně

Abychom ale jen nechválili, nelze pominout další rozpory, které zůstávají. Naprostá většina zamítnutých připomínek, které zůstaly nevyřešeny, pocházejí od Asociace krajů a Svazu měst a obcí, tedy zástupců samospráv. Část argumentů se překrývá, respektive je uplatněna ze strany vícero subjektů, a tak je možné pozorovat, že zatímco úřady a ministerstva se s tímtéž vyjádřením spokojily a dále na připomínce netrvají, tato dvě uskupení oportunisticky jakýkoliv ústupek odmítají. A to dokonce i v těch případech, kdy je jejich námitce vyhověno (např. připomínka č. 326).

Podobnou strategii „boje za principy“ přitom předvedly už v minulosti. Třeba v době, kdy ještě šlo efektivně zachránit celou Českou poštu redukcí nadbytečných poboček na úroveň 2100, byli to právě starostové sdružení ve Svazu měst a obcí, kdo se proti tomu ostře postavil. Čas ukázal, že to je stejně jediné možné řešení, jenže přišlo pozdě a v dopadu na zákazníka v daleko tvrdší podobě… Podobná revolta, tentokrát proti většímu bezpečí v kyberprostoru, se z řad starostů rodí i nyní.

Obce se tak bouří proti tomu, že by ty s rozšířenou působností měly být zařazeny pod regulaci (byť jen v mírnějším režimu, vyjma municipalit v režimu vyšších povinností), chybí jim definice výrazu „hrozba“, i že na trhu práce je nedostatek IT odborníků a málokterá obec bude mít dostatek zdrojů na to, aby kvalitní experty přilákala.

MM Influenceři

Jenže tyto nářky na vládě snad kromě čtyř ministrů za STAN nikoho nedojmou. Podobně jako průmyslový sektor, zástupci telekomunikačních operátorů i asociace samospráv se nejspíš budou muset připravit na bitvu o své zájmy ve sněmovně. Tam se dá v příštích měsících čekat horečnatá aktivita lobbistů, kteří budou chtít zákonodárce vybudit k poslanecké iniciativě a pozměňovacím návrhům.

Příští díl našeho seriálu o kybernetické bezpečnosti nabídneme podcast s právníkem tentokrát hájícím zájmy druhé strany než předkladatele zákona. Řeč se dostane určitě na mechanismus bezpečnosti dodavatelského řetězce.

  • Chcete mít Lupu bez bannerů?
  • Chcete dostávat speciální týdenní newsletter o zákulisí českého internetu?
  • Chcete mít k dispozici strojové přepisy podcastů?
  • Chcete získat slevu 1 000 Kč na jednu z našich konferencí?

Staňte se naším podporovatelem

Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Redaktor serveru Lupa.cz se zaměřením na telekomunikace, média, IT a právo. Dříve šéfredaktor Právního rádce a mluvčí Českého telekomunikačního úřadu.

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
Upozorníme vás na články, které by vám neměly uniknout (maximálně 2x týdně).
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »