Home | History | Annotate | Download | only in talking_alarm_clock
      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 
      5 var blankClockImage;
      6 var blankClockAnim1Image;
      7 var blankClockAnim2Image;
      8 var animationTimer;
      9 var currentClockImage;
     10 var port;
     11 
     12 function 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 
     36 function 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 
     54 function 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 
    122 function 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
    154 window.stopAlarmAnimation = function() {
    155   window.clearTimeout(animationTimer);
    156   currentClockImage = blankClockImage;
    157   drawClock();
    158   isAnimating = false;
    159 };
    160 
    161 // Override from common.js
    162 window.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 
    180 function 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 
    191 function 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 
    403 document.addEventListener('DOMContentLoaded', load);
    404