/** * Created by intelWorx on 19/11/2015. */ (function () { 'use strict'; /** * Main module, your application should depend on this * @module {mdWavesurfer} */ var app = angular.module('mdWavesurfer', ['ngMaterial']); /** * @ngdoc service * @name $mdWavesurferUtils * * @description * * Utility service for this directive, exposes method: * - getLength(url), which returns a promise for the length of the audio specified by URL * * ```js * app.directive('myFancyDirective', function(mdWavesurferUtils) { * return { * restrict: 'e', * link: function(scope, el, attrs) { * mdWavesurferUtils(attrs.url) * .then(function(l){ * scope.length = l; * }, function(){ * someErrorhandler() * }) * ; * } * }; * }); * ``` */ app.factory('mdWavesurferUtils', ['$q', '$document', '$timeout', function ($q, $document, $timeout) { return { getLength: function (object) { var deferred = $q.defer(); var estimateLength = function (url) { var audio = $document[0].createElement('audio'); audio.src = url; audio.addEventListener('loadeddata', function listener() { deferred.resolve(this.duration); audio.removeEventListener('loadeddata', listener); audio.src = 'data:audio/mpeg,0';//destroy loading. }); audio.addEventListener('error', function (e) { deferred.resolve(e.target.error); }); }; if (typeof object === 'string') { //this is a URL estimateLength(object); } else { $timeout(function () { deferred.reject(new DOMError("NotSupportedError", "Specified argument is not supported")); }); } return deferred.promise; } }; } ]); /** * @ngdoc filter * @name mdWavesurferTimeFormat * * Simple filter to convert value in seconds to MM:SS format * * @param Number duration in seconds */ app.filter('mdWavesurferTimeFormat', function () { return function (input) { if (!input) { return "00:00"; } var minutes = Math.floor(input / 60); var seconds = Math.ceil(input) % 60; return (minutes < 10 ? '0' : '') + minutes + ":" + (seconds < 10 ? '0' : '') + seconds; }; }); app.controller('mdWavesurferAudioController', ['$attrs', '$element', function (attributes, $element) { var audio = this; audio.tracks = []; audio.selectedIndex = audio.selectedIndex || 0; audio.currentTrack = null; //adds to an audio track audio.addTrack = function (trackScope) { if (audio.tracks.indexOf(trackScope) < 0) { audio.tracks.push(trackScope); } if (!audio.currentTrack) { audio.currentTrack = audio.tracks[audio.selectedIndex]; } }; //remove audio track audio.removeTrack = function (trackScope) { var idx = audio.tracks.indexOf(trackScope); if (idx >= 0) { audio.tracks.splice(idx, 1); } }; audio.playerProperties = {} var nKey; for (var attr in attributes) { if (attr.match(/^player/)) { nKey = attr.replace(/^player([A-Z])/, function (m, $1) { return $1.toLowerCase(); }); audio.playerProperties[nKey] = attributes[attr]; } } var getPlayer = function(){ return $element.find('md-wavesurfer-player').controller('mdWavesurferPlayer'); }; var setAutoPlay = function (forcePlay) { var controller = getPlayer(); if (controller && (forcePlay || controller.surfer.isPlaying())) { controller.autoPlay = true; } }; audio.setTrack = function (idx, forcePlay) { if (audio.tracks.length > idx) { if (audio.selectedIndex === idx) { var ctrl = getPlayer(); ctrl.surfer.playPause(); } else { setAutoPlay(forcePlay); audio.currentTrack = audio.tracks[idx]; audio.selectedIndex = idx; } } }; audio.extraButtons = [{ icon: 'zmdi zmdi-skip-previous', title: 'Previous', action: function ($event) { if (audio.selectedIndex > 0) { audio.setTrack(audio.selectedIndex - 1); } }, class: '' }, { icon: 'zmdi zmdi-skip-next', title: 'Next', action: function ($event) { if (audio.selectedIndex < audio.tracks.length - 1) { audio.setTrack(audio.selectedIndex + 1); } }, class: '' }]; } ]); /** * @ngdoc directive * @name md-wavesurfer-audio * * Directive for playing a set of audio files. This directive is analogous to `<audio>` HTML tag. * The audio files, should be specified using the `md-wavesurfer-source` * * WaveSurfer properties can be passed in using the prefix : player-* for attributes, e.g. `player-wave-color` is * equivalent to WaveSurfer's waveColor option. * * Must be used as an element. * * @usage * ```html * <md-wavesurfer-audio player-wave-color="gray" player-progress-color="black" player-backend="MediaElement"> * <md-wavesurfer-source src="source1" title="Title-1"></md-wavesurfer-source> * <md-wavesurfer-source src="source2" title="Title-2"></md-wavesurfer-source> * <md-wavesurfer-source src="source3" title="Title-3"></md-wavesurfer-source> * ... * <md-wavesurfer-source src="sourceN" title="Рассказы о сновидениях"></md-wavesurfer-source> * </md-wavesurfer-audio> * ``` * * @param string player-* specifies WaveSurfer properties. * */ app.directive('mdWavesurferAudio', [ function () { return { restrict: 'E', templateUrl: 'md-player-audio.partial.html', transclude: true, controller: 'mdWavesurferAudioController', controllerAs: 'audio' }; } ]); /** * @ngdoc directive * * @name md-wavesurfer-source * * This directive is used within the `md-wavesurfer-audio` directive to specify an audio file source, it is * synonymous to `<source>` tag in HTML * * The directive cannot be used as standalone. * * @usage * * ```html * <md-wavesurfer-source src="source3" title="Title-3" album-art="Album-Art-Url" duration=""></md-wavesurfer-source> * ``` * @param String src the URL to the audio file, this is required. * @param String title track title * @param String album-art the album art URL * @param Number duration the length of the audio file in seconds, will be auto-detected if not specified. * */ app.directive('mdWavesurferSource', ['mdWavesurferUtils', function (mdWavesurferUtils) { return { restrict: 'E', require: '^mdWavesurferAudio', scope: { src: '@', albumArt: '@', title: '@', duration: '=' }, link: function (scope, element, attrs, audio) { audio.addTrack(scope); if (!scope.duration) { mdWavesurferUtils.getLength(scope.src).then(function (dur) { scope.duration = dur; }, function (e) { scope.duration = 0; console.log('Failed to get audio length, reason: ', e.message); }); } element.on('$destroy', function () { audio.removeTrack(audio); }); } }; } ]); app.controller('mdWavesurferPlayerController', ['$element', '$scope', '$attrs', '$interval', '$mdTheming', function ($element, $scope, attributes, $interval, $mdTheme) { var control = this, timeInterval; control.themeClass = "md-" + $mdTheme.defaultTheme() + "-theme"; control.isReady = false; control.surfer = null; control.toggleMute = function () { if (control.surfer) { control.surfer.toggleMute(); control.isMute = !control.isMute; } }; var initWaveSurfer = function () { control.isReady = false; control.currentTime = 0; if (!control.surfer) { var options = { container: $element[0].querySelector('.waveSurferWave') }, defaults = { scrollParent: true, waveColor: 'violet', progressColor: 'purple' }; options = angular.extend(defaults, attributes, (control.properties || {}), options); control.surfer = WaveSurfer.create(options); control.surfer.on('ready', function () { control.isReady = true; if (control.autoPlay) { control.surfer.play(); } $scope.$apply(); }); control.surfer.on('pause', function () { stopInterval(); }); control.surfer.on('finish', function () { stopInterval(); }); control.surfer.on('play', function () { startInterval(); }); } control.title = control.title || control.src.split('/').pop(); control.surfer.load(control.src); }; var startInterval = function () { timeInterval = $interval(function () { control.currentTime = control.isReady ? control.surfer.getCurrentTime() : 0; }, 1000); }, stopInterval = function () { $interval.cancel(timeInterval); }; initWaveSurfer(); $scope.$watch('control.src', function (src1, src2) { if (src1 != src2) { initWaveSurfer(); } }); $element.on('$destroy', function () { if (control.surfer) { control.surfer.destroy(); } stopInterval(); }); $scope.$watch(function () { var div = $element[0].querySelector('.audioPlayerWrapper'); return div ? div.offsetWidth : 0; }, function (width) { if (width < 1) { //hidden control.surfer.pause(); } }); } ]); /** * @ngdoc directive * * @name md-wavesurfer-player * * @usage * This directive can be used as a stand-alone directive to display Audio WaveSurfer with a few controls, by default * this will only display play/pause, fast-forward, rewind and mute toggle buttons, however, you can add extra * buttons using the `extra-buttons` parameters. * * ```html * <md-wavesurfer-player url="trackUrl" title="Track Title" * extra-buttons="extraButtons" properties="properties"> * </md-wavesurfer-player> * ``` * * @param {string} url the URL of the audio file * @param {string} title title of the audio track * @param {object} properties an object specifying init options for WaveSurfer * @param {boolean} auto-play specifies if the player should start as soon as it's loaded. * @param {object[]} extra-buttons a list of extra buttons to add to the control panel * each button should be an object with the following properties: * { * title: "button title" * action: "call back to call when button is clicked, executed in parent scope", * icon: "md-font-icon parameter for the button" * class: "extra classes to add to the button." * } * * Every other attribute passed to this directive is assumed to a WaveSurver init parameter. */ app.directive('mdWavesurferPlayer', function () { return { restrict: 'E', templateUrl: 'md-player.partial.html', scope: { src: '@url', title: '@', extraButtons: '=', toolbarClass: '@', autoPlay: '=', properties: '=' }, controller: 'mdWavesurferPlayerController', controllerAs: 'control', bindToController: true }; }); ; })();