1/** 2 * Content Video 3 * Collection 4 */ 5(function( app ) { 6 /** 7 * Module Namespace 8 * Use to register module and events. 9 */ 10 var NAMESPACE = 'contentVideo'; 11 12 /** 13 * Default configuration 14 * Required configuration to ensure minimal functionality. 15 * 16 * @property {string} selector - selector string 17 * @property {object} magnificPopup - plugin settings, see http://dimsemenov.com/plugins/magnific-popup/documentation.html#options 18 */ 19 var defaults = { 20 selector: '.content-video:not(.content-tabs .content-video, .c-slider-plus__slide-video-helper)', 21 autoStart: true 22 }; 23 24 /** 25 * Private variables/state 26 * Variables not to be exposed, nor inherited or modified. 27 * e.g. shortcuts to dependencies or variables to spare memory. 28 */ 29 var conf; 30 var $ = app.$; 31 var trackingApi; 32 var MIPlayerClass; 33 // List of IDs of videos that have been played 34 var videosPlayed = []; 35 36 // Module startup 37 // The DOM is ready to be manipulated 38 app.ready(function() { 39 // apply custom config if present 40 conf = _.defaults({}, app.getConfig(NAMESPACE), defaults); 41 42 // set private variables and check dependencies 43 trackingApi = app.tracking; 44 45 app.mi24PlayerApiReady(function() { 46 MIPlayerClass = window.MIPlayer; 47 }); 48 49 // Register Module 50 app.register(NAMESPACE, Module); 51 52 // Auto-initialize collection 53 if ( conf.autoStart === true ) { 54 $(conf.selector).each(function() { 55 Module({ 56 targetElem: $(this) 57 }); 58 }); 59 } 60 }); 61 62 /** 63 * Module api 64 * Return new instances of a prototype or plain functionality. 65 * Depending on options supplied you may choose what object/functionality to run/return. 66 */ 67 var Module = function( options ) { 68 return ContentVideo(_.extend({}, conf, options)); 69 }; 70 71 /** 72 * Render videos. Fires videoRender event. 73 * @param {object} options - options object 74 * @param {HTMLElement} options.rootElement - root element, on this element the render event is also dispatched 75 * @param {string} options.videoType - mi-24, vimeo, youtube 76 * @param {string} options.videoSelector - selector for video element 77 * @param {function} options.onRender - callback function 78 */ 79 Module.renderVideo = function( {rootElement, videoType, videoSelector = '.content-video__video', onRender} ) { 80 if ( videoType === 'mi-24' ) { 81 app.mi24PlayerApiReady(function() { 82 renderMovingimageVideo(); 83 }); 84 } 85 else if ( videoType === 'vimeo' ) { 86 app.vimeoPlayerApiReady(render); 87 } else if ( videoType === 'youtube' ) { 88 app.youtubePlayerApiReady(render); 89 } 90 91 function renderMovingimageVideo() { 92 let video = rootElement.querySelector(videoSelector); 93 if ( !video || video.playerInstance ) return; 94 // may be enhanced as an array of times 95 let isTimePlayed50Fired = false; 96 97 if ( typeof onRender === 'function' ) { 98 onRender(); 99 } 100 rootElement.dispatchEvent(new CustomEvent('videoRender', { detail: videoType, bubbles: true })); 101 102 const player = new MIPlayerClass(video.id, { 103 videoId: video.dataset.videoId, 104 playerId: video.dataset.playerId, 105 channelId: video.dataset.channelId 106 }); 107 video.playerInstance = player; 108 109 player.on('play', function() { 110 video.playing = true; 111 112 if ( !videosPlayed.includes(video.playerInstance.getVideoId()) ) { 113 videosPlayed.push(video.playerInstance.getVideoId()); 114 115 video.dispatchEvent(new CustomEvent('videoFirstPlay', { detail: videoType, bubbles: true })); 116 trackingApi.gtmPush({ 117 event: 'movingimageVideoFirstPlay', 118 element: video 119 }); 120 } 121 // detail param is redundant for now 122 video.dispatchEvent(new CustomEvent('videoPlay', { detail: videoType, bubbles: true })); 123 trackingApi.gtmPush({ 124 event: 'movingimageVideoPlay', 125 element: video 126 }); 127 }); 128 player.on('timeupdate', function(event) { 129 const timePlayed = (event.time / player.getDuration()) * 100; 130 if ( isNaN(timePlayed) ) return; 131 const timePlayedSanitized = timePlayed - (timePlayed % 10);
132 if ( isTimePlayed50Fired === true ) return; 133 if ( timePlayedSanitized === 50 ) { 134 trackingApi.gtmPush({ 135 event: 'movingimageVideoTimePlayed', 136 timePlayedInPercent: timePlayedSanitized, 137 element: video 138 }); 139 isTimePlayed50Fired = true; 140 } 141 }); 142 player.on('pause', function() { 143 video.playing = false; 144 }); 145 player.on('ended', function() { 146 video.playing = false; 147 player.setCurrentTime(0); 148 trackingApi.gtmPush({ 149 event: 'movingimageVideoEnded', 150 element: video 151 }); 152 }); 153 } 154 155 function render() { 156 let noscript = rootElement.querySelector('noscript'); 157 if (!noscript) return; 158 noscript.outerHTML = noscript.textContent; 159 160 if ( typeof onRender === 'function' ) { 161 onRender(); 162 } 163 rootElement.dispatchEvent(new CustomEvent('videoRender', { detail: videoType, bubbles: true })); 164 } 165 } 166 167 var ContentVideo = function( options ) { 168 var api; 169 var $targetElem = $(options.targetElem); 170 if ( !$targetElem.length ) return; 171 if ( $targetElem.data(NAMESPACE) ) return; 172 var videoType = $targetElem.attr('class').match('content-video--([a-z0-9-]+)') || []; 173 videoType = videoType[1]; 174 175 if ( videoType === 'mi-24' ) { 176 app.mi24PlayerApiReady(function() { 177 Module.renderVideo({ 178 rootElement: $targetElem[0], 179 videoType: videoType, 180 onRender: function() { 181 $targetElem.removeClass('content-video--has-consent-gate'); 182 } 183 }); 184 }); 185 } else if ( videoType === 'vimeo' ) { 186 app.vimeoPlayerApiReady(function() { 187 Module.renderVideo({ 188 rootElement: $targetElem[0], 189 videoType: videoType, 190 onRender: function() { 191 $targetElem.removeClass('content-video--has-consent-gate'); 192 } 193 }); 194 }); 195 } else if ( videoType === 'youtube' ) { 196 app.youtubePlayerApiReady(function() { 197 Module.renderVideo({ 198 rootElement: $targetElem[0], 199 videoType: videoType, 200 onRender: function() { 201 $targetElem.removeClass('content-video--has-consent-gate'); 202 } 203 }); 204 }); 205 } 206 207 api = { 208 getVideoType: function() { 209 return videoType; 210 } 211 }; 212 213 // expose API to public 214 $targetElem.data(NAMESPACE, api); 215 216 return api; 217 }; 218 219}(app));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.