/** * @license videogular v1.4.4 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); } } scope.$watch( function () { return API.currentState; }, function (newVal, oldVal) { if (scope.vgAutohide) scope.showControls(); } ); } } }] ); /** * @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. * * @param {array} vgSpeeds Bindable array with a list of speed options as strings. Default ['0.5', '1', '1.5', '2'] *
 * 
 *    
 *
 *    
 *        
 *    
 * 
 * 
* */ 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'; }, scope: { vgSpeeds: '=?' }, 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 = scope.vgSpeeds || ['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 end = (cuePoints[i].timeLapse.end >= 0) ? cuePoints[i].timeLapse.end : cuePoints[i].timeLapse.start + 1; var cuePointDuration = (end - cuePoints[i].timeLapse.start) * 1000; var position = (cuePoints[i].timeLapse.start * 100 / (Math.round(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:vgScrubBarThumbnails * @restrict E * @description * Layer inside vg-scrub-bar to display thumbnails. * * Param thumbnails could be a string url pointing to a strip of thumbnails or an array of objects with the same * format that you can find in cue points. * * **Strip of thumbnails** * Must be an image with exactly 100 thumbnails. Recommended size per each thumbnail 107x60 * Example of param value: "assets/images/strip-of-thumbnails.jpg" * * To create a strip of thumbnails you can use ffmpeg: * ffmpeg -loglevel panic -y -i app/assets/videos/videogular.mp4 -frames 1 -q:v 1 -vf * "select=not(mod(n\,29)),scale=-1:60,tile=100x1" app/assets/thumbnails/thumbnail.jpg * * **List of thumbnails** * Array with a list of cue points as images. You can specify start or a lapse with start and end. * Example of param value: * * [ * { * "timeLapse": { * "start": 5 * }, * params: { * "thumbnail": "assets/thumbnails/thumbnail-shown-at-second-5.jpg" * } * }, * { * "timeLapse": { * "start": 49, * "end": 60 * }, * "params": { * "thumbnail": "assets/thumbnails/thumbnail-shown-between-seconds-49-and-60.jpg" * } * } * ] * *
 * 
 *    
 *
 *    
 *        
 *            
 *        
 *    
 * 
 * 
* */ angular.module("com.2fdevs.videogular.plugins.controls") .run(["$templateCache", function ($templateCache) { $templateCache.put("vg-templates/vg-scrub-bar-thumbnails", '
' + '
' + '
' + '
' ); } ]) .directive("vgScrubBarThumbnails", ["VG_UTILS", function (VG_UTILS) { return { restrict: "E", require: "^videogular", templateUrl: function (elem, attrs) { return attrs.vgTemplate || 'vg-templates/vg-scrub-bar-thumbnails'; }, scope: { "vgThumbnails": "=" }, link: function (scope, elem, attr, API) { var thumbnailsWidth = 0; var thumbWidth = 0; var slider = elem[0].querySelector(".background"); var isStrip = (typeof scope.vgThumbnails === "string"); scope.thumbnails = false; scope.thumbnailContainer = {}; 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.onLoadThumbnails = function(event) { thumbnailsWidth = event.currentTarget.naturalWidth; thumbWidth = thumbnailsWidth / 100; }; scope.onLoadThumbnail = function(event) { thumbWidth = event.currentTarget.naturalWidth; }; scope.updateThumbnails = function(second) { var percentage = Math.round(second * 100 / (API.totalTime / 1000)); var thPos = (slider.scrollWidth * percentage / 100) - (thumbWidth / 2); if (isStrip) { var bgPos = Math.round(thumbnailsWidth * percentage / 100); scope.thumbnailContainer = { "width": thumbWidth + "px", "left": thPos + "px" }; scope.thumbnails = { "background-image": 'url("' + scope.vgThumbnails + '")', "background-position": -bgPos + "px 0px" }; } else { var secondsByPixel = API.totalTime / slider.scrollWidth / 1000; var lapse = { start: Math.floor(second - (secondsByPixel / 2)), end: Math.ceil(second) }; if (lapse.start < 0) lapse.start = 0; if (lapse.end > API.totalTime) lapse.end = API.totalTime; scope.thumbnailContainer = { "left": thPos + "px" }; scope.thumbnails = { "background-image": 'none' }; if (scope.vgThumbnails) { for (var i=0, l=scope.vgThumbnails.length; i= 0) { if (lapse.start >= th.timeLapse.start && (lapse.end <= th.timeLapse.end || lapse.end <= th.timeLapse.start)) { scope.thumbnails = { "background-image": 'url("' + th.params.thumbnail + '")' }; break; } } else { if (th.timeLapse.start >= lapse.start && th.timeLapse.start <= lapse.end) { scope.thumbnails = { "background-image": 'url("' + th.params.thumbnail + '")' }; break; } } } } } }; scope.onMouseMove = function($event) { var second = Math.round($event.offsetX * API.mediaElement[0].duration / slider.scrollWidth); scope.updateThumbnails(second); scope.$digest(); }; scope.onTouchMove = function($event) { var touches = $event.touches; var touchX = scope.getOffset(touches[0]); var second = Math.round(touchX * API.mediaElement[0].duration / slider.scrollWidth); scope.updateThumbnails(second); scope.$digest(); }; scope.onMouseLeave = function(event) { scope.thumbnails = false; scope.$digest(); }; scope.onTouchLeave = function(event) { scope.thumbnails = false; scope.$digest(); }; scope.onDestroy = function() { elem.unbind("touchmove", scope.onTouchMove); elem.unbind("touchleave", scope.onTouchLeave); elem.unbind("touchend", scope.onTouchLeave); elem.unbind("mousemove", scope.onMouseMove); elem.unbind("mouseleave", scope.onMouseLeave); }; var thLoader; if (isStrip) { thLoader = new Image(); thLoader.onload = scope.onLoadThumbnails.bind(scope); thLoader.src = scope.vgThumbnails; } else { thLoader = new Image(); thLoader.onload = scope.onLoadThumbnail.bind(scope); thLoader.src = scope.vgThumbnails[0].params.thumbnail; } // Touch move is really buggy in Chrome for Android, maybe we could use mouse move that works ok if (VG_UTILS.isMobileDevice()) { elem.bind("touchmove", scope.onTouchMove); elem.bind("touchleave", scope.onTouchLeave); elem.bind("touchend", scope.onTouchLeave); } else { elem.bind("mousemove", scope.onMouseMove); elem.bind("mouseleave", scope.onMouseLeave); } scope.$on('destroy', scope.onDestroy.bind(scope)); } } } ]); /** * @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'; }, scope: { vgThumbnails: "=" }, 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; var thumbnailsWidth = 0; var thumbWidth = 0; var slider = elem[0].querySelector("div[role=slider]"); scope.thumbnails = false; scope.thumbnailContainer = {}; scope.API = API; scope.onLoadThumbnails = function(event) { thumbnailsWidth = event.path[0].naturalWidth; thumbWidth = thumbnailsWidth / 100; }; 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 / slider.scrollWidth); scope.$digest(); }; scope.onScrubBarTouchEnd = function onScrubBarTouchEnd($event) { var event = $event.originalEvent || $event; if (isPlayingWhenSeeking) { isPlayingWhenSeeking = false; API.play(); } isSeeking = false; scope.$digest(); }; scope.onScrubBarTouchMove = function onScrubBarTouchMove($event) { var event = $event.originalEvent || $event; var touches = event.touches; var touchX = scope.getOffset(touches[0]); if (scope.vgThumbnails && scope.vgThumbnails.length) { var second = Math.round(touchX * API.mediaElement[0].duration / slider.scrollWidth); var percentage = Math.round(second * 100 / (API.totalTime / 1000)); scope.updateThumbnails(percentage); } if (isSeeking) { API.seekTime(touchX * API.mediaElement[0].duration / slider.scrollWidth); } scope.$digest(); }; scope.onScrubBarTouchLeave = function onScrubBarTouchLeave(event) { isSeeking = false; scope.thumbnails = false; scope.$digest(); }; 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 / slider.scrollWidth); scope.$digest(); }; 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 / slider.scrollWidth); scope.$digest(); }; scope.onScrubBarMouseMove = function onScrubBarMouseMove(event) { if (scope.vgThumbnails && scope.vgThumbnails.length) { var second = Math.round(event.offsetX * API.mediaElement[0].duration / slider.scrollWidth); var percentage = Math.round(second * 100 / (API.totalTime / 1000)); scope.updateThumbnails(percentage); } if (isSeeking) { event = VG_UTILS.fixEventOffset(event); API.seekTime(event.offsetX * API.mediaElement[0].duration / slider.scrollWidth); } scope.$digest(); }; scope.onScrubBarMouseLeave = function onScrubBarMouseLeave(event) { isSeeking = false; scope.thumbnails = false; scope.$digest(); }; 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.updateThumbnails = function updateThumbnails(percentage) { var bgPos = Math.round(thumbnailsWidth * percentage / 100); var thPos = (slider.scrollWidth * percentage / 100) - (thumbWidth / 2); scope.thumbnailContainer = { "width": thumbWidth + "px", "left": thPos + "px" }; scope.thumbnails = { "background-image": 'url("' + scope.vgThumbnails + '")', "background-position": -bgPos + "px 0px" }; }; 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.onDestroy = function() { elem.unbind("touchstart", scope.onScrubBarTouchStart); elem.unbind("touchend", scope.onScrubBarTouchEnd); elem.unbind("touchmove", scope.onScrubBarTouchMove); elem.unbind("touchleave", scope.onScrubBarTouchLeave); elem.unbind("mousedown", scope.onScrubBarMouseDown); elem.unbind("mouseup", scope.onScrubBarMouseUp); elem.unbind("mousemove", scope.onScrubBarMouseMove); elem.unbind("mouseleave", scope.onScrubBarMouseLeave); }; scope.$watch( function () { return API.currentState; }, function (newVal, oldVal) { if (newVal != oldVal) { scope.setState(newVal); } } ); if (scope.vgThumbnails) { var thLoader = new Image(); thLoader.onload = scope.onLoadThumbnails.bind(scope); thLoader.src = scope.vgThumbnails; } // 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); } scope.$on('destroy', scope.onDestroy.bind(scope)); } } } ]); /** * @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.onMuteButtonLeave = function onMuteButtonLeave() { document.activeElement.blur(); }; 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"; }); }; scope.onDestroy = function() { elem.unbind("mouseover", scope.onScrubBarTouchStart); elem.unbind("mouseleave", scope.onScrubBarTouchEnd); }; // 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); } scope.$on('destroy', scope.onDestroy.bind(scope)); } } }] );