1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5var blankClockImage;
6var blankClockAnim1Image;
7var blankClockAnim2Image;
8var animationTimer;
9var currentClockImage;
10var port;
11
12function updateEnabledStatus(alarm) {
13  var enabled = $('a' + alarm + '_on').checked;
14  $('a' + alarm + '_tt').disabled = !enabled;
15  $('a' + alarm + '_ampm').disabled = !enabled;
16  var valid = true;
17  try {
18    var tt = $('a' + alarm + '_tt').value;
19    var ampm = $('a' + alarm + '_ampm').selectedIndex;
20    parseTime(tt, ampm);
21  } catch (x) {
22    valid = false;
23  }
24  if (valid) {
25    $('a' + alarm + '_wrap').removeAttribute('aria-invalid');
26  } else {
27    $('a' + alarm + '_wrap').setAttribute('aria-invalid', 'true');
28  }
29  if (enabled) {
30    $('a' + alarm + '_wrap').classList.remove('disabled');
31  } else {
32    $('a' + alarm + '_wrap').classList.add('disabled');
33  }
34}
35
36function loadAllImages() {
37  var loadCount = 0;
38  var img = new Image();
39  img.onload = function() {
40    blankClockImage = img;
41    currentClockImage = blankClockImage;
42    drawClock();
43  };
44  img.src = 'blank-clock-150.png';
45
46  // These will finish loading before they're needed, no need
47  // for an onload handler.
48  blankClockAnim1Image = new Image();
49  blankClockAnim1Image.src = 'blank-clock-ring1-150.png';
50  blankClockAnim2Image = new Image();
51  blankClockAnim2Image.src = 'blank-clock-ring2-150.png';
52}
53
54function drawClock(hh, mm, ss) {
55  if (hh == undefined || mm == undefined) {
56    var d = new Date();
57    hh = d.getHours();
58    mm = d.getMinutes();
59    ss = d.getSeconds() + 0.001 * d.getMilliseconds();
60  }
61
62  if (!currentClockImage) {
63    loadAllImages();
64    return;
65  }
66
67  var ctx = $('clock').getContext('2d');
68  ctx.drawImage(currentClockImage, 0, 0);
69
70  // Move the hour by the fraction of the minute
71  hh = (hh % 12) + (mm / 60);
72
73  // Move the minute by the fraction of the second
74  mm += (ss / 60);
75
76  var hourAngle = Math.PI * hh / 6;
77  var hourX = Math.sin(hourAngle);
78  var hourY = -Math.cos(hourAngle);
79  var minAngle = Math.PI * mm / 30;
80  var minX = Math.sin(minAngle);
81  var minY = -Math.cos(minAngle);
82  var secAngle = Math.PI * ss / 30;
83  var secX = Math.sin(secAngle);
84  var secY = -Math.cos(secAngle);
85
86  var cx = 75;
87  var cy = 77;
88
89  ctx.lineWidth = 5;
90  ctx.strokeStyle = '#ffffff';
91  ctx.globalAlpha = 0.5;
92  ctx.beginPath();
93  ctx.moveTo(cx - 4 * hourX, cy - 4 * hourY);
94  ctx.lineTo(cx + 20 * hourX, cy + 20 * hourY);
95  ctx.stroke();
96  ctx.beginPath();
97  ctx.moveTo(cx - 8 * minX, cy - 8 * minY);
98  ctx.lineTo(cx + 35 * minX, cy + 33 * minY);
99  ctx.stroke();
100
101  ctx.lineWidth = 3;
102  ctx.strokeStyle = '#696969';
103  ctx.globalAlpha = 1.0;
104  ctx.beginPath();
105  ctx.moveTo(cx - 4 * hourX, cy - 4 * hourY);
106  ctx.lineTo(cx + 20 * hourX, cy + 20 * hourY);
107  ctx.stroke();
108  ctx.beginPath();
109  ctx.moveTo(cx - 8 * minX, cy - 8 * minY);
110  ctx.lineTo(cx + 35 * minX, cy + 33 * minY);
111  ctx.stroke();
112
113  ctx.lineWidth = 1;
114  ctx.strokeStyle = '#990000';
115  ctx.globalAlpha = 1.0;
116  ctx.beginPath();
117  ctx.moveTo(cx - 4 * secX, cy - 4 * secY);
118  ctx.lineTo(cx + 40 * secX, cy + 40 * secY);
119  ctx.stroke();
120}
121
122function updateCurrentTime() {
123  var now = new Date();
124  var hh = now.getHours();
125  var mm = now.getMinutes();
126  var ss = now.getSeconds();
127  var str = '';
128  if (hh % 12 == 0) {
129    str += '12';
130  } else {
131    str += (hh % 12);
132  }
133  str += ':';
134  if (mm >= 10) {
135    str += mm;
136  } else {
137    str += '0' + mm;
138  }
139  str += ':';
140  if (ss >= 10) {
141    str += ss;
142  } else {
143    str += '0' + ss;
144  }
145  if (hh >= 12) {
146    str += " PM";
147  } else {
148    str += " AM";
149  }
150  $('current_time').innerText = str;
151}
152
153// Override from common.js
154window.stopAlarmAnimation = function() {
155  window.clearTimeout(animationTimer);
156  currentClockImage = blankClockImage;
157  drawClock();
158  isAnimating = false;
159};
160
161// Override from common.js
162window.displayAlarmAnimation = function() {
163  isAnimating = true;
164  var rings = 100;
165  function ring() {
166    if (rings == 0) {
167      stopAlarmAnimation();
168      return;
169    }
170    currentClockImage = (rings % 2 == 0)?
171                        blankClockAnim1Image:
172                        blankClockAnim2Image;
173    drawClock();
174    rings--;
175    animationTimer = window.setTimeout(ring, 50);
176  }
177  ring();
178};
179
180function addOutlineStyleListeners() {
181  document.addEventListener('click', function(evt) {
182    document.body.classList.add('nooutline');
183    return true;
184  }, true);
185  document.addEventListener('keydown', function(evt) {
186    document.body.classList.remove('nooutline');
187    return true;
188  }, true);
189}
190
191function load() {
192  try {
193    port = chrome.runtime.connect();
194    port.onMessage.addListener(function(msg) {
195      if (msg.cmd == 'anim') {
196        displayAlarmAnimation();
197      }
198    });
199  } catch (e) {
200  }
201
202  addOutlineStyleListeners();
203
204  stopAll();
205  drawClock();
206  setInterval(drawClock, 100);
207
208  updateCurrentTime();
209  setInterval(updateCurrentTime, 250);
210
211  function updateTime(timeElement) {
212    if (!parseTime(timeElement.value)) {
213      return false;
214    }
215
216    timeElement.valueAsNumber =
217        timeElement.valueAsNumber % (12 * 60 * 60 * 1000);
218    if (timeElement.valueAsNumber < (1 * 60 * 60 * 1000))
219      timeElement.valueAsNumber += (12 * 60 * 60 * 1000);
220    return true;
221  }
222
223  $('clock').addEventListener('click', function(evt) {
224    if (isPlaying || isSpeaking || isAnimating) {
225      stopAll();
226    } else {
227      ringAlarmWithCurrentTime();
228    }
229  }, false);
230  $('clock').addEventListener('keydown', function(evt) {
231    if (evt.keyCode == 13 || evt.keyCode == 32) {
232      if (isPlaying || isSpeaking || isAnimating) {
233        stopAll();
234      } else {
235        ringAlarmWithCurrentTime();
236      }
237    }
238  }, false);
239
240  // Alarm 1
241
242  var a1_tt = localStorage['a1_tt'] || DEFAULT_A1_TT;
243  $('a1_tt').value = a1_tt;
244  $('a1_tt').addEventListener('input', function(evt) {
245    updateEnabledStatus(1);
246    if (!updateTime($('a1_tt'))) {
247      evt.stopPropagation();
248      return false;
249    }
250    localStorage['a1_tt'] = $('a1_tt').value;
251    updateEnabledStatus(1);
252    return true;
253  }, false);
254  $('a1_tt').addEventListener('change', function(evt) {
255    if ($('a1_tt').value.length == 4 &&
256        parseTime('0' + $('a1_tt').value)) {
257      $('a1_tt').value = '0' + $('a1_tt').value;
258    }
259    if (!updateTime($('a1_tt'))) {
260      evt.stopPropagation();
261      return false;
262    }
263    localStorage['a1_tt'] = $('a1_tt').value;
264    updateEnabledStatus(1);
265    return true;
266  }, false);
267
268  var a1_on = (localStorage['a1_on'] == 'true');
269  $('a1_on').checked = a1_on;
270  $('a1_on').addEventListener('change', function(evt) {
271    window.setTimeout(function() {
272      localStorage['a1_on'] = $('a1_on').checked;
273      updateEnabledStatus(1);
274    }, 0);
275  }, false);
276
277  var a1_ampm = localStorage['a1_ampm'] || DEFAULT_A1_AMPM;
278  $('a1_ampm').selectedIndex = a1_ampm;
279  $('a1_ampm').addEventListener('change', function(evt) {
280    localStorage['a1_ampm'] = $('a1_ampm').selectedIndex;
281  }, false);
282
283  updateEnabledStatus(1);
284
285  // Alarm 2
286
287  var a2_tt = localStorage['a2_tt'] || DEFAULT_A2_TT;
288  $('a2_tt').value = a2_tt;
289  $('a2_tt').addEventListener('input', function(evt) {
290    updateEnabledStatus(2);
291    if (!updateTime($('a2_tt'))) {
292      evt.stopPropagation();
293      return false;
294    }
295    localStorage['a2_tt'] = $('a2_tt').value;
296    updateEnabledStatus(2);
297    return true;
298  }, false);
299  $('a2_tt').addEventListener('change', function(evt) {
300    if ($('a2_tt').value.length == 4 &&
301        parseTime('0' + $('a2_tt').value)) {
302      $('a2_tt').value = '0' + $('a2_tt').value;
303    }
304    if (!updateTime($('a2_tt'))) {
305      evt.stopPropagation();
306      return false;
307    }
308    localStorage['a2_tt'] = $('a2_tt').value;
309    updateEnabledStatus(2);
310    return true;
311  }, false);
312
313  var a2_on = (localStorage['a2_on'] == 'true');
314  $('a2_on').checked = a2_on;
315  $('a2_on').addEventListener('change', function(evt) {
316    window.setTimeout(function() {
317      localStorage['a2_on'] = $('a2_on').checked;
318      updateEnabledStatus(2);
319    }, 0);
320  }, false);
321
322  var a2_ampm = localStorage['a2_ampm'] || DEFAULT_A2_AMPM;
323  $('a2_ampm').selectedIndex = a2_ampm;
324  $('a2_ampm').addEventListener('change', function(evt) {
325    localStorage['a2_ampm'] = $('a2_ampm').selectedIndex;
326  }, false);
327
328  updateEnabledStatus(2);
329
330  // Phrase
331
332  var phrase = localStorage['phrase'] || DEFAULT_PHRASE;
333  $('phrase').value = phrase;
334  $('phrase').addEventListener('change', function(evt) {
335    localStorage['phrase'] = $('phrase').value;
336  }, false);
337
338  // Speech parameters
339
340  var rateElement = $('rate');
341  var volumeElement = $('volume');
342  var rate = localStorage['rate'] || DEFAULT_RATE;
343  var volume = localStorage['volume'] || DEFAULT_VOLUME;
344  rateElement.value = rate;
345  volumeElement.value = volume;
346  function listener(evt) {
347    rate = rateElement.value;
348    localStorage['rate'] = rate;
349    volume = volumeElement.value;
350    localStorage['volume'] = volume;
351  }
352  rateElement.addEventListener('keyup', listener, false);
353  volumeElement.addEventListener('keyup', listener, false);
354  rateElement.addEventListener('mouseup', listener, false);
355  volumeElement.addEventListener('mouseup', listener, false);
356
357  var sound = $('sound');
358  var currentSound = localStorage['sound'] || DEFAULT_SOUND;
359  for (var i = 0; i < sound.options.length; i++) {
360    if (sound.options[i].value == currentSound) {
361      sound.selectedIndex = i;
362      break;
363    }
364  }
365  localStorage['sound'] = sound.options[sound.selectedIndex].value;
366  sound.addEventListener('change', function() {
367    localStorage['sound'] = sound.options[sound.selectedIndex].value;
368  }, false);
369
370  var playSoundButton = $('playsound');
371  playSoundButton.addEventListener('click', function(evt) {
372    playSound(false);
373  });
374
375  var playSpeechButton = $('playspeech');
376  playSpeechButton.addEventListener('click', function(evt) {
377    speakPhraseWithCurrentTime();
378  });
379
380  var voice = $('voice');
381  var voiceArray = [];
382  if (chrome && chrome.tts) {
383    chrome.tts.getVoices(function(va) {
384      voiceArray = va;
385      for (var i = 0; i < voiceArray.length; i++) {
386        var opt = document.createElement('option');
387        var name = voiceArray[i].voiceName;
388        if (name == localStorage['voice']) {
389          opt.setAttribute('selected', '');
390        }
391        opt.setAttribute('value', name);
392        opt.innerText = voiceArray[i].voiceName;
393        voice.appendChild(opt);
394      }
395    });
396  }
397  voice.addEventListener('change', function() {
398    var i = voice.selectedIndex;
399    localStorage['voice'] = voiceArray[i].voiceName;
400  }, false);
401}
402
403document.addEventListener('DOMContentLoaded', load);
404