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