Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / chromeos / chromevox / walkers / table_walker.js
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 /**
6  * @fileoverview A class for walking tables.
7  * NOTE: This class has a very different interface than the other walkers.
8  * This means it does not lend itself easily to e.g. decorators.
9  * TODO (stoarca): This might be able to be fixed by breaking it up into
10  * separate walkers for cell, row and column.
11  */
12
13
14 goog.provide('cvox.TableWalker');
15
16 goog.require('cvox.AbstractWalker');
17 goog.require('cvox.BrailleUtil');
18 goog.require('cvox.DescriptionUtil');
19 goog.require('cvox.DomUtil');
20 goog.require('cvox.NavDescription');
21 goog.require('cvox.QueueMode');
22 goog.require('cvox.TraverseTable');
23
24 /**
25  * @constructor
26  * @extends {cvox.AbstractWalker}
27  */
28 cvox.TableWalker = function() {
29   cvox.AbstractWalker.call(this);
30
31   /**
32    * Only used as a cache for faster lookup.
33    * @type {!cvox.TraverseTable}
34    */
35   this.tt = new cvox.TraverseTable(null);
36 };
37 goog.inherits(cvox.TableWalker, cvox.AbstractWalker);
38
39 /**
40  * @override
41  */
42 cvox.TableWalker.prototype.next = function(sel) {
43   // TODO (stoarca): See bug 6677953
44   return this.nextRow(sel);
45 };
46
47 /**
48  * @override
49  */
50 cvox.TableWalker.prototype.sync = function(sel) {
51   return this.goTo_(sel, goog.bind(function(position) {
52       return this.tt.goToCell(position);
53   }, this));
54 };
55
56 /**
57  * @override
58  * @suppress {checkTypes} actual parameter 2 of
59  * cvox.Msgs.prototype.getMsg does not match formal parameter
60  * found   : Array.<number>
61  * required: (Array.<string>|null|undefined)
62  */
63 cvox.TableWalker.prototype.getDescription = function(prevSel, sel) {
64   var position = this.syncPosition_(sel);
65   if (!position) {
66     return [];
67   }
68   this.tt.goToCell(position);
69   var descs = cvox.DescriptionUtil.getCollectionDescription(prevSel, sel);
70   if (descs.length == 0) {
71     descs.push(new cvox.NavDescription({
72       annotation: cvox.ChromeVox.msgs.getMsg('empty_cell')
73     }));
74   }
75   return descs;
76 };
77
78 /**
79  * @override
80  */
81 cvox.TableWalker.prototype.getBraille = function(prevSel, sel) {
82   var ret = new cvox.NavBraille({});
83   var position = this.syncPosition_(sel);
84   if (position) {
85     var text =
86         cvox.BrailleUtil.getTemplated(prevSel.start.node, sel.start.node);
87     text.append(' ' + ++position[0] + '/' + ++position[1]);
88   }
89   return new cvox.NavBraille({text: text});
90 };
91
92 /**
93  * @override
94  */
95 cvox.TableWalker.prototype.getGranularityMsg = goog.abstractMethod;
96
97
98 /** Table Actions. */
99
100
101 /**
102  * Returns the first cell of the table that this selection is inside.
103  * @param {!cvox.CursorSelection} sel The selection.
104  * @return {cvox.CursorSelection} The selection for first cell of the table.
105  * @expose
106  */
107 cvox.TableWalker.prototype.goToFirstCell = function(sel) {
108   return this.goTo_(sel, goog.bind(function(position) {
109     return this.tt.goToCell([0, 0]);
110   }, this));
111 };
112
113 /**
114  * Returns the last cell of the table that this selection is inside.
115  * @param {!cvox.CursorSelection} sel The selection.
116  * @return {cvox.CursorSelection} The selection for the last cell of the table.
117  * @expose
118  */
119 cvox.TableWalker.prototype.goToLastCell = function(sel) {
120   return this.goTo_(sel, goog.bind(function(position) {
121     return this.tt.goToLastCell();
122   }, this));
123 };
124
125 /**
126  * Returns the first cell of the row that the selection is in.
127  * @param {!cvox.CursorSelection} sel The selection.
128  * @return {cvox.CursorSelection} The selection for the first cell in the row.
129  * @expose
130  */
131 cvox.TableWalker.prototype.goToRowFirstCell = function(sel) {
132   return this.goTo_(sel, goog.bind(function(position) {
133     return this.tt.goToCell([position[0], 0]);
134   }, this));
135 };
136
137 /**
138  * Returns the last cell of the row that the selection is in.
139  * @param {!cvox.CursorSelection} sel The selection.
140  * @return {cvox.CursorSelection} The selection for the last cell in the row.
141  * @expose
142  */
143 cvox.TableWalker.prototype.goToRowLastCell = function(sel) {
144   return this.goTo_(sel, goog.bind(function(position) {
145     return this.tt.goToRowLastCell();
146   }, this));
147 };
148
149 /**
150  * Returns the first cell of the column that the selection is in.
151  * @param {!cvox.CursorSelection} sel The selection.
152  * @return {cvox.CursorSelection} The selection for the first cell in the col.
153  * @expose
154  */
155 cvox.TableWalker.prototype.goToColFirstCell = function(sel) {
156   return this.goTo_(sel, goog.bind(function(position) {
157     return this.tt.goToCell([0, position[1]]);
158   }, this));
159 };
160
161 /**
162  * Returns the last cell of the column that the selection is in.
163  * @param {!cvox.CursorSelection} sel The selection.
164  * @return {cvox.CursorSelection} The selection for the last cell in the col.
165  * @expose
166  */
167 cvox.TableWalker.prototype.goToColLastCell = function(sel) {
168   return this.goTo_(sel, goog.bind(function(position) {
169     return this.tt.goToColLastCell();
170   }, this));
171 };
172
173 /**
174  * Returns the first cell in the row after the current selection.
175  * @param {!cvox.CursorSelection} sel The selection.
176  * @return {cvox.CursorSelection} The selection for the first cell in the next
177  * row.
178  * @expose
179  */
180 cvox.TableWalker.prototype.nextRow = function(sel) {
181   return this.goTo_(sel, goog.bind(function(position) {
182     return this.tt.goToCell([position[0] + (sel.isReversed() ? -1 : 1),
183                               position[1]]);
184   }, this));
185 };
186
187 /**
188  * Returns the first cell in the column after the current selection.
189  * @param {!cvox.CursorSelection} sel The selection.
190  * @return {cvox.CursorSelection} The selection for the first cell in the
191  * next col.
192  * @expose
193  */
194 cvox.TableWalker.prototype.nextCol = function(sel) {
195   return this.goTo_(sel, goog.bind(function(position) {
196     return this.tt.goToCell([position[0],
197                               position[1] + (sel.isReversed() ? -1 : 1)]);
198   }, this));
199 };
200
201 /**
202  * @param {!cvox.CursorSelection} sel The current selection.
203  * @return {cvox.CursorSelection} The resulting selection.
204  * @expose
205  */
206 cvox.TableWalker.prototype.announceHeaders = function(sel) {
207   cvox.ChromeVox.tts.speak(this.getHeaderText_(sel),
208                            cvox.QueueMode.FLUSH,
209                            cvox.AbstractTts.PERSONALITY_ANNOTATION);
210   return sel;
211 };
212
213 /**
214  * @param {!cvox.CursorSelection} sel The current selection.
215  * @return {cvox.CursorSelection} The resulting selection.
216  * @expose
217  */
218 cvox.TableWalker.prototype.speakTableLocation = function(sel) {
219   cvox.ChromeVox.navigationManager.speakDescriptionArray(
220       this.getLocationDescription_(sel),
221       cvox.QueueMode.FLUSH,
222       null);
223   return sel;
224 };
225
226
227 /**
228  * @param {!cvox.CursorSelection} sel The current selection.
229  * @return {cvox.CursorSelection} The resulting selection.
230  * @expose
231  */
232 cvox.TableWalker.prototype.exitShifterContent = function(sel) {
233   var tableNode = this.getTableNode_(sel);
234   if (!tableNode) {
235     return null;
236   }
237   var nextNode = cvox.DomUtil.directedNextLeafNode(tableNode, false);
238   return cvox.CursorSelection.fromNode(nextNode);
239 };
240
241
242 /** End of actions. */
243
244
245 /**
246  * Returns the text content of the header(s) of the cell that contains sel.
247  * @param {!cvox.CursorSelection} sel The selection.
248  * @return {!string} The header text.
249  * @private
250  */
251 cvox.TableWalker.prototype.getHeaderText_ = function(sel) {
252   this.tt.initialize(this.getTableNode_(sel));
253   var position = this.tt.findNearestCursor(sel.start.node);
254   if (!position) {
255     return cvox.ChromeVox.msgs.getMsg('not_inside_table');
256   }
257   if (!this.tt.goToCell(position)) {
258     return cvox.ChromeVox.msgs.getMsg('not_inside_table');
259   }
260   return (
261       this.getRowHeaderText_(position) +
262       ' ' +
263       this.getColHeaderText_(position));
264 };
265
266 /**
267  * Returns the location description.
268  * @param {!cvox.CursorSelection} sel A valid selection.
269  * @return {Array.<cvox.NavDescription>} The location description.
270  * @suppress {checkTypes} actual parameter 2 of
271  * cvox.Msgs.prototype.getMsg does not match
272  * formal parameter
273  * found   : Array.<number>
274  * required: (Array.<string>|null|undefined)
275  * @private
276  */
277 cvox.TableWalker.prototype.getLocationDescription_ = function(sel) {
278   var locationInfo = this.getLocationInfo(sel);
279   if (locationInfo == null) {
280     return null;
281   }
282   return [new cvox.NavDescription({
283     text: cvox.ChromeVox.msgs.getMsg('table_location', locationInfo)
284   })];
285 };
286
287 /**
288  * Returns the text content of the row header(s) of the cell that contains sel.
289  * @param {!Array.<number>} position The selection.
290  * @return {!string} The header text.
291  * @private
292  */
293 cvox.TableWalker.prototype.getRowHeaderText_ = function(position) {
294   // TODO(stoarca): OPTMZ Replace with join();
295   var rowHeaderText = '';
296
297   var rowHeaders = this.tt.getCellRowHeaders();
298   if (rowHeaders.length == 0) {
299     var firstCellInRow = this.tt.getCellAt([position[0], 0]);
300     rowHeaderText += cvox.DomUtil.collapseWhitespace(
301         cvox.DomUtil.getValue(firstCellInRow) + ' ' +
302             cvox.DomUtil.getName(firstCellInRow));
303     return cvox.ChromeVox.msgs.getMsg('row_header') + rowHeaderText;
304   }
305
306   for (var i = 0; i < rowHeaders.length; ++i) {
307     rowHeaderText += cvox.DomUtil.collapseWhitespace(
308         cvox.DomUtil.getValue(rowHeaders[i]) + ' ' +
309             cvox.DomUtil.getName(rowHeaders[i]));
310   }
311   if (rowHeaderText == '') {
312     return cvox.ChromeVox.msgs.getMsg('empty_row_header');
313   }
314   return cvox.ChromeVox.msgs.getMsg('row_header') + rowHeaderText;
315 };
316
317 /**
318  * Returns the text content of the col header(s) of the cell that contains sel.
319  * @param {!Array.<number>} position The selection.
320  * @return {!string} The header text.
321  * @private
322  */
323 cvox.TableWalker.prototype.getColHeaderText_ = function(position) {
324   // TODO(stoarca): OPTMZ Replace with join();
325   var colHeaderText = '';
326
327   var colHeaders = this.tt.getCellColHeaders();
328   if (colHeaders.length == 0) {
329     var firstCellInCol = this.tt.getCellAt([0, position[1]]);
330     colHeaderText += cvox.DomUtil.collapseWhitespace(
331         cvox.DomUtil.getValue(firstCellInCol) + ' ' +
332         cvox.DomUtil.getName(firstCellInCol));
333     return cvox.ChromeVox.msgs.getMsg('column_header') + colHeaderText;
334   }
335
336   for (var i = 0; i < colHeaders.length; ++i) {
337     colHeaderText += cvox.DomUtil.collapseWhitespace(
338         cvox.DomUtil.getValue(colHeaders[i]) + ' ' +
339             cvox.DomUtil.getName(colHeaders[i]));
340   }
341   if (colHeaderText == '') {
342     return cvox.ChromeVox.msgs.getMsg('empty_row_header');
343   }
344   return cvox.ChromeVox.msgs.getMsg('column_header') + colHeaderText;
345 };
346
347 /**
348  * Returns the location info of sel within the containing table.
349  * @param {!cvox.CursorSelection} sel The selection.
350  * @return {Array.<number>} The location info:
351  *  [row index, row count, col index, col count].
352  */
353 cvox.TableWalker.prototype.getLocationInfo = function(sel) {
354   this.tt.initialize(this.getTableNode_(sel));
355   var position = this.tt.findNearestCursor(sel.start.node);
356   if (!position) {
357     return null;
358   }
359   // + 1 to account for 0-indexed
360   return [
361     position[0] + 1,
362     this.tt.rowCount,
363     position[1] + 1,
364     this.tt.colCount
365   ].map(function(x) {return cvox.ChromeVox.msgs.getNumber(x);});
366 };
367
368 /**
369  * Returns true if sel is inside a table.
370  * @param {!cvox.CursorSelection} sel The selection.
371  * @return {boolean} True if inside a table node.
372  */
373 cvox.TableWalker.prototype.isInTable = function(sel) {
374   return this.getTableNode_(sel) != null;
375 };
376
377 /**
378  * Wrapper for going to somewhere so that boilerplate is not repeated.
379  * @param {!cvox.CursorSelection} sel The selection from which to base the
380  * movement.
381  * @param {function(Array.<number>):boolean} f The function to use for moving.
382  * Returns true on success and false on failure.
383  * @return {cvox.CursorSelection} The resulting selection.
384  * @private
385  */
386 cvox.TableWalker.prototype.goTo_ = function(sel, f) {
387   this.tt.initialize(this.getTableNode_(sel));
388   var position = this.tt.findNearestCursor(sel.end.node);
389   if (!position) {
390     return null;
391   }
392   this.tt.goToCell(position);
393   if (!f(position)) {
394     return null;
395   }
396   return cvox.CursorSelection.fromNode(this.tt.getCell()).
397       setReversed(sel.isReversed());
398 };
399
400 /**
401  * Returns the nearest table node containing the end of the selection
402  * @param {!cvox.CursorSelection} sel The selection.
403  * @return {Node} The table node containing sel. null if not in a table.
404  * @private
405  */
406 cvox.TableWalker.prototype.getTableNode_ = function(sel) {
407   return cvox.DomUtil.getContainingTable(sel.end.node);
408 };
409
410 /**
411  * Sync the backing traversal utility to the given selection.
412  * @param {!cvox.CursorSelection} sel The selection.
413  * @return {Array.<number>} The position [x, y] of the selection.
414  * @private
415  */
416 cvox.TableWalker.prototype.syncPosition_ = function(sel) {
417   var tableNode = this.getTableNode_(sel);
418   this.tt.initialize(tableNode);
419   // we need to align the TraverseTable with our sel because our walker
420   // uses parts of it (for example isSpanned relies on being at a specific cell)
421   return this.tt.findNearestCursor(sel.end.node);
422 };