1// ------------------------------------------------------------------------------------------- 2// 3// AVIA VIDEO API - make sure that youtube, vimeo and html 5 use the same interface 4// 5// requires froogaloop vimeo library and youtube iframe api (yt can be loaded async) 6// 7// ------------------------------------------------------------------------------------------- 8 9 10(function($) 11{ 12 "use strict"; 13 14 $.AviaVideoAPI = function(options, video, option_container) 15 { 16 this.videoElement = video; 17 18 // actual video element. either iframe or video 19 this.$video = $( video ); 20 21 // container where the AviaVideoAPI object will be stored as data, and that can receive events like play, pause etc 22 // also the container that allows to overwrite javacript options by adding html data- attributes 23 this.$option_container = option_container ? $( option_container ) : this.$video; 24 25 // button to click to actually start the loading process of the video 26 this.load_btn = this.$option_container.find('.av-click-to-play-overlay'); 27 28 // contains video list 29 this.video_wrapper = this.$video.parents('ul').eq(0); 30 31 //check if we should load immediately or on click 32 this.lazy_load = this.video_wrapper.hasClass('av-show-video-on-click') ? true : false; 33 34 //mobile device? 35 this.isMobile = $.avia_utilities.isMobile; 36 37 //iamge fallback use 38 this.fallback = this.isMobile ? this.$option_container.is('.av-mobile-fallback-image') : false; 39 40 if( this.fallback ) 41 { 42 return; 43 } 44 45 // set up the whole api object 46 this._init( options ); 47 }; 48 49 $.AviaVideoAPI.defaults = { 50 51 loop: false, 52 mute: false, 53 controls: false, 54 events: 'play pause mute unmute loop toggle reset unload' 55 56 }; 57 58 $.AviaVideoAPI.apiFiles = 59 { 60 youtube : {loaded: false, src: 'https://www.youtube.com/iframe_api' } 61 }; 62 63 $.AviaVideoAPI.players = 64 { 65 66 }; 67 68 $.AviaVideoAPI.prototype = 69 { 70 _init: function( options ) 71 { 72 // set slider options 73 this.options = this._setOptions(options); 74 75 // info which video service we are using: html5, vimeo or youtube 76 this.type = this._getPlayerType(); 77 78 this.player = false; 79 80 // store the player object to the this.player variable created by one of the APIs (mediaelement, youtube, vimeo) 81 this._bind_player(); 82 83 // set to true once the events are bound so it doesnt happen a second time by accident or racing condition 84 this.eventsBound = false; 85 86 // info if the video is playing 87 this.playing = false; 88 89 //set css class that video is currently not playing 90 this.$option_container.addClass('av-video-paused'); 91 92 //play pause indicator 93 this.pp = $.avia_utilities.playpause(this.$option_container); 94 }, 95 96 97 //set the video options by first merging the default options and the passed options, then checking the video element if any data attributes overwrite the option set 98 _setOptions: function(options) 99 { 100 var newOptions = $.extend( true, {}, $.AviaVideoAPI.defaults, options ), 101 htmlData = this.$option_container.data(), 102 i = ""; 103 104 //overwritte passed option set with any data properties on the html element 105 for( i in htmlData ) 106 { 107 if( htmlData.hasOwnProperty(i) && (typeof htmlData[i] === "string" || typeof htmlData[i] === "number" || typeof htmlData[i] === "boolean") ) 108 { 109 newOptions[i] = htmlData[i]; 110 } 111 } 112 113 return newOptions; 114 }, 115 116 //get the player type 117 _getPlayerType: function() 118 { 119 var vid_src = this.$video.get(0).src || this.$video.data('src'); 120 121 if( this.$video.is('video') ) 122 { 123 return 'html5'; 124 } 125 126 if( this.$video.is('.av_youtube_frame') ) 127 { 128 return 'youtube'; 129 } 130 131 if( vid_src.indexOf('vimeo.com') != -1 ) 132 { 133 return 'vimeo'; 134 } 135 if( vid_src.indexOf('youtube.com') != -1) 136 { 137 return 'youtube'; 138 } 139 }, 140 141 _bind_player: function() 142 { 143 var _self = this; 144 145 // check if videos are disabled by user setting via cookie - or user must opt in. 146 var cookie_check = $('html').hasClass('av-cookies-needs-opt-in') || $('html').hasClass('av-cookies-can-opt-out'); 147 var allow_continue = true;
148 var silent_accept_cookie = $('html').hasClass('av-cookies-user-silent-accept'); 149 var self_hosted = 'html5' == this.type; 150 151 if( cookie_check && ! silent_accept_cookie && ! self_hosted ) 152 { 153 if( ! document.cookie.match(/aviaCookieConsent/) || $('html').hasClass('av-cookies-session-refused') ) 154 { 155 allow_continue = false; 156 } 157 else 158 { 159 if( ! document.cookie.match(/aviaPrivacyRefuseCookiesHideBar/) ) 160 { 161 allow_continue = false; 162 } 163 else if( ! document.cookie.match(/aviaPrivacyEssentialCookiesEnabled/) ) 164 { 165 allow_continue = false; 166 } 167 else if( document.cookie.match(/aviaPrivacyVideoEmbedsDisabled/) ) 168 { 169 allow_continue = false; 170 } 171 } 172 } 173 174 // allow 3-rd party plugins to hook (see enfold\config-cookiebot\cookiebot.js) 175 if( window['wp'] && wp.hooks ) 176 { 177 allow_continue = wp.hooks.applyFilters( 'aviaCookieConsent_allow_continue', allow_continue ); 178 } 179 180 let allow_class = allow_continue ? 'avia-video-cookie-allowed' : 'avia-video-cookie-not-allowed'; 181 $('html').addClass( allow_class ); 182 183 if( ! allow_continue ) 184 { 185 this._use_external_link(); 186 return; 187 } 188 189 if(this.lazy_load && this.load_btn.length && this.type != "html5") 190 { 191 this.$option_container.addClass('av-video-lazyload'); 192 193 this.load_btn.on('click', function() 194 { 195 _self.load_btn.remove(); 196 _self._setPlayer(); 197 }); 198 } 199 else 200 { 201 this.lazy_load = false; 202 this._setPlayer(); 203 } 204 }, 205 206 //if the user has disabled video slides via cookie a click event will open the video externally 207 _use_external_link: function() 208 { 209 //display the play button no matter what 210 this.$option_container.addClass('av-video-lazyload'); 211 212 this.load_btn.on('click', function(e) 213 { 214 if (e.originalEvent === undefined) return; //human click only 215 216 var src_url = $(this).parents('.avia-slide-wrap').find('div[data-original_url]').data('original_url');
217 if( src_url ) window.open(src_url , '_blank'); 218 }); 219 }, 220 221 //get the player object 222 _setPlayer: function() 223 { 224 var _self = this; 225 226 switch( this.type ) 227 { 228 case "html5": 229 230 this.player = this.$video.data('mediaelementplayer'); 231 232 //apply fallback. sometimes needed for safari 233 if( ! this.player ) 234 { 235 this.$video.data('mediaelementplayer', $.AviaVideoAPI.players[ this.$video.attr('id').replace(/_html5/,'') ] ); 236 this.player = this.$video.data('mediaelementplayer'); 237 } 238 239 this._playerReady(); 240 break; 241 242 case "vimeo": 243 244 //we need to create an iframe and append it to the html first 245 var ifrm = document.createElement("iframe"); 246 var $ifrm = $( ifrm ); 247 248 //note: unmuted vimeo videos in chrome do often times not work due to chrome blocking them 249 ifrm.onload = function() 250 { 251 _self.player = Froogaloop( ifrm ); 252 _self._playerReady(); 253 _self.$option_container.trigger('av-video-loaded'); 254 }; 255 256 ifrm.setAttribute("src", this.$video.data('src') ); 257 258 //we replace the old html structure with the iframe 259 $ifrm.insertAfter( this.$video ); 260 this.$video.remove(); 261 this.$video = ifrm; 262 break; 263 264 case "youtube": 265 266 this._getAPI(this.type); 267 $('body').on('av-youtube-iframe-api-loaded', function(){ _self._playerReady(); }); 268 break; 269 } 270 }, 271 272 _getAPI: function( api ) 273 { 274 //make sure the api file is loaded only once 275 if($.AviaVideoAPI.apiFiles[api].loaded === false) 276 { 277 $.AviaVideoAPI.apiFiles[api].loaded = true; 278 //load the file async 279 var tag = document.createElement('script'), 280 first = document.getElementsByTagName('script')[0]; 281 282 tag.src = $.AviaVideoAPI.apiFiles[api].src; 283 first.parentNode.insertBefore(tag, first); 284 } 285 }, 286 287 //wait for player to be ready, then bind events 288 _playerReady: function() 289 { 290 var _self = this; 291 292 this.$option_container.on('av-video-loaded', function(){ _self._bindEvents(); }); 293 294 switch(this.type) 295 { 296 case "html5": 297 this.$video.on( 'av-mediajs-loaded', function(){ _self.$option_container.trigger('av-video-loaded'); } ); 298 this.$video.on( 'av-mediajs-ended' , function(){ _self.$option_container.trigger('av-video-ended'); } ); 299 break; 300 case "vimeo": 301 //finish event must be applied after ready event for firefox 302 _self.player.addEvent('ready', function() 303 { 304 _self.$option_container.trigger('av-video-loaded'); 305 _self.player.addEvent('finish', function(){ _self.$option_container.trigger('av-video-ended'); }); 306 }); 307 break; 308 309 case "youtube": 310 311 var params = _self.$video.data(); 312 313 if( _self._supports_video() ) 314 { 315 params.html5 = 1; 316 } 317 318 _self.player = new YT.Player(_self.$video.attr('id'), 319 { 320 videoId: params.videoid, 321 height: _self.$video.attr('height'), 322 width: _self.$video.attr('width'), 323 playerVars: params, 324 events: { 325 'onReady': function(){ _self.$option_container.trigger('av-video-loaded'); }, 326 'onError': function(player){ $.avia_utilities.log('YOUTUBE ERROR:', 'error', player); }, 327 'onStateChange': function(event){ 328 if (event.data === YT.PlayerState.ENDED) 329 { 330 var command = _self.options.loop != false ? 'loop' : 'av-video-ended'; 331 _self.$option_container.trigger(command); 332 } 333 } 334 } 335 }); 336 break; 337 } 338 339 //fallback always trigger after 2 seconds 340 setTimeout(function() 341 { 342 if(_self.eventsBound == true || typeof _self.eventsBound == 'undefined' || _self.type == 'youtube' ) 343 { 344 return; 345 } 346 347 $.avia_utilities.log('Fallback Video Trigger "' + _self.type + '":', 'log', _self ); 348 349 _self.$option_container.trigger('av-video-loaded'); 350 351 }, 2000 ); 352 }, 353 354 //bind events we should listen to, to the player 355 _bindEvents: function() 356 { 357 if( this.eventsBound == true || typeof this.eventsBound == 'undefined') 358 { 359 return; 360 } 361 362 var _self = this, volume = 'unmute'; 363 364 this.eventsBound = true; 365 366 this.$option_container.on(this.options.events, function(e) 367 { 368 _self.api(e.type); 369 }); 370 371 if( !_self.isMobile ) 372 { 373 //set up initial options 374 if( this.options.mute != false ) 375 { 376 volume = "mute"; 377 } 378 379 if( this.options.loop != false ) 380 { 381 _self.api('loop'); 382 } 383 384 _self.api(volume); 385 } 386 387 //set timeout to prevent racing conditions with other scripts 388 setTimeout( function() 389 {
390 _self.$option_container.trigger('av-video-events-bound').addClass('av-video-events-bound'); 391 }, 50 ); 392 }, 393 394 395 _supports_video: function() 396 { 397 return !!document.createElement('video').canPlayType; 398 }, 399 400 401 /************************************************************************ 402 PUBLIC Methods 403 *************************************************************************/ 404 405 406 api: function( action ) 407 { 408 //commands on mobile can not be executed if the player was not started manually 409 if(this.isMobile && !this.was_started()) return; 410 411 // prevent calling of unbound function 412 if(this.options.events.indexOf(action) === -1) return; 413 414 // broadcast that the command was executed 415 this.$option_container.trigger('av-video-'+action+'-executed'); 416 417 // console.log("video player api action: " + action); 418 419 // calls the function based on action. eg: _html5_play() 420 if(typeof this[ '_' + this.type + '_' + action] == 'function') 421 { 422 this[ '_' + this.type + '_' + action].call(this); 423 } 424 425 //call generic function eg: _toggle() or _play() 426 if(typeof this[ '_' + action] == 'function') 427 { 428 this[ '_' + action].call(this); 429 } 430 }, 431 432 was_started: function() 433 { 434 if(!this.player) return false; 435 436 switch(this.type) 437 { 438 case "html5": 439 if( this.player.getCurrentTime() > 0 ) 440 { 441 return true; 442 } 443 break; 444 445 case "vimeo": 446 if( this.player.api('getCurrentTime') > 0 ) 447 { 448 return true; 449 } 450 break; 451 452 case "youtube": 453 if( this.player.getPlayerState() !== -1 ) 454 { 455 return true; 456 } 457 break; 458 } 459 460 return false; 461 }, 462 463 /************************************************************************ 464 Generic Methods, are always executed and usually set variables 465 *************************************************************************/ 466 _play: function() 467 { 468 this.playing = true; 469 this.$option_container.addClass('av-video-playing').removeClass('av-video-paused'); 470 }, 471 472 _pause: function() 473 { 474 this.playing = false; 475 this.$option_container.removeClass('av-video-playing').addClass('av-video-paused'); 476 }, 477 478 _loop: function() 479 { 480 this.options.loop = true; 481 }, 482 483 _toggle: function( ) 484 { 485 var command = this.playing == true ? 'pause' : 'play'; 486 487 this.api(command); 488 this.pp.set(command); 489 }, 490 491 492 /************************************************************************ 493 VIMEO Methods 494 *************************************************************************/ 495 496 _vimeo_play: function( ) 497 { 498 this.player.api('play'); 499 }, 500 501 _vimeo_pause: function( ) 502 { 503 this.player.api('pause'); 504 }, 505 506 _vimeo_mute: function( ) 507 { 508 this.player.api('setVolume', 0); 509 }, 510 511 _vimeo_unmute: function( ) 512 { 513 this.player.api('setVolume', 0.7); 514 }, 515 516 _vimeo_loop: function( ) 517 { 518 // currently throws error, must be set in iframe 519 // this.player.api('setLoop', true); 520 }, 521 522 _vimeo_reset: function( ) 523 { 524 this.player.api('seekTo',0); 525 }, 526 527 _vimeo_unload: function() 528 { 529 this.player.api('unload'); 530 }, 531 532 /************************************************************************ 533 YOUTUBE Methods 534 *************************************************************************/ 535 536 _youtube_play: function( ) 537 { 538 this.player.playVideo(); 539 }, 540 541 _youtube_pause: function( ) 542 { 543 this.player.pauseVideo(); 544 }, 545 546 _youtube_mute: function( ) 547 { 548 this.player.mute(); 549 }, 550 551 _youtube_unmute: function( ) 552 { 553 this.player.unMute(); 554 }, 555 556 _youtube_loop: function( ) 557 { 558 // does not work properly with iframe api. needs to manual loop on "end" event 559 // this.player.setLoop(true); 560 if(this.playing == true) this.player.seekTo(0); 561 }, 562 563 _youtube_reset: function( ) 564 { 565 this.player.stopVideo(); 566 }, 567 568 _youtube_unload: function() 569 { 570 this.player.clearVideo(); 571 }, 572 573 /************************************************************************ 574 HTML5 Methods 575 *************************************************************************/ 576 577 _html5_play: function( ) 578 { 579 //disable stoping of other videos in case the user wants to run section bgs 580 if(this.player) 581 { 582 this.player.options.pauseOtherPlayers = false;
583 this.player.play(); 584 } 585 586 }, 587 588 _html5_pause: function( ) 589 { 590 if(this.player) this.player.pause(); 591 }, 592 593 _html5_mute: function( ) 594 { 595 if(this.player) this.player.setMuted(true); 596 }, 597 598 _html5_unmute: function( ) 599 { 600 if(this.player) this.player.setVolume(0.7); 601 }, 602 603 _html5_loop: function( ) 604 { 605 if(this.player) this.player.options.loop = true; 606 }, 607 608 _html5_reset: function( ) 609 { 610 if(this.player) this.player.setCurrentTime(0); 611 }, 612 613 _html5_unload: function() 614 { 615 this._html5_pause(); 616 this._html5_reset(); 617 } 618 }; 619 620 //simple wrapper to call the api. makes sure that the api data is not applied twice 621 $.fn.aviaVideoApi = function( options , apply_to_parent) 622 { 623 return this.each( function() 624 { 625 // by default save the object as data to the initial video. 626 // in the case of slideshows its more benefitial to save it to a parent element (eg: the slide) 627 var applyTo = this; 628 629 if( apply_to_parent ) 630 { 631 applyTo = $(this).parents(apply_to_parent).get(0); 632 } 633 634 var self = $.data( applyTo, 'aviaVideoApi' ); 635 636 if( ! self ) 637 { 638 self = $.data( applyTo, 'aviaVideoApi', new $.AviaVideoAPI( options, this, applyTo ) ); 639 } 640 }); 641 }; 642 643})( jQuery ); 644 645window.onYouTubeIframeAPIReady = function(){ jQuery('body').trigger('av-youtube-iframe-api-loaded'); }; 646 647 648// Init style shamelessly stolen from jQuery http://jquery.com 649// min version: https://f.vimeocdn.com/js/froogaloop2.min.js 650var Froogaloop = (function(){ 651 // Define a local copy of Froogaloop 652 function Froogaloop(iframe) { 653 // The Froogaloop object is actually just the init constructor 654 return new Froogaloop.fn.init(iframe); 655 } 656 657 var eventCallbacks = {}, 658 hasWindowEvent = false, 659 isReady = false, 660 slice = Array.prototype.slice, 661 playerOrigin = '*'; 662 663 Froogaloop.fn = Froogaloop.prototype = { 664 element: null, 665 666 init: function(iframe) { 667 if (typeof iframe === "string") { 668 iframe = document.getElementById(iframe); 669 } 670 671 this.element = iframe; 672 673 return this; 674 }, 675 676 /* 677 * Calls a function to act upon the player. 678 * 679 * @param {string} method The name of the Javascript API method to call. Eg: 'play'. 680 * @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method 681 * or callback function when the method returns a value. 682 */ 683 api: function(method, valueOrCallback) { 684 if (!this.element || !method) { 685 return false; 686 } 687 688 var self = this, 689 element = self.element, 690 target_id = element.id !== '' ? element.id : null, 691 params = !isFunction(valueOrCallback) ? valueOrCallback : null, 692 callback = isFunction(valueOrCallback) ? valueOrCallback : null; 693 694 // Store the callback for get functions 695 if (callback) { 696 storeCallback(method, callback, target_id); 697 } 698 699 postMessage(method, params, element); 700 return self; 701 }, 702 703 /* 704 * Registers an event listener and a callback function that gets called when the event fires. 705 * 706 * @param eventName (String): Name of the event to listen for. 707 * @param callback (Function): Function that should be called when the event fires. 708 */ 709 addEvent: function(eventName, callback) { 710 if (!this.element) { 711 return false; 712 } 713 714 var self = this, 715 element = self.element, 716 target_id = element.id !== '' ? element.id : null; 717 718 719 storeCallback(eventName, callback, target_id); 720 721 // The ready event is not registered via postMessage. It fires regardless. 722 if (eventName != 'ready') { 723 postMessage('addEventListener', eventName, element); 724 } 725 else if (eventName == 'ready' && isReady) { 726 callback.call(null, target_id); 727 } 728 729 return self; 730 }, 731 732 /* 733 * Unregisters an event listener that gets called when the event fires. 734 * 735 * @param eventName (String): Name of the event to stop listening for. 736 */ 737 removeEvent: function(eventName) { 738 if (!this.element) { 739 return false;
740 } 741 742 var self = this, 743 element = self.element, 744 target_id = element.id !== '' ? element.id : null, 745 removed = removeCallback(eventName, target_id); 746 747 // The ready event is not registered 748 if (eventName != 'ready' && removed) { 749 postMessage('removeEventListener', eventName, element); 750 } 751 } 752 }; 753 754 /** 755 * Handles posting a message to the parent window. 756 * 757 * @param method (String): name of the method to call inside the player. For api calls 758 * this is the name of the api method (api_play or api_pause) while for events this method 759 * is api_addEventListener. 760 * @param params (Object or Array): List of parameters to submit to the method. Can be either 761 * a single param or an array list of parameters. 762 * @param target (HTMLElement): Target iframe to post the message to. 763 */ 764 function postMessage(method, params, target) { 765 if (!target.contentWindow.postMessage) { 766 return false; 767 } 768 769 var data = JSON.stringify({ 770 method: method, 771 value: params 772 }); 773 774 target.contentWindow.postMessage(data, playerOrigin); 775 } 776 777 /** 778 * Event that fires whenever the window receives a message from its parent 779 * via window.postMessage. 780 */ 781 function onMessageReceived(event) 782 { 783 784 var data, method; 785 786 try { 787 data = JSON.parse(event.data); 788 method = data.event || data.method; 789 } 790 catch(e) { 791 //fail silently... like a ninja! 792 } 793 794 if (method == 'ready' && !isReady) { 795 isReady = true; 796 } 797 798 // Handles messages from the vimeo player only 799 if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) { 800 return false; 801 } 802 803 if (playerOrigin === '*') { 804 playerOrigin = event.origin; 805 } 806 var value = data.value, 807 eventData = data.data, 808 target_id = target_id === '' ? null : data.player_id, 809 810 callback = getCallback(method, target_id), 811 params = []; 812 813 if (!callback) { 814 return false; 815 } 816 817 if (value !== undefined) { 818 params.push(value); 819 } 820 821 if (eventData) { 822 params.push(eventData); 823 } 824 825 if (target_id) { 826 params.push(target_id); 827 } 828 829 return params.length > 0 ? callback.apply(null, params) : callback.call(); 830 } 831 832 833 /** 834 * Stores submitted callbacks for each iframe being tracked and each 835 * event for that iframe. 836 * 837 * @param eventName (String): Name of the event. Eg. api_onPlay 838 * @param callback (Function): Function that should get executed when the 839 * event is fired. 840 * @param target_id (String) [Optional]: If handling more than one iframe then 841 * it stores the different callbacks for different iframes based on the iframe's 842 * id. 843 */ 844 function storeCallback(eventName, callback, target_id) { 845 if (target_id) { 846 if (!eventCallbacks[target_id]) { 847 eventCallbacks[target_id] = {}; 848 } 849 eventCallbacks[target_id][eventName] = callback; 850 } 851 else { 852 eventCallbacks[eventName] = callback; 853 } 854 } 855 856 /** 857 * Retrieves stored callbacks. 858 */ 859 function getCallback(eventName, target_id) { 860 861 /*modified by kriesi - removing this will result in a js error. */ 862 if (target_id && eventCallbacks[target_id] && eventCallbacks[target_id][eventName]) 863 { 864 return eventCallbacks[target_id][eventName]; 865 } 866 else 867 { 868 return eventCallbacks[eventName]; 869 } 870 } 871 872 function removeCallback(eventName, target_id) { 873 if (target_id && eventCallbacks[target_id]) { 874 if (!eventCallbacks[target_id][eventName]) { 875 return false; 876 } 877 eventCallbacks[target_id][eventName] = null; 878 } 879 else { 880 if (!eventCallbacks[eventName]) { 881 return false;
882 } 883 eventCallbacks[eventName] = null; 884 } 885 886 return true; 887 } 888 889 function isFunction(obj) { 890 return !!(obj && obj.constructor && obj.call && obj.apply); 891 } 892 893 function isArray(obj) { 894 return toString.call(obj) === '[object Array]'; 895 } 896 897 // Give the init function the Froogaloop prototype for later instantiation 898 Froogaloop.fn.init.prototype = Froogaloop.fn; 899 900 // Listens for the message event. 901 // W3C 902 if (window.addEventListener) { 903 window.addEventListener('message', onMessageReceived, false); 904 } 905 // IE 906 else { 907 window.attachEvent('onmessage', onMessageReceived); 908 } 909 910 // Expose froogaloop to the global object 911 return (window.Froogaloop = window.$f = Froogaloop); 912 913})();
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.