Regulace podle NIS2: Co dělat, když přijde kybernetický útok nebo jiný incident

22. 8. 2023
Doba čtení: 5 minut

Sdílet

Autor: Pixabay, podle licence: Public Domain CC0
Od zjištění problému běží lhůta 24 hodin na jeho oznámení. Nesplnění této povinnosti navrhovaný zákon trestá nejpřísnější sankcí v řádu stovek milionů korun.

Předchozích několik dílů seriálu Regulace podle NIS2 jsme věnovali prevenci. Popsali jsme si, jaké povinnosti budou mít firmy podle nové směrnice NIS2, aby předcházely problémům se zajištěním kybernetické bezpečnosti. Dnes se podíváme na to, jak postupovat, když se i přes veškerou snahu udržet systémy stranou od nástrah nenechavých útočníků nepodaří.

Přitom oblast kybernetických incidentů upravená NIS2 a novým zákonem podle právníků představuje největší změny pro stávající povinné osoby. „Přísné lhůty pro prvotní hlášení společně s povinností u vybraných incidentů následně poskytnout závěrečnou zprávu budou pro řadu organizací znamenat nutnost přepracovat procesy pro řešení incidentů,“ vysvětluje advokát a odborník na kybernetickou bezpečnost z Rowan Legal Jan Tomíšek.

Rowan Legal

ROWAN LEGAL je přední tuzemskou advokátní kanceláří specializující se na právo IT, kybernetickou bezpečnost, telekomunikace, řešení sporů a arbitráží, korporátní a obchodní právo, duševní vlastnictví a hospodářskou soutěž včetně veřejných zakázek. Mezi její klienty patří největší národní a mezinárodní korporace včetně veřejných institucí. V kancelářích v Praze a Brně zaměstnává přes 100 spolupracovníků, z toho více než 90 zkušených právníků pravidelně oceňovaných v tuzemských i mezinárodních oborových žebříčcích.

ROWAN LEGAL, partner seriálu Regulace podle NIS2.

Český zákon chce oznamovat i nevýznamné incidenty

NIS2 rozlišuje mezi významnou událostí, incidentem a rozsáhlým kybernetickým bezpečnostním incidentem. Ve všech případech je to událost narušující bezpečnost, autenticitu, integritu nebo důvěrnost uchovávaných, předávaných nebo zpracovávaných dat nebo služeb, které jsou nabízeny prostřednictvím informačních sítí a informačních systémů nebo které jsou jejich prostřednictvím dostupné.

Definice významné události a incidentu se liší jen v tom, že zatímco u incidentu tato škodlivá událost již nastala, u významné události jejímu plnému rozvinutí bylo úspěšně zabráněno nebo jen hrozila, ale nenastala. Vedle toho rozsáhlý kybernetický bezpečnostní incident se vyznačuje škodlivostí následku. Úroveň narušení musí být taková, že přesahuje schopnost členského státu EU na takový incident reagovat, anebo se významně dotýká alespoň dvou členských zemí, tedy bude to událost typicky s přeshraničním přesahem. Směrnice dále definuje kvalifikovanou kategorii incidentů, a to významných incidentů. To jsou takové, které buď přímo poškozenému subjektu způsobily, či mohly způsobit závažné provozní narušení služby nebo finanční ztráty, anebo třetí osoby postihly značnou hmotnou nebo nehmotnou újmou.

Směrnice v čl. 23 omezuje oznamovací povinnost právě jen na významné incidenty. Návrh kybernetického zákona je v tomto ohledu přísnější a další postup dělí podle toho, jestli se jedná o subjekt v režimu nižších povinností (important),nebo vyšších povinností (essential). Zatímco přísněji regulované subjekty mají podle návrhu kybernetického zákona povinnost (§ 16 odst. 1) hlásit veškeré kybernetické bezpečnostní incidenty, které mají původ v kybernetickém prostoru, subjekty s nižší regulací ji mají definovanou (§ 16 odst. 2) jen u incidentů s významným dopadem na poskytování regulované služby. „Poskytovatelé v nižším režimu si významnost dopadů incidentů určují sami podle dopadových kritérií nastavených na základě § 15 vyhlášky o bezpečnostních opatřeních poskytovatelů regulovaných služeb v režimu nižších povinností,“ dodává advokát Jan Tomíšek. Disproporce mezi evropskou regulací a národní úpravou je dalším z terčů kritiky nového kyberzákona v rámci meziresortního připomínkového řízení, a není tedy vyloučeno, že konečná podoba zákona ještě dozná změn.

