var VpaidVideoPlayer = function() { this._slot = null; this._videoSlot = null; this._eventsCallbacks = {}; this._parameters = {}; this._quartileEvent = 0; this._quartiles = ['AdVideoFirstQuartile', 'AdVideoMidpoint', 'AdVideoThirdQuartile']; }; function adScale(width, height, frame) { let oH = height-70; let oW = width; let scaleH = oH > 280 ? Math.min(1.25,oH/280.0) : Math.min(1,Math.max(0.75, (oH+70)/280.0)); //console.log("================scale==================", scaleH,"oH", oH); let scaleW = oW > 336 ? Math.min(1.25,oW/336.0) : Math.min(1,Math.max(0.75, oW/336)); //console.log("================scale==================", scaleW,"oW", oW); let scale = Math.min(scaleH,scaleW); let trans = "scale("+scale+")"; let divH = scale < 1 ? Math.max(280,(oH+70)/scale) : Math.max(280,Math.min(350,(oH+70)/scale)); console.log("ad height", divH, "ad scale", scale); if(frame) { frame.height=divH; frame.style.transform=trans; } else { console.log("ad frame is null hence could not be scaled"); } } VpaidVideoPlayer.prototype.initAd = function(width, height, viewMode, desiredBitrate, creativeData, environmentVars) { this._slot = environmentVars.slot; this._videoSlot = environmentVars.videoSlot; // Parse the incoming parameters this._parameters = JSON.parse(creativeData['AdParameters']); this._attributes = this._parameters['attributes']; this._attributes['width'] = width; this._attributes['height'] = height; this._attributes['viewMode'] = viewMode; this._attributes['desiredBitrate'] = desiredBitrate; this.isAdPaused=false; if (this._attributes['linear']) { this._updateVideoSlot(); var th = this; if(this._containsVideoSource()) { this._videoSlot.addEventListener('timeupdate', this._timeHandler.bind(this), false); this._videoSlot.addEventListener('ended', this.stopAd.bind(this),false); } else { this._videoSlot.currentTime=0; setInterval(function() {/*console.log("ad state:"+th.isAdPaused);*/if(!th.isAdPaused) {th._noSourceTimeHandler();} else {}},100); } } //create click-through container var obj = this._slot; if( 'string' == typeof obj ) { obj = document.getElementById( obj ); } if( 'object' != typeof obj || 'string' != typeof obj.innerHTML ) { return; } var script, html; /* let oH = obj.offsetHeight-70; let oW = obj.offsetWidth; let scaleH = oH > 280 ? Math.min(1.25,oH/280.0) : Math.min(1,Math.max(0.75, (oH+70)/280.0)); console.log("================scale==================", scaleH,"oH", oH, "obj height",obj.offsetHeight); console.log(obj.getBoundingClientRect()); console.log(this._videoSlot); let scaleW = oW > 336 ? Math.min(1.25,oW/336.0) : Math.min(1,Math.max(0.75, oW/336)); console.log("================scale==================", scaleW,"oW", oW); let scale = Math.min(scaleH,scaleW); let trans = "transform:scale("+scale+")"; let divH = scale < 1 ? Math.max(280,(oH+70)/scale) : Math.max(280,Math.min(350,(oH+70))/scale); */ html = '
'; obj.innerHTML += ( html ); insertCss(obj); adScale(width, height, obj.querySelector('#vpaid-container-frame')); var vpaidContainer = obj;//document.getElementById('vpaid-container'); vpaidContainer.addEventListener('click', this._adClickThrough.bind(this), false); this._callEvent('AdLoaded'); }; VpaidVideoPlayer.prototype._adClickThrough = function() { this._callEvent('AdClickThru'); this._videoSlot.pause(); this._callEvent('AdPaused'); }; VpaidVideoPlayer.prototype._updateVideoSlot = function() { if (this._videoSlot == null) { this._videoSlot = document.createElement('videoAd'); this._slot.appendChild(this._videoSlot); } var foundSource = false; var videos = this._parameters.videos || []; for (var i = 0; i < videos.length; i++) { // Choose the first video with a supported mimetype. if (this._videoSlot.canPlayType(videos[i].mimetype) != '') { this._videoSlot.setAttribute('src', videos[i].url); foundSource = true; break; } } if (!foundSource) { // Unable to find a source video. this._callEvent('AdError'); } }; VpaidVideoPlayer.prototype._containsVideoSource = function() { var foundSource = false; var videos = this._parameters.videos || []; for (var i = 0; i < videos.length; i++) { // Choose the first video with a supported mimetype. if (undefined != videos[i].url && videos[i].url != '') { //this._videoSlot.setAttribute('src', videos[i].url); foundSource = true; break; } } return foundSource; }; VpaidVideoPlayer.prototype._noSourceTimeHandler = function() { if(this.getAdRemainingTime()<0) { this.stopAd(); } var quartile = Math.floor(((this.getAdDuration()-this.getAdRemainingTime())/this.getAdDuration()*100)/25); if (this._quartileEvent !== quartile) { this._quartileEvent = quartile; this._callEvent(this._quartiles[quartile-1]); } // change remaining time this._attributes['remainingTime'] = this.getAdRemainingTime() - 0.1; this._callEvent('AdRemainingTimeChange'); } VpaidVideoPlayer.prototype._timeHandler = function() { // call quartile event var quartile; var adDuration = (this._videoSlot.duration !== this._videoSlot.duration || this._videoSlot.duration==0)? this.getAdDuration() : this._videoSlot.duration; var currentTime = (this._videoSlot.currentTime !== this._videoSlot.currentTime) ? 0 : this._videoSlot.currentTime; if(!this._containsVideoSource()) { this._videoSlot.currentTime = currentTime + 0.5; this._videoSlot.duration = this.getAdDuration(); if(this.getAdRemainingTime()<0) { this.stopAd(); } } quartile = Math.floor((this._videoSlot.currentTime/adDuration*100)/25); if (this._quartileEvent !== quartile) { this._quartileEvent = quartile; this._callEvent(this._quartiles[quartile-1]); } // change remaining time this._attributes['remainingTime'] = adDuration - this._videoSlot.currentTime; this._callEvent('AdRemainingTimeChange'); /* } else { console.log("ad paused:"+this.isAdPaused); if(!this.isAdPaused) { this._attributes['remainingTime'] = this._attributes['remainingTime']-0.5; this._callEvent('AdRemainingTimeChange'); } } */ } /** * Returns the versions of VPAID ad supported. */ VpaidVideoPlayer.prototype.handshakeVersion = function(version) { return '2.0'; }; /** * Called by the wrapper to start the ad. */ VpaidVideoPlayer.prototype.startAd = function() { window.vgAdPlayer=this; if (this._attributes['linear']) { if(this._containsVideoSource()) { this._videoSlot.play(); } else { this._videoSlot.load(); } if(undefined != this.displayAdRendered && this.displayAdRendered) { // add skip button if skippable if (this.getAdSkippableState()) { var preSkipButton = this._createAdButton('You can skip this ad in ' + this._attributes['skipTime'], this.skipAd, 'small'); setInterval( function() { var skipTime= Math.ceil(vgAdPlayer._attributes['skipTime'] - (Math.max(0,vgAdPlayer.getAdDuration() - vgAdPlayer.getAdRemainingTime()))); if( skipTime < 1 ) { preSkipButton.innerHTML = "Skip Ad"; preSkipButton.className = null; preSkipButton.addEventListener('click', vgAdPlayer.skipAd.bind(vgAdPlayer), false); } else { preSkipButton.innerHTML = 'You can skip this ad in ' + skipTime; preSkipButton.className = 'small'; } }, 1000); } // this._createAdButton('Resume', this.resumeAd); // this._createAdButton('Pause', this.pauseAd); this._callEvent('AdStarted'); this._callEvent('AdImpression'); } } }; VpaidVideoPlayer.prototype._createAdButton = function(text, eventType, className) { var adButton = document.createElement('button'); adButton.className= className; var buttonText = document.createTextNode(text); adButton.appendChild(buttonText); adButton.addEventListener('click', null,/*eventType.bind(this),*/ false); preventEventBubbling(adButton); var vpaidContainer = this._slot.childNodes.item("vpaid-container"); vpaidContainer.appendChild(adButton); return adButton; }; VpaidVideoPlayer.prototype._updateText = function(id, text, className) { document.getElementById(id); }; function preventEventBubbling(button) { button.onclick = function(e) { e.stopPropagation(); }; } /** * Called by the wrapper to stop the ad. */ VpaidVideoPlayer.prototype.stopAd = function() { // Calling AdStopped immediately terminates the ad. Setting a timeout allows // events to go through var callback = this._callEvent.bind(this); this._callEvent('AdVideoComplete'); setTimeout(callback, 75, ['AdStopped']); }; /** * @param {number} value The volume in percentage. */ VpaidVideoPlayer.prototype.setAdVolume = function(value) { this._attributes['volume'] = value; this._callEvent('AdVolumeChanged'); }; /** * @return {number} The volume of the ad. */ VpaidVideoPlayer.prototype.getAdVolume = function() { return this._attributes['volume']; }; /** * @param {number} width The new width. * @param {number} height A new height. * @param {string} viewMode A new view mode. */ VpaidVideoPlayer.prototype.resizeAd = function(width, height, viewMode) { this._attributes['width'] = width; this._attributes['height'] = height; this._attributes['viewMode'] = viewMode; console.log("resize :",width, height); adScale(width, height, this._slot.querySelector("#vpaid-container-frame")); // if linear, resize video if (this._attributes['linear']) { try { this._videoSlot.setAttribute('width', width); this._videoSlot.setAttribute('height', height); this._videoSlot.style.width = width + 'px'; this._videoSlot.style.height = height + 'px'; } catch (e) { console.log('Could not resize video ad'); } } this._callEvent('AdSizeChange'); }; /** * Pauses the ad. */ VpaidVideoPlayer.prototype.pauseAd = function() { this._videoSlot.pause(); this.isAdPaused=true; this._callEvent('AdPaused'); }; /** * Resumes the ad. */ VpaidVideoPlayer.prototype.resumeAd = function() { this._videoSlot.play(); this.isAdPaused=false; this._callEvent('AdPlaying'); }; /** * Expands the ad. */ VpaidVideoPlayer.prototype.expandAd = function() { this._attributes['expanded'] = true; if (elem.requestFullscreen) { elem.requestFullscreen(); } this._callEvent('AdExpanded'); }; /** * Returns true if the ad is expanded. * @return {boolean} */ VpaidVideoPlayer.prototype.getAdExpanded = function() { return this._attributes['expanded']; }; /** * Returns the skippable state of the ad. * @return {boolean} */ VpaidVideoPlayer.prototype.getAdSkippableState = function() { return this._attributes['skippableState']; }; /** * Collapses the ad. */ VpaidVideoPlayer.prototype.collapseAd = function() { this._attributes['expanded'] = false; }; /** * Skips the ad. */ VpaidVideoPlayer.prototype.skipAd = function() { var skippableState = this._attributes['skippableState']; if (skippableState) { this._eventsCallbacks['AdSkipped'](); var callback = this._callEvent.bind(this); setTimeout(callback, 75, ['AdStopped']); // this._videoSlot.currentTime=0; } }; /** * Registers a callback for an event. * @param {Function} aCallback The callback function. * @param {string} eventName The callback type. * @param {Object} aContext The context for the callback. */ VpaidVideoPlayer.prototype.subscribe = function(aCallback, eventName, aContext) { var callBack = aCallback.bind(aContext); this._eventsCallbacks[eventName] = callBack; }; /** * Removes a callback based on the eventName. * * @param {string} eventName The callback type. */ VpaidVideoPlayer.prototype.unsubscribe = function(eventName) { this._eventsCallbacks[eventName] = null; }; /** * @return {number} The ad width. */ VpaidVideoPlayer.prototype.getAdWidth = function() { return this._attributes['width']; }; /** * @return {number} The ad height. */ VpaidVideoPlayer.prototype.getAdHeight = function() { return this._attributes['height']; }; /** * @return {number} The time remaining in the ad. */ VpaidVideoPlayer.prototype.getAdRemainingTime = function() { return this._attributes['remainingTime']; }; /** * @return {number} The duration of the ad. */ VpaidVideoPlayer.prototype.getAdDuration = function() { return this._attributes['duration']; }; /** * @return {string} List of companions in vast xml. */ VpaidVideoPlayer.prototype.getAdCompanions = function() { return this._attributes['companions']; }; /** * @return {string} A list of icons. */ VpaidVideoPlayer.prototype.getAdIcons = function() { return this._attributes['icons']; }; VpaidVideoPlayer.prototype.onAdImpression = function() { this._callEvent('AdImpression'); } /** * @return {boolean} True if the ad is a linear, false for non linear. */ VpaidVideoPlayer.prototype.getAdLinear = function() { return this._attributes['linear']; }; /** * Calls an event if there is a callback. * @param {string} eventType */ VpaidVideoPlayer.prototype._callEvent = function(eventType) { if (eventType in this._eventsCallbacks) { try { this._eventsCallbacks[eventType](); } catch(e) { ; // likely async call in VPAID module. We can't see the error event in try/catch } } }; /** * Main function called by wrapper to get the VPAID ad. */ var getVPAIDAd = function() { return new VpaidVideoPlayer(); }; function insertCss(obj) { var s = document.createElement('STYLE'); s.type = 'text/css'; s.innerHTML = '#vpaid-container button {' + ' background-color: rgba(0,0,0,0.8);' + ' border : 1px solid rgba(255,255,255,0.5);' + ' border-right: none;' + ' border-radius: 2px 0 0 2px;' + ' bottom : 37px;' + ' box-sizing : content-box;' + ' color : white !important;' + ' cursor : pointer;' + ' font-family : Arial, Helvetica, system-ui, sans-serif;' + ' font-size : 18px;' + ' font-weight : 500 !important;' + ' height : auto;' + ' line-height : normal;' + ' min-width : 100px;' + ' padding : 7px 6px 7px 10px;' + ' position : absolute;' + ' right : 0;' + ' text-align : center;' + ' text-transform: none;' + ' width : auto;' + ' }' + '#vpaid-container button:hover {' + ' border-color: white;' + ' }' + '#vpaid-container button::after {' + ' background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAABGdBTUEAALGPC/xhBQAAAAJiS0dEAP+Hj8y/AAAAZElEQVQ4y+3SwQmDQBBGYRFy9mYLSRXWEyvRRtKBbdhDUoTH8HkVllX+u++48GD2zTTNTYiPrnhDXeBnyAT+Zo9EgNUrE9iMmQCLvhTas2jXWY8jvbNPP5OsU5L1my0uPY2bCjtXdo6mqRVtTgAAAABJRU5ErkJggg==) !important;' + ' background-repeat: no-repeat;' + ' content : " ";' + ' display : inline-block;' + ' height : 28px;' + ' margin-left : 2px;' + ' vertical-align : middle;' + ' width : 24px;' + '}' + '#vpaid-container button.small {' + ' font-size: 70%;' + ' }' + '#vpaid-container button.small:hover {' + ' border-color: grey;' + ' }' + '#vpaid-container button.small::after {' + ' background-image: none;' + ' display: none;' + ' }' ; //document.head.appendChild( s ); obj.appendChild( s ); } var vgui = { postMessage: {} }; vgui.postMessage.preload = function() { console.log('vgui.postMessage.preload'); if (window.addEventListener) { window.addEventListener('message', vgui.postMessage.blender, false); } else { window.attachEvent('onmessage', vgui.postMessage.blender, false); } vgui.postMessage.send({ destination: 'parent', callback : 'getMessages', message : vgui.originalUrl || document.location.href }); } vgui.postMessage.blender = function( m ) { /** * message protocol: * videogram:function_name:JSON-data * */ console.log( 'vgui.postmessage received:', m ) if( m && m.data && 'string' == typeof m.data && 0 == m.data.indexOf('videogram:') ) { var request = m.data.substr(10).split(':') ; } else return; if( vgui.postMessage[ request[0] ] instanceof Function ) { vgui.postMessage[ request[0] ]( request[1] ); } else if( request[0] && vgui[ request[0] ] && vgui[ request[0] ].postMessage instanceof Function ) { vgui[ request[0] ].postMessage( request[1] ) } } vgui.postMessage.send = function( request ) { var d; switch( request.destination ) { case 'parent': d = parent; request.origin = document.referrer.split('/').slice(0,3).join('/') ; if( '' == request.origin ) return; break; default: d = document.getElementById( request[0] ).contentWindow; } console.log('vgui+: sending postmessage', request.callback + ':' + request.message , request.origin, document.getElementById( request[0] ) ); try { if( ! d.postMessage ) d = d.contentWindow; d.postMessage( 'videogram:' + request.callback + ':' + request.message , request.origin ); console.log('vgui+: postmessage sent', request.callback + ':' + request.message , request.origin ) } catch(e) { console.error('caught error in postmessage to ' + request.destination, request.callback + ':' + request.message, request.origin, e ) } } vgui.postMessage.preload(); vgui.postMessage.exampleCallback = function( parameters ) { // this function gets called when another frame does // vgui.postMessage.send({ callback: 'exampleCallback', message: "some text", origin: 'parent' } // var parameters will contain "some text". Up to you to parse the text if needed. console.log('vgui.postmessage exampleCallback', parameters); if( ! window.vgAdPlayer ) { console.log("vgAdPlayer object is not initialized"); return; } if(parameters=='ad-rendered') { window.vgAdPlayer.displayAdRendered= true; vgAdPlayer.startAd(); } else { window.vgAdPlayer.displayAdRendered=false; vgAdPlayer.stopAd(); } //window.vgAdPlayer._videoSlot.pause(); }