Důchodci zrušili realitce prohlídku bytu během pandemie koronaviru. Ústavní soud jim dal šanci na nižší smluvní pokutu

28. 12. 2023
Doba čtení: 9 minut

Sdílet

Autor: Depositphotos
Mají smluvní pokutu ve výši poměrně nízké provize 60 000 Kč senioři zaplatit za jedno jediné porušení smlouvy? Realitka nesehnala po dobu dvou třetin spolupráce zájemce, a tak senioři ihned po jejím skončení prodali byt sami. Soudy ale smluvní pokutu nesnížily.

Manželé v důchodovém věku uzavřeli dne 8. 1. 2020 s realitní kanceláří na dobu 3 měsíců smlouvu o zprostředkování příležitosti koupě bytu v jejich vlastnictví. (Jinak řečeno realitní kancelář měla zajistit kupce pro jejich byt.)

Kupní cena bytu činila 1 690 000 Kč; provize realitní kanceláře byla sjednána na 60 000 Kč. To znamená, že provize byla nižší než ve většině případů, kdy činí 5 % z kupní ceny. Asi je na Ostravsku, kde se případ odehrál, větší konkurence mezi realitními kancelářemi. A čím nižší provize, tím i o něco nižší kupní cena, takže je nemovitost o něco snáze prodejná. Ale nakonec byl byt prodán ještě levněji, ale bez přispění realitní kanceláře.

Proč mají prodávající platit pokutu

Senioři se ve smlouvě mj. zavázali nemařit zprostředkovatelskou činnost realitní kanceláře a umožnit provádění prohlídek bytu se zájemci. Prohlídky stěžovatelé umožňovali až do 16. 3. 2020, kdy realitní kanceláři oznámili, že s ohledem na propuknutí koronavirové pandemie – a na ni reagující rozhodnutí vlády o vyhlášení nouzového stavu – nadále provádění prohlídek bytu neumožní.

To zní dosti pochopitelně. Ve smlouvě však bylo sjednáno, že poruší-li manželé některou ze smluvních povinností, jsou povinni zaplatit realitní kanceláři smluvní pokutu ve výši provize.

Dne 9. 4. 2020 (následující den po ukončení účinnosti smlouvy s realitní kanceláří) vlastníci uzavřeli kupní smlouvu, kterou prodali předmětný byt zájemci, jehož nezprostředkovala realitní kancelář, za kupní cenu 1 500 000 Kč. Realitní kancelář proto prodávající zažalovala a domáhala se zaplacení 60 000 Kč jako pokuty za porušení ve smlouvě dohodnuté povinnosti.

Jak argumentovala realitní kancelář

Tvrzení seniorů o nouzovém stavu považovala realitní kancelář za účelové; situaci bylo podle ní možno vyřešit předáním klíčů. Navíc si manželé podle realitky nepoctivě obstarali vlastního zájemce, jednali s ním ještě za trvání smluvního vztahu s realitní kanceláří a následně mu nemovitost prodali.

Kupní smlouvu uzavřeli na poště, kde byla vyšší koncentrace lidí, než by byla v předmětném bytě. To jsou na první pohled dobré argumenty.

Prvostupňový soud uznal, že si senioři chránili zdraví

Soud prvního stupně peníze realitní kanceláři nepřiznal. Dospěl k závěru, že odmítli-li prodávající v dosud bezprecedentní a život ohrožující situaci pandemie prohlídky předmětného bytu, jednali oprávněně v zájmu svého zdraví, života a jejich jednání bylo zároveň solidární vůči celé společnosti; neposkytnutí součinnosti v tomto mimořádném kontextu nelze hodnotit jako porušení právní povinnosti.

Skutečnost, že vlastníci ihned po ukončení smluvního vztahu s realitní kanceláří předmětný byt prodali třetí osobě, nalézací soud nehodnotil jako nepoctivé a nemravné jednání, protože v té době již nebyli vázáni smluvním vztahem a se svou nemovitostí mohli volně nakládat.

