Allow only one bookmark to be added for multiple fast starring
[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).
13 /**
14 * A class containing the information needed to speak
15 * a text change event to the user.
17 * @constructor
18 * @param {Element} dateElem The time widget element.
19 * @param {cvox.TtsInterface} tts The TTS object from ChromeVox.
21 cvox.ChromeVoxHTMLDateWidget = function(dateElem, tts){
22 var self = this;
23 /**
24 * Currently selected field in the widget.
25 * @type {number}
26 * @private
28 this.pos_ = 0;
29 var maxpos = 2;
30 if (dateElem.type == 'month' || dateElem.type == 'week') {
31 maxpos = 1;
33 /**
34 * The maximum number of fields in the widget.
35 * @type {number}
36 * @private
38 this.maxPos_ = maxpos;
39 /**
40 * The HTML node of the widget.
41 * @type {Node}
42 * @private
44 this.dateElem_ = dateElem;
45 /**
46 * A handle to the ChromeVox TTS object.
47 * @type {Object}
48 * @private
50 this.dateTts_ = tts;
51 /**
52 * The previous value of the year field.
53 * @type {number}
54 * @private
56 this.pYear_ = -1;
57 /**
58 * The previous value of the month field.
59 * @type {number}
60 * @private
62 this.pMonth_ = -1;
63 /**
64 * The previous value of the week field.
65 * @type {number}
66 * @private
68 this.pWeek_ = -1;
69 /**
70 * The previous value of the day field.
71 * @type {number}
72 * @private
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);
80 this.blurListener_ = function(evt) {
81 self.shutdown();
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);
105 this.update_(true);
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.
121 * @private
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.
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.
154 weekString = 'W' + weekString;
155 valueString = yearString + '-' + weekString;
156 break;
157 default:
158 valueString = yearString + '-' + monthString + '-' + dayString;
159 break;
161 this.dateElem_.setAttribute('value', valueString);
165 * Ensure that the position stays within bounds.
166 * @private
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;
183 break;
184 case 1:
185 if (this.dateElem_.type == 'date') {
186 this.pDay_ = -1;
187 } else {
188 this.pYear_ = -1;
190 break;
191 case 2:
192 this.pYear_ = -1;
193 break;
198 * Speaks any changes to the control.
199 * @private
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_();
206 return;
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);
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';
234 this.pWeek_ = week;
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;
277 changeMessage = changeMessage + monthName + '\n';
278 this.pMonth_ = month;
281 if (day != this.pDay_) {
282 changeMessage = changeMessage + day + '\n';
283 this.pDay_ = day;
286 if (year != this.pYear_) {
287 changeMessage = changeMessage + year + '\n';
288 this.pYear_ = year;
291 if (changeMessage.length > 0) {
292 this.dateTts_.speak(changeMessage, 0, null);
297 * Handles user key events.
298 * @private
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)) {
306 this.pos_++;
307 this.handlePosChange_();
308 shouldSpeakLabel = true;
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;
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);