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