Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / common / date_widget.js
blobc4a53c0252dd25a0ac954485be7bd5c82a462527
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');
7 /**
8  * @fileoverview Gives the user spoken feedback as they interact with the date
9  * widget (input type=date).
10  *
11  */
13 /**
14  * A class containing the information needed to speak
15  * a text change event to the user.
16  *
17  * @constructor
18  * @param {Element} dateElem The time widget element.
19  * @param {cvox.TtsInterface} tts The TTS object from ChromeVox.
20  */
21 cvox.ChromeVoxHTMLDateWidget = function(dateElem, tts){
22   var self = this;
23   /**
24    * Currently selected field in the widget.
25    * @type {number}
26    * @private
27    */
28   this.pos_ = 0;
29   var maxpos = 2;
30   if (dateElem.type == 'month' || dateElem.type == 'week') {
31     maxpos = 1;
32   }
33   /**
34    * The maximum number of fields in the widget.
35    * @type {number}
36    * @private
37    */
38   this.maxPos_ = maxpos;
39   /**
40    * The HTML node of the widget.
41    * @type {Node}
42    * @private
43    */
44   this.dateElem_ = dateElem;
45   /**
46    * A handle to the ChromeVox TTS object.
47    * @type {Object}
48    * @private
49    */
50   this.dateTts_ = tts;
51   /**
52    * The previous value of the year field.
53    * @type {number}
54    * @private
55    */
56   this.pYear_ = -1;
57   /**
58    * The previous value of the month field.
59    * @type {number}
60    * @private
61    */
62   this.pMonth_ = -1;
63   /**
64    * The previous value of the week field.
65    * @type {number}
66    * @private
67    */
68   this.pWeek_ = -1;
69   /**
70    * The previous value of the day field.
71    * @type {number}
72    * @private
73    */
74   this.pDay_ = -1;
76   // Use listeners to make this work when running tests inside of ChromeVox.
77   this.keyListener_ = function(evt) {
78     self.eventHandler_(evt);
79   }
80   this.blurListener_ = function(evt) {
81     self.shutdown();
82   }
84   // Ensure we have a reasonable value to start with.
85   if (this.dateElem_.value.length == 0) {
86     this.forceInitTime_();
87   }
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);
100   }
102   this.dateElem_.addEventListener('keydown', this.keyListener_, false);
103   this.dateElem_.addEventListener('keyup', this.keyListener_, false);
104   this.dateElem_.addEventListener('blur', this.blurListener_, false);
105   this.update_(true);
109  * Removes the key listeners for the time widget.
111  */
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.
121  * @private
122  */
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
128   // start at 1.
129   var monthString = currentDate.getMonth() + 1 + '';
130   if (monthString.length < 2) {
131     monthString = '0' + monthString; // Month format is MM.
132   }
133   var dayString = currentDate.getDate() + '';
135   switch (this.dateElem_.type) {
136     case 'month':
137       valueString = yearString + '-' + monthString;
138       break;
139     case 'week':
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
144       currentDate.setDate(
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
149       var weekString =
150           Math.ceil(( ( (currentDate - yearStart) / 86400000) + 1)/7) + '';
151       if (weekString.length < 2) {
152         weekString = '0' + weekString; // Week format is WXX.
153       }
154       weekString = 'W' + weekString;
155       valueString = yearString + '-' + weekString;
156       break;
157     default:
158       valueString = yearString + '-' + monthString + '-' + dayString;
159       break;
160   }
161   this.dateElem_.setAttribute('value', valueString);
165  * Ensure that the position stays within bounds.
166  * @private
167  */
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
174   // week  == ww/yyyy
175   // month == mm/yyyy.
176   switch (this.pos_) {
177     case 0:
178       if (this.dateElem_.type == 'week') {
179         this.pWeek_ = -1;
180       } else {
181         this.pMonth_ = -1;
182       }
183       break;
184     case 1:
185       if (this.dateElem_.type == 'date') {
186         this.pDay_ = -1;
187       } else {
188         this.pYear_ = -1;
189       }
190       break;
191     case 2:
192       this.pYear_ = -1;
193       break;
194   }
198  * Speaks any changes to the control.
199  * @private
200  * @param {boolean} shouldSpeakLabel Whether or not to speak the label.
201  */
202 cvox.ChromeVoxHTMLDateWidget.prototype.update_ = function(shouldSpeakLabel) {
203   var splitDate = this.dateElem_.value.split("-");
204   if (splitDate.length < 1){
205     this.forceInitTime_();
206     return;
207   }
209   var year = -1;
210   var month = -1;
211   var week = -1;
212   var day = -1;
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);
221   } else {
222     month = parseInt(splitDate[1], 10);
223   }
225   var changeMessage = ''
227   if (shouldSpeakLabel) {
228     changeMessage = cvox.DomUtil.getName(this.dateElem_, true, true) + '\n';
229   }
231   if (week != this.pWeek_) {
232     changeMessage = changeMessage +
233         cvox.ChromeVox.msgs.getMsg('datewidget_week') + week + '\n';
234     this.pWeek_ = week;
235   }
237   if (month != this.pMonth_) {
238     var monthName = '';
239     switch (month) {
240       case 1:
241         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_january');
242         break;
243       case 2:
244         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_february');
245         break;
246       case 3:
247         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_march');
248         break;
249       case 4:
250         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_april');
251         break;
252       case 5:
253         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_may');
254         break;
255       case 6:
256         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_june');
257         break;
258       case 7:
259         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_july');
260         break;
261       case 8:
262         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_august');
263         break;
264       case 9:
265         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_september');
266         break;
267       case 10:
268         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_october');
269         break;
270       case 11:
271         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_november');
272         break;
273       case 12:
274         monthName = cvox.ChromeVox.msgs.getMsg('datewidget_december');
275         break;
276     }
277     changeMessage = changeMessage + monthName + '\n';
278     this.pMonth_ = month;
279   }
281   if (day != this.pDay_) {
282     changeMessage = changeMessage + day + '\n';
283     this.pDay_ = day;
284   }
286   if (year != this.pYear_) {
287     changeMessage = changeMessage + year + '\n';
288     this.pYear_ = year;
289   }
291   if (changeMessage.length > 0) {
292     this.dateTts_.speak(changeMessage, 0, null);
293   }
297  * Handles user key events.
298  * @private
299  * @param {Event} evt The event to be handled.
300  */
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)) {
306       this.pos_++;
307       this.handlePosChange_();
308       shouldSpeakLabel = true;
309     }
310     // Handle shift+tab/left arrow
311     if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) {
312       this.pos_--;
313       this.handlePosChange_();
314       shouldSpeakLabel = true;
315     }
316     // For all other cases, fall through and let update_ decide if there are any
317     // changes that need to be spoken.
318   }
319   this.update_(shouldSpeakLabel);