// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. goog.provide('cvox.ChromeVoxHTMLTimeWidget'); /** * @fileoverview Gives the user spoken feedback as they interact with the time * widget (input type=time). * */ /** * A class containing the information needed to speak * a text change event to the user. * * @constructor * @param {Element} timeElem The time widget element. * @param {cvox.TtsInterface} tts The TTS object from ChromeVox. */ cvox.ChromeVoxHTMLTimeWidget = function(timeElem, tts) { var self = this; this.timeElem_ = timeElem; this.timeTts_ = tts; this.pHours_ = -1; this.pMinutes_ = -1; this.pSeconds_ = 0; this.pMilliseconds_ = 0; this.pAmpm_ = ''; this.pos_ = 0; this.maxPos_ = 2; this.keyListener_ = function(evt) { self.eventHandler_(evt); }; this.blurListener_ = function(evt) { self.shutdown(); }; if (this.timeElem_.hasAttribute('step')) { var step = this.timeElem_.getAttribute('step'); if (step > 0) { // 0 or invalid values show hh:mm AM/PM if (step >= 1) { this.maxPos_ = 3; // Anything larger than 1 shows hh:mm:ss AM/PM } else { this.maxPos_ = 4; // Anything less than 1 shows hh:mm:ss.ms AM/PM } } } // Ensure we have a reasonable value to start with. if (this.timeElem_.value.length == 0) { this.forceInitTime_(); } // Move the cursor to the first position so that we are guaranteed to start // off at the hours position. for (var i = 0; i < this.maxPos_; i++) { var evt = document.createEvent('KeyboardEvent'); evt.initKeyboardEvent( 'keydown', true, true, window, 'Left', 0, false, false, false, false); this.timeElem_.dispatchEvent(evt); evt = document.createEvent('KeyboardEvent'); evt.initKeyboardEvent( 'keyup', true, true, window, 'Left', 0, false, false, false, false); this.timeElem_.dispatchEvent(evt); } this.timeElem_.addEventListener('keydown', this.keyListener_, false); this.timeElem_.addEventListener('keyup', this.keyListener_, false); this.timeElem_.addEventListener('blur', this.blurListener_, false); this.update_(true); }; /** * Removes the key listeners for the time widget. * */ cvox.ChromeVoxHTMLTimeWidget.prototype.shutdown = function() { this.timeElem_.removeEventListener('blur', this.blurListener_, false); this.timeElem_.removeEventListener('keydown', this.keyListener_, false); this.timeElem_.removeEventListener('keyup', this.keyListener_, false); }; /** * Initialize to midnight. * @private */ cvox.ChromeVoxHTMLTimeWidget.prototype.forceInitTime_ = function() { this.timeElem_.setAttribute('value', '12:00'); }; /** * Called when the position changes. * @private */ cvox.ChromeVoxHTMLTimeWidget.prototype.handlePosChange_ = function() { if (this.pos_ < 0) { this.pos_ = 0; } if (this.pos_ > this.maxPos_) { this.pos_ = this.maxPos_; } // Reset the cached state of the new field so that the field will be spoken // in the update. if (this.pos_ == this.maxPos_) { this.pAmpm_ = ''; return; } switch (this.pos_) { case 0: this.pHours_ = -1; break; case 1: this.pMinutes_ = -1; break; case 2: this.pSeconds_ = -1; break; case 3: this.pMilliseconds_ = -1; break; } }; /** * @param {boolean} shouldSpeakLabel True if the label should be spoken. * @private */ cvox.ChromeVoxHTMLTimeWidget.prototype.update_ = function(shouldSpeakLabel) { var splitTime = this.timeElem_.value.split(':'); if (splitTime.length < 1) { this.forceInitTime_(); return; } var hours = splitTime[0]; var minutes = -1; var seconds = 0; var milliseconds = 0; var ampm = cvox.ChromeVox.msgs.getMsg('timewidget_am'); if (splitTime.length > 1) { minutes = splitTime[1]; } if (splitTime.length > 2) { var splitSecondsAndMilliseconds = splitTime[2].split('.'); seconds = splitSecondsAndMilliseconds[0]; if (splitSecondsAndMilliseconds.length > 1) { milliseconds = splitSecondsAndMilliseconds[1]; } } if (hours > 12) { hours = hours - 12; ampm = cvox.ChromeVox.msgs.getMsg('timewidget_pm'); } if (hours == 12) { ampm = cvox.ChromeVox.msgs.getMsg('timewidget_pm'); } if (hours == 0) { hours = 12; ampm = cvox.ChromeVox.msgs.getMsg('timewidget_am'); } var changeMessage = ''; if (shouldSpeakLabel) { changeMessage = cvox.DomUtil.getName(this.timeElem_, true, true) + '\n'; } if (hours != this.pHours_) { changeMessage = changeMessage + hours + ' ' + cvox.ChromeVox.msgs.getMsg('timewidget_hours') + '\n'; this.pHours_ = hours; } if (minutes != this.pMinutes_) { changeMessage = changeMessage + minutes + ' ' + cvox.ChromeVox.msgs.getMsg('timewidget_minutes') + '\n'; this.pMinutes_ = minutes; } if (seconds != this.pSeconds_) { changeMessage = changeMessage + seconds + ' ' + cvox.ChromeVox.msgs.getMsg('timewidget_seconds') + '\n'; this.pSeconds_ = seconds; } if (milliseconds != this.pMilliseconds_) { changeMessage = changeMessage + milliseconds + ' ' + cvox.ChromeVox.msgs.getMsg('timewidget_milliseconds') + '\n'; this.pMilliseconds_ = milliseconds; } if (ampm != this.pAmpm_) { changeMessage = changeMessage + ampm; this.pAmpm_ = ampm; } if (changeMessage.length > 0) { this.timeTts_.speak(changeMessage, cvox.QueueMode.FLUSH, null); } }; /** * @param {Object} evt The event to handle. * @private */ cvox.ChromeVoxHTMLTimeWidget.prototype.eventHandler_ = function(evt) { var shouldSpeakLabel = false; if (evt.type == 'keydown') { if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) { this.pos_++; this.handlePosChange_(); shouldSpeakLabel = true; } if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) { this.pos_--; this.handlePosChange_(); shouldSpeakLabel = true; } } this.update_(shouldSpeakLabel); };