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