summaryrefslogtreecommitdiff
path: root/www/lib/videogular-controls
diff options
context:
space:
mode:
Diffstat (limited to 'www/lib/videogular-controls')
-rw-r--r--www/lib/videogular-controls/.bower.json19
-rw-r--r--www/lib/videogular-controls/.gitignore3
-rw-r--r--www/lib/videogular-controls/Gruntfile.js35
-rw-r--r--www/lib/videogular-controls/LICENSE20
-rw-r--r--www/lib/videogular-controls/README.md59
-rw-r--r--www/lib/videogular-controls/bower.json8
-rw-r--r--www/lib/videogular-controls/index.js4
-rw-r--r--www/lib/videogular-controls/package.json15
-rw-r--r--www/lib/videogular-controls/vg-controls.js1408
-rw-r--r--www/lib/videogular-controls/vg-controls.min.js1
10 files changed, 1572 insertions, 0 deletions
diff --git a/www/lib/videogular-controls/.bower.json b/www/lib/videogular-controls/.bower.json
new file mode 100644
index 00000000..43396874
--- /dev/null
+++ b/www/lib/videogular-controls/.bower.json
@@ -0,0 +1,19 @@
+{
+ "name": "videogular-controls",
+ "version": "1.4.4",
+ "main": "./vg-controls.js",
+ "dependencies": {
+ "videogular": "~1.4.4"
+ },
+ "homepage": "https://github.com/2fdevs/bower-videogular-controls",
+ "_release": "1.4.4",
+ "_resolution": {
+ "type": "version",
+ "tag": "v1.4.4",
+ "commit": "07a9b7f3f4066016dcb5959da36232874d180358"
+ },
+ "_source": "https://github.com/2fdevs/bower-videogular-controls.git",
+ "_target": "~1.4.4",
+ "_originalSource": "videogular-controls",
+ "_direct": true
+} \ No newline at end of file
diff --git a/www/lib/videogular-controls/.gitignore b/www/lib/videogular-controls/.gitignore
new file mode 100644
index 00000000..ac2c9712
--- /dev/null
+++ b/www/lib/videogular-controls/.gitignore
@@ -0,0 +1,3 @@
+/node_modules/
+/bower_components/
+.idea \ No newline at end of file
diff --git a/www/lib/videogular-controls/Gruntfile.js b/www/lib/videogular-controls/Gruntfile.js
new file mode 100644
index 00000000..e5b2f2f7
--- /dev/null
+++ b/www/lib/videogular-controls/Gruntfile.js
@@ -0,0 +1,35 @@
+module.exports = function(grunt) {
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ release: {
+ options: {
+ //bump: false, //default: true
+ //file: 'bower.json', //default: package.json
+ //add: false, //default: true
+ //commit: false, //default: true
+ //tag: false, //default: true
+ //push: false, //default: true
+ //pushTags: false, //default: true
+ //npmtag: true, //default: no tag
+ //folder: 'folder/to/publish/to/npm', //default project root
+ //commitMessage: 'check out my release <%= version %>', //default: 'release <%= version %>'
+ //tagMessage: 'tagging version <%= version %>', //default: 'Version <%= version %>',
+ //tagName: 'v<%= version %>', //default: '<%= version %>'
+
+ file: 'bower.json', //default: package.json
+ add: false, //default: true
+ commit: false, //default: true
+ tag: false, //default: true
+ push: false, //default: true
+ pushTags: false, //default: true
+ npm: false
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-release');
+
+ grunt.registerTask('major', ['release:major']);
+ grunt.registerTask('minor', ['release:minor']);
+ grunt.registerTask('patch', ['release:patch']);
+};
diff --git a/www/lib/videogular-controls/LICENSE b/www/lib/videogular-controls/LICENSE
new file mode 100644
index 00000000..2a6b69f7
--- /dev/null
+++ b/www/lib/videogular-controls/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 2fdevs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/www/lib/videogular-controls/README.md b/www/lib/videogular-controls/README.md
new file mode 100644
index 00000000..706b94ea
--- /dev/null
+++ b/www/lib/videogular-controls/README.md
@@ -0,0 +1,59 @@
+bower-videogular-controls
+=========================
+
+Videogular `controls` plugin repository for distribution on `bower`.
+
+## Install
+
+Install [Videogular](http://www.videogular.com/) `controls` plugin with Bower:
+
+`bower install videogular-controls`
+
+### Install Videogular
+
+Install [Videogular](http://www.videogular.com/) with Bower:
+
+`bower install videogular`
+
+### Install themes
+
+Install [Videogular](http://www.videogular.com/) themes with Bower:
+
+`bower install videogular-themes-default`
+
+### Install plugins
+
+Install [Videogular](http://www.videogular.com/) plugins with Bower:
+
+`bower install videogular-buffering`
+
+`bower install videogular-overlay-play`
+
+`bower install videogular-poster`
+
+## Documentation
+
+It's available on [Videogular's project Wiki](https://github.com/2fdevs/videogular/wiki).
+
+## License
+
+The MIT License (MIT)
+
+Copyright (c) 2013 2fdevs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/www/lib/videogular-controls/bower.json b/www/lib/videogular-controls/bower.json
new file mode 100644
index 00000000..91990ff5
--- /dev/null
+++ b/www/lib/videogular-controls/bower.json
@@ -0,0 +1,8 @@
+{
+ "name": "videogular-controls",
+ "version": "1.4.4",
+ "main": "./vg-controls.js",
+ "dependencies": {
+ "videogular": "~1.4.4"
+ }
+}
diff --git a/www/lib/videogular-controls/index.js b/www/lib/videogular-controls/index.js
new file mode 100644
index 00000000..7c75799a
--- /dev/null
+++ b/www/lib/videogular-controls/index.js
@@ -0,0 +1,4 @@
+require('videogular');
+require('./vg-controls');
+
+module.exports = 'com.2fdevs.videogular.plugins.controls';
diff --git a/www/lib/videogular-controls/package.json b/www/lib/videogular-controls/package.json
new file mode 100644
index 00000000..a5cc0b7a
--- /dev/null
+++ b/www/lib/videogular-controls/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "videogular-controls",
+ "version": "1.4.4",
+ "main": "index.js",
+ "dependencies": {
+ "videogular": "~1.4.4"
+ },
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-release": "~0.7.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+}
diff --git a/www/lib/videogular-controls/vg-controls.js b/www/lib/videogular-controls/vg-controls.js
new file mode 100644
index 00000000..2c79e4bb
--- /dev/null
+++ b/www/lib/videogular-controls/vg-controls.js
@@ -0,0 +1,1408 @@
+/**
+ * @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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'></vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ * @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",
+ '<div class="controls-container" ng-mousemove="onMouseMove()" ng-class="animationClass" ng-transclude></div>');
+ }]
+)
+ .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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-fullscreen-button></vg-fullscreen-button>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(
+ ["$templateCache", function ($templateCache) {
+ $templateCache.put("vg-templates/vg-fullscreen-button",
+ '<button class="iconButton" ng-click="onClickFullScreen()" ng-class="fullscreenIcon" aria-label="Toggle full screen" type="button"> </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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-play-pause-button></vg-play-pause-button>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(
+ ["$templateCache", function ($templateCache) {
+ $templateCache.put("vg-templates/vg-play-pause-button",
+ '<button class="iconButton" ng-click="onClickPlayPause()" ng-class="playPauseIcon" aria-label="Play/Pause" type="button"></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']
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-playback-button vg-speeds='config.playbackSpeeds'></vg-playback-button>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(
+ ["$templateCache", function ($templateCache) {
+ $templateCache.put("vg-templates/vg-playback-button",
+ '<button class="playbackValue iconButton" ng-click="onClickPlayback()">{{playback}}x</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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-scrub-bar>
+ * <vg-scrub-bar-buffer></vg-scrub-bar-buffer>
+ * </vg-scrub-bar>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls>
+ * <vg-scrub-bar>
+ * <vg-scrub-bar-cue-points vg-cue-points='config.cuePoints[0]'></vg-scrub-bar-cue-points>
+ * </vg-scrub-bar>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(["$templateCache",
+ function ($templateCache) {
+ $templateCache.put("vg-templates/vg-scrub-bar-cue-points",
+ '<div class="cue-point-timeline">' +
+ '<div ng-repeat="cuePoint in vgCuePoints" class="cue-point" ng-style="cuePoint.$$style"></div>' +
+ '</div>');
+ }
+ ])
+ .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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-scrub-bar>
+ * <vg-scrub-bar-current-time></vg-scrub-bar-current-time>
+ * </vg-scrub-bar>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+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"
+ * }
+ * }
+ * ]
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls>
+ * <vg-scrub-bar>
+ * <vg-scrub-bar-thumbnails vg-thumbnails='config.thumbnails'></vg-scrub-bar-thumbnails>
+ * </vg-scrub-bar>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(["$templateCache",
+ function ($templateCache) {
+ $templateCache.put("vg-templates/vg-scrub-bar-thumbnails",
+ '<div class="vg-thumbnails" ng-show="thumbnails" ng-style="thumbnailContainer">' +
+ '<div class="image-thumbnail" ng-style="thumbnails"></div>' +
+ '</div>' +
+ '<div class="background"></div>'
+ );
+ }
+ ])
+ .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<l; i++) {
+ var th = scope.vgThumbnails[i];
+
+ if (th.timeLapse.end >= 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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-scrub-bar></vg-scrub-bar>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(["$templateCache",
+ function ($templateCache) {
+ $templateCache.put("vg-templates/vg-scrub-bar",
+ '<div role="slider" ' +
+ 'aria-valuemax="{{ariaTime(API.totalTime)}}" ' +
+ 'aria-valuenow="{{ariaTime(API.currentTime)}}" ' +
+ 'aria-valuemin="0" ' +
+ 'aria-label="Time scrub bar" ' +
+ 'tabindex="0" ' +
+ 'ng-keydown="onScrubBarKeyDown($event)">' +
+ '</div>' +
+ '<div class="container" ng-transclude></div>'
+ );
+ }]
+ )
+ .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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-time-display>{{currentTime | date:'hh:mm'}}</vg-time-display>
+ * <vg-time-display>{{timeLeft | date:'mm:ss'}}</vg-time-display>
+ * <vg-time-display>{{totalTime | date:'hh:mm:ss'}}</vg-time-display>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-volume>
+ * <vg-mute-button><vg-mute-button>
+ * </vg-volume>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(
+ ["$templateCache", function ($templateCache) {
+ $templateCache.put("vg-templates/vg-mute-button",
+ '<button type="button" class="iconButton" ng-class="muteIcon" ng-click="onClickMute()" ng-focus="onMuteButtonFocus()" ng-blur="onMuteButtonLoseFocus()" ng-mouseleave="onMuteButtonLeave()" ng-keydown="onMuteButtonKeyDown($event)" aria-label="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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-volume>
+ * <vg-mute-button><vg-mute-button>
+ * <vg-volume-bar><vg-volume-bar>
+ * </vg-volume>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+angular.module("com.2fdevs.videogular.plugins.controls")
+ .run(
+ ["$templateCache", function ($templateCache) {
+ $templateCache.put("vg-templates/vg-volume-bar",
+ '<div class="verticalVolumeBar">\
+ <div class="volumeBackground" ng-click="onClickVolume($event)" ng-mousedown="onMouseDownVolume()" ng-mouseup="onMouseUpVolume()" ng-mousemove="onMouseMoveVolume($event)" ng-mouseleave="onMouseLeaveVolume()">\
+ <div class="volumeValue"></div>\
+ <div class="volumeClickArea"></div>\
+ </div>\
+ </div>');
+ }]
+)
+ .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.
+ *
+ * <pre>
+ * <videogular vg-theme="config.theme.url">
+ * <vg-media vg-src="sources"></vg-media>
+ *
+ * <vg-controls vg-autohide='config.autohide' vg-autohide-time='config.autohideTime'>
+ * <vg-volume></vg-volume>
+ * </vg-controls>
+ * </videogular>
+ * </pre>
+ *
+ */
+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));
+ }
+ }
+ }]
+);
diff --git a/www/lib/videogular-controls/vg-controls.min.js b/www/lib/videogular-controls/vg-controls.min.js
new file mode 100644
index 00000000..27d47e4a
--- /dev/null
+++ b/www/lib/videogular-controls/vg-controls.min.js
@@ -0,0 +1 @@
+"use strict";angular.module("com.2fdevs.videogular.plugins.controls",[]).run(["$templateCache",function(a){a.put("vg-templates/vg-controls",'<div class="controls-container" ng-mousemove="onMouseMove()" ng-class="animationClass" ng-transclude></div>')}]).directive("vgControls",["$timeout","VG_STATES",function(a,b){return{restrict:"E",require:"^videogular",transclude:!0,templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-controls"},scope:{vgAutohide:"=?",vgAutohideTime:"=?"},link:function(c,d,e,f){var g,h=2e3;c.API=f,c.onMouseMove=function(){c.vgAutohide&&c.showControls()},c.setAutohide=function(d){d&&f.currentState==b.PLAY?g=a(c.hideControls,h):(c.animationClass="",a.cancel(g),c.showControls())},c.setAutohideTime=function(a){h=a},c.hideControls=function(){c.animationClass="hide-animation"},c.showControls=function(){c.animationClass="show-animation",a.cancel(g),c.vgAutohide&&f.currentState==b.PLAY&&(g=a(c.hideControls,h))},f.isConfig?c.$watch("API.config",function(){if(c.API.config){var a=c.API.config.plugins.controls.autohide||!1,b=c.API.config.plugins.controls.autohideTime||2e3;c.vgAutohide=a,c.vgAutohideTime=b,c.setAutohideTime(b),c.setAutohide(a)}}):(void 0!=c.vgAutohide&&c.$watch("vgAutohide",c.setAutohide),void 0!=c.vgAutohideTime&&c.$watch("vgAutohideTime",c.setAutohideTime)),c.$watch(function(){return f.currentState},function(a,b){c.vgAutohide&&c.showControls()})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-fullscreen-button",'<button class="iconButton" ng-click="onClickFullScreen()" ng-class="fullscreenIcon" aria-label="Toggle full screen" type="button"> </button>')}]).directive("vgFullscreenButton",[function(){return{restrict:"E",require:"^videogular",scope:{},templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-fullscreen-button"},link:function(a,b,c,d){a.onChangeFullScreen=function(b){a.fullscreenIcon={enter:!b,exit:b}},a.onClickFullScreen=function(){d.toggleFullScreen()},a.fullscreenIcon={enter:!0},a.$watch(function(){return d.isFullScreen},function(b,c){b!=c&&a.onChangeFullScreen(b)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-play-pause-button",'<button class="iconButton" ng-click="onClickPlayPause()" ng-class="playPauseIcon" aria-label="Play/Pause" type="button"></button>')}]).directive("vgPlayPauseButton",["VG_STATES",function(a){return{restrict:"E",require:"^videogular",scope:{},templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-play-pause-button"},link:function(b,c,d,e){b.setState=function(c){switch(c){case a.PLAY:b.playPauseIcon={pause:!0};break;case a.PAUSE:b.playPauseIcon={play:!0};break;case a.STOP:b.playPauseIcon={play:!0}}},b.onClickPlayPause=function(){e.playPause()},b.playPauseIcon={play:!0},b.$watch(function(){return e.currentState},function(a,c){b.setState(a)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-playback-button",'<button class="playbackValue iconButton" ng-click="onClickPlayback()">{{playback}}x</button>')}]).directive("vgPlaybackButton",[function(){return{restrict:"E",require:"^videogular",templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-playback-button"},scope:{vgSpeeds:"=?"},link:function(a,b,c,d){a.playback="1",a.setPlayback=function(b){a.playback=b,d.setPlayback(parseFloat(b))},a.onClickPlayback=function(){var b=a.vgSpeeds||["0.5","1","1.5","2"],c=b.indexOf(a.playback.toString())+1;c>=b.length?a.playback=b[0]:a.playback=b[c],a.setPlayback(a.playback)},a.$watch(function(){return d.playback},function(b,c){b!=c&&a.setPlayback(b)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").directive("vgScrubBarBuffer",[function(){return{restrict:"E",require:"^videogular",link:function(a,b,c,d){var e=0;a.onUpdateBuffer=function(a){"number"==typeof a&&d.totalTime?(e=100*(a/d.totalTime),b.css("width",e+"%")):b.css("width",0)},a.$watch(function(){return d.bufferEnd},function(b,c){a.onUpdateBuffer(b)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-scrub-bar-cue-points",'<div class="cue-point-timeline"><div ng-repeat="cuePoint in vgCuePoints" class="cue-point" ng-style="cuePoint.$$style"></div></div>')}]).directive("vgScrubBarCuePoints",[function(){return{restrict:"E",require:"^videogular",templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-scrub-bar-cue-points"},scope:{vgCuePoints:"="},link:function(a,b,c,d){a.onPlayerReady=function(){a.updateCuePoints(a.vgCuePoints)},a.updateCuePoints=function(a){var c;if(a){c=parseInt(b[0].clientWidth);for(var e=0,f=a.length;f>e;e++){var g=a[e].timeLapse.end>=0?a[e].timeLapse.end:a[e].timeLapse.start+1,h=1e3*(g-a[e].timeLapse.start),i=100*a[e].timeLapse.start/Math.round(d.totalTime/1e3)+"%",j=0;"number"==typeof h&&d.totalTime&&(j=100*h/d.totalTime+"%"),a[e].$$style={width:j,left:i}}}},a.$watch("vgCuePoints",a.updateCuePoints),a.$watch(function(){return d.totalTime},function(b,c){b>0&&a.onPlayerReady()})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").directive("vgScrubBarCurrentTime",[function(){return{restrict:"E",require:"^videogular",link:function(a,b,c,d){var e=0;a.onUpdateTime=function(a){"number"==typeof a&&d.totalTime?(e=100*(a/d.totalTime),b.css("width",e+"%")):b.css("width",0)},a.$watch(function(){return d.currentTime},function(b,c){a.onUpdateTime(b)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-scrub-bar-thumbnails",'<div class="vg-thumbnails" ng-show="thumbnails" ng-style="thumbnailContainer"><div class="image-thumbnail" ng-style="thumbnails"></div></div><div class="background"></div>')}]).directive("vgScrubBarThumbnails",["VG_UTILS",function(a){return{restrict:"E",require:"^videogular",templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-scrub-bar-thumbnails"},scope:{vgThumbnails:"="},link:function(b,c,d,e){var f=0,g=0,h=c[0].querySelector(".background"),i="string"==typeof b.vgThumbnails;b.thumbnails=!1,b.thumbnailContainer={},b.getOffset=function(a){for(var b=a.target,c=0;b&&!isNaN(b.offsetLeft);)c+=b.offsetLeft-b.scrollLeft,b=b.offsetParent;return a.clientX-c},b.onLoadThumbnails=function(a){f=a.currentTarget.naturalWidth,g=f/100},b.onLoadThumbnail=function(a){g=a.currentTarget.naturalWidth},b.updateThumbnails=function(a){var c=Math.round(100*a/(e.totalTime/1e3)),d=h.scrollWidth*c/100-g/2;if(i){var j=Math.round(f*c/100);b.thumbnailContainer={width:g+"px",left:d+"px"},b.thumbnails={"background-image":'url("'+b.vgThumbnails+'")',"background-position":-j+"px 0px"}}else{var k=e.totalTime/h.scrollWidth/1e3,l={start:Math.floor(a-k/2),end:Math.ceil(a)};if(l.start<0&&(l.start=0),l.end>e.totalTime&&(l.end=e.totalTime),b.thumbnailContainer={left:d+"px"},b.thumbnails={"background-image":"none"},b.vgThumbnails)for(var m=0,n=b.vgThumbnails.length;n>m;m++){var o=b.vgThumbnails[m];if(o.timeLapse.end>=0){if(l.start>=o.timeLapse.start&&(l.end<=o.timeLapse.end||l.end<=o.timeLapse.start)){b.thumbnails={"background-image":'url("'+o.params.thumbnail+'")'};break}}else if(o.timeLapse.start>=l.start&&o.timeLapse.start<=l.end){b.thumbnails={"background-image":'url("'+o.params.thumbnail+'")'};break}}}},b.onMouseMove=function(a){var c=Math.round(a.offsetX*e.mediaElement[0].duration/h.scrollWidth);b.updateThumbnails(c),b.$digest()},b.onTouchMove=function(a){var c=a.touches,d=b.getOffset(c[0]),f=Math.round(d*e.mediaElement[0].duration/h.scrollWidth);b.updateThumbnails(f),b.$digest()},b.onMouseLeave=function(a){b.thumbnails=!1,b.$digest()},b.onTouchLeave=function(a){b.thumbnails=!1,b.$digest()},b.onDestroy=function(){c.unbind("touchmove",b.onTouchMove),c.unbind("touchleave",b.onTouchLeave),c.unbind("touchend",b.onTouchLeave),c.unbind("mousemove",b.onMouseMove),c.unbind("mouseleave",b.onMouseLeave)};var j;i?(j=new Image,j.onload=b.onLoadThumbnails.bind(b),j.src=b.vgThumbnails):(j=new Image,j.onload=b.onLoadThumbnail.bind(b),j.src=b.vgThumbnails[0].params.thumbnail),a.isMobileDevice()?(c.bind("touchmove",b.onTouchMove),c.bind("touchleave",b.onTouchLeave),c.bind("touchend",b.onTouchLeave)):(c.bind("mousemove",b.onMouseMove),c.bind("mouseleave",b.onMouseLeave)),b.$on("destroy",b.onDestroy.bind(b))}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-scrub-bar",'<div role="slider" aria-valuemax="{{ariaTime(API.totalTime)}}" aria-valuenow="{{ariaTime(API.currentTime)}}" aria-valuemin="0" aria-label="Time scrub bar" tabindex="0" ng-keydown="onScrubBarKeyDown($event)"></div><div class="container" ng-transclude></div>')}]).directive("vgScrubBar",["VG_STATES","VG_UTILS",function(a,b){return{restrict:"E",require:"^videogular",transclude:!0,templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-scrub-bar"},scope:{vgThumbnails:"="},link:function(c,d,e,f){var g=!1,h=!1,i=!1,j=37,k=39,l=5,m=0,n=0,o=d[0].querySelector("div[role=slider]");if(c.thumbnails=!1,c.thumbnailContainer={},c.API=f,c.onLoadThumbnails=function(a){m=a.path[0].naturalWidth,n=m/100},c.ariaTime=function(a){return Math.round(a/1e3)},c.getOffset=function(a){for(var b=a.target,c=0;b&&!isNaN(b.offsetLeft);)c+=b.offsetLeft-b.scrollLeft,b=b.offsetParent;return a.clientX-c},c.onScrubBarTouchStart=function(a){var b=a.originalEvent||a,d=b.touches,e=c.getOffset(d[0]);g=!0,h&&(i=!0),f.pause(),f.seekTime(e*f.mediaElement[0].duration/o.scrollWidth),c.$digest()},c.onScrubBarTouchEnd=function(a){a.originalEvent||a;i&&(i=!1,f.play()),g=!1,c.$digest()},c.onScrubBarTouchMove=function(a){var b=a.originalEvent||a,d=b.touches,e=c.getOffset(d[0]);if(c.vgThumbnails&&c.vgThumbnails.length){var h=Math.round(e*f.mediaElement[0].duration/o.scrollWidth),i=Math.round(100*h/(f.totalTime/1e3));c.updateThumbnails(i)}g&&f.seekTime(e*f.mediaElement[0].duration/o.scrollWidth),c.$digest()},c.onScrubBarTouchLeave=function(a){g=!1,c.thumbnails=!1,c.$digest()},c.onScrubBarMouseDown=function(a){a=b.fixEventOffset(a),g=!0,h&&(i=!0),f.pause(),f.seekTime(a.offsetX*f.mediaElement[0].duration/o.scrollWidth),c.$digest()},c.onScrubBarMouseUp=function(a){i&&(i=!1,f.play()),g=!1,c.$digest()},c.onScrubBarMouseMove=function(a){if(c.vgThumbnails&&c.vgThumbnails.length){var d=Math.round(a.offsetX*f.mediaElement[0].duration/o.scrollWidth),e=Math.round(100*d/(f.totalTime/1e3));c.updateThumbnails(e)}g&&(a=b.fixEventOffset(a),f.seekTime(a.offsetX*f.mediaElement[0].duration/o.scrollWidth)),c.$digest()},c.onScrubBarMouseLeave=function(a){g=!1,c.thumbnails=!1,c.$digest()},c.onScrubBarKeyDown=function(a){var b=f.currentTime/f.totalTime*100;a.which===j||a.keyCode===j?(f.seekTime(b-l,!0),a.preventDefault()):(a.which===k||a.keyCode===k)&&(f.seekTime(b+l,!0),a.preventDefault())},c.updateThumbnails=function(a){var b=Math.round(m*a/100),d=o.scrollWidth*a/100-n/2;c.thumbnailContainer={width:n+"px",left:d+"px"},c.thumbnails={"background-image":'url("'+c.vgThumbnails+'")',"background-position":-b+"px 0px"}},c.setState=function(b){if(!g)switch(b){case a.PLAY:h=!0;break;case a.PAUSE:h=!1;break;case a.STOP:h=!1}},c.onDestroy=function(){d.unbind("touchstart",c.onScrubBarTouchStart),d.unbind("touchend",c.onScrubBarTouchEnd),d.unbind("touchmove",c.onScrubBarTouchMove),d.unbind("touchleave",c.onScrubBarTouchLeave),d.unbind("mousedown",c.onScrubBarMouseDown),d.unbind("mouseup",c.onScrubBarMouseUp),d.unbind("mousemove",c.onScrubBarMouseMove),d.unbind("mouseleave",c.onScrubBarMouseLeave)},c.$watch(function(){return f.currentState},function(a,b){a!=b&&c.setState(a)}),c.vgThumbnails){var p=new Image;p.onload=c.onLoadThumbnails.bind(c),p.src=c.vgThumbnails}b.isMobileDevice()?(d.bind("touchstart",c.onScrubBarTouchStart),d.bind("touchend",c.onScrubBarTouchEnd),d.bind("touchmove",c.onScrubBarTouchMove),d.bind("touchleave",c.onScrubBarTouchLeave)):(d.bind("mousedown",c.onScrubBarMouseDown),d.bind("mouseup",c.onScrubBarMouseUp),d.bind("mousemove",c.onScrubBarMouseMove),d.bind("mouseleave",c.onScrubBarMouseLeave)),c.$on("destroy",c.onDestroy.bind(c))}}}]),angular.module("com.2fdevs.videogular.plugins.controls").directive("vgTimeDisplay",[function(){return{require:"^videogular",restrict:"E",link:function(a,b,c,d){a.currentTime=d.currentTime,a.timeLeft=d.timeLeft,a.totalTime=d.totalTime,a.isLive=d.isLive,a.$watch(function(){return d.currentTime},function(b,c){a.currentTime=b}),a.$watch(function(){return d.timeLeft},function(b,c){a.timeLeft=b}),a.$watch(function(){return d.totalTime},function(b,c){a.totalTime=b}),a.$watch(function(){return d.isLive},function(b,c){a.isLive=b})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-mute-button",'<button type="button" class="iconButton" ng-class="muteIcon" ng-click="onClickMute()" ng-focus="onMuteButtonFocus()" ng-blur="onMuteButtonLoseFocus()" ng-mouseleave="onMuteButtonLeave()" ng-keydown="onMuteButtonKeyDown($event)" aria-label="Mute"></button>')}]).directive("vgMuteButton",[function(){return{restrict:"E",require:"^videogular",templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-mute-button"},link:function(a,b,c,d){var e=!1,f=38,g=40,h=.05;a.onClickMute=function(){e?a.currentVolume=a.defaultVolume:(a.currentVolume=0,a.muteIcon={mute:!0}),e=!e,d.setVolume(a.currentVolume)},a.onMuteButtonFocus=function(){a.volumeVisibility="visible"},a.onMuteButtonLoseFocus=function(){a.volumeVisibility="hidden"},a.onMuteButtonLeave=function(){document.activeElement.blur()},a.onMuteButtonKeyDown=function(a){var b,c=null!=d.volume?d.volume:1;a.which===f||a.keyCode===f?(b=c+h,b>1&&(b=1),d.setVolume(b),a.preventDefault()):(a.which===g||a.keyCode===g)&&(b=c-h,0>b&&(b=0),d.setVolume(b),a.preventDefault())},a.onSetVolume=function(b){a.currentVolume=b,e=0===a.currentVolume,e?b>0&&(a.defaultVolume=b):a.defaultVolume=b;var c=Math.round(100*b);0==c?a.muteIcon={mute:!0}:c>0&&25>c?a.muteIcon={level0:!0}:c>=25&&50>c?a.muteIcon={level1:!0}:c>=50&&75>c?a.muteIcon={level2:!0}:c>=75&&(a.muteIcon={level3:!0})},a.defaultVolume=1,a.currentVolume=a.defaultVolume,a.muteIcon={level3:!0},a.onSetVolume(d.volume),a.$watch(function(){return d.volume},function(b,c){b!=c&&a.onSetVolume(b)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").run(["$templateCache",function(a){a.put("vg-templates/vg-volume-bar",'<div class="verticalVolumeBar"> <div class="volumeBackground" ng-click="onClickVolume($event)" ng-mousedown="onMouseDownVolume()" ng-mouseup="onMouseUpVolume()" ng-mousemove="onMouseMoveVolume($event)" ng-mouseleave="onMouseLeaveVolume()"> <div class="volumeValue"></div> <div class="volumeClickArea"></div> </div> </div>')}]).directive("vgVolumeBar",["VG_UTILS",function(a){return{restrict:"E",require:"^videogular",templateUrl:function(a,b){return b.vgTemplate||"vg-templates/vg-volume-bar"},link:function(b,c,d,e){var f=!1,g=angular.element(c[0].getElementsByClassName("volumeBackground")),h=angular.element(c[0].getElementsByClassName("volumeValue"));b.onClickVolume=function(b){b=a.fixEventOffset(b);var c=parseInt(g.prop("offsetHeight")),d=100*b.offsetY/c,f=1-d/100;e.setVolume(f)},b.onMouseDownVolume=function(){f=!0},b.onMouseUpVolume=function(){f=!1},b.onMouseLeaveVolume=function(){f=!1},b.onMouseMoveVolume=function(b){if(f){b=a.fixEventOffset(b);var c=parseInt(g.prop("offsetHeight")),d=100*b.offsetY/c,h=1-d/100;e.setVolume(h)}},b.updateVolumeView=function(a){a=100*a,h.css("height",a+"%"),h.css("top",100-a+"%")},b.onChangeVisibility=function(a){c.css("visibility",a)},c.css("visibility",b.volumeVisibility),b.$watch("volumeVisibility",b.onChangeVisibility),b.updateVolumeView(e.volume),b.$watch(function(){return e.volume},function(a,c){a!=c&&b.updateVolumeView(a)})}}}]),angular.module("com.2fdevs.videogular.plugins.controls").directive("vgVolume",["VG_UTILS",function(a){return{restrict:"E",link:function(b,c,d){b.onMouseOverVolume=function(){b.$evalAsync(function(){b.volumeVisibility="visible"})},b.onMouseLeaveVolume=function(){b.$evalAsync(function(){b.volumeVisibility="hidden"})},b.onDestroy=function(){c.unbind("mouseover",b.onScrubBarTouchStart),c.unbind("mouseleave",b.onScrubBarTouchEnd)},a.isMobileDevice()?c.css("display","none"):(b.volumeVisibility="hidden",c.bind("mouseover",b.onMouseOverVolume),c.bind("mouseleave",b.onMouseLeaveVolume)),b.$on("destroy",b.onDestroy.bind(b))}}}]); \ No newline at end of file