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