Byt byl prodán bez osobního kontaktu s kupujícím, který vypracování smlouvy zajistil a v elektronické podobě ji stěžovatelům zaslal.

Odvolací soud uznal realitce nárok na plnou výši smluvní pokuty

Ovšem odvolací soud rozhodl, že manželé musejí 60 000 Kč smluvní pokuty realitní kanceláři zaplatit. Soud měl za to, že ji nelze hodnotit jako nepřiměřenou vzhledem k její výši, která je stejná jako předpokládaná provize. To si ovšem autor článku nemyslí.

Konstrukce smluvní pokuty je podle odvolacího soudu logická, protože v případě, že by klienti mařili prohlídky bytu, zabránili by jeho prodeji zájemcům, čímž by realitní kanceláři vznikly marně vynaložené náklady (na inzerci, fotografie, zaměstnance atd.).

S tím lze souhlasit jen částečně. Ony snad realitní kanceláře pracují bez nároku na zisk? Činily snad náklady na práci realitní kanceláře 60 000 Kč? Nadto některé náklady ještě nebyly a ani nemohly být vynaloženy, třeba úplaty za sepis smlouvy advokátem. Smluvní pokuta by měla především nahradit zbytečně vynaložené náklady a případně částečně odškodnit za ztrátu zisku.

Soud by se proto měl zabývat tím, kolik stála inzerce, kolik bylo uskutečněno prohlídek bytu (zohlednit hodnotu času a práce makléře) apod., kolik tedy realitní kancelář zbytečně vynaložila peněz.

Pak by se třeba mohla smluvní pokuta blížit třeba až polovině požadované částky, takže by mohla činit až 30 000 Kč.

Nejvyšší soud ještě nevzal v potaz pravidla moderace (možnosti snížení) smluvní pokuty

Nejvyšší soud dovolání prodávajících odmítl, nepovažoval smluvní pokutu za nemravnou nebo nepřiměřenou. Manželé se proto obrátili s ústavní stížností na Ústavní soud.

Krom dřívějších argumentů správně argumentovali i tím, že při posouzení přiměřenosti smluvní pokuty mohou hrát roli nejen okolnosti známé již v době sjednávání smluvní pokuty, ale i ty, které zde byly při porušení smluvní povinnosti, jakož i okolnosti, jež nastaly později.

Nejvyšší soud se totiž vypořádal s případem příliš snadno, aniž reflektoval nový soudní (judikatorní) přístup ke smluvní pokutě, který sám formuloval (i když, pravda, oficiálně, formálně až o několik dnů později, než vynesl verdikt v případu sporu realitky a důchodců, ale příslušný právní výklad jistě byl v rámci soudu rozebírán, a tudíž již znám) a jak jsme vás o něm informovali v článku Smluvní pokuta už není, co bývala. Dlužníci se jí teď nemusí tolik bát.

Prodávající byli před smluvní pokutou varováni

Ústavní soud ve svém nálezu (spis. zn. I. ÚS 990/23, ze dne 27. září 2023) zhodnotil, že Nejvyšší soud již stěžovatelům vysvětlil, že jejich povinnost zaplatit smluvní pokutu byla vyvážena nevýhodností (nejistotou) odměny, kterou realitní kancelář obdrží pouze v případě úspěšného zprostředkování koupě nemovitosti. Uvedený dílčí závěr je podle Ústavního soudu srozumitelný, logický a není excesivní.

Nicméně hovořit o čemkoliv nevýhodném pro realitní kancelář ve vztahu realitní kanceláře a prodávajících mi přijde poněkud zvláštní, je to přece ona, kdo připravuje smlouvu o spolupráci s prodávajícími, a je to ona, kdo určuje výši provize (koneckonců i výši kupní ceny).

To, že odměnu obdrží realitní kancelář pouze při zprostředkování koupě, je prostě fakt, zvyklost v oboru, ve kterém realitní kanceláře podnikají, je to prostě ono známé riziko podnikání.

Za nedostatečné však Ústavní soud považoval hodnocení další otázky týkající se přiměřenosti výše sjednané smluvní pokuty a její případné moderace (tj. snížení) ve světle okolností, které nastaly při a po porušení smluvní povinnosti stěžovatelů.

