Built motion from commit 943eae279.|1.0.24
[motion.git] / public / bower_components / angular-timer / angular-timer.js
1 /**
2  * angular-timer - v1.3.3 - 2015-05-28 9:05 AM
3  * https://github.com/siddii/angular-timer
4  *
5  * Copyright (c) 2015 Siddique Hameed
6  * Licensed MIT <https://github.com/siddii/angular-timer/blob/master/LICENSE.txt>
7  */
8 var timerModule = angular.module('timer', [])
9   .directive('timer', ['$compile', function ($compile) {
10     return  {
11       restrict: 'EA',
12       replace: false,
13       scope: {
14         interval: '=interval',
15         startTimeAttr: '=startTime',
16         endTimeAttr: '=endTime',
17         countdownattr: '=countdown',
18         finishCallback: '&finishCallback',
19         autoStart: '&autoStart',
20         language: '@?',
21         fallback: '@?',
22         maxTimeUnit: '='
23       },
24       controller: ['$scope', '$element', '$attrs', '$timeout', 'I18nService', '$interpolate', 'progressBarService', function ($scope, $element, $attrs, $timeout, I18nService, $interpolate, progressBarService) {
25
26         // Checking for trim function since IE8 doesn't have it
27         // If not a function, create tirm with RegEx to mimic native trim
28         if (typeof String.prototype.trim !== 'function') {
29           String.prototype.trim = function () {
30             return this.replace(/^\s+|\s+$/g, '');
31           };
32         }
33
34         //angular 1.2 doesn't support attributes ending in "-start", so we're
35         //supporting both "autostart" and "auto-start" as a solution for
36         //backward and forward compatibility.
37         $scope.autoStart = $attrs.autoStart || $attrs.autostart;
38
39
40         $scope.language = $scope.language || 'en';
41         $scope.fallback = $scope.fallback || 'en';
42
43         //allow to change the language of the directive while already launched
44         $scope.$watch('language', function(newVal, oldVal) {
45           if(newVal !== undefined) {
46             i18nService.init(newVal, $scope.fallback);
47           }
48         });
49
50         //init momentJS i18n, default english
51         var i18nService = new I18nService();
52         i18nService.init($scope.language, $scope.fallback);
53
54         //progress bar
55         $scope.displayProgressBar = 0;
56         $scope.displayProgressActive = 'active'; //Bootstrap active effect for progress bar
57
58         if ($element.html().trim().length === 0) {
59           $element.append($compile('<span>' + $interpolate.startSymbol() + 'millis' + $interpolate.endSymbol() + '</span>')($scope));
60         } else {
61           $element.append($compile($element.contents())($scope));
62         }
63
64         $scope.startTime = null;
65         $scope.endTime = null;
66         $scope.timeoutId = null;
67         $scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) >= 0 ? parseInt($scope.countdownattr, 10) : undefined;
68         $scope.isRunning = false;
69
70         $scope.$on('timer-start', function () {
71           $scope.start();
72         });
73
74         $scope.$on('timer-resume', function () {
75           $scope.resume();
76         });
77
78         $scope.$on('timer-stop', function () {
79           $scope.stop();
80         });
81
82         $scope.$on('timer-clear', function () {
83           $scope.clear();
84         });
85
86         $scope.$on('timer-reset', function () {
87           $scope.reset();
88         });
89
90         $scope.$on('timer-set-countdown', function (e, countdown) {
91           $scope.countdown = countdown;
92         });
93
94         function resetTimeout() {
95           if ($scope.timeoutId) {
96             clearTimeout($scope.timeoutId);
97           }
98         }
99
100         $scope.$watch('startTimeAttr', function(newValue, oldValue) {
101           if (newValue !== oldValue && $scope.isRunning) {
102             $scope.start();
103           }
104         });
105
106         $scope.$watch('endTimeAttr', function(newValue, oldValue) {
107           if (newValue !== oldValue && $scope.isRunning) {
108             $scope.start();
109           }
110         });
111
112         $scope.start = $element[0].start = function () {
113           $scope.startTime = $scope.startTimeAttr ? moment($scope.startTimeAttr) : moment();
114           $scope.endTime = $scope.endTimeAttr ? moment($scope.endTimeAttr) : null;
115           if (!$scope.countdown) {
116             $scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) > 0 ? parseInt($scope.countdownattr, 10) : undefined;
117           }
118           resetTimeout();
119           tick();
120           $scope.isRunning = true;
121         };
122
123         $scope.resume = $element[0].resume = function () {
124           resetTimeout();
125           if ($scope.countdownattr) {
126             $scope.countdown += 1;
127           }
128           $scope.startTime = moment().diff((moment($scope.stoppedTime).diff(moment($scope.startTime))));
129           tick();
130           $scope.isRunning = true;
131         };
132
133         $scope.stop = $scope.pause = $element[0].stop = $element[0].pause = function () {
134           var timeoutId = $scope.timeoutId;
135           $scope.clear();
136           $scope.$emit('timer-stopped', {timeoutId: timeoutId, millis: $scope.millis, seconds: $scope.seconds, minutes: $scope.minutes, hours: $scope.hours, days: $scope.days});
137         };
138
139         $scope.clear = $element[0].clear = function () {
140           // same as stop but without the event being triggered
141           $scope.stoppedTime = moment();
142           resetTimeout();
143           $scope.timeoutId = null;
144           $scope.isRunning = false;
145         };
146
147         $scope.reset = $element[0].reset = function () {
148           $scope.startTime = $scope.startTimeAttr ? moment($scope.startTimeAttr) : moment();
149           $scope.endTime = $scope.endTimeAttr ? moment($scope.endTimeAttr) : null;
150           $scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) > 0 ? parseInt($scope.countdownattr, 10) : undefined;
151           resetTimeout();
152           tick();
153           $scope.isRunning = false;
154           $scope.clear();
155         };
156
157         $element.bind('$destroy', function () {
158           resetTimeout();
159           $scope.isRunning = false;
160         });
161
162
163         function calculateTimeUnits() {
164           var timeUnits = {}; //will contains time with units
165
166           if ($attrs.startTime !== undefined){
167             $scope.millis = moment().diff(moment($scope.startTimeAttr));
168           }
169
170           timeUnits = i18nService.getTimeUnits($scope.millis);
171
172           // compute time values based on maxTimeUnit specification
173           if (!$scope.maxTimeUnit || $scope.maxTimeUnit === 'day') {
174             $scope.seconds = Math.floor(($scope.millis / 1000) % 60);
175             $scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
176             $scope.hours = Math.floor((($scope.millis / (3600000)) % 24));
177             $scope.days = Math.floor((($scope.millis / (3600000)) / 24));
178             $scope.months = 0;
179             $scope.years = 0;
180           } else if ($scope.maxTimeUnit === 'second') {
181             $scope.seconds = Math.floor($scope.millis / 1000);
182             $scope.minutes = 0;
183             $scope.hours = 0;
184             $scope.days = 0;
185             $scope.months = 0;
186             $scope.years = 0;
187           } else if ($scope.maxTimeUnit === 'minute') {
188             $scope.seconds = Math.floor(($scope.millis / 1000) % 60);
189             $scope.minutes = Math.floor($scope.millis / 60000);
190             $scope.hours = 0;
191             $scope.days = 0;
192             $scope.months = 0;
193             $scope.years = 0;
194           } else if ($scope.maxTimeUnit === 'hour') {
195             $scope.seconds = Math.floor(($scope.millis / 1000) % 60);
196             $scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
197             $scope.hours = Math.floor($scope.millis / 3600000);
198             $scope.days = 0;
199             $scope.months = 0;
200             $scope.years = 0;
201           } else if ($scope.maxTimeUnit === 'month') {
202             $scope.seconds = Math.floor(($scope.millis / 1000) % 60);
203             $scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
204             $scope.hours = Math.floor((($scope.millis / (3600000)) % 24));
205             $scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30);
206             $scope.months = Math.floor((($scope.millis / (3600000)) / 24) / 30);
207             $scope.years = 0;
208           } else if ($scope.maxTimeUnit === 'year') {
209             $scope.seconds = Math.floor(($scope.millis / 1000) % 60);
210             $scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
211             $scope.hours = Math.floor((($scope.millis / (3600000)) % 24));
212             $scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30);
213             $scope.months = Math.floor((($scope.millis / (3600000)) / 24 / 30) % 12);
214             $scope.years = Math.floor(($scope.millis / (3600000)) / 24 / 365);
215           }
216           // plural - singular unit decision (old syntax, for backwards compatibility and English only, could be deprecated!)
217           $scope.secondsS = ($scope.seconds === 1) ? '' : 's';
218           $scope.minutesS = ($scope.minutes === 1) ? '' : 's';
219           $scope.hoursS = ($scope.hours === 1) ? '' : 's';
220           $scope.daysS = ($scope.days === 1)? '' : 's';
221           $scope.monthsS = ($scope.months === 1)? '' : 's';
222           $scope.yearsS = ($scope.years === 1)? '' : 's';
223
224
225           // new plural-singular unit decision functions (for custom units and multilingual support)
226           $scope.secondUnit = timeUnits.seconds;
227           $scope.minuteUnit = timeUnits.minutes;
228           $scope.hourUnit = timeUnits.hours;
229           $scope.dayUnit = timeUnits.days;
230           $scope.monthUnit = timeUnits.months;
231           $scope.yearUnit = timeUnits.years;
232
233           //add leading zero if number is smaller than 10
234           $scope.sseconds = $scope.seconds < 10 ? '0' + $scope.seconds : $scope.seconds;
235           $scope.mminutes = $scope.minutes < 10 ? '0' + $scope.minutes : $scope.minutes;
236           $scope.hhours = $scope.hours < 10 ? '0' + $scope.hours : $scope.hours;
237           $scope.ddays = $scope.days < 10 ? '0' + $scope.days : $scope.days;
238           $scope.mmonths = $scope.months < 10 ? '0' + $scope.months : $scope.months;
239           $scope.yyears = $scope.years < 10 ? '0' + $scope.years : $scope.years;
240
241         }
242
243         //determine initial values of time units and add AddSeconds functionality
244         if ($scope.countdownattr) {
245           $scope.millis = $scope.countdownattr * 1000;
246
247           $scope.addCDSeconds = $element[0].addCDSeconds = function (extraSeconds) {
248             $scope.countdown += extraSeconds;
249             $scope.$digest();
250             if (!$scope.isRunning) {
251               $scope.start();
252             }
253           };
254
255           $scope.$on('timer-add-cd-seconds', function (e, extraSeconds) {
256             $timeout(function () {
257               $scope.addCDSeconds(extraSeconds);
258             });
259           });
260
261           $scope.$on('timer-set-countdown-seconds', function (e, countdownSeconds) {
262             if (!$scope.isRunning) {
263               $scope.clear();
264             }
265
266             $scope.countdown = countdownSeconds;
267             $scope.millis = countdownSeconds * 1000;
268             calculateTimeUnits();
269           });
270         } else {
271           $scope.millis = 0;
272         }
273         calculateTimeUnits();
274
275         var tick = function tick() {
276           var typeTimer = null; // countdown or endTimeAttr
277           $scope.millis = moment().diff($scope.startTime);
278           var adjustment = $scope.millis % 1000;
279
280           if ($scope.endTimeAttr) {
281             typeTimer = $scope.endTimeAttr;
282             $scope.millis = moment($scope.endTime).diff(moment());
283             adjustment = $scope.interval - $scope.millis % 1000;
284           }
285
286           if ($scope.countdownattr) {
287             typeTimer = $scope.countdownattr;
288             $scope.millis = $scope.countdown * 1000;
289           }
290
291           if ($scope.millis < 0) {
292             $scope.stop();
293             $scope.millis = 0;
294             calculateTimeUnits();
295             if($scope.finishCallback) {
296               $scope.$eval($scope.finishCallback);
297             }
298             return;
299           }
300           calculateTimeUnits();
301
302           //We are not using $timeout for a reason. Please read here - https://github.com/siddii/angular-timer/pull/5
303           $scope.timeoutId = setTimeout(function () {
304             tick();
305             $scope.$digest();
306           }, $scope.interval - adjustment);
307
308           $scope.$emit('timer-tick', {timeoutId: $scope.timeoutId, millis: $scope.millis});
309
310           if ($scope.countdown > 0) {
311             $scope.countdown--;
312           }
313           else if ($scope.countdown <= 0) {
314             $scope.stop();
315             if($scope.finishCallback) {
316               $scope.$eval($scope.finishCallback);
317             }
318           }
319
320           if(typeTimer !== null){
321             //calculate progress bar
322             $scope.progressBar = progressBarService.calculateProgressBar($scope.startTime, $scope.millis, $scope.endTime, $scope.countdownattr);
323
324             if($scope.progressBar === 100){
325               $scope.displayProgressActive = ''; //No more Bootstrap active effect
326             }
327           }
328         };
329
330         if ($scope.autoStart === undefined || $scope.autoStart === true) {
331           $scope.start();
332         }
333       }]
334     };
335     }]);
336
337 /* commonjs package manager support (eg componentjs) */
338 if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
339   module.exports = timerModule;
340 }
341
342 var app = angular.module('timer');
343
344 app.factory('I18nService', function() {
345
346     var I18nService = function() {};
347
348     I18nService.prototype.language = 'en';
349     I18nService.prototype.fallback = 'en';
350     I18nService.prototype.timeHumanizer = {};
351
352     I18nService.prototype.init = function init(lang, fallback) {
353         var supported_languages = humanizeDuration.getSupportedLanguages();
354
355         this.fallback = (fallback !== undefined) ? fallback : 'en';
356         if (supported_languages.indexOf(fallback) === -1) {
357             this.fallback = 'en';
358         }
359
360         this.language = lang;
361         if (supported_languages.indexOf(lang) === -1) {
362             this.language = this.fallback;
363         }
364
365         //moment init
366         moment.locale(this.language); //@TODO maybe to remove, it should be handle by the user's application itself, and not inside the directive
367
368         //human duration init, using it because momentjs does not allow accurate time (
369         // momentJS: a few moment ago, human duration : 4 seconds ago
370         this.timeHumanizer = humanizeDuration.humanizer({
371             language: this.language,
372             halfUnit:false
373         });
374     };
375
376     /**
377      * get time with units from momentJS i18n
378      * @param {int} millis
379      * @returns {{millis: string, seconds: string, minutes: string, hours: string, days: string, months: string, years: string}}
380      */
381     I18nService.prototype.getTimeUnits = function getTimeUnits(millis) {
382         var diffFromAlarm = Math.round(millis/1000) * 1000; //time in milliseconds, get rid of the last 3 ms value to avoid 2.12 seconds display
383
384         var time = {};
385
386         if (typeof this.timeHumanizer != 'undefined'){
387             time = {
388                 'millis' : this.timeHumanizer(diffFromAlarm, { units: ["milliseconds"] }),
389                 'seconds' : this.timeHumanizer(diffFromAlarm, { units: ["seconds"] }),
390                 'minutes' : this.timeHumanizer(diffFromAlarm, { units: ["minutes", "seconds"] }) ,
391                 'hours' : this.timeHumanizer(diffFromAlarm, { units: ["hours", "minutes", "seconds"] }) ,
392                 'days' : this.timeHumanizer(diffFromAlarm, { units: ["days", "hours", "minutes", "seconds"] }) ,
393                 'months' : this.timeHumanizer(diffFromAlarm, { units: ["months", "days", "hours", "minutes", "seconds"] }) ,
394                 'years' : this.timeHumanizer(diffFromAlarm, { units: ["years", "months", "days", "hours", "minutes", "seconds"] })
395             };
396         }
397         else {
398             console.error('i18nService has not been initialized. You must call i18nService.init("en") for example');
399         }
400
401         return time;
402     };
403
404     return I18nService;
405 });
406
407 var app = angular.module('timer');
408
409 app.factory('progressBarService', function() {
410
411   var ProgressBarService = function() {};
412
413   /**
414    * calculate the remaining time in a progress bar in percentage
415    * @param {momentjs} startValue in seconds
416    * @param {integer} currentCountdown, where are we in the countdown
417    * @param {integer} remainingTime, remaining milliseconds
418    * @param {integer} endTime, end time, can be undefined
419    * @param {integer} coutdown, original coutdown value, can be undefined
420    *
421    * joke : https://www.youtube.com/watch?v=gENVB6tjq_M
422    * @return {float} 0 --> 100
423    */
424   ProgressBarService.prototype.calculateProgressBar = function calculateProgressBar(startValue, remainingTime, endTimeAttr, coutdown) {
425     var displayProgressBar = 0,
426       endTimeValue,
427       initialCountdown;
428
429     remainingTime = remainingTime / 1000; //seconds
430
431
432     if(endTimeAttr !== null){
433       endTimeValue = moment(endTimeAttr);
434       initialCountdown = endTimeValue.diff(startValue, 'seconds');
435       displayProgressBar = remainingTime * 100 / initialCountdown;
436     }
437     else {
438       displayProgressBar = remainingTime * 100 / coutdown;
439     }
440
441     displayProgressBar = 100 - displayProgressBar; //To have 0 to 100 and not 100 to 0
442     displayProgressBar = Math.round(displayProgressBar * 10) / 10; //learn more why : http://stackoverflow.com/questions/588004/is-floating-point-math-broken
443
444     if(displayProgressBar > 100){ //security
445       displayProgressBar = 100;
446     }
447
448     return displayProgressBar;
449   };
450
451   return new ProgressBarService();
452 });