├── .gitignore
├── README.md
├── js
├── app.js
└── pomodoro.js
├── templates
└── timer.html
├── bower.json
├── index.html
├── LICENSE
├── karma.conf.js
└── tests
├── pomodoroControllerSpec.js
└── pomodoroServiceSpec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pomodoro
2 | An angular js pomodoro
3 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | angular.module('foc-app', ['foc-pomodoro']);
--------------------------------------------------------------------------------
/templates/timer.html:
--------------------------------------------------------------------------------
1 | :
2 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pomodoro",
3 | "version": "0.0.0",
4 | "homepage": "https://github.com/drFabio/pomodoro",
5 | "authors": [
6 | "fabio "
7 | ],
8 | "license": "MIT",
9 | "ignore": [
10 | "**/.*",
11 | "node_modules",
12 | "bower_components",
13 | "test",
14 | "tests"
15 | ],
16 | "dependencies": {
17 | "angular": "~1.3.15"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 | Test
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Fabio Oliveira Costa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Tue May 19 2015 20:38:49 GMT-0300 (BRT)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine'],
14 |
15 |
16 | files: [
17 | './bower_components/angular/angular.min.js',
18 | './node_modules/angular-mocks/angular-mocks.js',
19 | './js/*.js',
20 | './tests/*Spec.js'
21 | // './tests/pomodoroServiceSpec.js'
22 | // './tests/pomodoroControllerSpec.js'
23 |
24 | ],
25 |
26 |
27 | // list of files to exclude
28 | exclude: [
29 | ],
30 |
31 |
32 | // preprocess matching files before serving them to the browser
33 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
34 | preprocessors: {
35 | },
36 |
37 |
38 | // test results reporter to use
39 | // possible values: 'dots', 'progress'
40 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
41 | reporters: ['progress'],
42 |
43 |
44 | // web server port
45 | port: 9876,
46 |
47 |
48 | // enable / disable colors in the output (reporters and logs)
49 | colors: true,
50 |
51 |
52 | // level of logging
53 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
54 | logLevel: config.LOG_INFO,
55 |
56 |
57 | // enable / disable watching file and executing tests whenever any file changes
58 | autoWatch: false,
59 |
60 |
61 | // start these browsers
62 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
63 | browsers: ['PhantomJS'],
64 |
65 |
66 | // Continuous Integration mode
67 | // if true, Karma captures browsers, runs the tests and exits
68 | singleRun: true
69 | });
70 | };
71 |
--------------------------------------------------------------------------------
/tests/pomodoroControllerSpec.js:
--------------------------------------------------------------------------------
1 | describe('PomodoroCtrl', function(){
2 | var scope; //we'll use this scope in our tests
3 | var ctrl;
4 | var theInterval;
5 | var rootScope;
6 | var mockPomodoroService={
7 | startPomodoro:function(){
8 | rootScope.$broadcast('focNewPomodoroStarted','task',25);
9 | },
10 | getStatus:function(){
11 | return 'task';
12 | },
13 | getStatusDuration:function(){
14 | return 25;
15 | },
16 | getEllapsedSeconds:function(){
17 | return 0;
18 | },
19 | getEllapsedMinutes:function(){
20 | return 0;
21 | },
22 |
23 | };
24 | // mock Application to allow us to inject our own dependencies
25 | beforeEach(angular.mock.module('foc-pomodoro'));
26 | //mock the controller for the same reason and include $rootScope and $controller
27 | beforeEach(angular.mock.inject(function($rootScope, $controller,$interval){
28 | theInterval=$interval;
29 | //create an empty scope
30 | scope = $rootScope.$new();
31 | rootScope=$rootScope;
32 | //declare the controller and inject our empty scope
33 | ctrl=$controller('PomodoroCtrl', {$scope: scope,$interval:$interval,focPomodoroService:mockPomodoroService});
34 | }));
35 |
36 | // tests start here
37 | it('Starst a timer on count', function(){
38 | spyOn(mockPomodoroService, 'startPomodoro').and.callThrough();
39 | ctrl.start();
40 | expect(mockPomodoroService.startPomodoro).toHaveBeenCalled();
41 | theInterval.flush(1200);
42 |
43 | expect(ctrl.minutes).toEqual(0);
44 | expect(ctrl.seconds).toEqual(1);
45 | });
46 |
47 | it('Stops counting when the minute limit is reached',function(){
48 | ctrl.start();
49 | var duration=mockPomodoroService.getStatusDuration();
50 | theInterval.flush(duration*1000*60+200);
51 | expect(ctrl.seconds).toEqual(0);
52 | expect(ctrl.minutes).toEqual(duration);
53 | });
54 |
55 | it('Starts counting when a new event is triggered ',function(){
56 | spyOn(ctrl,'startCount');
57 | rootScope.$broadcast('focNewPomodoroStarted','task',25);
58 | expect(ctrl.startCount).toHaveBeenCalled();
59 | });
60 | // it('Stops the count when it\'s cancelled',function(){
61 |
62 | // });
63 |
64 |
65 | });
--------------------------------------------------------------------------------
/tests/pomodoroServiceSpec.js:
--------------------------------------------------------------------------------
1 | describe('Pomodoro Service',function(){
2 | var pomodoroService;
3 | var scope;
4 | var timeout;
5 | describe('Default config',function(){
6 | beforeEach(function(){
7 | angular.mock.module('foc-pomodoro');
8 | angular.mock.inject(function($rootScope,$timeout,$injector,focPomodoroService){
9 | scope = $rootScope;
10 | timeout=$timeout;
11 | pomodoroService=focPomodoroService;
12 | });
13 | });
14 | it('Starts as a task state ',function(){
15 | expect(pomodoroService.getStatus()).toEqual('task');
16 | });
17 | it('Have the default task duration of 25 minutes ',function(){
18 | pomodoroService._setStatus('task');
19 | expect(pomodoroService.getStatusDuration()).toEqual(25);
20 | });
21 |
22 | it('Have the default long break duration of 15 minutes ',function(){
23 | pomodoroService._setStatus('long_break');
24 | expect(pomodoroService.getStatusDuration()).toEqual(15);
25 | });
26 | it('Have the default short break duration of 5 minutes ',function(){
27 | pomodoroService._setStatus('short_break');
28 | expect(pomodoroService.getStatusDuration()).toEqual(5);
29 | });
30 | it('Follows the cycle as Task,Short,Short,Short,Break',function(){
31 | var expectedResults=['task','short_break','task','short_break','task','short_break','task','long_break'];
32 | var expectedStatus;
33 | var actualStatus;
34 | for(var j=0;j<2;j++){
35 | for(var i=0;i=60){
33 | self.seconds=0;
34 | self.minutes++;
35 | }
36 | if(self.minutes>=self.statusDuration){
37 | self.stopCount();
38 | }
39 | }, 1000);
40 | };
41 | this.stopCount = function() {
42 | if (angular.isDefined(stop)) {
43 | $interval.cancel(stop);
44 | stop = undefined;
45 | }
46 | };
47 | $scope.$on("$destroy", function() {
48 | self.stopCount();
49 | unbindList.map(function(u){
50 | u();
51 | })
52 | });
53 | }
54 | function PomodoroService($rootScope,$timeout){
55 | this.currentStatus=TASK_STATUS;
56 | this.durations={};
57 | this.durations[TASK_STATUS]=25;
58 | this.durations[SHORT_BREAK_STATUS]=5;
59 | this.durations[LONG_BREAK_STAUS]=15;
60 | this.pomodoroCount=0;
61 | this.behaviour=AUTO_BEHAVIOUR;
62 | this._scope=$rootScope;
63 | this._timeout=$timeout;
64 | this._timer;
65 | this._timePomodoroStarted;
66 | }
67 | PomodoroService.prototype.getEllapsedSeconds = function() {
68 | var ellapsedSeconds=Math.round((new Date().getTime()-this._timePomodoroStarted)/1000);
69 | return ellapsedSeconds%60;
70 | };
71 | PomodoroService.prototype.getEllapsedMinutes = function() {
72 | var ellapsedSeconds=Math.round((new Date().getTime()-this._timePomodoroStarted)/1000);
73 | return Math.round(ellapsedSeconds/60);
74 | };
75 | PomodoroService.prototype.cancelPomodoro = function() {
76 | this.cancelTimer();
77 | var ellapsedSeconds=(new Date().getTime()-this._timePomodoroStarted)/1000;
78 | this._scope.$broadcast(POMODORO_CANCELLED_EVENT,this.getStatus(),this.getStatusDuration(),ellapsedSeconds);
79 | };
80 | PomodoroService.prototype.startPomodoro = function() {
81 | this.startCounting();
82 | this._scope.$broadcast(NEW_POMODORO_EVENT,this.getStatus(),this.getStatusDuration());
83 | };
84 | PomodoroService.prototype.startCounting = function() {
85 | this.cancelTimer();
86 | this._timePomodoroStarted=new Date().getTime();
87 | var self=this;
88 | this._timer=this._timeout(function(){
89 | self._timerFinished();
90 | },this.getStatusDuration()*60*1000);
91 | };
92 | PomodoroService.prototype._startNewEvent = function() {
93 | this.nextStatus();
94 | this.startPomodoro();
95 | };
96 | PomodoroService.prototype._timerFinished = function() {
97 | this._scope.$broadcast(POMODORO_FINISHED_EVENT,this.getStatus(),this.getStatusDuration());
98 | if(this.behaviour==AUTO_BEHAVIOUR){
99 | this._startNewEvent();
100 | }
101 | };
102 | PomodoroService.prototype.cancelTimer = function() {
103 | if (angular.isDefined(this._timer)) {
104 | this._timeout.cancel(this._timer);
105 | this._timer = undefined;
106 | }
107 | };
108 | PomodoroService.prototype.getStatus=function(){
109 | return this.currentStatus;
110 | };
111 | PomodoroService.prototype._setStatus=function(status){
112 | this.currentStatus=status;
113 | };
114 | PomodoroService.prototype.getStatusDuration=function(){
115 | return this.durations[this.getStatus()];
116 | };
117 | PomodoroService.prototype.nextStatus=function(){
118 | switch(this.getStatus()) {
119 | case TASK_STATUS:
120 | this.pomodoroCount++;
121 | if(this.pomodoroCount>=4){
122 | this.pomodoroCount=0;
123 | this._setStatus(LONG_BREAK_STAUS);
124 | }
125 | else{
126 | this._setStatus(SHORT_BREAK_STATUS);
127 | }
128 | break;
129 | case LONG_BREAK_STAUS:
130 | case SHORT_BREAK_STATUS:
131 | this._setStatus(TASK_STATUS);
132 | break;
133 | }
134 | return this.getStatus();
135 | };
136 | PomodoroService.prototype.setDurations = function(durations) {
137 | var allowedDurations=[TASK_STATUS,SHORT_BREAK_STATUS,LONG_BREAK_STAUS];
138 | var self=this;
139 | allowedDurations.forEach(function(a){
140 | self.durations[a]=durations[a];
141 | });
142 | };
143 | angular.module('foc-pomodoro', [])
144 | .service('focPomodoroService',['$rootScope','$timeout',PomodoroService])
145 | .controller('TasksCtrl', function($scope) {})
146 | .directive('pomodoroTimer', [function() {
147 | return {
148 | templateUrl:'/templates/timer.html',
149 | restrict: 'E',
150 | controllerAs:'pomodoroCtrl',
151 | controller:['$scope','$interval','focPomodoroService',pomodoroController]
152 | };
153 | }]
154 | );
--------------------------------------------------------------------------------