Ústavní soud si myslí, že smluvní pokuta by měla být blíže uvážena, třeba i snížena

Ústavní soud dospěl k názoru, že se odvolací soud měl podrobněji zabývat tím, zda bylo po stěžovatelích v důchodovém věku ze strany realitní kanceláře přiměřené požadovat, aby umožnili (jednu) prohlídku bytu v době bezprecedentního stavu pandemie, který objektivně život (nejen) starších lidí ohrožoval. Bylo to především období počátku pandemie, jež bylo ovlivněno intenzivními obavami z dosud nepoznaného hromadného nebezpečí.

Podle Ústavního soudu nelze brát odůvodněné obavy stěžovatelů o jejich zdraví na lehkou váhu, a to zejména s ohledem na jejich vysoký věk i každodenní varování státních orgánů a sdělovacích prostředků o nebezpečnosti viru (ať už byla činěna v jakékoliv formě).

Prodávající sice mohli realitce předat klíče od bytu, ale chtěli u prohlídek být, když měli v bytě majetek

Odvolací soud však považoval tvrzení o obavě stěžovatelů o jejich zdraví za účelové. Podle Ústavního soudu však uvedený závěr není v odůvodnění rozhodnutí dostatečně vysvětlen.

Uvádí-li odvolací soud, že stěžovatelé mohli realitnímu makléři předat klíče bytu bezkontaktně, pomíjí jejich námitku, že v prodávaném bytě měli své osobní věci, a že proto měli oprávněný zájem se prohlídky bytu účastnit.

V tomto ohledu se odvolací soud nevypořádal ani s tím, že ve zprostředkovatelské smlouvě není uvedeno, že realitní kancelář může uskutečňovat prohlídky v nepřítomnosti stěžovatelů; naopak ve smlouvě je výslovně uvedeno, že kromě obecné povinnosti poskytnout součinnost má klient povinnost „účastnit se jednání se zájemci a RK, dostavovat se na naplánované schůzky, jednat o podmínkách smlouvy se zájemcem“.

Bylo jednání seniorů účelové – báli se, že by RK sehnala zájemce, když už sháněli sami?

Odvolací soud dále uvedl, že argumentace prodávajících byla účelová, protože den po ukončení smluvního vtahu uzavřeli kupní smlouvu s třetí osobou. V tomto ohledu však soud nereagoval na vznesenou námitku stěžovatelů, že nový kupní zájemce stěžovatele kontaktoval sám telefonicky, netrval na prohlídce bytu a smluvní dokumentaci vypracovanou advokátní kanceláří zaslal stěžovatelům elektronicky. Stěžovatelé přitom tvrdili, že zájemci existenci smluvního vztahu s realitní kanceláří oznámili.

Podle Ústavního soudu chybí bližší vysvětlení, proč by takové tvrzené jednání během existence smluvního vztahu s realitní kanceláří mělo bez dalšího účelovost chování stěžovatelů potvrzovat.

My si to však jistě dokážeme snadno domyslet. Nemovitost byla při přímém prodeji bez zprostředkování levnější. A prodej určitě rychlejší. Komunikace obou stran jednodušší a bezprostřednější. Zájemce-kupující, zřejmě podle všeho člověk, který se v realitách vyzná, mohl mít vše ve své režii a nemusel být konfrontován s častou liknavostí realitní kanceláře, která hájí především svoje zájmy. A někdy nekoná dostatečně rychle a vstřícně, jak jsme popisovali v článku Realitky nesmí požadovat pokutu, když nakonec neuzavřete kupní smlouvu.

Vše mohlo proběhnout rychle a senioři se mohli rychle dostat ke svým penězům. Ostatně, kdyby realitní kanceláře byly tak profesionální, jak tvrdí, neměli by ani kupující, ani prodávající zájem je obcházet. Navíc dosáhl nový zájemce slevy z kupní ceny.

Na prohlídky s realitkou senioři nemohli, ale na poštu ověřit podpisy ano?

