Built motion from commit 7767ffc.|0.0.132
[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: ['$scope', '$attrs', '$element', '$timeout', 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: ['$scope', '$attrs', '$element', '$timeout', 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     performance: 25,
146     unlock: true,
147     volume:1
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                     globalVolume: ngAudioGlobals.volume
264                 };
265             }, function(newValue, oldValue) {
266                 //console.log("ngaudio watch callback for: " + audioObject.id);
267                 if (newValue.currentTime !== oldValue.currentTime) {
268                     audioObject.setCurrentTime(newValue.currentTime);
269                 }
270
271                 if (newValue.progress !== oldValue.progress) {
272                     audioObject.setProgress(newValue.progress);
273                 }
274                 if (newValue.volume !== oldValue.volume) {
275                     audioObject.setVolume(newValue.volume);
276                 }
277
278                 if (newValue.playbackRate !== oldValue.playbackRate) {
279                     audioObject.setPlaybackRate(newValue.playbackRate);
280                 }
281
282                 if (newValue.globalVolume !== oldValue.globalVolume) {
283                     if (newValue.globalVolume === 0) {
284                         audioObject.setMuting(true);
285                     } else {
286                         audioObject.setMuting(false);
287                         audioObject.setVolume(newValue.globalVolume);
288                     }
289                 }
290
291
292
293                 $looping = newValue.loop;
294
295                 if (newValue.muting !== oldValue.muting) {
296                     audioObject.setMuting(newValue.muting);
297                 }
298             }, true);
299         }
300
301         cleverAudioFindingService.find(id)
302             .then(function(nativeAudio) {
303                 audio = nativeAudio;
304                 if (ngAudioGlobals.unlock) {
305
306                     window.addEventListener("click", twiddle);
307
308                     audio.addEventListener('playing', function() {
309                         window.removeEventListener("click",twiddle);
310                     });
311
312                 }
313
314                 audio.addEventListener('canplay', function() {
315                     audioObject.canPlay = true;
316                 });
317
318             }, function(error) {
319                 audioObject.error = true;
320                 console.warn(error);
321             });
322
323
324         var interval = $interval(checkWatchers, ngAudioGlobals.performance);
325         $intervalWatch = $scope.$watch(function(){
326             return ngAudioGlobals.performance;
327         },function(){
328             $interval.cancel(interval);
329             interval = $interval(checkWatchers, ngAudioGlobals.performance);
330         })
331
332         function checkWatchers() {
333             if ($audioWatch) {
334                 $audioWatch();
335             }
336             if (audio) {
337
338                 if ($isMuting || ngAudioGlobals.muting) {
339                     audio.volume = 0;
340                 } else {
341                     audio.volume = audioObject.volume !== undefined ? audioObject.volume : 1;
342                 }
343
344                 if ($willPlay) {
345                     audio.play();
346                     $willPlay = false;
347                 }
348
349                 if ($willRestart) {
350                     audio.src = 'about:blank';
351                     $willRestart = false;
352                 }
353
354                 if ($willPause) {
355                     audio.pause();
356                     $willPause = false;
357                 }
358
359                 if ($willChangePlaybackRate) {
360                     audio.playbackRate = $newPlaybackRate;
361                     $willChangePlaybackRate = false;
362                 }
363
364                 if ($volumeToSet) {
365                     audio.volume = $volumeToSet;
366                     $volumeToSet = undefined;
367                 }
368
369                 if ($observeProperties) {
370                     audioObject.currentTime = audio.currentTime;
371                     audioObject.duration = audio.duration;
372                     audioObject.remaining = audio.duration - audio.currentTime;
373                                         audioObject.progress = 0; //We set initial value to 0
374                     audioObject.paused = audio.paused;
375                     audioObject.src = audio.src;
376
377                                         //After we check if progress is bigger than 0, and we set
378                     var tempProgress = (audio.currentTime / audio.duration);
379                     if(tempProgress  > 0 ){
380                       audioObject.progress = tempProgress;
381                     }
382
383                     if (audioObject.currentTime >= audioObject.duration) {
384                         completeListeners.forEach(function(listener){
385                             listener(audioObject);
386                         })
387                     }
388
389                     if ($looping && audioObject.currentTime >= audioObject.duration) {
390                         if ($looping !== true) {
391                             $looping--;
392                             audioObject.loop--;
393                             // if (!$looping) return;
394                         }
395                         audioObject.setCurrentTime(0);
396                         audioObject.play();
397
398                     }
399                 }
400
401                 if (!$isMuting && !ngAudioGlobals.muting) {
402                     audioObject.volume = audio.volume;
403                 }
404
405                 audioObject.audio = audio;
406             }
407
408             $setWatch();
409         }
410     };
411 }])
412 .service('ngAudio', ['NgAudioObject', 'ngAudioGlobals', function(NgAudioObject, ngAudioGlobals) {
413     this.play = function(id, scope) {
414
415         var audio = new NgAudioObject(id, scope);
416         audio.play();
417         return audio;
418     };
419
420     this.load = function(id, scope) {
421         return new NgAudioObject(id, scope);
422     };
423
424     this.mute = function() {
425         ngAudioGlobals.muting = true;
426     };
427
428     this.unmute = function() {
429         ngAudioGlobals.muting = false;
430     };
431
432     this.toggleMute = function() {
433         ngAudioGlobals.muting = !ngAudioGlobals.muting;
434     };
435
436     this.setUnlock = function(unlock) {
437       ngAudioGlobals.unlock = unlock;
438     };
439
440     this.setGlobalVolume = function(globalVolume) {
441       ngAudioGlobals.volume = globalVolume;
442     };
443 }])
444 .filter("trackTime", function(){
445     /* Conveniently takes a number and returns the track time */
446
447     return function(input){
448
449         var totalSec = Math.floor(input | 0);
450
451         var output = "";
452         var hours = 0;
453         var minutes = 0;
454         var seconds = 0;
455
456         if (totalSec > 3599) {
457
458             hours = Math.floor(totalSec / 3600);
459             minutes = Math.floor((totalSec - (hours * 3600)) / 60);
460             seconds = (totalSec - ((minutes * 60) + (hours * 3600)));
461
462             if (hours.toString().length == 1) {
463                 hours = "0" + (Math.floor(totalSec / 3600)).toString();
464             }
465
466             if (minutes.toString().length == 1) {
467                 minutes = "0" + (Math.floor((totalSec - (hours * 3600)) / 60)).toString();
468             }
469
470             if (seconds.toString().length == 1) {
471                 seconds = "0" + (totalSec - ((minutes * 60) + (hours * 3600))).toString();
472             }
473
474             output = hours + ":" + minutes + ":" + seconds;
475
476         } else if (totalSec > 59) {
477
478             minutes = Math.floor(totalSec / 60);
479             seconds = totalSec - (minutes * 60);
480
481             if (minutes.toString().length == 1) {
482                  minutes = "0" + (Math.floor(totalSec / 60)).toString();
483             }
484
485             if (seconds.toString().length == 1) {
486                  seconds = "0" + (totalSec - (minutes * 60)).toString();
487             }
488
489             output = minutes + ":" + seconds;
490
491         } else {
492
493             seconds = totalSec;
494
495             if (seconds.toString().length == 1) {
496                 seconds = "0" + (totalSec).toString();
497             }
498
499             output = totalSec + "s";
500
501         }
502
503         if (typeof Number.isNaN === "function" && Number.isNaN(output)){
504             debugger;
505         }
506
507         return output;
508     }
509 });