Menu Zavřít

Přejeme si, aby byl Adam ziskový co možná nejpozději, říká jeho šéf Sysel. Letos čeká platformu s řemeslníky několikanásobná expanze

20. 1. 2025
Doba čtení: 7 minut
Roman Sysel, šéf platformy Adam
Autor: Adam
  • Českou platformu Adam, jež se zaměřuje na zprostředkování řemeslníků, čeká letos série expanzí na další nové trhy
  • Zároveň hodlá posílit v rámci nedávno spuštěného segmentu elektrikářů, kvůli čemuž zakladatelé Adama Roman Sysel a Jakub Dvořák přibrali do týmu svého bývalého kolegu z tuzemské pobočky Boltu Marka Maxu
  • Vedení firmy pro letošek počítá s dalším významným růstem. Kdy se startup stane ziskový, v tuto chvíli neřeší. Ba naopak šéfové doufají, že to bude co možná nejpozději, neboť jedině tak dokáže Adam svým investorům maximalizovat jejich potenciální výnos

S českou platformou Adam, která svým zákazníkům zprostředkovává řemeslníky na nejrůznější typy domácích stavebních prací a úprav, se už setkal kdekdo. Ostatně, na trhu je od roku 2020, přičemž aktuálně jejích služeb využívají desítky tisíc lidí. A to navíc nejen zde v Česku, nýbrž i v 10 dalších evropských zemích a nově také v daleké Austrálii.

Firmu založila dvojice bývalých manažerů tuzemské pobočky Boltu Roman Sysel a Jakub Dvořák, a to za podpory samotného šéfa této přepravní společnosti Markuse Villiga. Nedávno se k nim pak připojil i další jejich bývalý kolega, a sice Marek Maxa, jenž v této původem estonské firmě pomáhal na zdejším trhu rozjíždět její tehdy novou rozvážkovou službu Bolt Food.

Outstream Placeholder

Podobný úkol teď Maxu čeká rovněž u Adama. Snad jen s tím rozdílem, že na místo doručování jídla lidem až ke dveřím k nim tentokrát bude posílat něco docela jiného. Respektive někoho jiného. A sice osvědčené elektrikáře. Právě do tohoto řemesla se totiž nyní Adam v rámci svého nezadržitelného růstu také rozhodl začít fušovat.

Cílem je především kontinuálně růst

Že Adam i po bezmála pěti letech na trhu kontinuálně roste, dokládají finanční výsledky. V roce 2023 se firemní obrat dostal vůbec poprvé v jeho historii nad hranici 100 milionů korun. 

Čísla za loňských 12 kalendářních měsíců vedení ještě sečtená a podtržená v tuto chvíli nemá. Na jeho začátku si každopádně startup dal za cíl dosáhnout dvojnásobku, přičemž dle Syslových odhadů od této hodnoty rozhodně nijak dramaticky vzdálený nebude. A to navzdory jinak obecně složitějšímu období. 

Největším bolehlavem českých startupů jsou zaměstnanecké akcie. Zbytečně jedeme se zataženou ruční brzdou, říká Pavlečka z Czech Founders VC
Přečtěte si také:

Největším bolehlavem českých startupů jsou zaměstnanecké akcie. Zbytečně jedeme se zataženou ruční brzdou, říká Pavlečka z Czech Founders VC

„Rok 2024 byl nejen pro nás, ale i pro celý startupový svět poměrně těžký. Projevuje se zde skutečnost, že existuje relativně hodně alternativních investičních příležitostí,“ přibližuje Sysel redakci Euro.cz „Spotřebitelé mohou díky stále slušné úrokové míře pohodlně investovat například do státních dluhopisů nebo dát peníze na spořicí účet, potažmo termínovaný vklad. Což logicky vede k tomu, že když se z nějakého důvodu rozhodnou pro investici do rizikového kapitálu – kam spadá i Adam –, tak požadují ještě vyšší výnos. A protože to není vždy snadné, tak pochopitelně část těchto peněz z venture kapitálového trhu mizí právě ve prospěch bezrizikových aktiv,“ podotýká spoluzakladatel Adama.

