/** * @license videogular v1.3.2 http://videogular.com * Two Fucking Developers http://twofuckingdevelopers.com * License: MIT */ /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgControls * @restrict E * @description * This directive acts as a container and you will need other directives to control the media. * Inside this directive you can add other directives like vg-play-pause-button and vg-scrub-bar. * *
 * 
 *    
 *
 *    
 * 
 * 
* * @param {boolean=false} vgAutohide Boolean variable or value to activate autohide. * @param {number=2000} vgAutohideTime Number variable or value that represents the time in milliseconds that will wait vgControls until it hides. * * */ "use strict"; angular.module("com.2fdevs.videogular.plugins.controls", []) .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-controls", '
'); }] ) .directive("vgControls", ["$timeout", "VG_STATES", function ($timeout, VG_STATES) { return { restrict: "E", require: "^videogular", transclude: true, templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-controls'; }, scope: { vgAutohide: "=?", vgAutohideTime: "=?" }, link: function (scope, elem, attr, API) { var w = 0; var h = 0; var autoHideTime = 2000; var hideInterval; scope.API = API; scope.onMouseMove = function onMouseMove() { if (scope.vgAutohide) scope.showControls(); }; scope.setAutohide = function setAutohide(value) { if (value && API.currentState == VG_STATES.PLAY) { hideInterval = $timeout(scope.hideControls, autoHideTime); } else { scope.animationClass = ""; $timeout.cancel(hideInterval); scope.showControls(); } }; scope.setAutohideTime = function setAutohideTime(value) { autoHideTime = value; }; scope.hideControls = function hideControls() { scope.animationClass = "hide-animation"; }; scope.showControls = function showControls() { scope.animationClass = "show-animation"; $timeout.cancel(hideInterval); if (scope.vgAutohide && API.currentState == VG_STATES.PLAY) hideInterval = $timeout(scope.hideControls, autoHideTime); }; if (API.isConfig) { scope.$watch("API.config", function () { if (scope.API.config) { var ahValue = scope.API.config.plugins.controls.autohide || false; var ahtValue = scope.API.config.plugins.controls.autohideTime || 2000; scope.vgAutohide = ahValue; scope.vgAutohideTime = ahtValue; scope.setAutohideTime(ahtValue); scope.setAutohide(ahValue); } } ); } else { // If vg-autohide has been set if (scope.vgAutohide != undefined) { scope.$watch("vgAutohide", scope.setAutohide); } // If vg-autohide-time has been set if (scope.vgAutohideTime != undefined) { scope.$watch("vgAutohideTime", scope.setAutohideTime); } } } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgFullscreenButton * @restrict E * @description * Directive to switch between fullscreen and normal mode. * *
 * 
 *    
 *
 *    
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-fullscreen-button", ''); }] ) .directive("vgFullscreenButton", [function () { return { restrict: "E", require: "^videogular", scope: {}, templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-fullscreen-button'; }, link: function (scope, elem, attr, API) { scope.onChangeFullScreen = function onChangeFullScreen(isFullScreen) { scope.fullscreenIcon = {enter: !isFullScreen, exit: isFullScreen}; }; scope.onClickFullScreen = function onClickFullScreen() { API.toggleFullScreen(); }; scope.fullscreenIcon = {enter: true}; scope.$watch( function () { return API.isFullScreen; }, function (newVal, oldVal) { if (newVal != oldVal) { scope.onChangeFullScreen(newVal); } } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgPlayPauseButton * @restrict E * @description * Adds a button inside vg-controls to play and pause media. * *
 * 
 *    
 *
 *    
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-play-pause-button", ''); }] ) .directive("vgPlayPauseButton", ["VG_STATES", function (VG_STATES) { return { restrict: "E", require: "^videogular", scope: {}, templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-play-pause-button'; }, link: function (scope, elem, attr, API) { scope.setState = function setState(newState) { switch (newState) { case VG_STATES.PLAY: scope.playPauseIcon = {pause: true}; break; case VG_STATES.PAUSE: scope.playPauseIcon = {play: true}; break; case VG_STATES.STOP: scope.playPauseIcon = {play: true}; break; } }; scope.onClickPlayPause = function onClickPlayPause() { API.playPause(); }; scope.playPauseIcon = {play: true}; scope.$watch( function () { return API.currentState; }, function (newVal, oldVal) { scope.setState(newVal); } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:ngPlaybackButton * @restrict E * @description * Directive to display a playback buttom to control the playback rate. * *
 * 
 *    
 *
 *    
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-playback-button", ''); }] ) .directive("vgPlaybackButton", [function () { return { restrict: "E", require: "^videogular", templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-playback-button'; }, link: function (scope, elem, attr, API) { scope.playback = '1'; scope.setPlayback = function(playback) { scope.playback = playback; API.setPlayback(parseFloat(playback)); }; scope.onClickPlayback = function onClickPlayback() { var playbackOptions = ['0.5', '1', '1.5', '2']; var nextPlaybackRate = playbackOptions.indexOf(scope.playback.toString()) + 1; if (nextPlaybackRate >= playbackOptions.length) { scope.playback = playbackOptions[0]; } else { scope.playback = playbackOptions[nextPlaybackRate]; } scope.setPlayback(scope.playback); }; scope.$watch( function () { return API.playback; }, function(newVal, oldVal) { if (newVal != oldVal) { scope.setPlayback(newVal); } } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgScrubBarBuffer * @restrict E * @description * Layer inside vg-scrub-bar to display the buffer. * *
 * 
 *    
 *
 *    
 *        
 *            
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .directive("vgScrubBarBuffer", [function () { return { restrict: "E", require: "^videogular", link: function (scope, elem, attr, API) { var percentTime = 0; scope.onUpdateBuffer = function onUpdateBuffer(newBuffer) { if (typeof newBuffer === 'number' && API.totalTime) { percentTime = 100 * (newBuffer / API.totalTime); elem.css("width", percentTime + "%"); } else { elem.css("width", 0); } }; scope.$watch( function () { return API.bufferEnd; }, function (newVal, oldVal) { scope.onUpdateBuffer(newVal); } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgScrubBarCuePoints * @restrict E * @description * Layer inside vg-scrub-bar to display a cue point timeline. * *
 * 
 *    
 *
 *    
 *        
 *            
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-scrub-bar-cue-points", '
' + '
' + '
'); }] ) .directive("vgScrubBarCuePoints", [function () { return { restrict: "E", require: "^videogular", templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-scrub-bar-cue-points'; }, scope: { "vgCuePoints": "=" }, link: function (scope, elem, attr, API) { scope.onPlayerReady = function onPlayerReady() { scope.updateCuePoints(scope.vgCuePoints); }; scope.updateCuePoints = function onUpdateCuePoints(cuePoints) { var totalWidth; if (cuePoints) { totalWidth = parseInt(elem[0].clientWidth); for (var i = 0, l = cuePoints.length; i < l; i++) { var cuePointDuration = (cuePoints[i].timeLapse.end - cuePoints[i].timeLapse.start) * 1000; var position = (cuePoints[i].timeLapse.start * 100 / API.totalTime * 1000) + "%"; var percentWidth = 0; if (typeof cuePointDuration === 'number' && API.totalTime) { percentWidth = ((cuePointDuration * 100) / API.totalTime) + "%"; } cuePoints[i].$$style = { width: percentWidth, left: position }; } } }; scope.$watch("vgCuePoints", scope.updateCuePoints); scope.$watch( function () { return API.totalTime; }, function (newVal, oldVal) { if (newVal > 0) scope.onPlayerReady(); } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgScrubBarCurrentTime * @restrict E * @description * Layer inside vg-scrub-bar to display the current time. * *
 * 
 *    
 *
 *    
 *        
 *            
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .directive("vgScrubBarCurrentTime", [function () { return { restrict: "E", require: "^videogular", link: function (scope, elem, attr, API) { var percentTime = 0; scope.onUpdateTime = function onUpdateTime(newCurrentTime) { if (typeof newCurrentTime === 'number' && API.totalTime) { percentTime = 100 * (newCurrentTime / API.totalTime); elem.css("width", percentTime + "%"); } else { elem.css("width", 0); } }; scope.$watch( function () { return API.currentTime; }, function (newVal, oldVal) { scope.onUpdateTime(newVal); } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgScrubBar * @restrict E * @description * Directive to control the time and display other information layers about the progress of the media. * This directive acts as a container and you can add more layers to display current time, cuepoints, buffer or whatever you need. * *
 * 
 *    
 *
 *    
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-scrub-bar", '
'); }] ) .directive("vgScrubBar", ["VG_STATES", "VG_UTILS", function (VG_STATES, VG_UTILS) { return { restrict: "E", require: "^videogular", transclude: true, templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-scrub-bar'; }, link: function (scope, elem, attr, API) { var isSeeking = false; var isPlaying = false; var isPlayingWhenSeeking = false; var LEFT = 37; var RIGHT = 39; var NUM_PERCENT = 5; scope.API = API; scope.ariaTime = function (time) { return Math.round(time / 1000); }; scope.getOffset = function getOffset(event) { var el = event.target, x = 0; while (el && !isNaN(el.offsetLeft)) { x += el.offsetLeft - el.scrollLeft; el = el.offsetParent; } return event.clientX - x; }; scope.onScrubBarTouchStart = function onScrubBarTouchStart($event) { var event = $event.originalEvent || $event; var touches = event.touches; var touchX = scope.getOffset(touches[0]); isSeeking = true; if (isPlaying) isPlayingWhenSeeking = true; API.pause(); API.seekTime(touchX * API.mediaElement[0].duration / elem[0].scrollWidth); scope.$apply(); }; scope.onScrubBarTouchEnd = function onScrubBarTouchEnd($event) { var event = $event.originalEvent || $event; if (isPlayingWhenSeeking) { isPlayingWhenSeeking = false; API.play(); } isSeeking = false; scope.$apply(); }; scope.onScrubBarTouchMove = function onScrubBarTouchMove($event) { var event = $event.originalEvent || $event; var touches = event.touches; var touchX = scope.getOffset(touches[0]); if (isSeeking) { API.seekTime(touchX * API.mediaElement[0].duration / elem[0].scrollWidth); } scope.$apply(); }; scope.onScrubBarTouchLeave = function onScrubBarTouchLeave(event) { isSeeking = false; scope.$apply(); }; scope.onScrubBarMouseDown = function onScrubBarMouseDown(event) { event = VG_UTILS.fixEventOffset(event); isSeeking = true; if (isPlaying) isPlayingWhenSeeking = true; API.pause(); API.seekTime(event.offsetX * API.mediaElement[0].duration / elem[0].scrollWidth); scope.$apply(); }; scope.onScrubBarMouseUp = function onScrubBarMouseUp(event) { //event = VG_UTILS.fixEventOffset(event); if (isPlayingWhenSeeking) { isPlayingWhenSeeking = false; API.play(); } isSeeking = false; //API.seekTime(event.offsetX * API.mediaElement[0].duration / elem[0].scrollWidth); scope.$apply(); }; scope.onScrubBarMouseMove = function onScrubBarMouseMove(event) { if (isSeeking) { event = VG_UTILS.fixEventOffset(event); API.seekTime(event.offsetX * API.mediaElement[0].duration / elem[0].scrollWidth); } scope.$apply(); }; scope.onScrubBarMouseLeave = function onScrubBarMouseLeave(event) { isSeeking = false; scope.$apply(); }; scope.onScrubBarKeyDown = function onScrubBarKeyDown(event) { var currentPercent = (API.currentTime / API.totalTime) * 100; if (event.which === LEFT || event.keyCode === LEFT) { API.seekTime(currentPercent - NUM_PERCENT, true); event.preventDefault(); } else if (event.which === RIGHT || event.keyCode === RIGHT) { API.seekTime(currentPercent + NUM_PERCENT, true); event.preventDefault(); } }; scope.setState = function setState(newState) { if (!isSeeking) { switch (newState) { case VG_STATES.PLAY: isPlaying = true; break; case VG_STATES.PAUSE: isPlaying = false; break; case VG_STATES.STOP: isPlaying = false; break; } } }; scope.$watch( function () { return API.currentState; }, function (newVal, oldVal) { if (newVal != oldVal) { scope.setState(newVal); } } ); // Touch move is really buggy in Chrome for Android, maybe we could use mouse move that works ok if (VG_UTILS.isMobileDevice()) { elem.bind("touchstart", scope.onScrubBarTouchStart); elem.bind("touchend", scope.onScrubBarTouchEnd); elem.bind("touchmove", scope.onScrubBarTouchMove); elem.bind("touchleave", scope.onScrubBarTouchLeave); } else { elem.bind("mousedown", scope.onScrubBarMouseDown); elem.bind("mouseup", scope.onScrubBarMouseUp); elem.bind("mousemove", scope.onScrubBarMouseMove); elem.bind("mouseleave", scope.onScrubBarMouseLeave); } } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgTimeDisplay * @restrict E * @description * Adds a time display inside vg-controls to play and pause media. * You have three scope variables to show current time, time left and total time. * * Those scope variables are in milliseconds, you can add a date filter to show the time as you wish. * *
 * 
 *    
 *
 *    
 *        {{currentTime | date:'hh:mm'}}
 *        {{timeLeft | date:'mm:ss'}}
 *        {{totalTime | date:'hh:mm:ss'}}
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .directive("vgTimeDisplay", [function () { return { require: "^videogular", restrict: "E", link: function (scope, elem, attr, API) { scope.currentTime = API.currentTime; scope.timeLeft = API.timeLeft; scope.totalTime = API.totalTime; scope.isLive = API.isLive; scope.$watch( function () { return API.currentTime; }, function (newVal, oldVal) { scope.currentTime = newVal; } ); scope.$watch( function () { return API.timeLeft; }, function (newVal, oldVal) { scope.timeLeft = newVal; } ); scope.$watch( function () { return API.totalTime; }, function (newVal, oldVal) { scope.totalTime = newVal; } ); scope.$watch( function () { return API.isLive; }, function (newVal, oldVal) { scope.isLive = newVal; } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgMuteButton * @restrict E * @description * Directive to display a button to mute volume. * *
 * 
 *    
 *
 *    
 *        
 *            
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-mute-button", ''); }] ) .directive("vgMuteButton", [function () { return { restrict: "E", require: "^videogular", templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-mute-button'; }, link: function (scope, elem, attr, API) { var isMuted = false; var UP = 38; var DOWN = 40; var CHANGE_PER_PRESS = 0.05; scope.onClickMute = function onClickMute() { if (isMuted) { scope.currentVolume = scope.defaultVolume; } else { scope.currentVolume = 0; scope.muteIcon = {mute: true}; } isMuted = !isMuted; API.setVolume(scope.currentVolume); }; scope.onMuteButtonFocus = function onMuteButtonFocus() { scope.volumeVisibility = "visible"; }; scope.onMuteButtonLoseFocus = function onMuteButtonLoseFocus() { scope.volumeVisibility = "hidden"; }; scope.onMuteButtonKeyDown = function onMuteButtonKeyDown(event) { var currentVolume = (API.volume != null) ? API.volume : 1; var newVolume; if (event.which === UP || event.keyCode === UP) { newVolume = currentVolume + CHANGE_PER_PRESS; if (newVolume > 1) newVolume = 1; API.setVolume(newVolume); event.preventDefault(); } else if (event.which === DOWN || event.keyCode === DOWN) { newVolume = currentVolume - CHANGE_PER_PRESS; if (newVolume < 0) newVolume = 0; API.setVolume(newVolume); event.preventDefault(); } }; scope.onSetVolume = function onSetVolume(newVolume) { scope.currentVolume = newVolume; isMuted = (scope.currentVolume === 0); // if it's not muted we save the default volume if (!isMuted) { scope.defaultVolume = newVolume; } else { // if was muted but the user changed the volume if (newVolume > 0) { scope.defaultVolume = newVolume; } } var percentValue = Math.round(newVolume * 100); if (percentValue == 0) { scope.muteIcon = {mute: true}; } else if (percentValue > 0 && percentValue < 25) { scope.muteIcon = {level0: true}; } else if (percentValue >= 25 && percentValue < 50) { scope.muteIcon = {level1: true}; } else if (percentValue >= 50 && percentValue < 75) { scope.muteIcon = {level2: true}; } else if (percentValue >= 75) { scope.muteIcon = {level3: true}; } }; scope.defaultVolume = 1; scope.currentVolume = scope.defaultVolume; scope.muteIcon = {level3: true}; //Update the mute button on initialization, then watch for changes scope.onSetVolume(API.volume); scope.$watch( function () { return API.volume; }, function (newVal, oldVal) { if (newVal != oldVal) { scope.onSetVolume(newVal); } } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgVolumeBar * @restrict E * @description * Directive to display a vertical volume bar to control the volume. * This directive must be inside vg-volume directive and requires vg-mute-button to be displayed. * *
 * 
 *    
 *
 *    
 *        
 *            
 *            
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run( ["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-volume-bar", '
\
\
\
\
\
'); }] ) .directive("vgVolumeBar", ["VG_UTILS", function (VG_UTILS) { return { restrict: "E", require: "^videogular", templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-volume-bar'; }, link: function (scope, elem, attr, API) { var isChangingVolume = false; var volumeBackElem = angular.element(elem[0].getElementsByClassName("volumeBackground")); var volumeValueElem = angular.element(elem[0].getElementsByClassName("volumeValue")); scope.onClickVolume = function onClickVolume(event) { event = VG_UTILS.fixEventOffset(event); var volumeHeight = parseInt(volumeBackElem.prop("offsetHeight")); var value = event.offsetY * 100 / volumeHeight; var volValue = 1 - (value / 100); API.setVolume(volValue); }; scope.onMouseDownVolume = function onMouseDownVolume() { isChangingVolume = true; }; scope.onMouseUpVolume = function onMouseUpVolume() { isChangingVolume = false; }; scope.onMouseLeaveVolume = function onMouseLeaveVolume() { isChangingVolume = false; }; scope.onMouseMoveVolume = function onMouseMoveVolume(event) { if (isChangingVolume) { event = VG_UTILS.fixEventOffset(event); var volumeHeight = parseInt(volumeBackElem.prop("offsetHeight")); var value = event.offsetY * 100 / volumeHeight; var volValue = 1 - (value / 100); API.setVolume(volValue); } }; scope.updateVolumeView = function updateVolumeView(value) { value = value * 100; volumeValueElem.css("height", value + "%"); volumeValueElem.css("top", (100 - value) + "%"); }; scope.onChangeVisibility = function onChangeVisibility(value) { elem.css("visibility", value); }; elem.css("visibility", scope.volumeVisibility); scope.$watch("volumeVisibility", scope.onChangeVisibility); //Update the volume bar on initialization, then watch for changes scope.updateVolumeView(API.volume); scope.$watch( function () { return API.volume; }, function (newVal, oldVal) { if (newVal != oldVal) { scope.updateVolumeView(newVal); } } ); } } }] ); /** * @ngdoc directive * @name com.2fdevs.videogular.plugins.controls.directive:vgVolume * @restrict E * @description * Directive to control the volume. * This directive acts as a container and you will need other directives like vg-mutebutton and vg-volumebar to control the volume. * In mobile will be hided since volume API is disabled for mobile devices. * *
 * 
 *    
 *
 *    
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .directive("vgVolume", ["VG_UTILS", function (VG_UTILS) { return { restrict: "E", link: function (scope, elem, attr) { scope.onMouseOverVolume = function onMouseOverVolume() { scope.$evalAsync(function () { scope.volumeVisibility = "visible"; }); }; scope.onMouseLeaveVolume = function onMouseLeaveVolume() { scope.$evalAsync(function () { scope.volumeVisibility = "hidden"; }); }; // We hide volume controls on mobile devices if (VG_UTILS.isMobileDevice()) { elem.css("display", "none"); } else { scope.volumeVisibility = "hidden"; elem.bind("mouseover", scope.onMouseOverVolume); elem.bind("mouseleave", scope.onMouseLeaveVolume); } } } }] );