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.
5 goog.provide('cvox.ChromeVoxHTMLDateWidget');
8 * @fileoverview Gives the user spoken feedback as they interact with the date
9 * widget (input type=date).
14 * A class containing the information needed to speak
15 * a text change event to the user.
18 * @param {Element} dateElem The time widget element.
19 * @param {cvox.TtsInterface} tts The TTS object from ChromeVox.
21 cvox.ChromeVoxHTMLDateWidget = function(dateElem, tts){
24 * Currently selected field in the widget.
30 if (dateElem.type == 'month' || dateElem.type == 'week') {
34 * The maximum number of fields in the widget.
38 this.maxPos_ = maxpos;
40 * The HTML node of the widget.
44 this.dateElem_ = dateElem;
46 * A handle to the ChromeVox TTS object.
52 * The previous value of the year field.
58 * The previous value of the month field.
64 * The previous value of the week field.
70 * The previous value of the day field.
76 // Use listeners to make this work when running tests inside of ChromeVox.
77 this.keyListener_ = function(evt) {
78 self.eventHandler_(evt);
80 this.blurListener_ = function(evt) {
84 // Ensure we have a reasonable value to start with.
85 if (this.dateElem_.value.length == 0) {
86 this.forceInitTime_();
89 // Move the cursor to the first position so that we are guaranteed to start
90 // off at the hours position.
91 for (var i = 0; i < this.maxPos_; i++) {
92 var evt = document.createEvent('KeyboardEvent');
93 evt.initKeyboardEvent(
94 'keydown', true, true, window, 'Left', 0, false, false, false, false);
95 this.dateElem_.dispatchEvent(evt);
96 evt = document.createEvent('KeyboardEvent');
97 evt.initKeyboardEvent(
98 'keyup', true, true, window, 'Left', 0, false, false, false, false);
99 this.dateElem_.dispatchEvent(evt);
102 this.dateElem_.addEventListener('keydown', this.keyListener_, false);
103 this.dateElem_.addEventListener('keyup', this.keyListener_, false);
104 this.dateElem_.addEventListener('blur', this.blurListener_, false);
109 * Removes the key listeners for the time widget.
112 cvox.ChromeVoxHTMLDateWidget.prototype.shutdown = function() {
113 this.dateElem_.removeEventListener('blur', this.blurListener_, false);
114 this.dateElem_.removeEventListener('keydown', this.keyListener_, false);
115 this.dateElem_.removeEventListener('keyup', this.keyListener_, false);
119 * Forces a sensible default value so that there is something there that can
120 * be inspected with JS.
123 cvox.ChromeVoxHTMLDateWidget.prototype.forceInitTime_ = function() {
124 var currentDate = new Date();
125 var valueString = '';
126 var yearString = currentDate.getFullYear() + '';
127 // Date.getMonth starts at 0, but the value for the HTML5 date widget needs to
129 var monthString = currentDate.getMonth() + 1 + '';
130 if (monthString.length < 2) {
131 monthString = '0' + monthString; // Month format is MM.
133 var dayString = currentDate.getDate() + '';
135 switch (this.dateElem_.type) {
137 valueString = yearString + '-' + monthString;
140 // Based on info from: http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
141 currentDate.setHours(0,0,0);
142 // Set to nearest Thursday: current date + 4 - current day number
143 // Make Sunday's day number 7
145 currentDate.getDate() + 4 - (currentDate.getDay()||7));
146 // Get first day of year
147 var yearStart = new Date(currentDate.getFullYear(),0,1);
148 // Calculate full weeks to nearest Thursday
150 Math.ceil(( ( (currentDate - yearStart) / 86400000) + 1)/7) + '';
151 if (weekString.length < 2) {
152 weekString = '0' + weekString; // Week format is WXX.
154 weekString = 'W' + weekString;
155 valueString = yearString + '-' + weekString;
158 valueString = yearString + '-' + monthString + '-' + dayString;
161 this.dateElem_.setAttribute('value', valueString);
165 * Ensure that the position stays within bounds.
168 cvox.ChromeVoxHTMLDateWidget.prototype.handlePosChange_ = function() {
169 this.pos_ = Math.max(this.pos_, 0);
170 this.pos_ = Math.min(this.pos_, this.maxPos_);
171 // TODO (clchen, dtseng): Make this logic i18n once there is a way to
172 // determine what the date format actually is. For now, assume that:
173 // date == mm/dd/yyyy
178 if (this.dateElem_.type == 'week') {
185 if (this.dateElem_.type == 'date') {
198 * Speaks any changes to the control.
200 * @param {boolean} shouldSpeakLabel Whether or not to speak the label.
202 cvox.ChromeVoxHTMLDateWidget.prototype.update_ = function(shouldSpeakLabel) {
203 var splitDate = this.dateElem_.value.split("-");
204 if (splitDate.length < 1){
205 this.forceInitTime_();
214 year = parseInt(splitDate[0], 10);
216 if (this.dateElem_.type == 'week') {
217 week = parseInt(splitDate[1].replace('W', ''), 10);
218 } else if (this.dateElem_.type == 'date') {
219 month = parseInt(splitDate[1], 10);
220 day = parseInt(splitDate[2], 10);
222 month = parseInt(splitDate[1], 10);
225 var changeMessage = ''
227 if (shouldSpeakLabel) {
228 changeMessage = cvox.DomUtil.getName(this.dateElem_, true, true) + '\n';
231 if (week != this.pWeek_) {
232 changeMessage = changeMessage +
233 cvox.ChromeVox.msgs.getMsg('datewidget_week') + week + '\n';
237 if (month != this.pMonth_) {
241 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_january');
244 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_february');
247 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_march');
250 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_april');
253 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_may');
256 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_june');
259 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_july');
262 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_august');
265 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_september');
268 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_october');
271 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_november');
274 monthName = cvox.ChromeVox.msgs.getMsg('datewidget_december');
277 changeMessage = changeMessage + monthName + '\n';
278 this.pMonth_ = month;
281 if (day != this.pDay_) {
282 changeMessage = changeMessage + day + '\n';
286 if (year != this.pYear_) {
287 changeMessage = changeMessage + year + '\n';
291 if (changeMessage.length > 0) {
292 this.dateTts_.speak(changeMessage, 0, null);
297 * Handles user key events.
299 * @param {Event} evt The event to be handled.
301 cvox.ChromeVoxHTMLDateWidget.prototype.eventHandler_ = function(evt) {
302 var shouldSpeakLabel = false;
303 if (evt.type == 'keydown') {
304 // Handle tab/right arrow
305 if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) {
307 this.handlePosChange_();
308 shouldSpeakLabel = true;
310 // Handle shift+tab/left arrow
311 if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) {
313 this.handlePosChange_();
314 shouldSpeakLabel = true;
316 // For all other cases, fall through and let update_ decide if there are any
317 // changes that need to be spoken.
319 this.update_(shouldSpeakLabel);