PageSourceSearch

https://www.cchr.org/js/routes/inline-video.js

js cchr.org collected 2026-10-01 10:42:41 UTC 10,816 bytes, 259 lines download raw bytes

1(function () {
2
3    JavascriptHandler.AddHandler(function () {
4        if (window.location.hash == "#editor") {
5            return;
6        }
7
8        // Every <video> on the PFV landing pages belongs to the trailer route below,
9        // which loads videojs lazily. Pulling it here would undo that.
10        if (ownedVideos().length === 0) {
11            return;
12        }
13
14        requireJS(['videojs'], init);
15    });
16
17    function ownedVideos() {
18        return $('video').filter(function () {
19            return $(this).closest('.js-pfv-form-trailer, .js-pfv-trailer-modal').length === 0;
20        });
21    }
22
23    function init() {
24        _.each($('video'), function (value, key, list) {
25            // Owned by the dedicated PFV form-trailer route: the inline muted-autoplay
26            // trailer (.js-pfv-form-trailer) and the click-to-open documentary modal
27            // (.js-pfv-trailer-modal). pfv-form-trailer.js must build the modal player
28            // itself WITH desktop HLS options — videojs 7.4.0 discards options if this
29            // loop initialises the element first.
30            if ($(value).closest('.js-pfv-form-trailer, .js-pfv-trailer-modal').length) { return; }
31
32            var videoId = $(value).attr('videoid');
33            var opts = {
34                autoplay: false,
35                autoplay: window.location.pathname == '/ban-ect/watch/therapy-or-torture-the-truth-about-electroshock.html',
36                controls: true,
37            };
38
39            var autoplayMuted = window.location.pathname == '/ban-ect/' || window.location.pathname == '/prescription-for-violence/';
40
41            if(autoplayMuted) {
42                opts.autoplay = true;
43                opts.muted = true;
44                opts.controls = false;
45            }
46
47            // ?play=1 → the user already clicked "play" on the previous page, so
48            // autoplay WITH sound here. Playback is driven manually below so we can
49            // fall back to muted if the browser blocks sound autoplay.
50            var soundAutoplay = /[?&]play=1(?:&|$)/.test(window.location.search);
51            if (soundAutoplay) {
52                opts.autoplay = false;
53                opts.muted = false;
54                opts.controls = true;
55            }
56
57            if (!$(value).attr('poster') && videoId) {
58                opts.poster = '/FURL/data/shared/web/assets/videos/master_thumbs/' + videoId + '.jpg';
59            }
60
61            videojs(value, opts).ready(function () {
62                var video = this;
63                if (globalIsMobile && opts.poster) {
64                    this.poster(opts.poster);
65                }
66
67                var src = JSON.parse(video.el().dataset.src);
68                for (var i = 0; i < src.length; i++) {
69                    if(!src[i].src) {
70                        src[i].src = src[i].source;
71                    }
72                }
73                video.src(src);
74
75                if (soundAutoplay) {
76                    video.muted(false);
77                    video.controls(true);
78                    var soundPlay = video.play();
79                    if (soundPlay && typeof soundPlay.catch === 'function') {
80                        soundPlay.catch(function () {
81                            // Browser blocked sound autoplay → play muted; controls let the user unmute.
82                            try { video.muted(true); } catch (e) {}
83                            var mutedPlay = video.play();
84                            if (mutedPlay && typeof mutedPlay.catch === 'function') {
85                                mutedPlay.catch(function () {});
86                            }
87                        });
88                    }
89                }
90
91                // Unified one-time user-intent: first click restarts, unmutes, shows controls, hides label
92                (function setupUserIntentHandlers(){
93                    var $playerEl = $(video.el());
94                    var $container = $playerEl.closest('.video-16-9');
95                    if ($container.length === 0) { $container = $playerEl.parent(); }
96
97                    // The mute/sound label renders either INSIDE .video-16-9 (video block
98                    // mutelabel) or as a SIBLING of .video-holder (separate previewLabel
99                    // yield, e.g. /prescription-for-violence/). Resolve a scope that holds
100                    // the label in both layouts so the unmute icon handler can bind.
101                    var $label = $container.find('.video-preview__label');
102                    if ($label.length === 0) {
103                        $label = $playerEl.closest('.video-holder').parent().find('.video-preview__label').first();
104                    }
105                    if ($label.length === 0) {
106                        $label = $playerEl.closest('.relative').find('.video-preview__label').first();
107                    }
108                    var hasLabel = $label.length > 0;
109
110                    if (autoplayMuted) {
111                        $container.addClass('cursor-pointer');
112                    }
113
114                    function handleFirstUserPlay() {
115                        if ($playerEl.data('userStarted')) { return; }
116                        $playerEl.data('userStarted', true);
117
118                        // Optional: hide any global progress bar if present
119                        $(".home-header--progress-bar").hide();
120
121                        try { video.muted(false); } catch(e) {}
122                        try { video.controls(true); } catch(e) {}
123                        try { video.currentTime(0); } catch(e) {}
124
125                        if ($label.length) { $label.addClass('d-none'); }
126
127                        $container.removeClass('cursor-pointer');
128
129                        var p = video.play();
130                        if (p && typeof p.catch === 'function') {
131                            p.catch(function(){ /* ignore autoplay promise errors */ });
132                        }
133                    }
134
135                    if (autoplayMuted || hasLabel) {
136                        // First click anywhere on the player
137                        $playerEl.one('click.inlineUserStart', function(){
138                            handleFirstUserPlay();
139                        });
140
141                        // First click on explicit unmute icon
142                        $label.find('.video-preview__label-icon').one('click.inlineUserStart', function(e){
143                            e.preventDefault();
144                            e.stopPropagation();
145                            handleFirstUserPlay();
146                        });
147                    }
148                })();
149
150                video.one("timeupdate", function(){
151                    var activeChapter = window.location.hash;
152                    if(activeChapter) {
153                        if(activeChapter.indexOf('#') == 0) {
154                            activeChapter = activeChapter.substring(1, activeChapter.length);
155                        }
156                        goToChapter(video, activeChapter);
157                    }
158                });
159
160                $(window).on('hashchange', function() {
161                    var activeChapter = window.location.hash;
162                    if(activeChapter) {
163                        if(activeChapter.indexOf('#') == 0) {
164                            activeChapter = activeChapter.substring(1, activeChapter.length);
165                        }
166                        video.play();
167                        goToChapter(video, activeChapter);
168                        TweenMax.to(window, 1, { scrollTo: $('.watch-film--watching').offset().top });
169                    }
170                });
171
172                // Note: unified user-intent handler supersedes older toggle-only behavior for muted previews
173
174                video.on('ended', function () {
175                    // set next link & title
176                    var $active = $('.documentary__chapters-list-bullet--active');
177                    var $next = $active.next();
178                    if($next.length > 0) {
179                        $('.js-up-next--title').html( $next.find('a').html() );
180                        $('.js-skip-next').attr('href', $next.find('a').attr('href'));
181                    }
182
183                    console.error('ended!', $next);
184
185                    $(this.el()).addClass('player-ended');
186
187                    // reset progress
188                    var $circle = $('#svg #bar');
189                    $circle.css({ strokeDashoffset: "565" });
190                    $('.autoplay-countdown').removeClass('hidden-autoplay');
191                    setTimeout(function () {
192                        setTimer(100);
193                        setTimeout(function () {
194                            if (!$('.autoplay-countdown').hasClass('hidden-autoplay')) {
195                                // using window location because safari doesn't allow clicking link
196                                var link = $('.js-skip-next').attr('href');
197                                if(link) {
198                                    window.location.pathname = link;
199                                }
200                                $('.autoplay-countdown').addClass('hidden-autoplay');
201                            }
202                        }, 5000);
203
204                    }, 1000);
205
206                    $('.cancel-autoplay').click(function (event) {
207                        $('.autoplay-countdown').addClass('hidden-autoplay');
208                        $('.hidden-endcard').removeClass('hidden-endcard');
209                    });
210
211                    // dont have suggested videos
212                    if ($('.js-up-next--title').length == 0 || $('.js-up-next--title').html().trim().length == 0) {
213                        $('.cancel-autoplay').click();
214                    }
215
216                });
217            });
218
219
220        });
221    }
222
223    function goToChapter(video, activeChapter) {
224        var chapters = video.el().dataset.chapters;
225        if(chapters) {
226            chapters = JSON.parse(chapters);
227
228            for (let i = 0; i < chapters.length; i++) {
229                var chapter = chapters[i];
230                console.log(chapter.id, activeChapter);
231                if(chapter.id == activeChapter) {
232                    console.log('found', chapter);
233                    video.currentTime(chapter.timing);
234                }
235            }
236        }
237    }
238
239    function setTimer(val) {
240        var $circle = $('#svg #bar');
241
242        if (isNaN(val)) {
243            val = 100;
244        } else {
245            var r = $circle.attr('r');
246            var c = Math.PI * (r * 2);
247
248            if (val < 0) { val = 0; }
249            if (val > 100) { val = 100; }
250
251            var pct = ((100 - val) / 100) * c;
252
253            $circle.css({ strokeDashoffset: pct });
254
255            $('#cont').attr('data-pct', val);
256        }
257    }
258
259})();

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.