1if ( typeof fcaPcPost === 'object' && fcaPcPost.video_enabled === "1" ) { 2 3 class EventEmitter { 4 5 constructor() { 6 this.events = {} 7 } 8 9 on(event, listener) { 10 if (typeof this.events[event] !== 'object') { 11 this.events[event] = [] 12 } 13 this.events[event].push(listener) 14 return () => this.removeListener(event, listener) 15 } 16 17 removeListener(event, listener) { 18 if (typeof this.events[event] === 'object') { 19 const idx = this.events[event].indexOf(listener) 20 if (idx > -1) { 21 this.events[event].splice(idx, 1) 22 } 23 } 24 } 25 26 emit(event, ...args) { 27 if (typeof this.events[event] === 'object') { 28 this.events[event].forEach(listener => listener.apply(this, args)) 29 } 30 } 31 32 once(event, listener) { 33 const remove = this.on(event, (...args) => { 34 remove() 35 listener.apply(this, args) 36 }) 37 } 38 } 39 40 class FCA_PC_Video extends EventEmitter { 41 42 constructor(selector) { 43 super() 44 45 this.video_ready = false 46 47 if (typeof selector === undefined) { 48 return 49 } 50 51 this.element = selector 52 this.init() 53 54 } 55 56 init() { 57 58 this.setup_events() 59 this.type = this.video_type() 60 this.load_api() 61 } 62 63 load_api() { 64 switch (this.type) { 65 case 'YOUTUBE': 66 this.api_youtube_load() 67 break 68 case 'WISTIA': 69 this.api_wistia_load() 70 break 71 case 'VIMEO': 72 this.api_vimeo_load() 73 break 74 } 75 } 76 77 setup_events() { 78 this.on( 'api_youtube_loaded', this.api_youtube_setup ) 79 this.on( 'api_vimeo_loaded', this.api_vimeo_setup ) 80 this.on( 'api_wistia_loaded', this.api_wistia_setup ) 81 } 82 83 video_type() { 84 let src = this.element.attr('src') 85 86 if (src === '') { 87 this.revoke( 'pixel cat video: video link not found.' ) 88 return false 89 } 90 91 if (src.indexOf('youtube.com') > -1) return 'YOUTUBE' 92 if (src.indexOf('vimeo.com') > -1) return 'VIMEO' 93 if (src.indexOf('fast.wistia.com') > -1) return 'WISTIA' 94 } 95 96 revoke( message ) { 97 if ( fcaPcCAPI.debug ) { 98 console.log( message ) 99 console.log( 'pixel cat video: revoked ') 100 } 101 // Do some code that revoke video JS here. 102 } 103 104 //VIMEO 105 106 api_vimeo_load() { 107 jQuery.getScript( 'https://player.vimeo.com/api/player.js', () => { 108 this.emit( 'api_vimeo_loaded' ) 109 } ) 110 } 111 112 api_vimeo_setup() { 113 let player_dom = this.element[0] 114 try { 115 this.api = new Vimeo.Player(player_dom) 116 this.api.ready().then( () => { 117 this.api_vimeo_ready() 118 } ) 119 } catch ( e ) { 120 if ( fcaPcCAPI.debug) 121 console.log( e.message ) 122 return 123 } 124 } 125 126 api_vimeo_ready() { 127 if (fcaPcCAPI.debug) { 128 console.log( 'Pixel Cat video: Vimeo video ready' ) 129 } 130 131 this.video_ready = true 132 133 this.api.getVideoId().then( id => { 134 this.video_id = id 135 } ) 136 137 this.api.getVideoTitle().then( title => { 138 this.title = title 139 } ) 140 141 this.api.getVideoUrl().then( url => { 142 this.url = url 143 } ) 144 145 this.api.getDuration().then( duration => { 146 this.duration = duration 147 } ) 148 149 this.events_triggered = {} 150 151 this.api_vimeo_state_change() 152 } 153 154 api_vimeo_state_change() { 155 this.api.on( 'play', event => { 156 this.trigger_event( 'VideoPlay' ) 157 this.timer = setInterval( this.track_progress.bind(this), 1000 ) 158 } ) 159 160 this.api.on( 'pause', event => { 161 this.trigger_event( 'VideoPause', { percentage_watched: this.percentage_watched() + '%' } ) 162 clearInterval( this.timer ) 163 } ) 164 165 this.api.on( 'ended', event => { 166 this.trigger_event( 'VideoWatchToEnd' ) 167 clearInterval( this.timer ) 168 } ) 169 } 170 171 //WISTIA 172 173 api_wistia_load() { 174 jQuery.getScript( '//fast.wistia.net/assets/external/E-v1.js', () => { 175 this.emit( 'api_wistia_loaded' ) 176 } ) 177 } 178 179 api_wistia_setup() { 180 181 let player_dom = this.element 182 183 let video_src = player_dom.attr('src') 184 185 if (typeof video_src === 'undefined') { 186 return 187 } 188 189 //GET VIDEO ID FROM URL 190 191 const pattern = /(https:\/\/fast\.wistia\.com\/embed\/medias)\/(.*)\.jsonp/ 192 193 let match = video_src.match(pattern) 194 195 console.log(match) 196 197 if (match === null) { 198 199 if (fcaPcCAPI) { 200 console.log('no video id match') 201 } 202 203 return 204 } 205 206 this.url = match[1] 207 208 let video_id = match[2] 209 210 if ( typeof video_id === 'undefined' ) { 211 if ( fcaPcCAPI.debug ) { 212 console.log ( 'pixel cat video: wistia video_id not found.' ) 213 } 214 return 215 } 216 217 window._wq = window._wq || [] 218 219 this.api = window._wq 220 221 this.api.push( { 222 id: video_id, 223 onReady: this.api_wistia_ready.bind(this) 224 } ) 225 } 226 227 api_wistia_ready( video ) { 228 229 if ( fcaPcCAPI.debug ) { 230 console.log ( 'pixel cat video: wistia video ready.' ) 231 } 232 233 this.video_ready = true 234 this.api = video 235 236 this.video_id = video.data.media.hashedId 237 this.title = video.data.media.name !== undefined ? video.data.media.name : 'Untitled' 238 this.url = this.url + '/' + this.video_id 239 this.events_triggered = {} 240 this.api_wistia_state_change.bind(this)() 241 } 242 243 api_wistia_state_change() { 244 this.api.bind( 'play', event => { 245 this.trigger_event( 'VideoPlay' ) 246 } ) 247 248 this.api.bind( 'pause', event => { 249 this.trigger_event( 'VideoPause', { percentage_watched: this.percentage_watched() + '%' } ) 250 } ) 251 252 this.api.bind( 'percentwatchedchanged', ( percent, lastPercent ) => { 253 this.track_progress() 254 } ) 255 256 this.api.bind( 'end', event => { 257 this.trigger_event( 'VideoWatchToEnd' ) 258 } ) 259 } 260 261 //YOUTUBE 262 263 api_youtube_load() { 264 this.load_async_script( 'https://www.youtube.com/iframe_api' ) 265 } 266 267 api_youtube_setup() { 268 let player_dom = this.element[0] 269 this.api = new YT.Player(player_dom, { 270 events: { 271 'onReady': this.api_youtube_ready.bind(this), 272 'onStateChange': this.api_youtube_state_change.bind(this) 273 } 274 }) 275 } 276 277 api_youtube_ready( event ) { 278 279 if ( fcaPcCAPI.debug ) { 280 console.log( 'pixel cat video: youtube video ready') 281 } 282 283 // @todo: we will need to build this one in future to make sure the video is ready to use the object 284 this.video_ready = true 285 this.data = this.api.getVideoData() 286 this.video_id = this.data.video_id 287 this.title = this.data.title 288 this.url = this.api.getVideoUrl() 289 290 this.events_triggered = {} 291 } 292 293 api_youtube_state_change( event ) { 294 if ( fcaPcCAPI.debug ) { 295 console.log( event ) 296 } 297 298 switch ( event.data ) { 299 case 0: 300 // end video 301 this.trigger_event( 'VideoWatchToEnd' ) 302 clearInterval( this.timer ) 303 break 304 305 case 1: 306 // play video 307 this.trigger_event( 'VideoPlay' ) 308 // Since we check progress using range: ( percent >= 10 && percent < 25 ) 309 // So we do not need to set interval too fast. 1 - 2 seconds should be fine 310 this.timer = setInterval( this.track_progress.bind(this), 1000 ) 311 break 312 313 case 2: 314 // pause video 315 this.trigger_event( 'VideoPause', { percentage_watched: this.percentage_watched() + '%' } ) 316 clearInterval( this.timer ) 317 break 318 } 319 } 320 321 video_youtube_end() { 322 if ( fcaPcCAPI ) { 323 console.log( 'end' ) 324 } 325 } 326 327 load_async_script( url ) { 328 let new_script = document.createElement('script') 329 let first_script = document.getElementsByTagName('script')[0] 330 331 new_script.src = url 332 333 first_script.parentNode.insertBefore(new_script, first_script) 334 335 if ( fcaPcCAPI.debug ) { 336 console.log('pixel cat video: ' + url + ' has been loaded.') 337 } 338 } 339 340 track_progress() { 341 switch( this.video_type() ) { 342 case 'YOUTUBE':
343 case 'VIMEO': 344 case 'WISTIA': 345 this.video_track_progress() 346 break 347 348 // Other cases 349 } 350 } 351 352 video_track_progress() { 353 let percent = this.percentage_watched() 354 355 if ( fcaPcCAPI.debug ) { 356 console.log( this.percentage_watched() ) 357 } 358 359 // Need to cover all the cases and reduce interval time -> better performance 360 // Use switch here for better code readable 361 switch ( true ) { 362 // 10% milestone 363 case ( percent >= 10 && percent < 25 ): 364 this.trigger_event('VideoWatch10Percent') 365 break 366 367 // 25% milestone 368 case ( percent >= 25 && percent < 50 ): 369 this.trigger_event('VideoWatch25Percent') 370 break 371 372 case ( percent >= 50 && percent < 75 ): 373 this.trigger_event('VideoWatch50Percent') 374 break 375 376 case ( percent >= 75 && percent < 90 ): 377 this.trigger_event('VideoWatch75Percent') 378 break 379 380 case ( percent >= 90 && percent < 100 ): 381 this.trigger_event('VideoWatch90Percent') 382 break 383 } 384 } 385 386 video_params() { 387 return { 388 video_title: this.title, 389 video_url: this.url, 390 video_id: this.video_id 391 } 392 } 393 394 percentage_watched() { 395 switch( this.type ) { 396 case 'YOUTUBE': 397 return Math.round( this.api.getCurrentTime() / this.api.getDuration() * 100 ) 398 399 case 'VIMEO': 400 this.api.getCurrentTime().then( current_time => { 401 this.current_time = current_time 402 } ) 403 return Math.round( this.current_time / this.duration * 100 ) 404 405 case 'WISTIA': 406 return Math.round( this.api.time() / this.api.duration() * 100 ) 407 408 default: 409 return false 410 } 411 } 412 413 trigger_event( action, additional_params = {} ) { 414 415 if ( this.events_triggered[action] ) { 416 return 417 } 418 419 let params = Object.assign( {}, this.video_params(), additional_params ) 420 421 this.emit( 'pixel_event', 'trackCustom', action, params ) 422 423 // Add exception for VideoPause event 424 if ( action !== 'VideoPause' ) { 425 this.events_triggered[action] = true 426 } 427 } 428 429 430 } 431 432 FCA_PC_Video.prototype.__proto__ = EventEmitter.prototype; 433 434 function onYouTubeIframeAPIReady() { 435 436 fcaPcVideos.forEach(video => { 437 if (video.type !== 'YOUTUBE') { 438 return 439 } 440 441 video.emit('api_youtube_loaded') 442 }) 443 444 } 445 446 window.fcaPcVideos = []; 447 448 // detect all videos on the page 449 function detect_videos() { 450 451 let videos = []; 452 453 jQuery( 'iframe' ).each(function () { 454 var char = jQuery(this)[0].src.includes( '?' ) ? '&' : '?' 455 jQuery(this)[0].src = jQuery(this)[0].src + char + 'enablejsapi=1' 456 videos.push(jQuery(this)) 457 }) 458 459 jQuery( 'script' ).each(function () { 460 461 let src = jQuery(this).attr('src') 462 463 if (typeof src === 'undefined') { 464 return 465 } 466 467 if (src.indexOf('https://fast.wistia.com/embed/medias/') === -1) { 468 return 469 } 470 471 videos.push(jQuery(this)) 472 473 }) 474 475 if (videos.length === 0) { 476 477 if (fcaPcCAPI) { 478 console.log('No videos detected') 479 } 480 481 return 482 } 483 484 videos.forEach(function (video) { 485 window.fcaPcVideos.push(new FCA_PC_Video(video)) 486 }) 487 488 489 } 490 491 492 detect_videos() 493 494}
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.