Built motion from commit 1038d87.|0.0.141
[motion.git] / public / bower_components / angular-audio / angular.audio.js
1 'use strict';
2 angular.module('ngAudio', [])
3 .directive('ngAudio', ['$compile', '$q', 'ngAudio', function($compile, $q, ngAudio) {
4     return {
5         restrict: 'AEC',
6         scope: {
7             volume: '=',
8             start: '=',
9             currentTime: '=',
10             loop: '=',
11             clickPlay: '=',
12             disablePreload:'='
13             //ngAudio:'='
14         },
15         controller: function($scope, $attrs, $element, $timeout) {
16             
17             /* Loads the sound from destination */
18             var audio;
19             function initSound(){
20                 audio = ngAudio.load($attrs.ngAudio, $scope);
21                 /* Add audio to local scope for modification with nested inputs */
22                 $scope.$audio = audio;
23
24                 /* Remove watching features for improved performance */
25                 audio.unbind();
26             }            
27
28             if (!$scope.disablePreload){
29                 initSound();
30             }        
31             
32
33             $element.on('click', function() {
34                 if ($scope.clickPlay === false) {
35                     return;
36                 }
37                 
38                 if ($scope.disablePreload){
39                     initSound();
40                 }        
41
42                 /* iOS workaround: Call the play method directly in listener function */
43                 audio.audio.play();
44                 
45                 /* Set volume to $scope volume if it exists, or default to audio's current value */
46                 audio.volume = $scope.volume || audio.volume;
47                 audio.loop = $scope.loop;
48                 audio.currentTime = $scope.start || 0;
49
50                 /* Fixes a bug with Firefox (???) */
51                 $timeout(function() {
52                     audio.play();
53                 }, 5);
54             });
55             
56             $element.on('$destroy', function() {
57                 audio.destroy();
58             });
59         }
60     };
61 }])
62
63 .directive('ngAudioHover', ['$compile', '$q', 'ngAudio', function($compile, $q, ngAudio) {
64     return {
65         restrict: 'AEC',
66         controller: function($scope, $attrs, $element, $timeout) {
67
68             var audio = ngAudio.load($attrs.ngAudioHover, $scope);
69
70             $element.on('mouseover rollover hover', function() {
71                 
72                 /* iOS workaround: Call the play method directly in listener function */
73                 audio.audio.play();
74                 
75                 audio.volume = $attrs.volumeHover || audio.volume;
76                 audio.loop = $attrs.loop;
77                 audio.currentTime = $attrs.startHover || 0;
78
79             });
80
81             $element.on('$destroy', function() {
82                 audio.destroy();
83             });
84         }
85     };
86 }])
87
88 .service('localAudioFindingService', ['$q', function($q) {
89
90     this.find = function(id) {
91         var deferred = $q.defer();
92         var $sound = document.getElementById(id);
93         if ($sound) {
94             deferred.resolve($sound);
95         } else {
96             deferred.reject(id);
97         }
98
99         return deferred.promise;
100     };
101 }])
102
103 .service('remoteAudioFindingService', ['$q', function($q) {
104
105     this.find = function(url) {
106         var deferred = $q.defer();
107         var audio = new Audio();
108
109         audio.addEventListener('error', function() {
110             deferred.reject();
111         });
112
113         audio.addEventListener('loadstart', function() {
114             deferred.resolve(audio);
115         });
116
117         // bugfix for chrome...
118         setTimeout(function() {
119             audio.src = url;
120         }, 1);
121
122         return deferred.promise;
123
124     };
125 }])
126
127 .service('cleverAudioFindingService', ['$q', 'localAudioFindingService', 'remoteAudioFindingService', function($q, localAudioFindingService, remoteAudioFindingService) {
128     this.find = function(id) {
129         var deferred = $q.defer();
130
131         id = id.replace('|', '/');
132
133         localAudioFindingService.find(id)
134             .then(deferred.resolve, function() {
135                 return remoteAudioFindingService.find(id);
136             })
137             .then(deferred.resolve, deferred.reject);
138
139         return deferred.promise;
140     };
141 }])
142
143 .value('ngAudioGlobals', {
144     muting: false,
145     songmuting: false,
146     performance: 25,
147     unlock: true
148 })
149
150 .factory('NgAudioObject', ['cleverAudioFindingService', '$rootScope', '$interval', '$timeout', 'ngAudioGlobals', function(cleverAudioFindingService, $rootScope, $interval, $timeout, ngAudioGlobals) {
151     return function(id, scope) {
152
153         function twiddle(){
154             audio.play();
155             audio.pause();
156             window.removeEventListener("click",twiddle);
157         }
158
159         var $audioWatch,
160             $intervalWatch,
161             $willPlay = false,
162             $willPause = false,
163             $willRestart = false,
164             $willChangePlaybackRate = false,
165             $newPlaybackRate = false,
166             $volumeToSet,
167             $looping,
168             $isMuting = false,
169             $observeProperties = true,
170             $destroyed = false,
171             $scope = scope || $rootScope,
172             audio,
173             audioObject = this;
174
175         this.id = id;
176         this.safeId = id.replace('/', '|');
177         this.loop = 0;
178
179         this.unbind = function() {
180             $observeProperties = false;
181         };
182
183         this.play = function() {
184             $willPlay = true;
185             return this;
186         };
187         
188         var completeListeners = [];
189         this.complete = function(callback){
190             completeListeners.push(callback);
191         }
192
193         this.pause = function() {
194             $willPause = true;
195         };
196
197         this.restart = function() {
198             $willRestart = true;
199         };
200
201         this.stop = function() {
202             this.restart();
203         };
204
205         this.setVolume = function(volume) {
206             $volumeToSet = volume;
207         };
208
209         this.setPlaybackRate = function(rate) {
210             $newPlaybackRate = rate;
211             $willChangePlaybackRate = true;
212         };
213
214         this.setMuting = function(muting) {
215             $isMuting = muting;
216         };
217
218         this.setProgress = function(progress) {
219             if (audio && audio.duration && isFinite(progress)) {
220                 audio.currentTime = audio.duration * progress;
221             }
222         };
223
224         this.setCurrentTime = function(currentTime) {
225             if (audio && audio.duration) {
226                 audio.currentTime = currentTime;
227             }
228         };
229
230         this.destroy = $destroy;
231         
232         $scope.$on('$destroy', function() {
233             $destroy();
234         });
235         
236         function $destroy() {
237             if (!$destroyed) {
238                 if (interval) {
239                     $interval.cancel(interval);
240                 }
241                 if ($intervalWatch) {
242                     $intervalWatch();
243                 }
244                 if ($audioWatch) {
245                     $audioWatch();
246                 }
247                 $destroyed = true;
248             }
249         }
250
251         function $setWatch() {
252             if ($destroyed) {
253                 return;
254             }
255             $audioWatch = $scope.$watch(function() {
256                 return {
257                     volume: audioObject.volume,
258                     currentTime: audioObject.currentTime,
259                     progress: audioObject.progress,
260                     muting: audioObject.muting,
261                     loop: audioObject.loop,
262                     playbackRate: audioObject.playbackRate
263                 };
264             }, function(newValue, oldValue) {
265                 //console.log("ngaudio watch callback for: " + audioObject.id);
266                 if (newValue.currentTime !== oldValue.currentTime) {
267                     audioObject.setCurrentTime(newValue.currentTime);
268                 }
269
270                 if (newValue.progress !== oldValue.progress) {
271                     audioObject.setProgress(newValue.progress);
272                 }
273                 if (newValue.volume !== oldValue.volume) {
274                     audioObject.setVolume(newValue.volume);
275                 }
276
277                 if (newValue.playbackRate !== oldValue.playbackRate) {
278                     audioObject.setPlaybackRate(newValue.playbackRate);
279                 }
280
281
282
283                 $looping = newValue.loop;
284
285                 if (newValue.muting !== oldValue.muting) {
286                     audioObject.setMuting(newValue.muting);
287                 }
288             }, true);
289         }
290
291         cleverAudioFindingService.find(id)
292             .then(function(nativeAudio) {
293                 audio = nativeAudio;
294                 if (ngAudioGlobals.unlock) {
295
296                     window.addEventListener("click", twiddle);
297                     
298                     audio.addEventListener('playing', function() {
299                         window.removeEventListener("click",twiddle);
300                     });
301
302                 }
303
304                 audio.addEventListener('canplay', function() {
305                     audioObject.canPlay = true;
306                 });
307
308             }, function(error) {
309                 audioObject.error = true;
310                 console.warn(error);
311             });
312
313
314         var interval = $interval(checkWatchers, ngAudioGlobals.performance);
315         $intervalWatch = $scope.$watch(function(){
316             return ngAudioGlobals.performance;
317         },function(){
318             $interval.cancel(interval);
319             interval = $interval(checkWatchers, ngAudioGlobals.performance);
320         })
321         
322         function checkWatchers() {
323             if ($audioWatch) {
324                 $audioWatch();
325             }
326             if (audio) {
327
328                 if ($isMuting || ngAudioGlobals.isMuting) {
329                     audio.volume = 0;
330                 } else {
331                     audio.volume = audioObject.volume !== undefined ? audioObject.volume : 1;
332                 }
333
334                 if ($willPlay) {
335                     audio.play();
336                     $willPlay = false;
337                 }
338
339                 if ($willRestart) {
340                     audio.pause();
341                     audio.currentTime = 0;
342                     $willRestart = false;
343                 }
344
345                 if ($willPause) {
346                     audio.pause();
347                     $willPause = false;
348                 }
349
350                 if ($willChangePlaybackRate) {
351                     audio.playbackRate = $newPlaybackRate;
352                     $willChangePlaybackRate = false;
353                 }
354
355                 if ($volumeToSet) {
356                     audio.volume = $volumeToSet;
357                     $volumeToSet = undefined;
358                 }
359
360                 if ($observeProperties) {
361                     audioObject.currentTime = audio.currentTime;
362                     audioObject.duration = audio.duration;
363                     audioObject.remaining = audio.duration - audio.currentTime;
364                     audioObject.progress = audio.currentTime / audio.duration;
365                     audioObject.paused = audio.paused;
366                     audioObject.src = audio.src;
367                     
368                     if (audioObject.currentTime >= audioObject.duration) {
369                         completeListeners.forEach(function(listener){
370                             listener(audioObject);
371                         })
372                     }
373
374                     if ($looping && audioObject.currentTime >= audioObject.duration) {
375                         if ($looping !== true) {
376                             $looping--;
377                             audioObject.loop--;
378                             // if (!$looping) return;
379                         }
380                         audioObject.setCurrentTime(0);
381                         audioObject.play();
382
383                     }
384                 }
385
386                 if (!$isMuting && !ngAudioGlobals.isMuting) {
387                     audioObject.volume = audio.volume;
388                 }
389
390                 audioObject.audio = audio;
391             }
392
393             $setWatch();
394         }
395     };
396 }])
397 .service('ngAudio', ['NgAudioObject', 'ngAudioGlobals', function(NgAudioObject, ngAudioGlobals) {
398     this.play = function(id, scope) {
399
400         var audio = new NgAudioObject(id, scope);
401         audio.play();
402         return audio;
403     };
404
405     this.load = function(id, scope) {
406         return new NgAudioObject(id, scope);
407     };
408
409     this.mute = function() {
410         ngAudioGlobals.muting = true;
411     };
412
413     this.unmute = function() {
414         ngAudioGlobals.muting = false;
415     };
416
417     this.toggleMute = function() {
418         ngAudioGlobals.muting = !ngAudioGlobals.muting;
419     };
420
421     this.setUnlock = function(unlock) {
422       ngAudioGlobals.unlock = unlock;
423     };
424 }])
425 .filter("trackTime", function(){
426     /* Conveniently takes a number and returns the track time */
427     
428     return function(input){
429
430         var totalSec = Math.floor(input | 0);
431
432         var output = "";
433         var hours = 0;
434         var minutes = 0;
435         var seconds = 0;
436
437         if (totalSec > 3599) {
438
439             hours = Math.floor(totalSec / 3600);
440             minutes = Math.floor((totalSec - (hours * 3600)) / 60);
441             seconds = (totalSec - ((minutes * 60) + (hours * 3600))); 
442
443             if (hours.toString().length == 1) {
444                 hours = "0" + (Math.floor(totalSec / 3600)).toString();
445             } 
446
447             if (minutes.toString().length == 1) {
448                 minutes = "0" + (Math.floor((totalSec - (hours * 3600)) / 60)).toString();
449             } 
450
451             if (seconds.toString().length == 1) {
452                 seconds = "0" + (totalSec - ((minutes * 60) + (hours * 3600))).toString(); 
453             } 
454
455             output = hours + ":" + minutes + ":" + seconds;
456
457         } else if (totalSec > 59) {
458
459             minutes = Math.floor(totalSec / 60);
460             seconds = totalSec - (minutes * 60);
461
462             if (minutes.toString().length == 1) {
463                  minutes = "0" + (Math.floor(totalSec / 60)).toString();
464             }
465
466             if (seconds.toString().length == 1) {
467                  seconds = "0" + (totalSec - (minutes * 60)).toString();
468             }
469
470             output = minutes + ":" + seconds;
471
472         } else {
473
474             seconds = totalSec;
475
476             if (seconds.toString().length == 1) {
477                 seconds = "0" + (totalSec).toString();
478             }
479
480             output = totalSec + "s";
481
482         }
483         
484         if (typeof Number.isNaN === "function" && Number.isNaN(output)){
485             debugger;
486         }
487
488         return output; 
489     }
490 });