PageSourceSearch

https://www.still.co.uk/typo3conf/ext/mmpackage/Resources/Public/Js/mmkit/modules/contentVideo.js?1790758003

js still.co.uk collected 2026-10-02 12:43:04 UTC 7,962 bytes, 219 lines download raw bytes

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.