1 // Copyright 2014 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 goog.provide('cvox.ChromeVoxHTMLTimeWidget'); 6 7 /** 8 * @fileoverview Gives the user spoken feedback as they interact with the time 9 * widget (input type=time). 10 * 11 */ 12 13 /** 14 * A class containing the information needed to speak 15 * a text change event to the user. 16 * 17 * @constructor 18 * @param {Element} timeElem The time widget element. 19 * @param {cvox.TtsInterface} tts The TTS object from ChromeVox. 20 */ 21 cvox.ChromeVoxHTMLTimeWidget = function(timeElem, tts){ 22 var self = this; 23 this.timeElem_ = timeElem; 24 this.timeTts_ = tts; 25 this.pHours_ = -1; 26 this.pMinutes_ = -1; 27 this.pSeconds_ = 0; 28 this.pMilliseconds_ = 0; 29 this.pAmpm_ = ''; 30 this.pos_ = 0; 31 this.maxPos_ = 2; 32 this.keyListener_ = function(evt) { 33 self.eventHandler_(evt); 34 } 35 this.blurListener_ = function(evt) { 36 self.shutdown(); 37 } 38 if (this.timeElem_.hasAttribute('step')) { 39 var step = this.timeElem_.getAttribute('step'); 40 if (step > 0) { // 0 or invalid values show hh:mm AM/PM 41 if (step >= 1) { 42 this.maxPos_ = 3; // Anything larger than 1 shows hh:mm:ss AM/PM 43 } else { 44 this.maxPos_ = 4; // Anything less than 1 shows hh:mm:ss.ms AM/PM 45 } 46 } 47 } 48 49 // Ensure we have a reasonable value to start with. 50 if (this.timeElem_.value.length == 0) { 51 this.forceInitTime_(); 52 } 53 54 // Move the cursor to the first position so that we are guaranteed to start 55 // off at the hours position. 56 for (var i = 0; i < this.maxPos_; i++) { 57 var evt = document.createEvent('KeyboardEvent'); 58 evt.initKeyboardEvent( 59 'keydown', true, true, window, 'Left', 0, false, false, false, false); 60 this.timeElem_.dispatchEvent(evt); 61 evt = document.createEvent('KeyboardEvent'); 62 evt.initKeyboardEvent( 63 'keyup', true, true, window, 'Left', 0, false, false, false, false); 64 this.timeElem_.dispatchEvent(evt); 65 } 66 67 this.timeElem_.addEventListener('keydown', this.keyListener_, false); 68 this.timeElem_.addEventListener('keyup', this.keyListener_, false); 69 this.timeElem_.addEventListener('blur', this.blurListener_, false); 70 this.update_(true); 71 }; 72 73 /** 74 * Removes the key listeners for the time widget. 75 * 76 */ 77 cvox.ChromeVoxHTMLTimeWidget.prototype.shutdown = function() { 78 this.timeElem_.removeEventListener('blur', this.blurListener_, false); 79 this.timeElem_.removeEventListener('keydown', this.keyListener_, false); 80 this.timeElem_.removeEventListener('keyup', this.keyListener_, false); 81 }; 82 83 cvox.ChromeVoxHTMLTimeWidget.prototype.forceInitTime_ = function() { 84 this.timeElem_.setAttribute('value', '12:00'); 85 }; 86 87 cvox.ChromeVoxHTMLTimeWidget.prototype.handlePosChange_ = function() { 88 if (this.pos_ < 0){ 89 this.pos_ = 0; 90 } 91 if (this.pos_ > this.maxPos_){ 92 this.pos_ = this.maxPos_; 93 } 94 // Reset the cached state of the new field so that the field will be spoken 95 // in the update. 96 if (this.pos_ == this.maxPos_){ 97 this.pAmpm_ = ''; 98 return; 99 } 100 switch (this.pos_){ 101 case 0: 102 this.pHours_ = -1; 103 break; 104 case 1: 105 this.pMinutes_ = -1; 106 break; 107 case 2: 108 this.pSeconds_ = -1; 109 break; 110 case 3: 111 this.pMilliseconds_ = -1; 112 break; 113 } 114 }; 115 116 117 cvox.ChromeVoxHTMLTimeWidget.prototype.update_ = function(shouldSpeakLabel) { 118 var splitTime = this.timeElem_.value.split(":"); 119 if (splitTime.length < 1){ 120 this.forceInitTime_(); 121 return; 122 } 123 124 var hours = splitTime[0]; 125 var minutes = -1; 126 var seconds = 0; 127 var milliseconds = 0; 128 var ampm = cvox.ChromeVox.msgs.getMsg('timewidget_am'); 129 if (splitTime.length > 1) { 130 minutes = splitTime[1]; 131 } 132 if (splitTime.length > 2) { 133 var splitSecondsAndMilliseconds = splitTime[2].split('.'); 134 seconds = splitSecondsAndMilliseconds[0]; 135 if (splitSecondsAndMilliseconds.length > 1){ 136 milliseconds = splitSecondsAndMilliseconds[1]; 137 } 138 } 139 if (hours > 12) { 140 hours = hours - 12; 141 ampm = cvox.ChromeVox.msgs.getMsg('timewidget_pm'); 142 } 143 if (hours == 12) { 144 ampm = cvox.ChromeVox.msgs.getMsg('timewidget_pm'); 145 } 146 if (hours == 0) { 147 hours = 12; 148 ampm = cvox.ChromeVox.msgs.getMsg('timewidget_am'); 149 } 150 151 var changeMessage = ''; 152 153 if (shouldSpeakLabel) { 154 changeMessage = cvox.DomUtil.getName(this.timeElem_, true, true) + '\n'; 155 } 156 157 if (hours != this.pHours_) { 158 changeMessage = changeMessage + hours + ' ' + 159 cvox.ChromeVox.msgs.getMsg('timewidget_hours') + '\n'; 160 this.pHours_ = hours; 161 } 162 163 if (minutes != this.pMinutes_) { 164 changeMessage = changeMessage + minutes + ' ' + 165 cvox.ChromeVox.msgs.getMsg('timewidget_minutes') + '\n'; 166 this.pMinutes_ = minutes; 167 } 168 169 if (seconds != this.pSeconds_) { 170 changeMessage = changeMessage + seconds + ' ' + 171 cvox.ChromeVox.msgs.getMsg('timewidget_seconds') + '\n'; 172 this.pSeconds_ = seconds; 173 } 174 175 if (milliseconds != this.pMilliseconds_) { 176 changeMessage = changeMessage + milliseconds + ' ' + 177 cvox.ChromeVox.msgs.getMsg('timewidget_milliseconds') + '\n'; 178 this.pMilliseconds_ = milliseconds; 179 } 180 181 if (ampm != this.pAmpm_) { 182 changeMessage = changeMessage + ampm; 183 this.pAmpm_ = ampm; 184 } 185 186 if (changeMessage.length > 0) { 187 this.timeTts_.speak(changeMessage, 0, null); 188 } 189 }; 190 191 cvox.ChromeVoxHTMLTimeWidget.prototype.eventHandler_ = function(evt) { 192 var shouldSpeakLabel = false; 193 if (evt.type == 'keydown') { 194 if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) { 195 this.pos_++; 196 this.handlePosChange_(); 197 shouldSpeakLabel = true; 198 } 199 if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) { 200 this.pos_--; 201 this.handlePosChange_(); 202 shouldSpeakLabel = true; 203 } 204 } 205 this.update_(shouldSpeakLabel); 206 }; 207