Jedním z hlavních cílů Sysla s Dvořákem pro nedávno skončený rok bylo získat pro svoji firmu další významnou investici. A to se navzdory všemu výše uvedenému doopravdy podařilo, když českou platformu v průběhu léta podpořil britský fondu Brighteye, který jí na její další rozvoj poslal 75 milionů korun.

Česká platforma Adam získala od britského fondu Brighteye 75 milionů korun. Díky penězům zamíří mimo Evropu
Přečtěte si také:

Česká platforma Adam získala od britského fondu Brighteye 75 milionů korun. Díky penězům zamíří mimo Evropu

„Nabrali jsme dodatečné prostředky, které nám umožní zase o něco lépe škálovat náš byznys a expandovat do dalších zemí a vertikál jako takových. Ale celkově vzato jsme se vloni v tomto ohledu spíše snažili být opatrnější,“ prozrazuje Sysel, podle kterého je pro všechny v týmu, ale hlavně pro samotné investory, zásadní především to, aby jejich startup kontinuálně rostl. Což se zatím daří. 

„Adam by sice už dávno mohl být ziskový, ale to my vlastně nechceme. A hned vysvětlím proč,“ říká a pokračuje dále: „Typický investor vám dá peníze a očekává, že mu je pomůžete zhodnotit. Samozřejmě platí – a tím spíš v tomto venture kapitálovém byznysu –, že ne všechny projekty se nakonec jako výnosné ukážou. Proto se ale investoři snaží své portfolio diverzifikovat.“

Investorům dle Sysla nejde o to, aby firma jejich peníze utratila co nejdřív a co nejdříve byla zisková. Místo toho jim záleží na tom, aby je utratila co nejlépe a onen potenciální zisk maximalizovala. „Což jinými slovy znamená, že investor bude preferovat, aby firma co nejrychleji a co nejvíce rostla, protože až růst přestane a začne generovat zisk, tak tento zisk, a tedy i samotný investorův podíl z něj, bude mnohem větší, než kdyby přestala růst a začala být zisková o pět deset let dříve. Jinak by se na to totiž mohl dívat tak, že jsme se nesnažili jeho peníze vynaložit tak, aby měl v budoucnu ze své investice ještě víc,“ objasňuje.

Není zisk jako zisk

Čistě z pohledu českého trhu už Adam relativně velkou firmou je, v celosvětovém měřítku je však podle Syslova názoru stále na úplném začátku. Tedy řečeno jinak, prostoru pro růst a současně zhodnocení všech dosud získaných investic, respektive potenciálního výnosu z nich, je stále spousta.

„Běžný člověk, když vidí, že je firma dlouhodobě ztrátová, tak ho automaticky napadne otázka, proč by někdo do takového podniku nějaké další peníze vůbec investoval. Jenže právě tak to vůbec není. Cílem investora není, aby firma generovala 10procentní zisk, když má hodnotu sto milionů korun. Cílem investora je, aby tento zisk generovala například až při miliardové tržní valuaci. A právě z tohoto důvodu doufám, že Adam ziskový jen tak nebude. Protože to bude znamenat, že nadále roste a že ten potenciální výnos bude mnohem větší,“ míní byznysmen a druhým dechem dodává: „Nic z toho nebrání Adamovi v tom, aby dělal byznys tak, že když bude muset, tak se do zisku dostane. Osobně ale doufám, že bude ziskový co nejpozději.“

Umět postavit celý dům

Když Adam před pěti lety na trh vstupoval, soustředil se výhradně na zprostředkování malířských prací. A protože malování mu šlo pěkně od ruky, rozhodli se Sysel s Dvořákem na platformu přibrat i zedníky, kteří si poradí například s menšími rekonstrukcemi.

Vize obou zakladatelů je, že jednoho dne budou moci lidé díky Adamovi postavit rovnou celý dům. A tak není divu, že nedávno došlo k další „profesní expanzi“, a to do již zmíněného segmentu elektrikářů.

