]> code.delx.au - countdown-timer/blob - timer.js
Initial commit
[countdown-timer] / timer.js
1 $(function(){
2
3 var theTimeoutSecs = null;
4 var theTimeoutEntry = null;
5
6 var theCurrentTimeoutSecs = null;
7 var theCurrentEndTimeSecs = null;
8 var theCurrentTimerId = null;
9
10 var elBody = $("body");
11 var elDisplay = $("#display");
12 var elCountdownControls = $("#countdown-controls");
13 var elConfigureControls = $("#configure-controls");
14 var audio = $("#alarm audio").get(0);
15
16
17 var flashElement = function(el) {
18 el.css("opacity", "0.5");
19 el.animate({"opacity": "1.0"}, {"duration": 200, "queue": false});
20 }
21
22 var setState = function(state) {
23 elBody.removeClass().addClass(state);
24 }
25
26 var addState = function(state) {
27 elBody.addClass(state);
28 };
29
30 var removeState = function(state) {
31 elBody.removeClass(state);
32 }
33
34 var padTwoDigits = function(x) {
35 if (x < 10) {
36 return "0" + x;
37 } else {
38 return x;
39 }
40 };
41
42 var formatTimeHMS = function(seconds, minutes, hours) {
43 return (
44 padTwoDigits(hours) +
45 ":" +
46 padTwoDigits(minutes) +
47 ":" +
48 padTwoDigits(seconds)
49 );
50 };
51
52 var getNowSecs = function() {
53 return Math.round(new Date().getTime() / 1000);
54 };
55
56 var updateTimeoutSecs = function() {
57 var seconds = theTimeoutEntry % 100;
58 var minutes = Math.floor(theTimeoutEntry / 100) % 100;
59 var hours = Math.floor(theTimeoutEntry / 10000) % 10000;
60 theTimeoutSecs = seconds + minutes*60 + hours*3600;
61
62 var text = formatTimeHMS(seconds, minutes, hours);
63 fillDisplay(text);
64 };
65
66 var fillDisplay = function(s) {
67 elDisplay.children().remove();
68 for (var i = 0; i < s.length; ++i) {
69 $("<span/>").text(s[i]).appendTo(elDisplay);
70 }
71 }
72
73 var formatTimeSecs = function(tsec) {
74 var seconds = tsec % 60;
75 var minutes = Math.floor(tsec / 60);
76 var hours = Math.floor(theTimeoutEntry / 3600);
77 return formatTimeHMS(seconds, minutes, hours);
78 };
79
80 var beginCountdownMode = function() {
81 if (theTimeoutSecs <= 0) {
82 beginConfigureMode();
83 return;
84 }
85 setState("countdown");
86 resetCountdown();
87 flashElement(elConfigureControls);
88 };
89
90 var beginConfigureMode = function() {
91 stopCountdown();
92 stopAlarm();
93 setState("configure");
94 resetConfigureMode();
95 flashElement(elCountdownControls);
96 };
97
98 var resetConfigureMode = function() {
99 theTimeoutSecs = 0;
100 theTimeoutEntry = 0;
101 updateTimeoutSecs();
102 flashElement(elBody);
103 };
104
105 var processDigitInput = function(newDigit) {
106 if (theTimeoutEntry >= 100000) {
107 // ignore too much input
108 return;
109 }
110 var newDigit = parseInt(newDigit);
111 theTimeoutEntry = theTimeoutEntry * 10 + newDigit;
112 updateTimeoutSecs();
113 }
114
115 var resetCountdown = function() {
116 stopCountdown();
117 theCurrentTimeoutSecs = theTimeoutSecs;
118 fillDisplay(formatTimeSecs(theCurrentTimeoutSecs));
119 flashElement(elDisplay);
120 };
121
122 var refreshCountdown = function() {
123 var remainingSecs = theCurrentEndTimeSecs - getNowSecs();
124 if (remainingSecs <= 0) {
125 remainingSecs = 0;
126 stopCountdown();
127 runAlarm();
128 }
129 fillDisplay(formatTimeSecs(remainingSecs));
130 };
131
132 var runAlarm = function() {
133 addState("alarm");
134 audio.play();
135 };
136
137 var stopAlarm = function() {
138 if (audio.duration > 0 && !audio.paused) {
139 audio.pause();
140 audio.currentTime = 0;
141 }
142 removeState("alarm");
143 resetCountdown();
144 };
145
146 var runCountdown = function() {
147 if (theCurrentTimerId != null) {
148 return;
149 }
150 theCurrentTimerId = setInterval(refreshCountdown, 100);
151 theCurrentEndTimeSecs = getNowSecs() + theCurrentTimeoutSecs;
152 addState("counting");
153 };
154
155 var stopCountdown = function() {
156 if (theCurrentTimerId == null) {
157 return;
158 }
159 clearInterval(theCurrentTimerId);
160 theCurrentTimerId = null;
161 removeState("counting");
162 };
163
164 var pauseCountdown = function() {
165 stopCountdown();
166 theCurrentTimeoutSecs = theCurrentEndTimeSecs - getNowSecs();
167 };
168
169
170 ////////////////////
171 // Event handlers //
172 ////////////////////
173
174 var handleConfigureKey = function(e) {
175 // Return/Enter/Space
176 if (e.which == 13 || e.which == 32) {
177 e.preventDefault();
178 beginCountdownMode();
179 return;
180 }
181
182 // Backspace/Delete/Escape
183 if (e.which == 8 || e.which == 46 || e.which == 27) {
184 e.preventDefault();
185 resetConfigureMode();
186 return;
187 }
188
189 var c = String.fromCharCode(e.which);
190 if ("0123456789".indexOf(c) >= 0) {
191 e.preventDefault();
192 processDigitInput(c);
193 return;
194 }
195 };
196
197 var handleNumberButton = function(e) {
198 e.preventDefault();
199 processDigitInput($(this).text());
200 };
201
202 var handleConfigureReset = function(e) {
203 e.preventDefault();
204 resetConfigureMode();
205 };
206
207 var handleConfigureSet = function(e) {
208 e.preventDefault();
209 beginCountdownMode();
210 };
211
212 var handleCountdownKey = function(e) {
213 if (e.which == 8 || e.which == 46) { // Backspace/Delete
214 e.preventDefault();
215 resetCountdown();
216 return;
217 }
218 if (e.which == 27) { // Escape
219 e.preventDefault();
220 beginConfigureMode();
221 }
222 };
223
224 var handleAlarmKey = function(e) {
225 // Return/Enter/Space
226 if (e.which == 13 || e.which == 32) {
227 e.preventDefault();
228 stopAlarm();
229 return;
230 }
231 };
232
233 var handleCountdownPlayKey = function(e) {
234 // Return/Enter/Space
235 if (e.which == 13 || e.which == 32) {
236 e.preventDefault();
237 runCountdown();
238 return false;
239 }
240 };
241
242 var handleCountdownPauseKey = function(e) {
243 // Return/Enter/Space
244 if (e.which == 13 || e.which == 32) {
245 e.preventDefault();
246 pauseCountdown();
247 return false;
248 }
249 };
250
251 var handleCountdownStart = function(e) {
252 e.preventDefault();
253 runCountdown();
254 };
255
256 var handleCountdownPause = function(e) {
257 e.preventDefault();
258 pauseCountdown();
259 };
260
261 var handleCountdownReset = function(e) {
262 e.preventDefault();
263 resetCountdown();
264 };
265
266 var handleCountdownReconfigure = function(e) {
267 e.preventDefault();
268 beginConfigureMode();
269 };
270
271
272 var init = function() {
273 $(document).on("keydown", ".configure", handleConfigureKey);
274 $("#number-buttons button").on("click", handleNumberButton);
275 $("#configure-reset").on("click", handleConfigureReset);
276 $("#configure-set").on("click", handleConfigureSet);
277
278 $(document).on("keydown", ".countdown", handleCountdownKey);
279 $(document).on("keydown", ".alarm", handleAlarmKey);
280 $(document).on("keydown", ".countdown:not(.counting):not(.alarm)", handleCountdownPlayKey);
281 $(document).on("keydown", ".counting", handleCountdownPauseKey);
282 $("#countdown-start").on("click", handleCountdownStart);
283 $("#countdown-pause").on("click", handleCountdownPause);
284 $("#countdown-reset").on("click", handleCountdownReset);
285 $("#countdown-reconfigure").on("click", handleCountdownReconfigure);
286
287 $("#alarm-stop").on("click", stopAlarm);
288 $(audio).on("ended", stopAlarm);
289
290 beginConfigureMode();
291 };
292
293 init();
294
295 });
296