├── .gitignore ├── LICENSE ├── README.md ├── advanced ├── ads.js ├── application.js ├── index.html ├── style.css └── video_player.js ├── attempt_to_autoplay ├── ads.js ├── index.html └── style.css ├── audio_in_video ├── ads.js ├── image.png ├── index.html └── style.css ├── live_stream_prefetch ├── ads.js ├── index.html └── style.css ├── mobile_auto_skippable ├── ads.js ├── index.html └── style.css ├── playlist ├── ads.js ├── application.js ├── img │ ├── android_preview.jpg │ └── doubleclick_preview.jpg ├── index.html ├── style.css └── video_player.js ├── simple ├── ads.js ├── index.html └── style.css └── vpaid ├── linear └── VpaidVideoAd.js └── nonlinear └── VpaidNonLinear.js /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Google Ads HTML5 IMA SDK 2 | 3 | This project hosts samples for the 4 | [HTML5 IMA SDK](//developers.google.com/interactive-media-ads/docs/sdks/html5/client-side). 5 | 6 | ### Samples Breakdown 7 | 8 | * [Simple](https://github.com/googleads/googleads-ima-html5/tree/main/simple) - 9 | the bare minimum required for an IMA integration 10 | * [Advanced](https://github.com/googleads/googleads-ima-html5/tree/main/advanced) - 11 | IMA integration with more advanced UI including event logging, play/pause, 12 | and fullscreen, and companion ads 13 | * [Playlist](https://github.com/googleads/googleads-ima-html5/tree/main/playlist) - 14 | expands on the advanced sample to demonstrate an integration with a video 15 | playlist 16 | * [Audio in video](https://github.com/googleads/googleads-ima-html5/tree/main/audio_in_video) - 17 | demonstrates the use of the 18 | [poster image feature](https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/audio-poster) 19 | in an app that supports either audio or video ads. 20 | * [VPAID](https://github.com/googleads/googleads-ima-html5/tree/main/vpaid) - 21 | these JavaScript samples are used in the 22 | [IMA VPAID sample ad tags](https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags#single-vpaid-2.0-linear). 23 | The code in these samples is needed in VPAID ads, but not in IMA 24 | implementations for VPAID ads. For more information on VPAID ads, see the 25 | [IAB VPAID docs](https://iabtechlab.com/standards/video-player-ad-interface-definition-vpaid/). 26 | 27 | ### Requirements 28 | 29 | * Your favorite text editor 30 | * An HTML5 compliant browser 31 | * A webserver on which to host the sample 32 | 33 | ### Downloads 34 | 35 | Check out the 36 | [releases section](https://github.com/googleads/googleads-ima-html5/releases) 37 | for downloadable zips of the source. 38 | 39 | ### More Info 40 | 41 | For more information, see the documentation at 42 | https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/. 43 | 44 | ### Announcements and Updates 45 | 46 | For API and client library updates and news, follow our 47 | [Google Ads Developers blog](http://googleadsdeveloper.blogspot.com/) 48 | 49 | Copyright 2013 Google Inc. All Rights Reserved. You may study, modify, and use 50 | this example for any purpose. Note that this example is provided "as is", 51 | WITHOUT WARRANTY of any kind either expressed or implied. 52 | -------------------------------------------------------------------------------- /advanced/ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | /** 7 | * Shows how to use the IMA SDK to request and display ads. 8 | */ 9 | const Ads = function(application, videoPlayer) { 10 | this.application_ = application; 11 | this.videoPlayer_ = videoPlayer; 12 | this.customClickDiv_ = document.getElementById('customClick'); 13 | this.contentCompleteCalled_ = false; 14 | google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); 15 | // Call setLocale() to localize language text and downloaded swfs 16 | // google.ima.settings.setLocale('fr'); 17 | this.adDisplayContainer_ = new google.ima.AdDisplayContainer( 18 | this.videoPlayer_.adContainer, this.videoPlayer_.contentPlayer, 19 | this.customClickDiv_); 20 | this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_); 21 | this.adsManager_ = null; 22 | 23 | this.adsLoader_.addEventListener( 24 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 25 | this.onAdsManagerLoaded_, false, this); 26 | this.adsLoader_.addEventListener( 27 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this); 28 | }; 29 | 30 | // On iOS and Android devices, video playback must begin in a user action. 31 | // AdDisplayContainer provides a initialize() API to be called at appropriate 32 | // time. 33 | // This should be called when the user clicks or taps. 34 | Ads.prototype.initialUserAction = function() { 35 | this.adDisplayContainer_.initialize(); 36 | this.videoPlayer_.contentPlayer.load(); 37 | }; 38 | 39 | Ads.prototype.requestAds = function(adTagUrl) { 40 | const adsRequest = new google.ima.AdsRequest(); 41 | adsRequest.adTagUrl = adTagUrl; 42 | adsRequest.linearAdSlotWidth = this.videoPlayer_.width; 43 | adsRequest.linearAdSlotHeight = this.videoPlayer_.height; 44 | adsRequest.nonLinearAdSlotWidth = this.videoPlayer_.width; 45 | adsRequest.nonLinearAdSlotHeight = this.videoPlayer_.height; 46 | this.adsLoader_.requestAds(adsRequest); 47 | }; 48 | 49 | Ads.prototype.pause = function() { 50 | if (this.adsManager_) { 51 | this.adsManager_.pause(); 52 | } 53 | }; 54 | 55 | Ads.prototype.resume = function() { 56 | if (this.adsManager_) { 57 | this.adsManager_.resume(); 58 | } 59 | }; 60 | 61 | Ads.prototype.resize = function(width, height) { 62 | if (this.adsManager_) { 63 | this.adsManager_.resize(width, height); 64 | } 65 | }; 66 | 67 | Ads.prototype.contentEnded = function() { 68 | this.contentCompleteCalled_ = true; 69 | this.adsLoader_.contentComplete(); 70 | }; 71 | 72 | Ads.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) { 73 | this.application_.log('Ads loaded.'); 74 | const adsRenderingSettings = new google.ima.AdsRenderingSettings(); 75 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 76 | this.adsManager_ = adsManagerLoadedEvent.getAdsManager( 77 | this.videoPlayer_.contentPlayer, adsRenderingSettings); 78 | this.startAdsManager_(this.adsManager_); 79 | }; 80 | 81 | Ads.prototype.startAdsManager_ = function(adsManager) { 82 | if (adsManager.isCustomClickTrackingUsed()) { 83 | this.customClickDiv_.style.display = 'table'; 84 | } 85 | // Attach the pause/resume events. 86 | adsManager.addEventListener( 87 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, 88 | this.onContentPauseRequested_, false, this); 89 | adsManager.addEventListener( 90 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 91 | this.onContentResumeRequested_, false, this); 92 | // Handle errors. 93 | adsManager.addEventListener( 94 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this); 95 | const events = [ 96 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, google.ima.AdEvent.Type.CLICK, 97 | google.ima.AdEvent.Type.COMPLETE, google.ima.AdEvent.Type.FIRST_QUARTILE, 98 | google.ima.AdEvent.Type.LOADED, google.ima.AdEvent.Type.MIDPOINT, 99 | google.ima.AdEvent.Type.PAUSED, google.ima.AdEvent.Type.STARTED, 100 | google.ima.AdEvent.Type.THIRD_QUARTILE 101 | ]; 102 | for (const index in events) { 103 | adsManager.addEventListener(events[index], this.onAdEvent_, false, this); 104 | } 105 | 106 | let initWidth; 107 | let initHeight; 108 | if (this.application_.fullscreen) { 109 | initWidth = this.application_.fullscreenWidth; 110 | initHeight = this.application_.fullscreenHeight; 111 | } else { 112 | initWidth = this.videoPlayer_.width; 113 | initHeight = this.videoPlayer_.height; 114 | } 115 | adsManager.init(initWidth, initHeight); 116 | 117 | adsManager.start(); 118 | }; 119 | 120 | Ads.prototype.onContentPauseRequested_ = function() { 121 | this.application_.pauseForAd(); 122 | this.application_.setVideoEndedCallbackEnabled(false); 123 | }; 124 | 125 | Ads.prototype.onContentResumeRequested_ = function() { 126 | this.application_.setVideoEndedCallbackEnabled(true); 127 | // Without this check the video starts over from the beginning on a 128 | // post-roll's CONTENT_RESUME_REQUESTED 129 | if (!this.contentCompleteCalled_) { 130 | this.application_.resumeAfterAd(); 131 | } 132 | }; 133 | 134 | Ads.prototype.onAdEvent_ = function(adEvent) { 135 | this.application_.log('Ad event: ' + adEvent.type); 136 | 137 | if (adEvent.type == google.ima.AdEvent.Type.CLICK) { 138 | this.application_.adClicked(); 139 | } else if (adEvent.type == google.ima.AdEvent.Type.LOADED) { 140 | const ad = adEvent.getAd(); 141 | if (!ad.isLinear()) { 142 | this.onContentResumeRequested_(); 143 | } 144 | } 145 | }; 146 | 147 | Ads.prototype.onAdError_ = function(adErrorEvent) { 148 | this.application_.log('Ad error: ' + adErrorEvent.getError().toString()); 149 | if (this.adsManager_) { 150 | this.adsManager_.destroy(); 151 | } 152 | this.application_.resumeAfterAd(); 153 | }; 154 | -------------------------------------------------------------------------------- /advanced/application.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | /** 7 | * Handles user interaction and creates the player and ads controllers. 8 | */ 9 | const Application = function() { 10 | this.adTagBox_ = document.getElementById('tagText'); 11 | this.sampleAdTag_ = document.getElementById('sampleAdTag'); 12 | this.sampleAdTag_.addEventListener( 13 | 'click', this.bind_(this, this.onSampleAdTagClick_), false); 14 | this.console_ = document.getElementById('console'); 15 | this.playButton_ = document.getElementById('playpause'); 16 | this.playButton_.addEventListener( 17 | 'click', this.bind_(this, this.onClick_), false); 18 | this.fullscreenButton_ = document.getElementById('fullscreen'); 19 | this.fullscreenButton_.addEventListener( 20 | 'click', this.bind_(this, this.onFullscreenClick_), false); 21 | 22 | this.fullscreenWidth = null; 23 | this.fullscreenHeight = null; 24 | 25 | const fullScreenEvents = 26 | ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange']; 27 | for (let key in fullScreenEvents) { 28 | document.addEventListener( 29 | fullScreenEvents[key], this.bind_(this, this.onFullscreenChange_), 30 | false); 31 | } 32 | 33 | this.playing_ = false; 34 | this.adsActive_ = false; 35 | this.adsDone_ = false; 36 | this.fullscreen = false; 37 | 38 | this.videoPlayer_ = new VideoPlayer(); 39 | this.ads_ = new Ads(this, this.videoPlayer_); 40 | this.adTagUrl_ = ''; 41 | 42 | this.videoEndedCallback_ = this.bind_(this, this.onContentEnded_); 43 | this.setVideoEndedCallbackEnabled(true); 44 | }; 45 | 46 | Application.prototype.SAMPLE_AD_TAG_ = 'https://pubads.g.doubleclick.net/' + 47 | 'gampad/ads?iu=/21775744923/external/single_ad_samples&sz=640x480&' + 48 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' + 49 | 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&' + 50 | 'correlator='; 51 | 52 | /** 53 | * Registers or removes video ended callback based on the 'enable' param. 54 | * @param {boolean} enable 55 | */ 56 | Application.prototype.setVideoEndedCallbackEnabled = function(enable) { 57 | if (enable) { 58 | this.videoPlayer_.registerVideoEndedCallback(this.videoEndedCallback_); 59 | } else { 60 | this.videoPlayer_.removeVideoEndedCallback(this.videoEndedCallback_); 61 | } 62 | }; 63 | 64 | /** 65 | * Logs messages to the console. 66 | * @param {string} message 67 | */ 68 | Application.prototype.log = function(message) { 69 | console.log(message); 70 | this.console_.innerHTML = this.console_.innerHTML + '
' + message; 71 | }; 72 | 73 | /** 74 | * Handles resuming content following ads. 75 | */ 76 | Application.prototype.resumeAfterAd = function() { 77 | this.videoPlayer_.play(); 78 | this.adsActive_ = false; 79 | this.updateChrome_(); 80 | }; 81 | 82 | /** 83 | * Handles pausing content for ad breaks. 84 | */ 85 | Application.prototype.pauseForAd = function() { 86 | this.adsActive_ = true; 87 | this.playing_ = true; 88 | this.videoPlayer_.pause(); 89 | this.updateChrome_(); 90 | }; 91 | 92 | /** 93 | * Pauses video on ad clicks. 94 | */ 95 | Application.prototype.adClicked = function() { 96 | this.updateChrome_(); 97 | }; 98 | 99 | /** 100 | * Function binding helper function. 101 | * @param {!Object} thisObj object to bind function. 102 | * @param {!Function} fn function being bound to object. 103 | * @return {!Function} returns the bound function. 104 | */ 105 | Application.prototype.bind_ = function(thisObj, fn) { 106 | return function() { 107 | fn.apply(thisObj, arguments); 108 | }; 109 | }; 110 | 111 | /** 112 | * Set the sample ad tag as the ad tag box's value. 113 | */ 114 | Application.prototype.onSampleAdTagClick_ = function() { 115 | this.adTagBox_.value = this.SAMPLE_AD_TAG_; 116 | }; 117 | 118 | /** 119 | * Handles pausing the content or ads for ad clicks. 120 | */ 121 | Application.prototype.onClick_ = function() { 122 | if (!this.adsDone_) { 123 | if (this.adTagBox_.value == '') { 124 | this.log('Error: Fill in an ad tag'); 125 | return; 126 | } else { 127 | this.adTagUrl_ = this.adTagBox_.value; 128 | } 129 | // The user clicked/tapped - inform the ads controller that this code 130 | // is being run in a user action thread. 131 | this.ads_.initialUserAction(); 132 | // At the same time, initialize the content player as well. 133 | // When content is loaded, we'll issue the ad request to prevent it 134 | // from interfering with the initialization. See 135 | // https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/ads#iosvideo 136 | // for more information. 137 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_)); 138 | this.adsDone_ = true; 139 | return; 140 | } 141 | 142 | if (this.adsActive_) { 143 | if (this.playing_) { 144 | this.ads_.pause(); 145 | } else { 146 | this.ads_.resume(); 147 | } 148 | } else { 149 | if (this.playing_) { 150 | this.videoPlayer_.pause(); 151 | } else { 152 | this.videoPlayer_.play(); 153 | } 154 | } 155 | 156 | this.playing_ = !this.playing_; 157 | 158 | this.updateChrome_(); 159 | }; 160 | 161 | /** 162 | * Handles making the video fullscreen, or renturning from fullscreen. 163 | */ 164 | Application.prototype.onFullscreenClick_ = function() { 165 | if (this.fullscreen) { 166 | // The video is currently in fullscreen mode 167 | const cancelFullscreen = document.exitFullscreen || 168 | document.exitFullScreen || document.webkitCancelFullScreen || 169 | document.mozCancelFullScreen; 170 | if (cancelFullscreen) { 171 | cancelFullscreen.call(document); 172 | } else { 173 | this.onFullscreenChange_(); 174 | } 175 | } else { 176 | // Try to enter fullscreen mode in the browser 177 | const requestFullscreen = document.documentElement.requestFullscreen || 178 | document.documentElement.webkitRequestFullscreen || 179 | document.documentElement.mozRequestFullscreen || 180 | document.documentElement.requestFullScreen || 181 | document.documentElement.webkitRequestFullScreen || 182 | document.documentElement.mozRequestFullScreen; 183 | if (requestFullscreen) { 184 | this.fullscreenWidth = window.screen.width; 185 | this.fullscreenHeight = window.screen.height; 186 | requestFullscreen.call(document.documentElement); 187 | } else { 188 | this.fullscreenWidth = window.innerWidth; 189 | this.fullscreenHeight = window.innerHeight; 190 | this.onFullscreenChange_(); 191 | } 192 | } 193 | requestFullscreen.call(document.documentElement); 194 | }; 195 | 196 | /** 197 | * Handles updating the play button image. 198 | */ 199 | Application.prototype.updateChrome_ = function() { 200 | if (this.playing_) { 201 | this.playButton_.textContent = 'II'; 202 | } else { 203 | // Unicode play symbol. 204 | this.playButton_.textContent = String.fromCharCode(9654); 205 | } 206 | }; 207 | 208 | /** 209 | * Removes the 'loadedmetadata' listener and makes the ad request. 210 | */ 211 | Application.prototype.loadAds_ = function() { 212 | this.videoPlayer_.removePreloadListener(); 213 | this.ads_.requestAds(this.adTagUrl_); 214 | }; 215 | 216 | /** 217 | * Handles resizing ads and content during fullscreen button clicks. 218 | */ 219 | Application.prototype.onFullscreenChange_ = function() { 220 | if (this.fullscreen) { 221 | // The user just exited fullscreen 222 | // Resize the ad container 223 | this.ads_.resize(this.videoPlayer_.width, this.videoPlayer_.height); 224 | // Return the video to its original size and position 225 | this.videoPlayer_.resize( 226 | 'relative', '', '', this.videoPlayer_.width, this.videoPlayer_.height); 227 | this.fullscreen = false; 228 | } else { 229 | // The fullscreen button was just clicked 230 | // Resize the ad container 231 | const width = this.fullscreenWidth; 232 | const height = this.fullscreenHeight; 233 | this.makeAdsFullscreen_(); 234 | // Make the video take up the entire screen 235 | this.videoPlayer_.resize('absolute', 0, 0, width, height); 236 | this.fullscreen = true; 237 | } 238 | }; 239 | 240 | /** 241 | * Resizes ads for fullscreen. 242 | */ 243 | Application.prototype.makeAdsFullscreen_ = function() { 244 | this.ads_.resize(this.fullscreenWidth, this.fullscreenHeight); 245 | }; 246 | 247 | /** 248 | * Makes call to update UI on content ending. 249 | */ 250 | Application.prototype.onContentEnded_ = function() { 251 | this.ads_.contentEnded(); 252 | }; 253 | -------------------------------------------------------------------------------- /advanced/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | 27 | 38 | 39 | IMA HTML5 SDK Advanced Demo 40 | 41 | 42 |
43 |
IMA HTML5 SDK Advanced Demo
44 | 45 |
46 | 47 | 48 |
49 | 50 |
51 | 54 |
55 |
56 | 57 | 58 |
59 | 60 |
61 |
Click here for more info on your ad.
62 |
63 | 64 | 66 |
67 | 73 |
74 | 75 |
76 | Welcome to IMA HTML5 SDK Demo! 77 |
78 | 79 | 80 |
81 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /advanced/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: arial, verdana, sans-serif; 3 | overflow: hidden; 4 | } 5 | 6 | header, footer { text-align:center; width: 100%; } 7 | 8 | header { 9 | height: 40px; 10 | font-size: 40px; 11 | font-weight: bold; 12 | margin-top: 20px; 13 | margin-bottom: 20px; 14 | } 15 | 16 | footer { 17 | margin-top: 20px; 18 | } 19 | 20 | #container { 21 | margin-left: auto; 22 | margin-right: auto; 23 | width: 728px; 24 | } 25 | 26 | #videoplayer { 27 | position: relative; 28 | background-color: #000; 29 | border-radius: 5px; 30 | box-shadow: 0px 0px 20px rgba(50, 50, 50, 0.95); 31 | border: 2px #ccc solid; 32 | width: 640px; 33 | height: 360px; 34 | margin-left: auto; 35 | margin-right: auto; 36 | margin-top: 20px; 37 | } 38 | 39 | #content-wrapper { 40 | position:relative; 41 | top: 0px; 42 | left: 0px 43 | } 44 | 45 | #playpause { 46 | position: absolute; 47 | left: 20px; 48 | bottom: 20px; 49 | height: 40px; 50 | width: 100px; 51 | border-style: none; 52 | font-weight: bold; 53 | font-size: 25px; 54 | opacity: 0.5; 55 | background-color: #fff; 56 | border-radius: 5px; 57 | border: 1px transparent solid; 58 | color: #000; 59 | cursor: pointer; 60 | line-height: 0; 61 | } 62 | 63 | #playpause:hover { 64 | border: 1px #f00 solid; 65 | color: #f00; 66 | } 67 | 68 | #fullscreen { 69 | position: absolute; 70 | bottom: 20px; 71 | left: 140px; 72 | height: 40px; 73 | width: 100px; 74 | border-style: none; 75 | font-weight: bold; 76 | font-size: 25px; 77 | opacity: 0.5; 78 | background-color: #fff; 79 | border-radius: 5px; 80 | border: 1px transparent solid; 81 | color: #000; 82 | cursor: pointer; 83 | line-height: 0; 84 | } 85 | 86 | #fullscreen:hover { 87 | border: 1px #f00 solid; 88 | color: #f00; 89 | } 90 | 91 | #content { 92 | overflow: hidden; 93 | } 94 | 95 | #content, #adcontainer { 96 | position: absolute; 97 | top: 0px; 98 | left: 0px; 99 | width: 640px; 100 | height: 360px; 101 | } 102 | 103 | #console { 104 | font-family: courier, monospace; 105 | font-size: 12px; 106 | margin-top: 20px; 107 | height: 200px; 108 | width: 630px; 109 | padding: 5px; 110 | border: 1px #ccc solid; 111 | overflow-y: scroll; 112 | margin-left: auto; 113 | margin-right: auto; 114 | } 115 | 116 | #companionDiv, #customClick { 117 | width: 728px; 118 | height: 90px; 119 | margin-top: 20px; 120 | } 121 | 122 | #customClick { 123 | background-color: #807F80; 124 | display: none; 125 | text-align: center; 126 | } 127 | 128 | #customClickTextWrapper { 129 | display: table-cell; 130 | vertical-align: middle; 131 | } 132 | 133 | .urlLink { 134 | color: blue; 135 | text-decoration: underline; 136 | cursor: pointer; 137 | } 138 | -------------------------------------------------------------------------------- /advanced/video_player.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | /** 7 | * Handles video player functionality. 8 | */ 9 | const VideoPlayer = function() { 10 | this.contentPlayer = document.getElementById('content'); 11 | this.adContainer = document.getElementById('adcontainer'); 12 | this.videoPlayerContainer_ = document.getElementById('videoplayer'); 13 | 14 | this.width = 640; 15 | this.height = 360; 16 | }; 17 | 18 | VideoPlayer.prototype.preloadContent = function(contentLoadedAction) { 19 | // If this is the initial user action on iOS or Android device, 20 | // simulate playback to enable the video element for later program-triggered 21 | // playback. 22 | if (this.isMobilePlatform()) { 23 | this.preloadListener_ = contentLoadedAction; 24 | this.contentPlayer.addEventListener( 25 | 'loadedmetadata', contentLoadedAction, false); 26 | this.contentPlayer.load(); 27 | } else { 28 | contentLoadedAction(); 29 | } 30 | }; 31 | 32 | VideoPlayer.prototype.removePreloadListener = function() { 33 | if (this.preloadListener_) { 34 | this.contentPlayer.removeEventListener( 35 | 'loadedmetadata', this.preloadListener_, false); 36 | this.preloadListener_ = null; 37 | } 38 | }; 39 | 40 | VideoPlayer.prototype.play = function() { 41 | this.contentPlayer.play(); 42 | }; 43 | 44 | VideoPlayer.prototype.pause = function() { 45 | this.contentPlayer.pause(); 46 | }; 47 | 48 | VideoPlayer.prototype.isMobilePlatform = function() { 49 | return this.contentPlayer.paused && 50 | (navigator.userAgent.match(/(iPod|iPhone|iPad)/) || 51 | navigator.userAgent.toLowerCase().indexOf('android') > -1); 52 | }; 53 | 54 | VideoPlayer.prototype.resize = function(position, top, left, width, height) { 55 | this.videoPlayerContainer_.style.position = position; 56 | this.videoPlayerContainer_.style.top = top + 'px'; 57 | this.videoPlayerContainer_.style.left = left + 'px'; 58 | this.videoPlayerContainer_.style.width = width + 'px'; 59 | this.videoPlayerContainer_.style.height = height + 'px'; 60 | this.contentPlayer.style.width = width + 'px'; 61 | this.contentPlayer.style.height = height + 'px'; 62 | }; 63 | 64 | VideoPlayer.prototype.registerVideoEndedCallback = function(callback) { 65 | this.contentPlayer.addEventListener('ended', callback, false); 66 | }; 67 | 68 | VideoPlayer.prototype.removeVideoEndedCallback = function(callback) { 69 | this.contentPlayer.removeEventListener('ended', callback, false); 70 | }; 71 | -------------------------------------------------------------------------------- /attempt_to_autoplay/ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | let adsManager; 16 | let adsLoader; 17 | let adDisplayContainer; 18 | let playButton; 19 | let videoContent; 20 | let adsInitialized; 21 | let autoplayAllowed; 22 | let autoplayRequiresMuted; 23 | 24 | /** 25 | * Initializes IMA setup. 26 | */ 27 | function initDesktopAutoplayExample() { 28 | videoContent = document.getElementById('contentElement'); 29 | playButton = document.getElementById('playButton'); 30 | playButton.addEventListener('click', () => { 31 | // Initialize the container. Must be done through a user action where 32 | // autoplay is not allowed. 33 | adDisplayContainer.initialize(); 34 | adsInitialized = true; 35 | videoContent.load(); 36 | playAds(); 37 | }); 38 | setUpIMA(); 39 | // Check if autoplay is supported. 40 | checkAutoplaySupport(); 41 | } 42 | 43 | /** 44 | * Attempts autoplay and handles success and failure cases. 45 | */ 46 | function checkAutoplaySupport() { 47 | // Test for autoplay support with our content player. 48 | const playPromise = videoContent.play(); 49 | if (playPromise !== undefined) { 50 | playPromise.then(onAutoplayWithSoundSuccess).catch(onAutoplayWithSoundFail); 51 | } 52 | } 53 | 54 | /** 55 | * Handles case where autoplay succeeded with sound. 56 | */ 57 | function onAutoplayWithSoundSuccess() { 58 | // If we make it here, unmuted autoplay works. 59 | videoContent.pause(); 60 | autoplayAllowed = true; 61 | autoplayRequiresMuted = false; 62 | autoplayChecksResolved(); 63 | } 64 | 65 | /** 66 | * Handles case where autoplay fails with sound. 67 | */ 68 | function onAutoplayWithSoundFail() { 69 | // Unmuted autoplay failed. Now try muted autoplay. 70 | checkMutedAutoplaySupport(); 71 | } 72 | 73 | /** 74 | * Checks if video can autoplay while muted. 75 | */ 76 | function checkMutedAutoplaySupport() { 77 | videoContent.volume = 0; 78 | videoContent.muted = true; 79 | const playPromise = videoContent.play(); 80 | if (playPromise !== undefined) { 81 | playPromise.then(onMutedAutoplaySuccess).catch(onMutedAutoplayFail); 82 | } 83 | } 84 | 85 | /** 86 | * Handles case where autoplay succeeded while muted. 87 | */ 88 | function onMutedAutoplaySuccess() { 89 | // If we make it here, muted autoplay works but unmuted autoplay does not. 90 | videoContent.pause(); 91 | autoplayAllowed = true; 92 | autoplayRequiresMuted = true; 93 | autoplayChecksResolved(); 94 | } 95 | 96 | /** 97 | * Handles case where autoplay failed while muted. 98 | */ 99 | function onMutedAutoplayFail() { 100 | // Both muted and unmuted autoplay failed. Fall back to click to play. 101 | videoContent.volume = 1; 102 | videoContent.muted = false; 103 | autoplayAllowed = false; 104 | autoplayRequiresMuted = false; 105 | autoplayChecksResolved(); 106 | } 107 | 108 | /** 109 | * Sets up IMA ad display container, ads loader, and makes an ad request. 110 | */ 111 | function setUpIMA() { 112 | // Create the ad display container. 113 | createAdDisplayContainer(); 114 | // Create ads loader. 115 | adsLoader = new google.ima.AdsLoader(adDisplayContainer); 116 | // Listen and respond to ads loaded and error events. 117 | adsLoader.addEventListener( 118 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 119 | onAdsManagerLoaded, false); 120 | adsLoader.addEventListener( 121 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); 122 | 123 | // An event listener to tell the SDK that our content video 124 | // is completed so the SDK can play any post-roll ads. 125 | videoContent.onended = contentEndedListener; 126 | } 127 | 128 | /** 129 | * Handles content ending and calls adsLoader.contentComplete() 130 | */ 131 | function contentEndedListener() { 132 | videoContent.onended = null; 133 | if (adsLoader) { 134 | adsLoader.contentComplete(); 135 | } 136 | } 137 | 138 | /** 139 | * Builds an ad request and uses it to request ads. 140 | */ 141 | function autoplayChecksResolved() { 142 | // Request video ads. 143 | const adsRequest = new google.ima.AdsRequest(); 144 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' + 145 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' + 146 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' + 147 | 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&' + 148 | 'impl=s&correlator='; 149 | 150 | // Specify the linear and nonlinear slot sizes. This helps the SDK to 151 | // select the correct creative if multiple are returned. 152 | adsRequest.linearAdSlotWidth = 640; 153 | adsRequest.linearAdSlotHeight = 400; 154 | 155 | adsRequest.nonLinearAdSlotWidth = 640; 156 | adsRequest.nonLinearAdSlotHeight = 150; 157 | 158 | adsRequest.setAdWillAutoPlay(autoplayAllowed); 159 | adsRequest.setAdWillPlayMuted(autoplayRequiresMuted); 160 | adsLoader.requestAds(adsRequest); 161 | } 162 | 163 | /** 164 | * Sets the 'adContainer' div as the IMA ad display container. 165 | */ 166 | function createAdDisplayContainer() { 167 | // We assume the adContainer is the DOM id of the element that will house 168 | // the ads. 169 | adDisplayContainer = new google.ima.AdDisplayContainer( 170 | document.getElementById('adContainer'), videoContent); 171 | } 172 | 173 | /** 174 | * Loads the video content and initializes IMA ad playback. 175 | */ 176 | function playAds() { 177 | try { 178 | if (!adsInitialized) { 179 | adDisplayContainer.initialize(); 180 | adsInitialized = true; 181 | } 182 | // Initialize the ads manager. Ad rules playlist will start at this time. 183 | adsManager.init(640, 360); 184 | // Call play to start showing the ad. Single video and overlay ads will 185 | // start at this time; the call will be ignored for ad rules. 186 | adsManager.start(); 187 | } catch (adError) { 188 | // An error may be thrown if there was a problem with the VAST response. 189 | videoContent.play(); 190 | } 191 | } 192 | 193 | /** 194 | * Handles the ad manager loading and sets ad event listeners. 195 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent 196 | */ 197 | function onAdsManagerLoaded(adsManagerLoadedEvent) { 198 | // Get the ads manager. 199 | const adsRenderingSettings = new google.ima.AdsRenderingSettings(); 200 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 201 | // videoContent should be set to the content video element. 202 | adsManager = 203 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); 204 | // Mute the ad if doing muted autoplay. 205 | const adVolume = (autoplayAllowed && autoplayRequiresMuted) ? 0 : 1; 206 | adsManager.setVolume(adVolume); 207 | 208 | // Add listeners to the required events. 209 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); 210 | adsManager.addEventListener( 211 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); 212 | adsManager.addEventListener( 213 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 214 | onContentResumeRequested); 215 | adsManager.addEventListener( 216 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); 217 | 218 | // Listen to any additional events, if necessary. 219 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); 220 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); 221 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); 222 | 223 | if (autoplayAllowed) { 224 | playAds(); 225 | } else { 226 | playButton.style.display = 'block'; 227 | } 228 | } 229 | 230 | /** 231 | * Handles actions taken in response to ad events. 232 | * @param {!google.ima.AdEvent} adEvent 233 | */ 234 | function onAdEvent(adEvent) { 235 | // Retrieve the ad from the event. Some events (for example, 236 | // ALL_ADS_COMPLETED) don't have ad object associated. 237 | const ad = adEvent.getAd(); 238 | switch (adEvent.type) { 239 | case google.ima.AdEvent.Type.LOADED: 240 | // This is the first event sent for an ad - it is possible to 241 | // determine whether the ad is a video ad or an overlay. 242 | if (!ad.isLinear()) { 243 | videoContent.play(); 244 | } 245 | break; 246 | } 247 | } 248 | 249 | /** 250 | * Handles ad errors. 251 | * @param {!google.ima.AdErrorEvent} adErrorEvent 252 | */ 253 | function onAdError(adErrorEvent) { 254 | // Handle the error logging. 255 | console.log(adErrorEvent.getError()); 256 | adsManager.destroy(); 257 | // Fall back to playing content. 258 | videoContent.play(); 259 | } 260 | 261 | /** 262 | * Pauses video content and sets up ad UI. 263 | */ 264 | function onContentPauseRequested() { 265 | videoContent.pause(); 266 | videoContent.onended = null; 267 | } 268 | 269 | /** 270 | * Resumes video content and removes ad UI. 271 | */ 272 | function onContentResumeRequested() { 273 | videoContent.play(); 274 | videoContent.onended = contentEndedListener; 275 | } 276 | -------------------------------------------------------------------------------- /attempt_to_autoplay/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | IMA HTML5 Attempt to Autoplay 4 | 5 | 6 | 7 | 8 | 9 | 10 |

11 | This player does the following: 12 |

    13 |
  1. Attempt to autoplay unmuted. If that works, do so. If not, move on to step 2.
  2. 14 |
  3. Attempt to autoplay muted. If that works, do so. If not, move on to step 3.
  4. 15 |
  5. Fall back to click-to-play.
  6. 16 |
17 |

18 |
19 |
20 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /attempt_to_autoplay/style.css: -------------------------------------------------------------------------------- 1 | #mainContainer { 2 | position: relative; 3 | width: 640px; 4 | height: 360px; 5 | } 6 | 7 | #content, #adContainer { 8 | position: absolute; 9 | top: 0px; 10 | left: 0px; 11 | width: 640px; 12 | height: 360px; 13 | } 14 | 15 | #contentElement { 16 | width: 640px; 17 | height: 360px; 18 | overflow: hidden; 19 | } 20 | 21 | #playButton { 22 | display: none; 23 | margin-top:10px; 24 | vertical-align: top; 25 | width: 350px; 26 | height: 60px; 27 | padding: 0; 28 | font-size: 22px; 29 | font-family: sans-serif; 30 | color: white; 31 | text-align: center; 32 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 33 | background: #2c3e50; 34 | border: 0; 35 | border-bottom: 2px solid #22303f; 36 | cursor: pointer; 37 | -webkit-box-shadow: inset 0 -2px #22303f; 38 | box-shadow: inset 0 -2px #22303f; 39 | } 40 | -------------------------------------------------------------------------------- /audio_in_video/ads.js: -------------------------------------------------------------------------------- 1 | let videoElement; 2 | let playButton; 3 | let adsLoaded = false; 4 | let adContainer; 5 | let adDisplayContainer; 6 | let adsLoader; 7 | let adsManager; 8 | let adsActive; 9 | let isPlaying; 10 | let contentCompleted = false; 11 | const RIGHT_POINTING_TRIANGLE_CHAR_CODE = 9654; 12 | const adTagUrl = 13 | 'https://pubads.g.doubleclick.net/gampad/ads?iu=/6075/Rahul_AdUnit_Test_1&description_url=[placeholder]&tfcd=0&npa=0&ad_type=audio_video&sz=640x360&ciu_szs=640x360&cust_params=yt_channel_id%3Drtryuyuu&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator='; 14 | 15 | // On window load, attach an event to the play button click 16 | // that triggers playback on the video element 17 | window.addEventListener('load', function(event) { 18 | videoElement = document.getElementById('video-element'); 19 | videoElement.addEventListener('play', function(event) { 20 | loadAds(event); 21 | }); 22 | initializeIMA(); 23 | playButton = document.getElementById('play-pause'); 24 | playButton.addEventListener('click', playPause); 25 | }); 26 | 27 | window.addEventListener('resize', function(event) { 28 | console.log('window resized'); 29 | if (adsManager) { 30 | let width = videoElement.clientWidth; 31 | let height = videoElement.clientHeight; 32 | adsManager.resize(width, height); 33 | } 34 | }); 35 | 36 | /** Implementing play&pause functionality for player control */ 37 | function playPause() { 38 | if (adsActive) { 39 | if (isPlaying) { 40 | adsManager.pause(); 41 | } else { 42 | adsManager.resume(); 43 | } 44 | } else { 45 | if (isPlaying) { 46 | videoElement.pause(); 47 | } else { 48 | videoElement.play(); 49 | } 50 | } 51 | 52 | isPlaying = !isPlaying; 53 | 54 | if (isPlaying) { 55 | playButton.textContent = '||'; 56 | } else { 57 | playButton.textContent = 58 | String.fromCharCode(RIGHT_POINTING_TRIANGLE_CHAR_CODE); 59 | } 60 | } 61 | 62 | /** Initializing IMA SDK */ 63 | function initializeIMA() { 64 | console.log('initializing IMA'); 65 | adContainer = document.getElementById('ad-container'); 66 | adDisplayContainer = 67 | new google.ima.AdDisplayContainer(adContainer, videoElement); 68 | 69 | // Set the feature flag and default image URL for the audio poster feature. 70 | google.ima.settings.setFeatureFlags( 71 | { 72 | 'audioPosterImageEnabled': true, 73 | 'audioPosterImageDefaultUrl': 74 | 'https://storage.googleapis.com/interactive-media-ads/images/ima_default_audio_sample.png' 75 | }); 76 | adsLoader = new google.ima.AdsLoader(adDisplayContainer); 77 | adsLoader.addEventListener( 78 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 79 | onAdsManagerLoaded, false); 80 | adsLoader.addEventListener( 81 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); 82 | 83 | // Let the AdsLoader know when the video has ended 84 | videoElement.addEventListener('ended', function() { 85 | adsLoader.contentComplete(); 86 | contentCompleted = true; 87 | }); 88 | 89 | const adsRequest = new google.ima.AdsRequest(); 90 | adsRequest.adTagUrl = adTagUrl; 91 | // Specify the linear and nonlinear slot sizes. This helps the SDK to 92 | // select the correct creative if multiple are returned. 93 | adsRequest.linearAdSlotWidth = videoElement.clientWidth; 94 | adsRequest.linearAdSlotHeight = videoElement.clientHeight; 95 | adsRequest.nonLinearAdSlotWidth = videoElement.clientWidth; 96 | adsRequest.nonLinearAdSlotHeight = videoElement.clientHeight / 3; 97 | 98 | // Pass the request to the adsLoader to request ads 99 | adsLoader.requestAds(adsRequest); 100 | } 101 | 102 | /** 103 | * Loading Ads 104 | * @param {!event} event The event triggering ad loading 105 | */ 106 | function loadAds(event) { 107 | // Prevent this function from running on if there are already ads loaded 108 | if (adsLoaded) { 109 | return; 110 | } 111 | adsLoaded = true; 112 | 113 | // Prevent triggering immediate playback when ads are loading 114 | event.preventDefault(); 115 | 116 | console.log('loading ads'); 117 | 118 | // Initialize the container. Must be done through a user action on mobile 119 | // devices. 120 | videoElement.load(); 121 | adDisplayContainer.initialize(); 122 | 123 | const width = videoElement.clientWidth; 124 | const height = videoElement.clientHeight; 125 | try { 126 | adsManager.init(width, height); 127 | adsManager.start(); 128 | } catch (adError) { 129 | // Play the video without ads, if an error occurs 130 | console.log('AdsManager could not be started'); 131 | videoElement.play(); 132 | isPlaying = true; 133 | } 134 | } 135 | 136 | /** 137 | * Handling adsManagerLoaded event 138 | * @param {!adsManagerLoadedEvent} adsManagerLoadedEvent The event indicating 139 | * adsManager has been loaded 140 | */ 141 | function onAdsManagerLoaded(adsManagerLoadedEvent) { 142 | // Instantiate the AdsManager from the adsLoader response and pass it the 143 | // video element 144 | adsManager = adsManagerLoadedEvent.getAdsManager(videoElement); 145 | 146 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); 147 | adsManager.addEventListener( 148 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); 149 | adsManager.addEventListener( 150 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 151 | onContentResumeRequested); 152 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdLoaded); 153 | adsManager.addEventListener(google.ima.AdEvent.Type.PAUSED, onAdEvent); 154 | adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED, onAdEvent); 155 | adsManager.addEventListener(google.ima.AdEvent.Type.RESUMED, onAdEvent); 156 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); 157 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); 158 | adsManager.addEventListener( 159 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); 160 | } 161 | 162 | /** 163 | * Handing error event 164 | * @param {!adErrorEvent} adErrorEvent Event indicating ad error 165 | */ 166 | function onAdError(adErrorEvent) { 167 | // Handle the error logging. 168 | console.log(adErrorEvent.getError()); 169 | if (adsManager) { 170 | adsManager.destroy(); 171 | } 172 | } 173 | 174 | /** Handing content pause */ 175 | function onContentPauseRequested() { 176 | videoElement.pause(); 177 | isPlaying = false; 178 | } 179 | 180 | /** Handing content resume */ 181 | function onContentResumeRequested() { 182 | adsActive = false; 183 | // condition to disable replaying of content video after post-roll ads 184 | if (!contentCompleted) { 185 | videoElement.play(); 186 | isPlaying = true; 187 | } 188 | } 189 | 190 | /** 191 | * Handing ad loaded event 192 | * @param {!adEvent} adEvent Ad Event 193 | */ 194 | function onAdLoaded(adEvent) { 195 | const ad = adEvent.getAd(); 196 | if (!ad.isLinear()) { 197 | videoElement.play(); 198 | isPlaying = true; 199 | } 200 | } 201 | 202 | /** 203 | * Handing ad play-related events 204 | * @param {!adEvent} adEvent Ad Event 205 | */ 206 | function onAdEvent(adEvent) { 207 | // Retrieve the ad from the event. Some events (for example, 208 | // ALL_ADS_COMPLETED) don't have ad object associated. 209 | const ad = adEvent.getAd(); 210 | switch (adEvent.type) { 211 | case google.ima.AdEvent.Type.LOADED: 212 | // This is the first event sent for an ad - it is possible to 213 | // determine whether the ad is a video ad or an overlay. 214 | if (!ad.isLinear()) { 215 | // Position AdDisplayContainer correctly for overlay. 216 | // Use ad.width and ad.height. 217 | videoContent.play(); 218 | } 219 | break; 220 | case google.ima.AdEvent.Type.STARTED: 221 | adsActive = true; 222 | isPlaying = true; 223 | break; 224 | case google.ima.AdEvent.Type.PAUSED: 225 | isPlaying = false; 226 | break; 227 | case google.ima.AdEvent.Type.RESUMED: 228 | isPlaying = true; 229 | break; 230 | case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: 231 | // reset the play button after all ads and content have completed 232 | if (contentCompleted) { 233 | isPlaying = false; 234 | playButton.textContent = String.fromCharCode(9654); 235 | } 236 | break; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /audio_in_video/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/audio_in_video/image.png -------------------------------------------------------------------------------- /audio_in_video/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Audio in Video Demo 5 | 6 | 7 | 8 | 9 | 10 |

Audio Ads in Video Player Demo

11 |
12 |
13 | 16 |
17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /audio_in_video/style.css: -------------------------------------------------------------------------------- 1 | h2, 2 | p { 3 | text-align: center; 4 | } 5 | 6 | #page-content { 7 | position: relative; 8 | /* this element's width controls the effective height */ 9 | /* of the video container's padding-bottom */ 10 | max-width: 640px; 11 | margin: 10px auto; 12 | } 13 | 14 | #video-container { 15 | position: relative; 16 | /* forces the container to match a 16x9 aspect ratio */ 17 | /* replace with 75% for a 4:3 aspect ratio, if needed */ 18 | padding-bottom: 56.25%; 19 | } 20 | 21 | #video-element { 22 | /* forces the contents to fill the container */ 23 | position: absolute; 24 | top: 0; 25 | left: 0; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | /* apply the ad-container styling to companionDiv */ 31 | #ad-container, 32 | #audio-companion { 33 | position: absolute; 34 | top: 0; 35 | left: 0; 36 | width: 100%; 37 | } 38 | 39 | #video-companion { 40 | display: block; 41 | height: 90px; 42 | width: 728px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } 46 | 47 | #message { 48 | text-align: center; 49 | } 50 | 51 | #play-pause { 52 | position: absolute; 53 | left: 0; 54 | bottom: 0; 55 | height: 40px; 56 | width: 100px; 57 | border-style: none; 58 | font-weight: bold; 59 | font-size: 25px; 60 | opacity: 0.5; 61 | background-color: #fff; 62 | border-radius: 5px; 63 | border: 1px solid; 64 | color: #000; 65 | cursor: pointer; 66 | line-height: 0; 67 | } 68 | 69 | #play-pause:hover { 70 | border: 1px #f00 solid; 71 | color: #f00; 72 | } 73 | -------------------------------------------------------------------------------- /live_stream_prefetch/ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | let adsManager; 7 | let adsLoader; 8 | let adDisplayContainer; 9 | let intervalTimer; 10 | let playButton; 11 | let videoContent; 12 | let adDisplayContainerInitialized; 13 | 14 | const AD_REQUEST_INTERVAL = 30; 15 | 16 | /** 17 | * Initializes IMA setup. 18 | */ 19 | function init() { 20 | videoContent = document.getElementById('contentElement'); 21 | playButton = document.getElementById('playButton'); 22 | playButton.addEventListener('click', playAds); 23 | setUpAdsLoader(); 24 | // We want a pre-roll, so the first time we request ads set 25 | // liveStreamPrefetchSeconds to 0. 26 | requestAds(0); 27 | } 28 | 29 | /** 30 | * Sets up ads loader and associated event listeners. 31 | */ 32 | function setUpAdsLoader() { 33 | // Create the ad display container. 34 | createAdDisplayContainer(); 35 | // Create ads loader. 36 | adsLoader = new google.ima.AdsLoader(adDisplayContainer); 37 | // Listen and respond to ads loaded and error events. 38 | adsLoader.addEventListener( 39 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 40 | onAdsManagerLoaded, false); 41 | adsLoader.addEventListener( 42 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); 43 | 44 | // An event listener to tell the SDK that our content video 45 | // is completed so the SDK can play any post-roll ads. 46 | const contentEndedListener = function() { 47 | adsLoader.contentComplete(); 48 | }; 49 | videoContent.onended = contentEndedListener; 50 | } 51 | 52 | /** 53 | * Makes an ad request with a prefetch time. 54 | * @param {number} liveStreamPrefetchSeconds prefetch time in seconds. 55 | */ 56 | function requestAds(liveStreamPrefetchSeconds) { 57 | if (adsLoader) { 58 | adsLoader.contentComplete(); 59 | } 60 | // Request video ads. 61 | const adsRequest = new google.ima.AdsRequest(); 62 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' + 63 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' + 64 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' + 65 | 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&' + 66 | 'correlator='; 67 | 68 | // Specify the linear and nonlinear slot sizes. This helps the SDK to 69 | // select the correct creative if multiple are returned. 70 | adsRequest.linearAdSlotWidth = 640; 71 | adsRequest.linearAdSlotHeight = 400; 72 | 73 | adsRequest.nonLinearAdSlotWidth = 640; 74 | adsRequest.nonLinearAdSlotHeight = 150; 75 | 76 | adsRequest.liveStreamPrefetchSeconds = liveStreamPrefetchSeconds; 77 | 78 | adsLoader.requestAds(adsRequest); 79 | } 80 | 81 | /** 82 | * Sets the 'adContainer' div as the IMA ad display container. 83 | */ 84 | function createAdDisplayContainer() { 85 | // We assume the adContainer is the DOM id of the element that will house 86 | // the ads. 87 | adDisplayContainer = new google.ima.AdDisplayContainer( 88 | document.getElementById('adContainer'), videoContent); 89 | } 90 | 91 | /** 92 | * Loads the video content and initializes IMA ad playback. 93 | */ 94 | function playAds() { 95 | if (!adDisplayContainerInitialized) { 96 | // Initialize the container. Must be done through a user action on mobile 97 | // devices. 98 | videoContent.load(); 99 | adDisplayContainer.initialize(); 100 | adDisplayContainerInitialized = true; 101 | } 102 | 103 | try { 104 | // Initialize the ads manager. Ad rules playlist will start at this time. 105 | adsManager.init(640, 360); 106 | // Call play to start showing the ad. Single video and overlay ads will 107 | // start at this time; the call will be ignored for ad rules. 108 | adsManager.start(); 109 | } catch (adError) { 110 | // An error may be thrown if there was a problem with the VAST response. 111 | videoContent.play(); 112 | } 113 | } 114 | 115 | /** 116 | * Handles the ad manager loading and sets ad event listeners. 117 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent 118 | */ 119 | function onAdsManagerLoaded(adsManagerLoadedEvent) { 120 | // Get the ads manager. 121 | const adsRenderingSettings = new google.ima.AdsRenderingSettings(); 122 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 123 | // videoContent should be set to the content video element. 124 | adsManager = 125 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); 126 | 127 | // Add listeners to the required events. 128 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); 129 | adsManager.addEventListener( 130 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); 131 | adsManager.addEventListener( 132 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 133 | onContentResumeRequested); 134 | adsManager.addEventListener( 135 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); 136 | 137 | // Listen to any additional events, if necessary. 138 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); 139 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); 140 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); 141 | } 142 | 143 | /** 144 | * Handles actions taken in response to ad events. 145 | * @param {!google.ima.AdEvent} adEvent 146 | */ 147 | function onAdEvent(adEvent) { 148 | // Retrieve the ad from the event. Some events (for example, 149 | // ALL_ADS_COMPLETED) don't have ad object associated. 150 | const ad = adEvent.getAd(); 151 | switch (adEvent.type) { 152 | case google.ima.AdEvent.Type.LOADED: 153 | // This is the first event sent for an ad - it is possible to 154 | // determine whether the ad is a video ad or an overlay. 155 | if (!ad.isLinear()) { 156 | // Position AdDisplayContainer correctly for overlay. 157 | // Use ad.width and ad.height. 158 | videoContent.play(); 159 | } 160 | break; 161 | case google.ima.AdEvent.Type.STARTED: 162 | // This event indicates the ad has started - the video player 163 | // can adjust the UI, for example display a pause button and 164 | // remaining time. 165 | if (ad.isLinear()) { 166 | // For a linear ad, a timer can be started to poll for 167 | // the remaining time. 168 | intervalTimer = setInterval( 169 | function() { 170 | // Example: const remainingTime = adsManager.getRemainingTime(); 171 | }, 172 | 300); // every 300ms 173 | } 174 | break; 175 | case google.ima.AdEvent.Type.COMPLETE: 176 | // This event indicates the ad has finished - the video player 177 | // can perform appropriate UI actions, such as removing the timer for 178 | // remaining time detection. 179 | if (ad.isLinear()) { 180 | clearInterval(intervalTimer); 181 | } 182 | break; 183 | case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: 184 | // Request ads no later than 5 seconds before our next ad break. 185 | requestAds(AD_REQUEST_INTERVAL - 5); 186 | // Play those ads at the next ad break. 187 | setTimeout(() => { 188 | playAds(); 189 | }, AD_REQUEST_INTERVAL * 1000); 190 | break; 191 | } 192 | } 193 | 194 | /** 195 | * Handles ad errors. 196 | * @param {!google.ima.AdErrorEvent} adErrorEvent 197 | */ 198 | function onAdError(adErrorEvent) { 199 | // Handle the error logging. 200 | console.log(adErrorEvent.getError()); 201 | adsManager.destroy(); 202 | } 203 | 204 | /** 205 | * Pauses video content and sets up ad UI. 206 | */ 207 | function onContentPauseRequested() { 208 | videoContent.pause(); 209 | // This function is where you should setup UI for showing ads (for example, 210 | // display ad timer countdown, disable seeking and more.) 211 | // setupUIForAds(); 212 | } 213 | 214 | /** 215 | * Resumes video content and removes ad UI. 216 | */ 217 | function onContentResumeRequested() { 218 | videoContent.play(); 219 | // This function is where you should ensure that your UI is ready 220 | // to play content. It is the responsibility of the Publisher to 221 | // implement this function when necessary. 222 | // setupUIForContent(); 223 | } 224 | 225 | // Wire UI element references and UI event listeners. 226 | init(); 227 | -------------------------------------------------------------------------------- /live_stream_prefetch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | IMA HTML5 Simple Demo 4 | 5 | 6 | 7 | 8 |
9 |
10 | 13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /live_stream_prefetch/style.css: -------------------------------------------------------------------------------- 1 | #mainContainer { 2 | position: relative; 3 | width: 640px; 4 | height: 360px; 5 | } 6 | 7 | #content, #adContainer { 8 | position: absolute; 9 | top: 0px; 10 | left: 0px; 11 | width: 640px; 12 | height: 360px; 13 | } 14 | 15 | #contentElement { 16 | width: 640px; 17 | height: 360px; 18 | overflow: hidden; 19 | } 20 | 21 | #playButton { 22 | margin-top:10px; 23 | vertical-align: top; 24 | width: 350px; 25 | height: 60px; 26 | padding: 0; 27 | font-size: 22px; 28 | color: white; 29 | text-align: center; 30 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 31 | background: #2c3e50; 32 | border: 0; 33 | border-bottom: 2px solid #22303f; 34 | cursor: pointer; 35 | -webkit-box-shadow: inset 0 -2px #22303f; 36 | box-shadow: inset 0 -2px #22303f; 37 | } 38 | -------------------------------------------------------------------------------- /mobile_auto_skippable/ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | let adsManager; 7 | let adsLoader; 8 | let adDisplayContainer; 9 | let intervalTimer; 10 | let videoContent; 11 | 12 | /** 13 | * Initializes IMA setup. 14 | */ 15 | function init() { 16 | videoContent = document.getElementById('contentElement'); 17 | setUpIMA(); 18 | } 19 | 20 | /** 21 | * Sets up IMA ad display container, ads loader, and makes an ad request. 22 | */ 23 | function setUpIMA() { 24 | google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); 25 | // Create the ad display container. 26 | createAdDisplayContainer(); 27 | // Create ads loader. 28 | adsLoader = new google.ima.AdsLoader(adDisplayContainer); 29 | // Listen and respond to ads loaded and error events. 30 | adsLoader.addEventListener( 31 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 32 | onAdsManagerLoaded, false); 33 | adsLoader.addEventListener( 34 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); 35 | 36 | // An event listener to tell the SDK that our content video 37 | // is completed so the SDK can play any post-roll ads. 38 | const contentEndedListener = function() { 39 | adsLoader.contentComplete(); 40 | }; 41 | videoContent.onended = contentEndedListener; 42 | 43 | // Request video ads. 44 | const adsRequest = new google.ima.AdsRequest(); 45 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' + 46 | 'iu=/21775744923/external/single_preroll_skippable&sz=640x480&' + 47 | 'ciu_szs=300x250%2C728x90&gdfp_req=1&' + 48 | 'output=vast&unviewed_position_start=1&env=vp&impl=s&correlator='; 49 | 50 | // Specify the linear and nonlinear slot sizes. This helps the SDK to 51 | // select the correct creative if multiple are returned. 52 | adsRequest.linearAdSlotWidth = 640; 53 | adsRequest.linearAdSlotHeight = 400; 54 | 55 | adsRequest.nonLinearAdSlotWidth = 640; 56 | adsRequest.nonLinearAdSlotHeight = 150; 57 | 58 | adsLoader.requestAds(adsRequest); 59 | } 60 | 61 | /** 62 | * Sets the 'adContainer' div as the IMA ad display container. 63 | */ 64 | function createAdDisplayContainer() { 65 | // We assume the adContainer is the DOM id of the element that will house 66 | // the ads. 67 | adDisplayContainer = new google.ima.AdDisplayContainer( 68 | document.getElementById('adContainer'), videoContent); 69 | } 70 | 71 | /** 72 | * Loads the video content and initializes IMA ad playback. 73 | */ 74 | function playAds() { 75 | // Initialize the container. Must be done through a user action on mobile 76 | // devices. 77 | videoContent.load(); 78 | adDisplayContainer.initialize(); 79 | 80 | try { 81 | // Initialize the ads manager. Ad rules playlist will start at this time. 82 | adsManager.init(640, 360); 83 | // Call play to start showing the ad. Single video and overlay ads will 84 | // start at this time; the call will be ignored for ad rules. 85 | adsManager.start(); 86 | } catch (adError) { 87 | // An error may be thrown if there was a problem with the VAST response. 88 | videoContent.play(); 89 | } 90 | } 91 | 92 | /** 93 | * Handles the ad manager loading and sets ad event listeners. 94 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent 95 | */ 96 | function onAdsManagerLoaded(adsManagerLoadedEvent) { 97 | // Get the ads manager. 98 | const adsRenderingSettings = new google.ima.AdsRenderingSettings(); 99 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 100 | // videoContent should be set to the content video element. 101 | adsManager = 102 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); 103 | 104 | // Add listeners to the required events. 105 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); 106 | adsManager.addEventListener( 107 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); 108 | adsManager.addEventListener( 109 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 110 | onContentResumeRequested); 111 | adsManager.addEventListener( 112 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); 113 | 114 | // Listen to any additional events, if necessary. 115 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); 116 | adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); 117 | adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); 118 | 119 | playAds(); 120 | } 121 | 122 | /** 123 | * Handles actions taken in response to ad events. 124 | * @param {!google.ima.AdEvent} adEvent 125 | */ 126 | function onAdEvent(adEvent) { 127 | // Retrieve the ad from the event. Some events (for example, 128 | // ALL_ADS_COMPLETED) don't have ad object associated. 129 | const ad = adEvent.getAd(); 130 | switch (adEvent.type) { 131 | case google.ima.AdEvent.Type.LOADED: 132 | // This is the first event sent for an ad - it is possible to 133 | // determine whether the ad is a video ad or an overlay. 134 | if (!ad.isLinear()) { 135 | // Position AdDisplayContainer correctly for overlay. 136 | // Use ad.width and ad.height. 137 | videoContent.play(); 138 | } 139 | break; 140 | case google.ima.AdEvent.Type.STARTED: 141 | // This event indicates the ad has started - the video player 142 | // can adjust the UI, for example display a pause button and 143 | // remaining time. 144 | if (ad.isLinear()) { 145 | // For a linear ad, a timer can be started to poll for 146 | // the remaining time. 147 | intervalTimer = setInterval( 148 | function() { 149 | // Example: const remainingTime = adsManager.getRemainingTime(); 150 | }, 151 | 300); // every 300ms 152 | } 153 | break; 154 | case google.ima.AdEvent.Type.COMPLETE: 155 | // This event indicates the ad has finished - the video player 156 | // can perform appropriate UI actions, such as removing the timer for 157 | // remaining time detection. 158 | if (ad.isLinear()) { 159 | clearInterval(intervalTimer); 160 | } 161 | break; 162 | } 163 | } 164 | 165 | /** 166 | * Handles ad errors. 167 | * @param {!google.ima.AdErrorEvent} adErrorEvent 168 | */ 169 | function onAdError(adErrorEvent) { 170 | // Handle the error logging. 171 | console.log(adErrorEvent.getError()); 172 | adsManager.destroy(); 173 | } 174 | 175 | /** 176 | * Pauses video content and sets up ad UI. 177 | */ 178 | function onContentPauseRequested() { 179 | videoContent.pause(); 180 | // This function is where you should setup UI for showing ads (for example, 181 | // display ad timer countdown, disable seeking and more.) 182 | // setupUIForAds(); 183 | } 184 | 185 | /** 186 | * Resumes video content and removes ad UI. 187 | */ 188 | function onContentResumeRequested() { 189 | videoContent.play(); 190 | // This function is where you should ensure that your UI is ready 191 | // to play content. It is the responsibility of the Publisher to 192 | // implement this function when necessary. 193 | // setupUIForContent(); 194 | } 195 | 196 | // Wire UI element references and UI event listeners. 197 | init(); 198 | -------------------------------------------------------------------------------- /mobile_auto_skippable/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | IMA HTML5 Simple Demo 4 | 5 | 6 | 7 | 8 |
9 |
10 | 13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /mobile_auto_skippable/style.css: -------------------------------------------------------------------------------- 1 | #mainContainer { 2 | position: relative; 3 | width: 640px; 4 | height: 360px; 5 | } 6 | 7 | #content, #adContainer { 8 | position: absolute; 9 | top: 0px; 10 | left: 0px; 11 | width: 640px; 12 | height: 360px; 13 | } 14 | 15 | #contentElement { 16 | width: 640px; 17 | height: 360px; 18 | overflow: hidden; 19 | } 20 | -------------------------------------------------------------------------------- /playlist/ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | /** 7 | * Shows how to use the IMA SDK to request and display ads. 8 | */ 9 | const Ads = function(application, videoPlayer) { 10 | this.application_ = application; 11 | this.videoPlayer_ = videoPlayer; 12 | this.customClickDiv_ = document.getElementById('customClick'); 13 | this.linearAdPlaying = false; 14 | google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED); 15 | this.adDisplayContainer_ = new google.ima.AdDisplayContainer( 16 | this.videoPlayer_.adContainer, this.videoPlayer_.contentPlayer, 17 | this.customClickDiv_); 18 | this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_); 19 | this.adsManager_ = null; 20 | 21 | this.adsLoader_.addEventListener( 22 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 23 | this.onAdsManagerLoaded_, false, this); 24 | this.adsLoader_.addEventListener( 25 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this); 26 | }; 27 | 28 | // On iOS and Android devices, video playback must begin in a user action. 29 | // AdDisplayContainer provides a initialize() API to be called at appropriate 30 | // time. 31 | // This should be called when the user clicks or taps. 32 | Ads.prototype.initialUserAction = function() { 33 | this.adDisplayContainer_.initialize(); 34 | this.videoPlayer_.contentPlayer.load(); 35 | }; 36 | 37 | Ads.prototype.requestAds = function(adTagUrl) { 38 | this.contentCompleteCalled = false; 39 | this.allAdsCompleted = false; 40 | const adsRequest = new google.ima.AdsRequest(); 41 | adsRequest.adTagUrl = adTagUrl; 42 | adsRequest.linearAdSlotWidth = this.videoPlayer_.width; 43 | adsRequest.linearAdSlotHeight = this.videoPlayer_.height; 44 | adsRequest.nonLinearAdSlotWidth = this.videoPlayer_.width; 45 | adsRequest.nonLinearAdSlotHeight = this.videoPlayer_.height; 46 | this.adsLoader_.requestAds(adsRequest); 47 | }; 48 | 49 | Ads.prototype.pause = function() { 50 | if (this.adsManager_) { 51 | this.adsManager_.pause(); 52 | } 53 | }; 54 | 55 | Ads.prototype.resume = function() { 56 | if (this.adsManager_) { 57 | this.adsManager_.resume(); 58 | } 59 | }; 60 | 61 | Ads.prototype.resize = function(width, height) { 62 | if (this.adsManager_) { 63 | this.adsManager_.resize(width, height); 64 | } 65 | }; 66 | 67 | Ads.prototype.contentCompleted = function() { 68 | this.contentCompleteCalled = true; 69 | this.adsLoader_.contentComplete(); 70 | }; 71 | 72 | /** 73 | * If we're playing post-rolls, ALL_ADS_COMPLETED will not have fired at this 74 | * point. Here the cotent video is done, so if ads are also done, we start the 75 | * next video. 76 | */ 77 | Ads.prototype.contentEnded = function() { 78 | this.contentCompleted(); 79 | if (this.allAdsCompleted) { 80 | this.application_.switchButtonToReplay(); 81 | } 82 | }; 83 | 84 | Ads.prototype.destroyAdsManager = function() { 85 | if (this.adsManager_) { 86 | this.adsManager_.destroy(); 87 | this.adsManager_ = null; 88 | } 89 | }; 90 | 91 | Ads.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) { 92 | this.application_.log('Ads loaded.'); 93 | const adsRenderingSettings = new google.ima.AdsRenderingSettings(); 94 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 95 | this.adsManager_ = adsManagerLoadedEvent.getAdsManager( 96 | this.videoPlayer_.contentPlayer, adsRenderingSettings); 97 | this.startAdsManager_(this.adsManager_); 98 | }; 99 | 100 | Ads.prototype.startAdsManager_ = function(adsManager) { 101 | if (adsManager.isCustomClickTrackingUsed()) { 102 | this.customClickDiv_.style.display = 'table'; 103 | } 104 | // Attach the pause/resume events. 105 | adsManager.addEventListener( 106 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, 107 | this.onContentPauseRequested_, false, this); 108 | adsManager.addEventListener( 109 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 110 | this.onContentResumeRequested_, false, this); 111 | // Handle errors. 112 | adsManager.addEventListener( 113 | google.ima.AdErrorEvent.Type.AD_ERROR, this.onAdError_, false, this); 114 | adsManager.addEventListener( 115 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, this.onAllAdsCompleted_, false, 116 | this); 117 | const events = [ 118 | google.ima.AdEvent.Type.ALL_ADS_COMPLETED, google.ima.AdEvent.Type.CLICK, 119 | google.ima.AdEvent.Type.COMPLETE, google.ima.AdEvent.Type.FIRST_QUARTILE, 120 | google.ima.AdEvent.Type.LOADED, google.ima.AdEvent.Type.MIDPOINT, 121 | google.ima.AdEvent.Type.PAUSED, google.ima.AdEvent.Type.STARTED, 122 | google.ima.AdEvent.Type.THIRD_QUARTILE 123 | ]; 124 | for (const index in events) { 125 | adsManager.addEventListener(events[index], this.onAdEvent_, false, this); 126 | } 127 | 128 | let initWidth; 129 | let initHeight; 130 | if (this.application_.fullscreen) { 131 | initWidth = this.application_.fullscreenWidth; 132 | initHeight = this.application_.fullscreenHeight; 133 | } else { 134 | initWidth = this.videoPlayer_.width; 135 | initHeight = this.videoPlayer_.height; 136 | } 137 | adsManager.init(initWidth, initHeight); 138 | 139 | adsManager.start(); 140 | }; 141 | 142 | Ads.prototype.onContentPauseRequested_ = function() { 143 | this.linearAdPlaying = true; 144 | this.application_.pauseForAd(); 145 | this.application_.setVideoEndedCallbackEnabled(false); 146 | }; 147 | 148 | Ads.prototype.onContentResumeRequested_ = function() { 149 | this.application_.setVideoEndedCallbackEnabled(true); 150 | this.linearAdPlaying = false; 151 | // Without this check the video starts over from the beginning on a 152 | // post-roll's CONTENT_RESUME_REQUESTED 153 | if (!this.contentCompleteCalled) { 154 | this.application_.resumeAfterAd(); 155 | } 156 | }; 157 | 158 | Ads.prototype.onAdEvent_ = function(adEvent) { 159 | this.application_.log('Ad event: ' + adEvent.type); 160 | 161 | if (adEvent.type == google.ima.AdEvent.Type.CLICK) { 162 | this.application_.adClicked(); 163 | } else if (adEvent.type == google.ima.AdEvent.Type.LOADED) { 164 | const ad = adEvent.getAd(); 165 | if (!ad.isLinear()) { 166 | this.onContentResumeRequested_(); 167 | } 168 | } 169 | }; 170 | 171 | Ads.prototype.onAdError_ = function(adErrorEvent) { 172 | this.application_.log('Ad error: ' + adErrorEvent.getError().toString()); 173 | if (this.adsManager_) { 174 | this.adsManager_.destroy(); 175 | } 176 | this.application_.resumeAfterAd(); 177 | }; 178 | 179 | /** 180 | * If we aren't playing post-rolls, ALL_ADS_COMPLETED will be fired before 181 | * the video player fires the ended event. Here ads are done, so if 182 | * the content video is done, we start the next video. If ads are done but the 183 | * content video is still playing, we just let it finish. 184 | * @private 185 | */ 186 | Ads.prototype.onAllAdsCompleted_ = function() { 187 | this.allAdsCompleted = true; 188 | if (this.contentCompleteCalled) { 189 | this.application_.switchButtonToReplay(); 190 | } 191 | }; 192 | -------------------------------------------------------------------------------- /playlist/application.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | /** 7 | * Handles user interaction and creates the player and ads controllers. 8 | */ 9 | const Application = function() { 10 | this.console_ = document.getElementById('console'); 11 | this.playButton_ = document.getElementById('playpause'); 12 | this.playButton_.addEventListener( 13 | 'click', this.bind_(this, this.onClick_), false); 14 | this.replayButton_ = document.getElementById('replay'); 15 | this.replayButton_.addEventListener( 16 | 'click', this.bind_(this, this.onReplay_), false); 17 | this.fullscreenButton_ = document.getElementById('fullscreen'); 18 | this.fullscreenButton_.addEventListener( 19 | 'click', this.bind_(this, this.onFullscreenClick_), false); 20 | 21 | const playlistDiv = document.getElementById('playlistDiv'); 22 | const playlistItems = playlistDiv.childNodes; 23 | for (let i = 0; i < playlistItems.length; i++) { 24 | if (playlistItems[i].tagName == 'DIV') { 25 | playlistItems[i].addEventListener( 26 | 'click', this.bind_(this, this.onPlaylistItemClick_), false); 27 | } 28 | } 29 | 30 | this.fullscreenWidth = null; 31 | this.fullscreenHeight = null; 32 | 33 | const fullScreenEvents = 34 | ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange']; 35 | for (let key in fullScreenEvents) { 36 | document.addEventListener( 37 | fullScreenEvents[key], this.bind_(this, this.onFullscreenChange_), 38 | false); 39 | } 40 | 41 | this.initialUserActionHappened_ = false; 42 | this.playing_ = false; 43 | this.adsActive_ = false; 44 | this.adsDone_ = false; 45 | this.fullscreen = false; 46 | 47 | this.videoPlayer_ = new VideoPlayer(); 48 | this.ads_ = new Ads(this, this.videoPlayer_); 49 | this.adTagUrl_ = 'https://pubads.g.doubleclick.net/gampad/ads?' + 50 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' + 51 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&' + 52 | 'output=vast&unviewed_position_start=1&env=vp&impl=s&correlator='; 53 | 54 | this.videoEndedCallback_ = this.bind_(this, this.onContentEnded_); 55 | this.setVideoEndedCallbackEnabled(true); 56 | }; 57 | 58 | /** 59 | * Registers or removes video ended callback based on the 'enable' param. 60 | * @param {boolean} enable 61 | */ 62 | Application.prototype.setVideoEndedCallbackEnabled = function(enable) { 63 | if (enable) { 64 | this.videoPlayer_.registerVideoEndedCallback(this.videoEndedCallback_); 65 | } else { 66 | this.videoPlayer_.removeVideoEndedCallback(this.videoEndedCallback_); 67 | } 68 | }; 69 | 70 | /** 71 | * Changes the play button to the replay button. 72 | */ 73 | Application.prototype.switchButtonToReplay = function() { 74 | this.playButton_.style.display = 'none'; 75 | this.replayButton_.style.display = 'block'; 76 | }; 77 | 78 | /** 79 | * Logs messages to the console. 80 | * @param {string} message 81 | */ 82 | Application.prototype.log = function(message) { 83 | console.log(message); 84 | this.console_.innerHTML = this.console_.innerHTML + '
' + message; 85 | }; 86 | 87 | /** 88 | * Handles resuming content following ads. 89 | */ 90 | Application.prototype.resumeAfterAd = function() { 91 | this.videoPlayer_.play(); 92 | this.adsActive_ = false; 93 | this.updateChrome_(); 94 | }; 95 | 96 | /** 97 | * Handles pausing content for ad breaks. 98 | */ 99 | Application.prototype.pauseForAd = function() { 100 | this.adsActive_ = true; 101 | this.playing_ = true; 102 | this.videoPlayer_.pause(); 103 | this.updateChrome_(); 104 | }; 105 | 106 | /** 107 | * Pauses video on ad clicks. 108 | */ 109 | Application.prototype.adClicked = function() { 110 | this.playing_ = false; 111 | this.updateChrome_(); 112 | }; 113 | 114 | /** 115 | * Function binding helper function. 116 | * @param {!Object} thisObj object to bind function. 117 | * @param {!Function} fn function being bound to object. 118 | * @return {!Function} returns the bound function. 119 | */ 120 | Application.prototype.bind_ = function(thisObj, fn) { 121 | return function() { 122 | fn.apply(thisObj, arguments); 123 | }; 124 | }; 125 | 126 | /** 127 | * Handles pausing the content or ads for ad clicks. 128 | */ 129 | Application.prototype.onClick_ = function() { 130 | if (!this.adsDone_) { 131 | if (!this.initialUserActionHappened_) { 132 | // The user clicked/tapped - inform the ads controller that this code 133 | // is being run in a user action thread. 134 | this.ads_.initialUserAction(); 135 | this.initialUserActionHappened_ = true; 136 | } 137 | // At the same time, initialize the content player as well. 138 | // When content is loaded, we'll issue the ad request to prevent it 139 | // from interfering with the initialization. See 140 | // https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/ads#iosvideo 141 | // for more information. 142 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_)); 143 | this.adsDone_ = true; 144 | return; 145 | } 146 | 147 | if (this.adsActive_) { 148 | if (this.playing_) { 149 | this.ads_.pause(); 150 | } else { 151 | this.ads_.resume(); 152 | } 153 | } else { 154 | if (this.playing_) { 155 | this.videoPlayer_.pause(); 156 | } else { 157 | this.videoPlayer_.play(); 158 | } 159 | } 160 | 161 | this.playing_ = !this.playing_; 162 | 163 | this.updateChrome_(); 164 | }; 165 | 166 | /** 167 | * Handles replaying the content and ads. 168 | */ 169 | Application.prototype.onReplay_ = function() { 170 | this.switchButtonToPlay_(); 171 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_)); 172 | this.adsDone_ = true; 173 | }; 174 | 175 | /** 176 | * Switches replay button to play button. 177 | */ 178 | Application.prototype.switchButtonToPlay_ = function() { 179 | this.replayButton_.style.display = 'none'; 180 | this.playButton_.style.display = 'block'; 181 | }; 182 | 183 | /** 184 | * Handles making the video fullscreen, or renturning from fullscreen. 185 | */ 186 | Application.prototype.onFullscreenClick_ = function() { 187 | if (this.fullscreen) { 188 | // The video is currently in fullscreen mode 189 | const cancelFullscreen = document.exitFullscreen || 190 | document.exitFullScreen || document.webkitCancelFullScreen || 191 | document.mozCancelFullScreen; 192 | if (cancelFullscreen) { 193 | cancelFullscreen.call(document); 194 | } else { 195 | this.onFullscreenChange_(); 196 | } 197 | } else { 198 | // Try to enter fullscreen mode in the browser 199 | const requestFullscreen = document.documentElement.requestFullscreen || 200 | document.documentElement.webkitRequestFullscreen || 201 | document.documentElement.mozRequestFullscreen || 202 | document.documentElement.requestFullScreen || 203 | document.documentElement.webkitRequestFullScreen || 204 | document.documentElement.mozRequestFullScreen; 205 | if (requestFullscreen) { 206 | this.fullscreenWidth = window.screen.width; 207 | this.fullscreenHeight = window.screen.height; 208 | requestFullscreen.call(document.documentElement); 209 | } else { 210 | this.fullscreenWidth = window.innerWidth; 211 | this.fullscreenHeight = window.innerHeight; 212 | this.onFullscreenChange_(); 213 | } 214 | } 215 | requestFullscreen.call(document.documentElement); 216 | }; 217 | 218 | /** 219 | * Handles updating the play button image. 220 | */ 221 | Application.prototype.updateChrome_ = function() { 222 | if (this.playing_) { 223 | this.playButton_.textContent = 'II'; 224 | } else { 225 | // Unicode play symbol. 226 | this.playButton_.textContent = String.fromCharCode(9654); 227 | } 228 | }; 229 | 230 | /** 231 | * Removes the 'loadedmetadata' listener and makes the ad request. 232 | */ 233 | Application.prototype.loadAds_ = function() { 234 | this.videoPlayer_.removePreloadListener(); 235 | this.ads_.requestAds(this.adTagUrl_); 236 | }; 237 | 238 | /** 239 | * Handles resizing ads and content during fullscreen button clicks. 240 | */ 241 | Application.prototype.onFullscreenChange_ = function() { 242 | if (this.fullscreen) { 243 | // The user just exited fullscreen 244 | // Resize the ad container 245 | this.ads_.resize(this.videoPlayer_.width, this.videoPlayer_.height); 246 | // Return the video to its original size and position 247 | this.videoPlayer_.resize( 248 | 'relative', '', '', this.videoPlayer_.width, this.videoPlayer_.height); 249 | this.fullscreen = false; 250 | } else { 251 | // The fullscreen button was just clicked 252 | // Resize the ad container 253 | const width = this.fullscreenWidth; 254 | const height = this.fullscreenHeight; 255 | this.makeAdsFullscreen_(); 256 | // Make the video take up the entire screen 257 | this.videoPlayer_.resize('absolute', 0, 0, width, height); 258 | this.fullscreen = true; 259 | } 260 | }; 261 | 262 | /** 263 | * Resizes ads for fullscreen. 264 | */ 265 | Application.prototype.makeAdsFullscreen_ = function() { 266 | this.ads_.resize(this.fullscreenWidth, this.fullscreenHeight); 267 | }; 268 | 269 | /** 270 | * Makes call to update UI on content ending. 271 | */ 272 | Application.prototype.onContentEnded_ = function() { 273 | this.ads_.contentEnded(); 274 | }; 275 | 276 | /** 277 | * Handles clicks on playlist items. 278 | * @param {!Object} event the click event. 279 | */ 280 | Application.prototype.onPlaylistItemClick_ = function(event) { 281 | // Terms of Service says we can't kill an ad prematurely, so we will only 282 | // switch videos if there isn't an ad playing. 283 | if (!this.ads_.linearAdPlaying) { 284 | this.ads_.destroyAdsManager(); 285 | this.ads_.contentCompleted(); 286 | if (!this.initialUserActionHappened_) { 287 | this.ads_.initialUserAction(); 288 | this.initialUserActionHappened_ = true; 289 | } 290 | this.adsDone_ = true; 291 | this.videoPlayer_.setContentVideoIndex(event.target.id); 292 | this.videoPlayer_.preloadContent(this.bind_(this, this.loadAds_)); 293 | } 294 | }; 295 | -------------------------------------------------------------------------------- /playlist/img/android_preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/playlist/img/android_preview.jpg -------------------------------------------------------------------------------- /playlist/img/doubleclick_preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googleads/googleads-ima-html5/bc910dc76a00f46b81483c81ee057da696933799/playlist/img/doubleclick_preview.jpg -------------------------------------------------------------------------------- /playlist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | 27 | 38 | 39 | IMA HTML5 SDK Playlist Demo 40 | 41 | 42 |
43 |
IMA HTML5 SDK Playlist Demo
44 | 45 |
46 | 49 |
50 |
51 | 52 | 53 | 54 |
55 | 56 |
57 |
Click here for more info on your ad.
58 |
59 | 60 | 62 |
63 | 69 |
70 | 71 |
72 |
73 | 74 | Video 1 75 |
76 |
77 | 78 | Video 2 79 |
80 |
81 | 82 |
83 | Welcome to IMA HTML5 SDK Demo! 84 |
85 | 86 | 87 |
88 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /playlist/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: arial, verdana, sans-serif; 3 | overflow: hidden; 4 | } 5 | 6 | header, footer { text-align:center; width: 100%; } 7 | 8 | header { 9 | height: 40px; 10 | font-size: 40px; 11 | font-weight: bold; 12 | margin-top: 20px; 13 | margin-bottom: 20px; 14 | } 15 | 16 | footer { 17 | margin-top: 20px; 18 | } 19 | 20 | #container { 21 | margin-left: auto; 22 | margin-right: auto; 23 | width: 728px; 24 | } 25 | 26 | #videoplayer { 27 | position: relative; 28 | background-color: #000; 29 | border-radius: 5px; 30 | box-shadow: 0px 0px 20px rgba(50, 50, 50, 0.95); 31 | border: 2px #ccc solid; 32 | width: 640px; 33 | height: 360px; 34 | margin-left: auto; 35 | margin-right: auto; 36 | margin-top: 20px; 37 | } 38 | 39 | #content-wrapper { 40 | position:relative; 41 | top: 0px; 42 | left: 0px 43 | } 44 | 45 | #playpause, #replay { 46 | position: absolute; 47 | left: 20px; 48 | bottom: 20px; 49 | height: 40px; 50 | width: 100px; 51 | border-style: none; 52 | font-weight: bold; 53 | font-size: 25px; 54 | opacity: 0.5; 55 | background-color: #fff; 56 | border-radius: 5px; 57 | border: 1px transparent solid; 58 | color: #000; 59 | cursor: pointer; 60 | line-height: 0; 61 | } 62 | 63 | #replay { 64 | display: none; 65 | } 66 | 67 | #playpause:hover, #replay:hover { 68 | border: 1px #f00 solid; 69 | color: #f00; 70 | } 71 | 72 | #fullscreen { 73 | position: absolute; 74 | bottom: 20px; 75 | left: 140px; 76 | height: 40px; 77 | width: 100px; 78 | border-style: none; 79 | font-weight: bold; 80 | font-size: 25px; 81 | opacity: 0.5; 82 | background-color: #fff; 83 | border-radius: 5px; 84 | border: 1px transparent solid; 85 | color: #000; 86 | cursor: pointer; 87 | line-height: 0; 88 | } 89 | 90 | #fullscreen:hover { 91 | border: 1px #f00 solid; 92 | color: #f00; 93 | } 94 | 95 | #content { 96 | overflow: hidden; 97 | } 98 | 99 | #content, #adcontainer { 100 | position: absolute; 101 | top: 0px; 102 | left: 0px; 103 | width: 640px; 104 | height: 360px; 105 | } 106 | 107 | #playlistDiv { 108 | height: 122px; 109 | width: 224px; 110 | margin-left: auto; 111 | margin-right: auto; 112 | margin-top: 10px; 113 | margin-bottom: 10px; 114 | text-align: center; 115 | } 116 | 117 | .playlistItem { 118 | height: 122px; 119 | width: 102px; 120 | float: left; 121 | margin-right: 10px; 122 | cursor: pointer; 123 | } 124 | 125 | .playlistImg { 126 | border: 1px solid; 127 | } 128 | 129 | #console { 130 | font-family: courier, monospace; 131 | font-size: 12px; 132 | margin-top: 20px; 133 | height: 200px; 134 | width: 630px; 135 | padding: 5px; 136 | border: 1px #ccc solid; 137 | overflow-y: scroll; 138 | margin-left: auto; 139 | margin-right: auto; 140 | } 141 | 142 | #companionDiv, #customClick { 143 | width: 728px; 144 | height: 90px; 145 | margin-top: 20px; 146 | } 147 | 148 | #customClick { 149 | background-color: #807F80; 150 | display: none; 151 | text-align: center; 152 | } 153 | 154 | #customClickTextWrapper { 155 | display: table-cell; 156 | vertical-align: middle; 157 | } 158 | -------------------------------------------------------------------------------- /playlist/video_player.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | /** 7 | * Handles video player functionality. 8 | */ 9 | const VideoPlayer = function() { 10 | this.contentPlayer = document.getElementById('content'); 11 | this.adContainer = document.getElementById('adcontainer'); 12 | this.videoPlayerContainer_ = document.getElementById('videoplayer'); 13 | 14 | this.contentIndex = 0; 15 | this.contentUrls = [ 16 | 'https://storage.googleapis.com/gvabox/media/samples/stock.mp4', 17 | 'https://storage.googleapis.com/gvabox/media/samples/android.mp4' 18 | ]; 19 | 20 | this.width = 640; 21 | this.height = 360; 22 | }; 23 | 24 | VideoPlayer.prototype.preloadContent = function(contentLoadedAction) { 25 | // If this is the initial user action on iOS or Android device, 26 | // simulate playback to enable the video element for later program-triggered 27 | // playback. 28 | if (this.isMobilePlatform()) { 29 | this.preloadListener_ = contentLoadedAction; 30 | this.contentPlayer.addEventListener( 31 | 'loadedmetadata', contentLoadedAction, false); 32 | this.setContentVideoSource_(this.contentIndex); 33 | } else { 34 | this.setContentVideoSource_(this.contentIndex); 35 | contentLoadedAction(); 36 | } 37 | }; 38 | 39 | VideoPlayer.prototype.removePreloadListener = function() { 40 | if (this.preloadListener_) { 41 | this.contentPlayer.removeEventListener( 42 | 'loadedmetadata', this.preloadListener_, false); 43 | this.preloadListener_ = null; 44 | } 45 | }; 46 | 47 | VideoPlayer.prototype.play = function() { 48 | this.contentPlayer.play(); 49 | }; 50 | 51 | VideoPlayer.prototype.pause = function() { 52 | this.contentPlayer.pause(); 53 | }; 54 | 55 | VideoPlayer.prototype.isMobilePlatform = function() { 56 | return this.contentPlayer.paused && 57 | (navigator.userAgent.match(/(iPod|iPhone|iPad)/) || 58 | navigator.userAgent.toLowerCase().indexOf('android') > -1); 59 | }; 60 | 61 | VideoPlayer.prototype.resize = function(position, top, left, width, height) { 62 | this.videoPlayerContainer_.style.position = position; 63 | this.videoPlayerContainer_.style.top = top + 'px'; 64 | this.videoPlayerContainer_.style.left = left + 'px'; 65 | this.videoPlayerContainer_.style.width = width + 'px'; 66 | this.videoPlayerContainer_.style.height = height + 'px'; 67 | this.contentPlayer.style.width = width + 'px'; 68 | this.contentPlayer.style.height = height + 'px'; 69 | }; 70 | 71 | VideoPlayer.prototype.registerVideoEndedCallback = function(callback) { 72 | this.contentPlayer.addEventListener('ended', callback, false); 73 | }; 74 | 75 | VideoPlayer.prototype.removeVideoEndedCallback = function(callback) { 76 | this.contentPlayer.removeEventListener('ended', callback, false); 77 | }; 78 | 79 | VideoPlayer.prototype.setContentVideoIndex = function(index) { 80 | this.contentIndex = index; 81 | }; 82 | 83 | VideoPlayer.prototype.setContentVideoSource_ = function(index) { 84 | this.contentIndex = index; 85 | this.contentPlayer.src = this.contentUrls[index]; 86 | this.contentPlayer.load(); 87 | }; 88 | -------------------------------------------------------------------------------- /simple/ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Google Inc. All Rights Reserved. 2 | // You may study, modify, and use this example for any purpose. 3 | // Note that this example is provided "as is", WITHOUT WARRANTY 4 | // of any kind either expressed or implied. 5 | 6 | // [START init_player] 7 | let adsManager; 8 | let adsLoader; 9 | let adDisplayContainer; 10 | let isAdPlaying; 11 | let isContentFinished; 12 | let playButton; 13 | let videoContent; 14 | let adContainer; 15 | 16 | // On window load, attach an event to the play button click 17 | // that triggers playback of the video element. 18 | window.addEventListener('load', function(event) { 19 | videoContent = document.getElementById('contentElement'); 20 | adContainer = document.getElementById('adContainer'); 21 | adContainer.addEventListener('click', adContainerClick); 22 | playButton = document.getElementById('playButton'); 23 | playButton.addEventListener('click', playAds); 24 | setUpIMA(); 25 | }); 26 | // [END init_player] 27 | 28 | // [START ima_setup] 29 | /** 30 | * Sets up IMA ad display container, ads loader, and makes an ad request. 31 | */ 32 | function setUpIMA() { 33 | // Create the ad display container. 34 | createAdDisplayContainer(); 35 | // Create ads loader. 36 | adsLoader = new google.ima.AdsLoader(adDisplayContainer); 37 | // Listen and respond to ads loaded and error events. 38 | adsLoader.addEventListener( 39 | google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, 40 | onAdsManagerLoaded, false); 41 | adsLoader.addEventListener( 42 | google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); 43 | 44 | // An event listener to tell the SDK that our content video 45 | // is completed so the SDK can play any post-roll ads. 46 | const contentEndedListener = function() { 47 | // An ad might have been playing in the content element, in which case the 48 | // content has not actually ended. 49 | if (isAdPlaying) return; 50 | isContentFinished = true; 51 | adsLoader.contentComplete(); 52 | }; 53 | videoContent.onended = contentEndedListener; 54 | 55 | // Request video ads. 56 | const adsRequest = new google.ima.AdsRequest(); 57 | adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' + 58 | 'iu=/21775744923/external/single_ad_samples&sz=640x480&' + 59 | 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&' + 60 | 'output=vast&unviewed_position_start=1&env=vp&impl=s&correlator='; 61 | 62 | // Specify the linear and nonlinear slot sizes. This helps the SDK to 63 | // select the correct creative if multiple are returned. 64 | adsRequest.linearAdSlotWidth = 640; 65 | adsRequest.linearAdSlotHeight = 400; 66 | 67 | adsRequest.nonLinearAdSlotWidth = 640; 68 | adsRequest.nonLinearAdSlotHeight = 150; 69 | 70 | adsLoader.requestAds(adsRequest); 71 | } 72 | // [END ima_setup] 73 | 74 | // [START create_ad_display_container] 75 | /** 76 | * Sets the 'adContainer' div as the IMA ad display container. 77 | */ 78 | function createAdDisplayContainer() { 79 | adDisplayContainer = new google.ima.AdDisplayContainer( 80 | document.getElementById('adContainer'), videoContent); 81 | } 82 | // [END create_ad_display_container] 83 | 84 | // [START play_ads] 85 | /** 86 | * Loads the video content and initializes IMA ad playback. 87 | */ 88 | function playAds() { 89 | // Initialize the container. Must be done through a user action on mobile 90 | // devices. 91 | videoContent.load(); 92 | adDisplayContainer.initialize(); 93 | 94 | try { 95 | // Initialize the ads manager. This call starts ad playback for VMAP ads. 96 | adsManager.init(640, 360); 97 | // Call play to start showing the ad. Single video and overlay ads will 98 | // start at this time; the call will be ignored for VMAP ads. 99 | adsManager.start(); 100 | } catch (adError) { 101 | // An error may be thrown if there was a problem with the VAST response. 102 | videoContent.play(); 103 | } 104 | } 105 | // [END play_ads] 106 | 107 | // [START ads_loader_events] 108 | /** 109 | * Handles the ad manager loading and sets ad event listeners. 110 | * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent 111 | */ 112 | function onAdsManagerLoaded(adsManagerLoadedEvent) { 113 | // Get the ads manager. 114 | const adsRenderingSettings = new google.ima.AdsRenderingSettings(); 115 | adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; 116 | // videoContent should be set to the content video element. 117 | adsManager = 118 | adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); 119 | 120 | // Add listeners to the required events. 121 | // [START ads_manager_error_handler] 122 | adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); 123 | // [END ads_manager_error_handler] 124 | // [START ads_manager_play_pause_handlers] 125 | adsManager.addEventListener( 126 | google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); 127 | adsManager.addEventListener( 128 | google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, 129 | onContentResumeRequested); 130 | // [END ads_manager_play_pause_handlers] 131 | // [START ads_manager_loaded_handler] 132 | adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdLoaded); 133 | // [END ads_manager_loaded_handler] 134 | } 135 | 136 | /** 137 | * Handles ad errors. 138 | * @param {!google.ima.AdErrorEvent} adErrorEvent 139 | */ 140 | function onAdError(adErrorEvent) { 141 | // Handle the error logging. 142 | console.log(adErrorEvent.getError()); 143 | adsManager.destroy(); 144 | } 145 | // [END ads_loader_events] 146 | 147 | // [START ad_container_click] 148 | /** 149 | * Handles clicks on the ad container to support expected play and pause 150 | * behavior on mobile devices. 151 | * @param {!Event} event 152 | */ 153 | function adContainerClick(event) { 154 | console.log("ad container clicked"); 155 | if(videoContent.paused) { 156 | videoContent.play(); 157 | } else { 158 | videoContent.pause(); 159 | } 160 | } 161 | // [END ad_container_click] 162 | 163 | // [START play_pause_responses] 164 | /** 165 | * Pauses video content and sets up ad UI. 166 | */ 167 | function onContentPauseRequested() { 168 | isAdPlaying = true; 169 | videoContent.pause(); 170 | // This function is where you should setup UI for showing ads (for example, 171 | // display ad timer countdown, disable seeking and more.) 172 | // setupUIForAds(); 173 | } 174 | 175 | /** 176 | * Resumes video content and removes ad UI. 177 | */ 178 | function onContentResumeRequested() { 179 | isAdPlaying = false; 180 | if (!isContentFinished) { 181 | videoContent.play(); 182 | } 183 | // This function is where you should ensure that your UI is ready 184 | // to play content. It is the responsibility of the Publisher to 185 | // implement this function when necessary. 186 | // setupUIForContent(); 187 | } 188 | // [END play_pause_responses] 189 | 190 | // [START ad_loaded_handler] 191 | /** 192 | * Handles ad loaded event to support non-linear ads. Continues content playback 193 | * if the ad is not linear. 194 | * @param {!google.ima.AdEvent} adEvent 195 | */ 196 | function onAdLoaded(adEvent) { 197 | let ad = adEvent.getAd(); 198 | if (!ad.isLinear()) { 199 | videoContent.play(); 200 | } 201 | } 202 | // [END ad_loaded_handler] 203 | 204 | // [START resize_handler] 205 | window.addEventListener('resize', function(event) { 206 | console.log("window resized"); 207 | if(adsManager) { 208 | let width = videoContent.clientWidth; 209 | let height = videoContent.clientHeight; 210 | adsManager.resize(width, height, google.ima.ViewMode.NORMAL); 211 | } 212 | }); 213 | // [END resize_handler] 214 | -------------------------------------------------------------------------------- /simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | IMA HTML5 Simple Demo 4 | 5 | 6 | 7 | 8 |
9 |
10 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /simple/style.css: -------------------------------------------------------------------------------- 1 | /* [START player_css] */ 2 | #mainContainer { 3 | position: relative; 4 | width: 640px; 5 | height: 360px; 6 | } 7 | 8 | #content { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | width: 640px; 13 | height: 360px; 14 | } 15 | 16 | #contentElement { 17 | width: 640px; 18 | height: 360px; 19 | overflow: hidden; 20 | } 21 | 22 | #playButton { 23 | margin-top:10px; 24 | vertical-align: top; 25 | width: 350px; 26 | height: 60px; 27 | padding: 0; 28 | font-size: 22px; 29 | color: white; 30 | text-align: center; 31 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 32 | background: #2c3e50; 33 | border: 0; 34 | border-bottom: 2px solid #22303f; 35 | cursor: pointer; 36 | -webkit-box-shadow: inset 0 -2px #22303f; 37 | box-shadow: inset 0 -2px #22303f; 38 | } 39 | /* [END player_css] */ 40 | 41 | /* [START ad_container_css] */ 42 | #adContainer { 43 | position: absolute; 44 | top: 0; 45 | left: 0; 46 | width: 640px; 47 | height: 360px; 48 | } 49 | /* [END ad_container_css] */ 50 | -------------------------------------------------------------------------------- /vpaid/linear/VpaidVideoAd.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview A sample VPAID ad useful for testing a VPAID JS enabled player. 3 | * This ad will just play a video. 4 | */ 5 | 6 | /** 7 | * @constructor 8 | */ 9 | const VpaidVideoPlayer = function() { 10 | /** 11 | * The slot is the div element on the main page that the ad is supposed to 12 | * occupy. 13 | * @type {Object} 14 | * @private 15 | */ 16 | this.slot_ = null; 17 | 18 | /** 19 | * The video slot is the video element used by the ad to render video content. 20 | * @type {Object} 21 | * @private 22 | */ 23 | this.videoSlot_ = null; 24 | 25 | /** 26 | * An object containing all registered events. These events are all 27 | * callbacks for use by the VPAID ad. 28 | * @type {Object} 29 | * @private 30 | */ 31 | this.eventsCallbacks_ = {}; 32 | 33 | /** 34 | * A list of getable and setable attributes. 35 | * @type {Object} 36 | * @private 37 | */ 38 | this.attributes_ = { 39 | 'companions': '', 40 | 'desiredBitrate': 256, 41 | 'duration': 10, 42 | 'expanded': false, 43 | 'height': 0, 44 | 'icons': '', 45 | 'linear': true, 46 | 'remainingTime': 10, 47 | 'skippableState': false, 48 | 'viewMode': 'normal', 49 | 'width': 0, 50 | 'volume': 1.0 51 | }; 52 | 53 | /** 54 | * A set of ad playback events to be reported. 55 | * @type {Object} 56 | * @private 57 | */ 58 | this.quartileEvents_ = [ 59 | {event: 'AdImpression', value: 0}, {event: 'AdVideoStart', value: 0}, 60 | {event: 'AdVideoFirstQuartile', value: 25}, 61 | {event: 'AdVideoMidpoint', value: 50}, 62 | {event: 'AdVideoThirdQuartile', value: 75}, 63 | {event: 'AdVideoComplete', value: 100} 64 | ]; 65 | 66 | /** 67 | * @type {number} An index into what quartile was last reported. 68 | * @private 69 | */ 70 | this.nextQuartileIndex_ = 0; 71 | 72 | /** 73 | * Parameters passed in from the AdParameters section of the VAST. 74 | * Used for video URL and MIME type. 75 | * @type {!object} 76 | * @private 77 | */ 78 | this.parameters_ = {}; 79 | }; 80 | 81 | /** 82 | * Returns the supported VPAID verion. 83 | * @param {string} version 84 | * @return {string} 85 | */ 86 | VpaidVideoPlayer.prototype.handshakeVersion = function(version) { 87 | return ('2.0'); 88 | }; 89 | 90 | /** 91 | * Initializes all attributes in the ad. The ad will not start until startAd is\ 92 | * called. 93 | * @param {number} width The ad width. 94 | * @param {number} height The ad height. 95 | * @param {string} viewMode The ad view mode. 96 | * @param {number} desiredBitrate The chosen bitrate. 97 | * @param {Object} creativeData Data associated with the creative. 98 | * @param {Object} environmentVars Runtime variables associated with the 99 | * creative like the slot and video slot. 100 | */ 101 | VpaidVideoPlayer.prototype.initAd = function( 102 | width, height, viewMode, desiredBitrate, creativeData, environmentVars) { 103 | this.attributes_['width'] = width; 104 | this.attributes_['height'] = height; 105 | this.attributes_['viewMode'] = viewMode; 106 | this.attributes_['desiredBitrate'] = desiredBitrate; 107 | 108 | // slot and videoSlot are passed as part of the environmentVars 109 | this.slot_ = environmentVars.slot; 110 | this.videoSlot_ = environmentVars.videoSlot; 111 | 112 | // Parse the incoming ad parameters. 113 | this.parameters_ = JSON.parse(creativeData['AdParameters']); 114 | 115 | this.log( 116 | 'initAd ' + width + 'x' + height + ' ' + viewMode + ' ' + desiredBitrate); 117 | this.updateVideoSlot_(); 118 | this.videoSlot_.addEventListener( 119 | 'timeupdate', this.timeUpdateHandler_.bind(this), false); 120 | this.videoSlot_.addEventListener( 121 | 'loadedmetadata', this.loadedMetadata_.bind(this), false); 122 | this.videoSlot_.addEventListener('ended', this.stopAd.bind(this), false); 123 | this.slot_.addEventListener('click', this.clickAd_.bind(this), false); 124 | this.callEvent_('AdLoaded'); 125 | }; 126 | 127 | /** 128 | * Called when the ad is clicked. 129 | * @private 130 | */ 131 | VpaidVideoPlayer.prototype.clickAd_ = function() { 132 | if ('AdClickThru' in this.eventsCallbacks_) { 133 | this.eventsCallbacks_['AdClickThru']('', '0', true); 134 | } 135 | }; 136 | 137 | /** 138 | * Called by the video element when video metadata is loaded. 139 | * @private 140 | */ 141 | VpaidVideoPlayer.prototype.loadedMetadata_ = function() { 142 | // The ad duration is not known until the media metadata is loaded. 143 | // Then, update the player with the duration change. 144 | this.attributes_['duration'] = this.videoSlot_.duration; 145 | this.callEvent_('AdDurationChange'); 146 | }; 147 | 148 | /** 149 | * Called by the video element when the video reaches specific points during 150 | * playback. 151 | * @private 152 | */ 153 | VpaidVideoPlayer.prototype.timeUpdateHandler_ = function() { 154 | if (this.nextQuartileIndex_ >= this.quartileEvents_.length) { 155 | return; 156 | } 157 | const percentPlayed = 158 | this.videoSlot_.currentTime * 100.0 / this.videoSlot_.duration; 159 | if (percentPlayed >= this.quartileEvents_[this.nextQuartileIndex_].value) { 160 | const lastQuartileEvent = 161 | this.quartileEvents_[this.nextQuartileIndex_].event; 162 | this.eventsCallbacks_[lastQuartileEvent](); 163 | this.nextQuartileIndex_ += 1; 164 | } 165 | if (this.videoSlot_.duration > 0) { 166 | this.attributes_['remainingTime'] = 167 | this.videoSlot_.duration - this.videoSlot_.currentTime; 168 | } 169 | }; 170 | 171 | /** 172 | * Creates or updates the video slot and fills it with a supported video. 173 | * @private 174 | */ 175 | VpaidVideoPlayer.prototype.updateVideoSlot_ = function() { 176 | if (this.videoSlot_ == null) { 177 | this.videoSlot_ = document.createElement('video'); 178 | this.log('Warning: No video element passed to ad, creating element.'); 179 | this.slot_.appendChild(this.videoSlot_); 180 | } 181 | this.updateVideoPlayerSize_(); 182 | let foundSource = false; 183 | const videos = this.parameters_.videos || []; 184 | for (let i = 0; i < videos.length; i++) { 185 | // Choose the first video with a supported mimetype. 186 | if (this.videoSlot_.canPlayType(videos[i].mimetype) != '') { 187 | this.videoSlot_.setAttribute('src', videos[i].url); 188 | foundSource = true; 189 | break; 190 | } 191 | } 192 | if (!foundSource) { 193 | // Unable to find a source video. 194 | this.callEvent_('AdError'); 195 | } 196 | }; 197 | 198 | /** 199 | * Helper function to update the size of the video player. 200 | * @private 201 | */ 202 | VpaidVideoPlayer.prototype.updateVideoPlayerSize_ = function() { 203 | this.videoSlot_.setAttribute('width', this.attributes_['width']); 204 | this.videoSlot_.setAttribute('height', this.attributes_['height']); 205 | }; 206 | 207 | /** 208 | * Called by the wrapper to start the ad. 209 | */ 210 | VpaidVideoPlayer.prototype.startAd = function() { 211 | this.log('Starting ad'); 212 | this.videoSlot_.play(); 213 | 214 | this.callEvent_('AdStarted'); 215 | }; 216 | 217 | /** 218 | * Called by the wrapper to stop the ad. 219 | */ 220 | VpaidVideoPlayer.prototype.stopAd = function() { 221 | this.log('Stopping ad'); 222 | // Calling AdStopped immediately terminates the ad. Setting a timeout allows 223 | // events to go through. 224 | const callback = this.callEvent_.bind(this); 225 | setTimeout(callback, 75, ['AdStopped']); 226 | }; 227 | 228 | /** 229 | * Called when the video player changes the width/height of the container. 230 | * @param {number} width The new width. 231 | * @param {number} height A new height. 232 | * @param {string} viewMode A new view mode. 233 | */ 234 | VpaidVideoPlayer.prototype.resizeAd = function(width, height, viewMode) { 235 | this.log('resizeAd ' + width + 'x' + height + ' ' + viewMode); 236 | this.attributes_['width'] = width; 237 | this.attributes_['height'] = height; 238 | this.attributes_['viewMode'] = viewMode; 239 | this.updateVideoPlayerSize_(); 240 | this.callEvent_('AdSizeChange'); 241 | }; 242 | 243 | /** 244 | * Pauses the ad. 245 | */ 246 | VpaidVideoPlayer.prototype.pauseAd = function() { 247 | this.log('pauseAd'); 248 | this.videoSlot_.pause(); 249 | this.callEvent_('AdPaused'); 250 | }; 251 | 252 | /** 253 | * Resumes the ad. 254 | */ 255 | VpaidVideoPlayer.prototype.resumeAd = function() { 256 | this.log('resumeAd'); 257 | this.videoSlot_.play(); 258 | this.callEvent_('AdPlaying'); 259 | }; 260 | 261 | /** 262 | * Expands the ad. 263 | */ 264 | VpaidVideoPlayer.prototype.expandAd = function() { 265 | this.log('expandAd'); 266 | this.attributes_['expanded'] = true; 267 | this.callEvent_('AdExpanded'); 268 | }; 269 | 270 | /** 271 | * Collapses the ad. 272 | */ 273 | VpaidVideoPlayer.prototype.collapseAd = function() { 274 | this.log('collapseAd'); 275 | this.attributes_['expanded'] = false; 276 | }; 277 | 278 | /** 279 | * Skips the ad. 280 | */ 281 | VpaidVideoPlayer.prototype.skipAd = function() { 282 | this.log('skipAd'); 283 | const skippableState = this.attributes_['skippableState']; 284 | if (skippableState) { 285 | this.callEvent_('AdSkipped'); 286 | } 287 | }; 288 | 289 | /** 290 | * Registers a callback for an event. 291 | * @param {Function} aCallback The callback function. 292 | * @param {string} eventName The callback type. 293 | * @param {Object} aContext The context for the callback. 294 | */ 295 | VpaidVideoPlayer.prototype.subscribe = function( 296 | aCallback, eventName, aContext) { 297 | this.log('Subscribe ' + eventName); 298 | const callBack = aCallback.bind(aContext); 299 | this.eventsCallbacks_[eventName] = callBack; 300 | }; 301 | 302 | /** 303 | * Removes a callback based on the eventName. 304 | * @param {string} eventName The callback type. 305 | */ 306 | VpaidVideoPlayer.prototype.unsubscribe = function(eventName) { 307 | this.log('unsubscribe ' + eventName); 308 | this.eventsCallbacks_[eventName] = null; 309 | }; 310 | 311 | /** 312 | * Returns whether the ad is linear. 313 | * @return {boolean} True if the ad is a linear, false for non linear. 314 | */ 315 | VpaidVideoPlayer.prototype.getAdLinear = function() { 316 | return this.attributes_['linear']; 317 | }; 318 | 319 | /** 320 | * Returns ad width. 321 | * @return {number} The ad width. 322 | */ 323 | VpaidVideoPlayer.prototype.getAdWidth = function() { 324 | return this.attributes_['width']; 325 | }; 326 | 327 | /** 328 | * Returns ad height. 329 | * @return {number} The ad height. 330 | */ 331 | VpaidVideoPlayer.prototype.getAdHeight = function() { 332 | return this.attributes_['height']; 333 | }; 334 | 335 | /** 336 | * Returns true if the ad is expanded. 337 | * @return {boolean} 338 | */ 339 | VpaidVideoPlayer.prototype.getAdExpanded = function() { 340 | this.log('getAdExpanded'); 341 | return this.attributes_['expanded']; 342 | }; 343 | 344 | /** 345 | * Returns the skippable state of the ad. 346 | * @return {boolean} 347 | */ 348 | VpaidVideoPlayer.prototype.getAdSkippableState = function() { 349 | this.log('getAdSkippableState'); 350 | return this.attributes_['skippableState']; 351 | }; 352 | 353 | /** 354 | * Returns the remaining ad time, in seconds. 355 | * @return {number} The time remaining in the ad. 356 | */ 357 | VpaidVideoPlayer.prototype.getAdRemainingTime = function() { 358 | return this.attributes_['remainingTime']; 359 | }; 360 | 361 | /** 362 | * Returns the duration of the ad, in seconds. 363 | * @return {number} The duration of the ad. 364 | */ 365 | VpaidVideoPlayer.prototype.getAdDuration = function() { 366 | return this.attributes_['duration']; 367 | }; 368 | 369 | /** 370 | * Returns the ad volume. 371 | * @return {number} The volume of the ad. 372 | */ 373 | VpaidVideoPlayer.prototype.getAdVolume = function() { 374 | this.log('getAdVolume'); 375 | return this.attributes_['volume']; 376 | }; 377 | 378 | /** 379 | * Sets the ad volume. 380 | * @param {number} value The volume in percentage. 381 | */ 382 | VpaidVideoPlayer.prototype.setAdVolume = function(value) { 383 | this.attributes_['volume'] = value; 384 | this.log('setAdVolume ' + value); 385 | this.callEvent_('AdVolumeChange'); 386 | }; 387 | 388 | /** 389 | * Returns a list of companion ads for the ad. 390 | * @return {string} List of companions in VAST XML. 391 | */ 392 | VpaidVideoPlayer.prototype.getAdCompanions = function() { 393 | return this.attributes_['companions']; 394 | }; 395 | 396 | /** 397 | * Returns a list of icons. 398 | * @return {string} A list of icons. 399 | */ 400 | VpaidVideoPlayer.prototype.getAdIcons = function() { 401 | return this.attributes_['icons']; 402 | }; 403 | 404 | /** 405 | * Logs events and messages. 406 | * @param {string} message 407 | */ 408 | VpaidVideoPlayer.prototype.log = function(message) { 409 | console.log(message); 410 | }; 411 | 412 | /** 413 | * Calls an event if there is a callback. 414 | * @param {string} eventType 415 | * @private 416 | */ 417 | VpaidVideoPlayer.prototype.callEvent_ = function(eventType) { 418 | if (eventType in this.eventsCallbacks_) { 419 | this.eventsCallbacks_[eventType](); 420 | } 421 | }; 422 | 423 | /** 424 | * Main function called by wrapper to get the VPAID ad. 425 | * @return {Object} The VPAID compliant ad. 426 | */ 427 | var getVPAIDAd = function() { 428 | return new VpaidVideoPlayer(); 429 | }; 430 | -------------------------------------------------------------------------------- /vpaid/nonlinear/VpaidNonLinear.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview A sample non-linear VPAID ad useful for testing a VPAID JS 3 | * enabled player. This ad will show a non-linear ad which can also enter linear 4 | * mode. 5 | */ 6 | 7 | /** @unrestricted */ 8 | const VpaidNonLinear = class { 9 | constructor() { 10 | /** 11 | * The slot is the div element on the main page that the ad is supposed to 12 | * occupy. 13 | * @private {Object} 14 | */ 15 | this.slot_ = null; 16 | 17 | /** 18 | * The video slot is the video element used by the ad to render video 19 | * content. 20 | * @private {Object} 21 | */ 22 | this.videoSlot_ = null; 23 | 24 | /** 25 | * An object containing all registered events. These events are all 26 | * callbacks for use by the VPAID ad. 27 | * @private {Object} 28 | */ 29 | this.eventsCallbacks_ = {}; 30 | 31 | /** 32 | * A list of getable and setable attributes. 33 | * @private {Object} 34 | */ 35 | this.attributes_ = { 36 | 'companions': '', 37 | 'desiredBitrate': 256, 38 | 'duration': 10, 39 | 'expanded': false, 40 | 'height': 0, 41 | 'icons': '', 42 | 'linear': false, 43 | 'skippableState': false, 44 | 'viewMode': 'normal', 45 | 'width': 0, 46 | 'volume': 1.0 47 | }; 48 | 49 | /** 50 | * When the ad was started. 51 | * @private {number} 52 | */ 53 | this.startTime_ = 0; 54 | 55 | /** 56 | * A set of ad playback events to be reported. 57 | * @private {Object} 58 | */ 59 | this.quartileEvents_ = [ 60 | {event: 'AdImpression', value: 0}, {event: 'AdVideoStart', value: 0}, 61 | {event: 'AdVideoFirstQuartile', value: 25}, 62 | {event: 'AdVideoMidpoint', value: 50}, 63 | {event: 'AdVideoThirdQuartile', value: 75}, 64 | {event: 'AdVideoComplete', value: 100} 65 | ]; 66 | 67 | /** 68 | * @private {number} An index into what quartile was last reported. 69 | */ 70 | this.nextQuartileIndex_ = 0; 71 | 72 | /** 73 | * Parameters passed in from the AdParameters section of the VAST. 74 | * Used for video URL and MIME type. 75 | * @private {!Object} 76 | */ 77 | this.parameters_ = {}; 78 | } 79 | 80 | /** 81 | * Returns the supported VPAID verion. 82 | * @param {string} version 83 | * @return {string} 84 | */ 85 | handshakeVersion(version) { 86 | return ('2.0'); 87 | } 88 | 89 | /** 90 | * Initializes all attributes in the ad. The ad will not start until startAd 91 | * is called. 92 | * @param {number} width The ad width. 93 | * @param {number} height The ad height. 94 | * @param {string} viewMode The ad view mode. 95 | * @param {number} desiredBitrate The chosen bitrate. 96 | * @param {Object} creativeData Data associated with the creative. 97 | * @param {Object} environmentVars Runtime variables associated with the 98 | * creative like the slot and video slot. 99 | */ 100 | initAd( 101 | width, height, viewMode, desiredBitrate, creativeData, environmentVars) { 102 | this.attributes_['width'] = width; 103 | this.attributes_['height'] = height; 104 | this.attributes_['viewMode'] = viewMode; 105 | this.attributes_['desiredBitrate'] = desiredBitrate; 106 | 107 | // slot and videoSlot are passed as part of the environmentVars 108 | this.slot_ = environmentVars.slot; 109 | this.videoSlot_ = environmentVars.videoSlot; 110 | 111 | // Parse the incoming ad parameters. 112 | this.parameters_ = JSON.parse(creativeData['AdParameters']); 113 | 114 | this.log( 115 | 'initAd ' + width + 'x' + height + ' ' + viewMode + ' ' + 116 | desiredBitrate); 117 | this.callEvent_('AdLoaded'); 118 | } 119 | 120 | /** 121 | * Helper function to update the size of the video player. 122 | * @private 123 | */ 124 | updateVideoPlayerSize_() { 125 | this.videoSlot_.setAttribute('width', this.attributes_['width']); 126 | this.videoSlot_.setAttribute('height', this.attributes_['height']); 127 | } 128 | 129 | /** 130 | * Called by the wrapper to start the ad. 131 | */ 132 | startAd() { 133 | this.log('Starting ad'); 134 | 135 | const date = new Date(); 136 | this.startTime_ = date.getTime(); 137 | 138 | // Create a div to contain our ad elements. 139 | const overlays = this.parameters_.overlays || []; 140 | 141 | const container = document.createElement('div'); 142 | container.style.display = 'block'; 143 | container.style.position = 'absolute'; 144 | container.style.width = '100%'; 145 | container.style.bottom = '0%'; 146 | this.slot_.appendChild(container); 147 | 148 | // Create a div to serve as a button to go from a non-linear ad to linear. 149 | const linearButton = document.createElement('div'); 150 | linearButton.style.background = 'green'; 151 | linearButton.style.display = 'block'; 152 | linearButton.style.margin = 'auto'; 153 | linearButton.style.textAlign = 'center'; 154 | linearButton.style.color = 'white'; 155 | linearButton.style.width = '480px'; 156 | linearButton.style.fontFamily = 'sans-serif'; 157 | linearButton.textContent = 'Click here to switch to a linear ad'; 158 | linearButton.addEventListener( 159 | 'click', this.linearButtonClick_.bind(this), false); 160 | container.appendChild(linearButton); 161 | 162 | // Create an img tag and populate it with the image passed in to the ad 163 | // parameters. 164 | const adImg = document.createElement('img'); 165 | adImg.src = overlays[0] || ''; 166 | adImg.style.margin = 'auto'; 167 | adImg.style.display = 'block'; 168 | adImg.addEventListener('click', this.adClick_.bind(this), false); 169 | container.appendChild(adImg); 170 | 171 | this.callEvent_('AdStarted'); 172 | this.callEvent_('AdImpression'); 173 | } 174 | 175 | /** 176 | * Called when the non-linear ad is clicked. 177 | * @private 178 | */ 179 | adClick_() { 180 | if ('AdClickThru' in this.eventsCallbacks_) { 181 | this.eventsCallbacks_['AdClickThru']('', '0', true); 182 | } 183 | } 184 | 185 | /** 186 | * Called when the linear overlay is clicked. Plays the video passed in the 187 | * parameters. 188 | * @private 189 | */ 190 | linearButtonClick_() { 191 | this.log('Linear Button Click'); 192 | // This will turn the ad into a linear ad. 193 | this.attributes_.linear = true; 194 | this.callEvent_('AdLinearChange'); 195 | // Remove all elements. 196 | while (this.slot_.firstChild) { 197 | this.slot_.removeChild(this.slot_.firstChild); 198 | } 199 | 200 | this.updateVideoPlayerSize_(); 201 | 202 | // Start a video. 203 | const videos = this.parameters_.videos || []; 204 | for (let i = 0; i < videos.length; i++) { 205 | // Choose the first video with a supported mimetype. 206 | if (this.videoSlot_.canPlayType(videos[i].mimetype) != '') { 207 | this.videoSlot_.setAttribute('src', videos[i].url); 208 | 209 | // Set start time of linear ad to calculate remaining time. 210 | const date = new Date(); 211 | this.startTime_ = date.getTime(); 212 | 213 | this.videoSlot_.addEventListener( 214 | 'timeupdate', this.timeUpdateHandler_.bind(this), false); 215 | this.videoSlot_.addEventListener( 216 | 'loadedmetadata', this.loadedMetadata_.bind(this), false); 217 | this.videoSlot_.addEventListener( 218 | 'ended', this.stopAd.bind(this), false); 219 | 220 | this.videoSlot_.play(); 221 | 222 | return; 223 | } 224 | } 225 | // Haven't found a video, so error. 226 | this.callEvent_('AdError'); 227 | } 228 | 229 | /** 230 | * Called by the video element when the video reaches specific points during 231 | * playback. 232 | * @private 233 | */ 234 | timeUpdateHandler_() { 235 | if (this.nextQuartileIndex_ >= this.quartileEvents_.length) { 236 | return; 237 | } 238 | const percentPlayed = 239 | this.videoSlot_.currentTime * 100.0 / this.videoSlot_.duration; 240 | let nextQuartile = this.quartileEvents_[this.nextQuartileIndex_]; 241 | if (percentPlayed >= nextQuartile.value) { 242 | this.eventsCallbacks_[nextQuartile.event](); 243 | this.nextQuartileIndex_ += 1; 244 | } 245 | if (this.videoSlot_.duration > 0) { 246 | this.attributes_['remainingTime'] = 247 | this.videoSlot_.duration - this.videoSlot_.currentTime; 248 | } 249 | } 250 | 251 | /** 252 | * Called by the video element when video metadata is loaded. 253 | * @private 254 | */ 255 | loadedMetadata_() { 256 | // The ad duration is not known until the media metadata is loaded. 257 | // Then, update the player with the duration change. 258 | this.attributes_['duration'] = this.videoSlot_.duration; 259 | this.callEvent_('AdDurationChange'); 260 | } 261 | 262 | /** 263 | * Called by the wrapper to stop the ad. 264 | */ 265 | stopAd() { 266 | this.log('Stopping ad'); 267 | // Calling AdStopped immediately terminates the ad. Setting a timeout allows 268 | // events to go through. 269 | const callback = this.callEvent_.bind(this); 270 | setTimeout(callback, 75, ['AdStopped']); 271 | } 272 | 273 | /** 274 | * Called when the video player changes the width/height of the container. 275 | * @param {number} width The new width. 276 | * @param {number} height A new height. 277 | * @param {string} viewMode A new view mode. 278 | */ 279 | resizeAd(width, height, viewMode) { 280 | this.log('resizeAd ' + width + 'x' + height + ' ' + viewMode); 281 | this.attributes_['width'] = width; 282 | this.attributes_['height'] = height; 283 | this.attributes_['viewMode'] = viewMode; 284 | this.updateVideoPlayerSize_(); 285 | this.callEvent_('AdSizeChange'); 286 | } 287 | 288 | /** 289 | * Pauses the ad. 290 | */ 291 | pauseAd() { 292 | this.log('pauseAd'); 293 | this.videoSlot_.pause(); 294 | this.callEvent_('AdPaused'); 295 | } 296 | 297 | /** 298 | * Resumes the ad. 299 | */ 300 | resumeAd() { 301 | this.log('resumeAd'); 302 | this.videoSlot_.play(); 303 | this.callEvent_('AdPlaying'); 304 | } 305 | 306 | /** 307 | * Expands the ad. 308 | */ 309 | expandAd() { 310 | this.log('expandAd'); 311 | this.attributes_['expanded'] = true; 312 | this.callEvent_('AdExpanded'); 313 | } 314 | 315 | /** 316 | * Collapses the ad. 317 | */ 318 | collapseAd() { 319 | this.log('collapseAd'); 320 | this.attributes_['expanded'] = false; 321 | } 322 | 323 | /** 324 | * Skips the ad. 325 | */ 326 | skipAd() { 327 | this.log('skipAd'); 328 | if (this.attributes_['skippableState']) { 329 | this.callEvent_('AdSkipped'); 330 | } 331 | } 332 | 333 | /** 334 | * Registers a callback for an event. 335 | * @param {Function} callback The callback function. 336 | * @param {string} eventName The callback type. 337 | * @param {Object} context The context for the callback. 338 | */ 339 | subscribe(callback, eventName, context) { 340 | this.log('Subscribe ' + eventName); 341 | this.eventsCallbacks_[eventName] = callback.bind(context); 342 | } 343 | 344 | /** 345 | * Removes a callback based on the eventName. 346 | * @param {string} eventName The callback type. 347 | */ 348 | unsubscribe(eventName) { 349 | this.log('unsubscribe ' + eventName); 350 | this.eventsCallbacks_[eventName] = null; 351 | } 352 | 353 | /** 354 | * Returns whether the ad is linear. 355 | * @return {boolean} True if the ad is a linear, false for non linear. 356 | */ 357 | getAdLinear() { 358 | return this.attributes_['linear']; 359 | } 360 | 361 | /** 362 | * Returns ad width. 363 | * @return {number} The ad width. 364 | */ 365 | getAdWidth() { 366 | return this.attributes_['width']; 367 | } 368 | 369 | /** 370 | * Returns ad height. 371 | * @return {number} The ad height. 372 | */ 373 | getAdHeight() { 374 | return this.attributes_['height']; 375 | } 376 | 377 | /** 378 | * Returns true if the ad is expanded. 379 | * @return {boolean} 380 | */ 381 | getAdExpanded() { 382 | this.log('getAdExpanded'); 383 | return this.attributes_['expanded']; 384 | } 385 | 386 | /** 387 | * Returns the skippable state of the ad. 388 | * @return {boolean} 389 | */ 390 | getAdSkippableState() { 391 | this.log('getAdSkippableState'); 392 | return this.attributes_['skippableState']; 393 | } 394 | 395 | /** 396 | * Returns the remaining ad time, in seconds. 397 | * @return {number} The time remaining in the ad. 398 | */ 399 | getAdRemainingTime() { 400 | return this.attributes_['remainingTime']; 401 | } 402 | 403 | /** 404 | * Returns the duration of the ad, in seconds. 405 | * @return {number} The duration of the ad. 406 | */ 407 | getAdDuration() { 408 | return this.attributes_['duration']; 409 | } 410 | 411 | /** 412 | * Returns the ad volume. 413 | * @return {number} The volume of the ad. 414 | */ 415 | getAdVolume() { 416 | this.log('getAdVolume'); 417 | return this.attributes_['volume']; 418 | } 419 | 420 | /** 421 | * Sets the ad volume. 422 | * @param {number} value The volume in percentage. 423 | */ 424 | setAdVolume(value) { 425 | this.attributes_['volume'] = value; 426 | this.log('setAdVolume ' + value); 427 | this.callEvent_('AdVolumeChange'); 428 | } 429 | 430 | /** 431 | * Returns a list of companion ads for the ad. 432 | * @return {string} List of companions in VAST XML. 433 | */ 434 | getAdCompanions() { 435 | return this.attributes_['companions']; 436 | } 437 | 438 | /** 439 | * Returns a list of icons. 440 | * @return {string} A list of icons. 441 | */ 442 | getAdIcons() { 443 | return this.attributes_['icons']; 444 | } 445 | 446 | /** 447 | * Logs events and messages. 448 | * @param {string} message 449 | */ 450 | log(message) { 451 | console.log(message); 452 | } 453 | 454 | /** 455 | * Calls an event if there is a callback. 456 | * @param {string} eventType 457 | * @private 458 | */ 459 | callEvent_(eventType) { 460 | if (eventType in this.eventsCallbacks_) { 461 | this.eventsCallbacks_[eventType](); 462 | } 463 | } 464 | }; 465 | 466 | /** 467 | * Main function called by wrapper to get the VPAID ad. 468 | * @return {Object} The VPAID compliant ad. 469 | */ 470 | var getVPAIDAd = function() { 471 | return new VpaidNonLinear(); 472 | }; 473 | --------------------------------------------------------------------------------