Obdobně podle Ústavního soudu nelze bez dalšího dovodit, že podepsali-li stěžovatelé kupní smlouvu (s novým zájemcem) osobně na poště (zajisté proto, aby tam rovnou dali ověřit své podpisy), znamená to, že se mohli účastnit osobní prohlídky bytu.

Úřední ověření podpisů na vkladovém listě je, připomíná Ústavní soud, zákonnou podmínkou pro provedení vkladu do katastru nemovitostí. Kdyby v tomto smyslu stěžovatelé měli být absolutně konzistentní a nevycházeli by z domu za žádných okolností, znamenalo by to, že kupní smlouvu běžným způsobem uzavřít nemohli vůbec. I v tomto aspektu tedy účelovost jednání stěžovatelů není zjevná tak, jak ji odvolací soud prezentoval.

Kupní cena byla nakonec nižší

Odvolací soud se ve vztahu k údajné účelovosti argumentace stěžovatelů nezabýval ani tím, že prodávající kupní smlouvu s novým zájemcem nakonec uzavřeli za nižší cenu 1 500 000 Kč (oproti potenciální kupní ceně 1 630 000 Kč po započtení provize realitní kanceláře). V tomto smyslu není zřejmé, jakou významnou výhodu by měli stěžovatelé účelovým „obejitím“ realitní kanceláře získat.

My už jsme tu ale jistou výhodu naznačili – nejspíš získali peníze mnohem rychleji a snadněji, než kdyby se spoléhali na práci a zprostředkování prodeje realitní kanceláří.

Za podstatné Ústavní soud dále považuje, že odvolací soud při posuzování přiměřenosti výše smluvní pokuty blíže nereflektoval chování stěžovatelů před vypuknutím pandemie. 

Stěžovatelé prohlídky realitní kanceláři provádět umožňovali, a to od 8. 1. 2020 do 16. 3. 2020, tedy více než dvě třetiny doby trvání smluvního vztahu. Jinými slovy, realitní kancelář možnost smlouvu zprostředkovat po nikoliv bezvýznamný čas měla. Proč by měla získat celou smluvní pokutu jako v případě, kdy by stěžovatelé nespolupracovali vůbec, odvolací soud nevysvětluje.

A je otázkou, dodává k pochybnostem Ústavního soudu autor, zda by vůbec realitní kancelář dokázala sehnat vhodného zájemce a prodej zprostředkovat.

Realitní kancelář přece nikdo nenutí, aby brala každý potenciální obchod

Dobře stanovená výše kupní ceny odpovídající skutečné hodnotě nemovitosti v praxi vede k rychlému prodeji. (Pokud má prodejce přemrštěné představy, musí ho realitka usměrnit, nebo nemusí jeho zastoupení na straně nabídky vůbec převzít.)

Realitní kancelář pak má nižší náklady a prodávající se dříve dočkají svých peněz. A v daném případě dokázal nový zájemce přesvědčit prodávající, aby mu prodali nemovitost za méně, než měl zajistit výnos z prodeje zajišťovaným realitní kanceláří.

Jak to bude dál?

Ústavní soud svým nálezem zrušil rozhodnutí Nejvyššího a odvolacího soudu. Případ se nyní vrací před odvolací soud.

bitcoin_smenarna

Jeho úkolem bude znovu zvážit snížení smluvní pokuty a zhodnotit spravedlivost požadavku na její zaplacení ve vztahu ke konkrétním okolnostem případu. Ústavní soud nenařídil obecným soudům, jak konkrétně mají rozhodnout.

Soud může případně znovu dospět k závěru, že jednání stěžovatelů bylo účelové, nebo opětovně nepřistoupit k moderaci smluvní pokuty. Svůj závěr však musí založit na solidních skutkových zjištěních a pečlivě jej odůvodnit.

Autor článku

Nenadávejte právníkům, zákony netvoří zdaleka jen oni. Oni je pak jen zašmodrchávají ve prospěch svých klientů, třeba zrovna vás. Budu se však snažit vám je vysvětlovat.

'; 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 »