Adam se stane i elektrikářem. Česká platforma pro zprostředkování řemeslníků zamíří do nového odvětví
Přečtěte si také:

Adam se stane i elektrikářem. Česká platforma pro zprostředkování řemeslníků zamíří do nového odvětví

Aktuálně jich mají k dispozici nižší desítky, úkolem Maxy coby nové manažerské posily nicméně bude jejich počet rychle navyšovat. „S elektrikáři to nyní vypadá velice nadějně, takže se nebojím říct, že rok 2025 bude rokem, během kterého budeme v této oblasti dále expandovat. Rok 2025 bude rokem elektrikářů,“ nastiňuje současné Adamovy plány Sysel.

A jakéže konkrétní služby u nich budou moci zákazníci poptávat? „Jedná se o lidi, kteří vám jsou schopni zavést elektřinu kompletně v celém domě. Tedy o velmi zkušené elektrikáře. Zároveň ale samozřejmě pomohou i ve chvíli, kdy člověk po rekonstrukci bytu potřebuje připravit elektriku pro zapojení domácích spotřebičů, udělat novou zásuvku a podobně,“ poodhaluje.

Po roce vyčkávání se opět pojede naplno

Letošní rok má být dle Sysla kromě důrazu na novou službu charakteristický i tím, že na rozdíl od roku uplynulého firma rozhodně nebude jen vyčkávat a s opatrností sledovat situaci na trhu, ba naopak bude nové příležitosti sama aktivně vyhledávat. Což jinými slovy znamená další expanzi. „Nepůjde o jednu zemi, nýbrž hned několik. Ale konkrétnější v tuto chvíli bohužel ještě být nemůžu,“ přiznává s tím, že Adamův fokus bude směřovat na nedávno otevřenou Austrálii.

„Než jsme se pro tento trh rozhodli, udělali jsme si – tak jako vždy – podrobnou analýzu. V rámci ní jsme vytipovali asi 15 zemí – od Japonska po Jižní Ameriku –, které by za nás připadaly v úvahu. No a Austrálie nám ze všeho vyšla jako nejlepší možná volba. I proto, že je tam hodně velkých měst a že se tam mluví anglicky,“ vysvětluje podnikatel.

V rámci expanze k protinožcům nicméně museli Sysel s Dvořákem vyřešit jeden poměrně zásadní háček. A sice skutečnost, že řídit tamní byznys z Česka by vzhledem k velkému časovému posunu bylo poněkud nepraktické. Takže na to museli jít jinak.

Rozšířený sortiment, nová akvizice a masivní expanze do zahraničí. Trenýrkárna.cz působí již ve 23 evropských zemích
Přečtěte si také:

Rozšířený sortiment, nová akvizice a masivní expanze do zahraničí. Trenýrkárna.cz působí již ve 23 evropských zemích

Nakonec oba dospěli k názoru, že nejlepší bude, když otevřou pobočku „někde poblíž“, kde k tomu existuje vhodné zázemí a kde je zároveň i hodně místního talentu. A tak se zrodil nápad zapojit do celého procesu Indii.   

CIF25

Na řízení australských operací tam v současné době pracují tři lidé. A ti budou mít letos nejspíš plné ruce práce (mimo jiné se sháněním nových kolegů), neboť právě posílení pozice na nejmenším kontinentu světa je něčím, co si Adam pro rok 2025 stanovil jako prioritu. Spolu s rozvojem již zmíněné „elektrikářské vertikály“.

„Nakonec právě proto jsme k nám do týmu Marka angažovali. Je to pro nás obrovská posila. Jednak proto, že se známe opravdu dobře a opravdu dlouho, a zároveň i z toho důvodu, že jde o velice kompetentního manažera. Markovi se u nás zatím daří náramně a naše očekávání od něj pro letošní rok a od celého tohoto segmentu jsou vskutku velká,“ uzavírá Sysel.

  • Našli jste v článku chybu?
'; 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'; } }
'; 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 »