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.