Významné incidenty pak mohou být spojeny ještě s jednou povinností. NÚKIB může podle § 19 kyberzákona uložit svým rozhodnutím provozovateli regulované služby povinnost informovat své uživatele o tomto incidentu. Rozsah a způsob splnění informační povinnosti stanoví úřad právě v rozhodnutí. Skončí tak praxe řady korporací tutlat a bagatelizovat rozsah kybernetických průšvihů. Připomeňme šest let starý případ úniku uživatelských dat e-shopu Mall.cz, kde byla reakce e-shopu mlživá a zapírající a až medializace a následné vymáhání odškodnění ze strany uživatelů ukázaly v plné nahotě rozsah úniku a škodlivost následků.

Národní úprava poskytuje až jednoroční odklad od registrace regulované služby, v němž povinnost hlásit kybernetické incidenty neplatí. A naopak dobrovolnosti meze neklade. Takže každému je umožněno hlásit přes web NÚKIBu incidenty, události, nebo i jen hrozby.

Napadli nás! Co dělat dřív?

Kybernetický incident je vždy nadělení, které si žádný administrátor nepřeje zažívat. Nebo alespoň ne moc často. Zatímco dosud se v případě vyřazení systému nebo jeho části z provozu mohl plně věnovat odstraňování škod a obnovení chodu systému, teď mu bude od zjištění problému běžet čas na nahlášení problému úřadu. Má na to maximálně 24 hodin, a to i v případě, kdy k problému dojde v pátek odpoledne na konci pracovní doby.

„Zákon nestanovuje, zda mají poskytovatelé služeb začít nahlášením, nebo odstraňováním incidentu. V případě jednoduchých problémů, které je možné vyřešit v řádu hodin a nijak významně neovlivní poskytování regulované služby, může subjekt incident nejdřív odstranit a až poté nahlásit,“ říká advokát z Rowan Legal Jan Tomíšek s tím, že v ostatních případech je nutné pamatovat na to, že s překročením lhůty jsou spojeny citelné sankce. Proto je potřeba provést základní opatření k zamezení šíření incidentu a nahlásit ho co nejdříve poté, co zjistíte základní informace.

Regulovaný subjekt na základě prvotního hlášení může obdržet od NÚKIBu nebo národního CERTu vyjádření k incidentu a může tyto instituce požádat o metodickou podporu. Z praktického hlediska je však podstatné začít souběžně řešit opatření, aby problém zbytečně neeskaloval a nešířil se.

Komu, jak a co hlásíme

Pro hlášení incidentu připraví NÚKIB na svém webovém portálu příslušný formulář. Poskytovatelé v režimu vyšších povinností incidenty hlásí přímo tomuto regulátorovi, subjekty s mírnější regulací pak národnímu CERTu. „Nesplnění povinnosti hlášení návrh zákona sankcionuje nejvyššími pokutami, tedy až 250 milionů korun nebo až 2 % celosvětového ročního obratu pro poskytovatele ve vyšším režimu,“ varuje Tomíšek. U mírněji regulovaných subjektů je strop pro pokutu za nenahlášení incidentu 175 milionů nebo až 1,4 % celosvětového ročního obratu.

Pokud nejde o významný incident, prvotním hlášením a zpětnou vazbou k němu (vyjádřením NÚKIBu nebo CERTu) to pro regulovaný subjekt končí. U významných incidentů ale administrativní zábava pokračuje. Do 72 hodin od zjištění incidentu musí povinný subjekt zaslat aktualizaci informací z prvotního hlášení a spolu s tím i údaje o dopadu a indikátorech kompromitace formou Oznámení incidentu. Pokud si to regulační orgány vyžádají, nebo pokud incident trvá déle než 30 dnů, pak se v této lhůtě zasílá i Průběžná zpráva o aktuálním stavu zvládání incidentu. A to celé završuje Závěrečná zpráva, opět ve lhůtě 30 dnů od vyřešení incidentu nebo od oznámení.

Zvládneme to

Už jsme naznačili, že oznámení nezůstává bez odezvy. Nejpozději do 24 hodin (a to i o svátcích, sobotách a nedělích) od obdržení hlášení regulátor odešle vyjádření k incidentu. Nadto má povinnost na žádost napadeného subjektu poskytnout metodickou podporu, a jedná-li se o incident s významným dopadem, ať už na regulovanou službu, nebo na kybernetický prostor státu, dá se získat i další technická podpora ke zvládnutí situace. „Instituce samotné mohou na incident reagovat a zmírnit tak jeho dopad nejen na poskytovatele, který jej nahlásil, ale i na ostatní subjekty, které by jím mohly být zasaženy,“ doplňuje Tomíšek.

CIF25

Do zákona je také zakotvena povinnost poskytnout na výzvu NÚKIBu nezbytné informace a součinnost pro zvládnutí incidentu. Ta platí, pokud efektu nelze dosáhnout jinak, nebo jen s podstatnými obtížemi.

Příští díl našeho seriálu budeme věnovat auditům kybernetické bezpečnosti.

  • 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 »