Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / braille / braille_input_handler_test.unitjs
blobaeec1fb08d5994ae3faf9d212fbeefa10f9e79ee
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 // Include test fixture.
6 GEN_INCLUDE(['../testing/chromevox_unittest_base.js']);
8 GEN_INCLUDE(['../testing/fake_objects.js']);
10 // Fake out the Chrome API namespace we depend on.
11 var chrome = {};
12 /** Fake chrome.runtime object. */
13 chrome.runtime = {};
14 /** Fake chrome.virtualKeyboardPrivate object. */
15 chrome.virtualKeyboardPrivate = {};
18 /**
19  * A fake input field that behaves like the Braille IME and also updates
20  * the input manager's knowledge about the display content when text changes
21  * in the edit field.
22  * @param {FakePort} port A fake port.
23  * @param {cvox.BrailleInputHandler} inputHandler to work with.
24  * @constructor
25  */
26 function FakeEditor(port, inputHandler) {
27   /** @private {FakePort} */
28   this.port_ = port;
29   /** @private {cvox.BrailleInputHandler} */
30   this.inputHandler_ = inputHandler;
31   /** @private {string} */
32   this.text_ = '';
33   /** @private {number} */
34   this.selectionStart_ = 0;
35   /** @private {number} */
36   this.selectionEnd_ = 0;
37   /** @private {number} */
38   this.contextID_ = 0;
39   /** @private {boolean} */
40   this.allowDeletes_ = false;
41   /** @private {string} */
42   this.uncommittedText_ = '';
43   /** @private {?Array<number>} */
44   this.extraCells_ = [];
45   port.postMessage = goog.bind(this.handleMessage_, this);
49 /**
50  * Sets the content and selection (or cursor) of the edit field.
51  * This fakes what happens when the field is edited by other means than
52  * via the braille keyboard.
53  * @param {string} text Text to replace the current content of the field.
54  * @param {number} selectionStart Start of the selection or cursor position.
55  * @param {number=} opt_selectionEnd End of selection, or ommited if the
56  *     selection is a cursor.
57  */
58 FakeEditor.prototype.setContent = function(
59     text, selectionStart, opt_selectionEnd) {
60   this.text_ = text;
61   this.selectionStart_ = selectionStart;
62   this.selectionEnd_ = goog.isDef(opt_selectionEnd) ?
63       opt_selectionEnd : selectionStart;
64   this.callOnDisplayContentChanged_();
68 /**
69  * Sets the selection in the editor.
70  * @param {number} selectionStart Start of the selection or cursor position.
71  * @param {number=} opt_selectionEnd End of selection, or ommited if the
72  *     selection is a cursor.
73  */
74 FakeEditor.prototype.select = function(selectionStart, opt_selectionEnd) {
75   this.setContent(this.text_, selectionStart, opt_selectionEnd);
79 /**
80  * Inserts text into the edit field, optionally selecting the inserted
81  * text.
82  * @param {string} newText Text to insert.
83  * @param {boolean=} opt_select If {@code true}, selects the inserted text,
84  *     otherwise leaves the cursor at the end of the new text.
85  */
86 FakeEditor.prototype.insert = function(newText, opt_select) {
87   this.text_ =
88       this.text_.substring(0, this.selectionStart_) +
89       newText +
90       this.text_.substring(this.selectionEnd_);
91   if (opt_select) {
92     this.selectionEnd_ = this.selectionStart_ + newText.length;
93   } else {
94     this.selectionStart_ += newText.length;
95     this.selectionEnd_ = this.selectionStart_;
96   }
97   this.callOnDisplayContentChanged_();
102  * Sets whether the editor should cause a test failure if the input handler
103  * tries to delete text before the cursor.  By default, thi value is
104  * {@code false}.
105  * @param {boolean} allowDeletes The new value.
106  */
107 FakeEditor.prototype.setAllowDeletes = function(allowDeletes) {
108   this.allowDeletes_ = allowDeletes;
113  * Signals to the input handler that the Braille IME is active or not active,
114  * depending on the argument.
115  * @param {boolean} value Whether the IME is active or not.
116  */
117 FakeEditor.prototype.setActive = function(value) {
118   this.message_({type: 'activeState', active: value});
123  * Fails if the current editor content and selection range don't match
124  * the arguments to this function.
125  * @param {string} text Text that should be in the field.
126  * @param {number} selectionStart Start of selection.
127  * @param {number+} opt_selectionEnd End of selection, default to selection
128  *     start to indicate a cursor.
129  */
130 FakeEditor.prototype.assertContentIs = function(
131     text, selectionStart, opt_selectionEnd) {
132   var selectionEnd = goog.isDef(opt_selectionEnd) ? opt_selectionEnd :
133       selectionStart;
134   assertEquals(text, this.text_);
135   assertEquals(selectionStart, this.selectionStart_);
136   assertEquals(selectionEnd, this.selectionEnd_);
141  * Asserts that the uncommitted text last sent to the IME is the given text.
142  * @param {string} text
143  */
144 FakeEditor.prototype.assertUncommittedTextIs = function(text) {
145   assertEquals(text, this.uncommittedText_);
150  * Asserts that the input handler has added 'extra cells' for uncommitted
151  * text into the braille content.
152  * @param {string} cells Cells as a space-separated list of numbers.
153  */
154 FakeEditor.prototype.assertExtraCellsAre = function(cells) {
155   assertEqualsJSON(cellsToArray(cells), this.extraCells_);
160  * Sends a message from the IME to the input handler.
161  * @param {Object} msg The message to send.
162  * @private
163  */
164 FakeEditor.prototype.message_ = function(msg) {
165   var listener = this.port_.onMessage.getListener();
166   assertNotEquals(null, listener);
167   listener(msg);
172  * Calls the {@code onDisplayContentChanged} method of the input handler
173  * with the current editor content and selection.
174  * @private
175  */
176 FakeEditor.prototype.callOnDisplayContentChanged_ = function() {
177   var content = cvox.BrailleUtil.createValue(
178       this.text_, this.selectionStart_, this.selectionEnd_)
179   var grabExtraCells = function() {
180     var span = content.getSpanInstanceOf(cvox.ExtraCellsSpan);
181     assertNotEquals(null, span);
182     // Convert the ArrayBuffer to a normal array for easier comparision.
183     this.extraCells_ = Array.prototype.map.call(new Uint8Array(span.cells),
184                                                 function(a) {return a;});
185   }.bind(this);
186   this.inputHandler_.onDisplayContentChanged(content, grabExtraCells);
187   grabExtraCells();
192  * Informs the input handler that a new text field is focused.  The content
193  * of the field is not cleared and should be updated separately.
194  * @param {string} fieldType The type of the field (see the documentation
195  *     for the {@code chrome.input.ime} API).
196  */
197 FakeEditor.prototype.focus = function(fieldType) {
198   this.contextID_++;
199   this.message_({type: 'inputContext',
200                  context: {type: fieldType,
201                            contextID: this.contextID_}});
206  * Inform the input handler that focus left the input field.
207  */
208 FakeEditor.prototype.blur = function() {
209   this.message_({type: 'inputContext', context: null});
210   this.contextID_ = 0;
215  * Handles a message from the input handler to the IME.
216  * @param {Object} msg The message.
217  * @private
218  */
219 FakeEditor.prototype.handleMessage_ = function(msg) {
220   assertEquals(this.contextID_, msg.contextID);
221   switch(msg.type) {
222     case 'replaceText':
223       var deleteBefore = msg.deleteBefore;
224       var newText = msg.newText;
225       assertTrue(goog.isNumber(deleteBefore));
226       assertTrue(goog.isString(newText));
227       assertTrue(deleteBefore <= this.selectionStart_);
228       if (deleteBefore > 0) {
229         assertTrue(this.allowDeletes_);
230         this.text_ =
231             this.text_.substring(0, this.selectionStart_ - deleteBefore) +
232             this.text_.substring(this.selectionEnd_);
233         this.selectionStart_ -= deleteBefore;
234         this.selectionEnd_ = this.selectionStart_;
235         this.callOnDisplayContentChanged_();
236       }
237       this.insert(newText);
238       break;
239     case 'setUncommitted':
240       assertTrue(goog.isString(msg.text));
241       this.uncommittedText_ = msg.text;
242       break;
243     case 'commitUncommitted':
244       this.insert(this.uncommittedText_);
245       this.uncommittedText_ = '';
246       break;
247     default:
248       throw new Error('Unexpected message to IME: ' + JSON.stringify(msg));
249   }
253  * Fakes a {@code Port} used for message passing in the Chrome extension APIs.
254  * @constructor
255  */
256 function FakePort() {
257   /** @type {FakeChromeEvent} */
258   this.onDisconnect = new FakeChromeEvent();
259   /** @type {FakeChromeEvent} */
260   this.onMessage = new FakeChromeEvent();
261   /** @type {string} */
262   this.name = cvox.BrailleInputHandler.IME_PORT_NAME_;
263   /** @type {{id: string}} */
264   this.sender = {id: cvox.BrailleInputHandler.IME_EXTENSION_ID_};
268  * Mapping from braille cells to Unicode characters.
269  * @const Array<Array<string> >
270  */
271 var UNCONTRACTED_TABLE = [
272   ['0', ' '],
273   ['1', 'a'], ['12', 'b'], ['14', 'c'], ['145', 'd'], ['15', 'e'],
274   ['124', 'f'], ['1245', 'g'], ['125', 'h'], ['24', 'i'], ['245', 'j'],
275   ['13', 'k'], ['123', 'l'], ['134', 'm'], ['1345', 'n'], ['135', 'o'],
276   ['1234', 'p'], ['12345', 'q'], ['1235', 'r'], ['234', 's'], ['2345', 't']
281  * Mapping of braille cells to the corresponding word in Grade 2 US English
282  * braille.  This table also includes the uncontracted table above.
283  * If a match 'pattern' starts with '^', it must be at the beginning of
284  * the string or be preceded by a blank cell.  Similarly, '$' at the end
285  * of a 'pattern' means that the match must be at the end of the string
286  * or be followed by a blank cell.  Note that order is significant in the
287  * table.  First match wins.
288  * @const
289  */
290 var CONTRACTED_TABLE = [
291   ['12 1235 123', 'braille'],
292   ['^12$', 'but'],
293   ['1456', 'this']].concat(UNCONTRACTED_TABLE);
296  * A fake braille translator that can do back translation according
297  * to one of the tables above.
298  * @param {Array<Array<number>>} table Backtranslation mapping.
299  * @param {boolean=} opt_capitalize Whether the result should be capitalized.
300  * @constructor
301  */
302 function FakeTranslator(table, opt_capitalize) {
303   /** @private */
304   this.table_ = table.map(function(entry) {
305     var cells = entry[0];
306     var result = [];
307     if (cells[0] === '^') {
308       result.start = true;
309       cells = cells.substring(1);
310     }
311     if (cells[cells.length - 1] === '$') {
312       result.end = true;
313       cells = cells.substring(0, cells.length - 1);
314     }
315     result[0] = cellsToArray(cells);
316     result[1] = entry[1];
317     return result;
318   });
319   /** @private {boolean} */
320   this.capitalize_ = opt_capitalize || false;
325  * Implements the {@code cvox.LibLouis.BrailleTranslator.backTranslate} method.
326  * @param {!ArrayBuffer} cells Cells to be translated.
327  * @param {function(?string)} callback Callback for result.
328  */
329 FakeTranslator.prototype.backTranslate = function(cells, callback) {
330   var cellsArray = new Uint8Array(cells);
331   var result = '';
332   var pos = 0;
333   while (pos < cellsArray.length) {
334     var match = null;
335     outer: for (var i = 0, entry; entry = this.table_[i]; ++i) {
336       if (pos + entry[0].length > cellsArray.length) {
337         continue;
338       }
339       if (entry.start && pos > 0 && cellsArray[pos - 1] !== 0) {
340         continue;
341       }
342       for (var j = 0; j < entry[0].length; ++j) {
343         if (entry[0][j] !== cellsArray[pos + j]) {
344           continue outer;
345         }
346       }
347       if (entry.end && pos + j < cellsArray.length &&
348           cellsArray[pos + j] !== 0) {
349         continue;
350       }
351       match = entry;
352       break;
353     }
354     assertNotEquals(
355         null, match,
356         'Backtranslating ' + cellsArray[pos] + ' at ' + pos);
357     result += match[1];
358     pos += match[0].length;
359   }
360   if (this.capitalize_) {
361     result = result.toUpperCase();
362   }
363   callback(result);
366 /** @extends {cvox.BrailleTranslatorManager} */
367 function FakeTranslatorManager() {
370 FakeTranslatorManager.prototype = {
371   defaultTranslator: null,
372   uncontractedTranslator: null,
373   changeListener: null,
375   /** @override */
376   getDefaultTranslator: function() {
377     return this.defaultTranslator;
378   },
380   /** @override */
381   getUncontractedTranslator: function() {
382     return this.uncontractedTranslator;
383   },
385   /** @override */
386   addChangeListener: function(listener) {
387     assertEquals(null, this.changeListener);
388   },
390   setTranslators: function(defaultTranslator, uncontractedTranslator) {
391     this.defaultTranslator = defaultTranslator;
392     this.uncontractedTranslator = uncontractedTranslator;
393     if (this.changeListener) {
394       this.changeListener();
395     }
396   }
400  * Converts a list of cells, represented as a string, to an array.
401  * @param {string} cells A string with space separated groups of digits.
402  *     Each group corresponds to one braille cell and each digit in a group
403  *     corresponds to a particular dot in the cell (1 to 8).  As a special
404  *     case, the digit 0 by itself represents a blank cell.
405  * @return {Array<number>} An array with each cell encoded as a bit
406  *     pattern (dot 1 uses bit 0, etc).
407  */
408 function cellsToArray(cells) {
409   if (!cells)
410     return [];
411   return cells.split(/\s+/).map(function(cellString) {
412     var cell = 0;
413     assertTrue(cellString.length > 0);
414     if (cellString != '0') {
415       for (var i = 0; i < cellString.length; ++i) {
416         var dot = cellString.charCodeAt(i) - '0'.charCodeAt(0);
417         assertTrue(dot >= 1);
418         assertTrue(dot <= 8);
419         cell |= 1 << (dot - 1);
420       }
421     }
422     return cell;
423   });
427  * Test fixture.
428  * @constructor
429  * @extends {ChromeVoxUnitTestBase}
430  */
431 function CvoxBrailleInputHandlerUnitTest() {}
433 CvoxBrailleInputHandlerUnitTest.prototype = {
434   __proto__: ChromeVoxUnitTestBase.prototype,
436   /** @override */
437   closureModuleDeps: [
438     'cvox.BrailleInputHandler',
439     'cvox.BrailleUtil',
440   ],
442   /**
443    * Creates an editor and establishes a connection from the IME.
444    * @return {FakeEditor}
445    */
446   createEditor: function() {
447     chrome.runtime.onConnectExternal.getListener()(this.port);
448     return new FakeEditor(this.port, this.inputHandler);
449   },
451   /**
452    * Sends a series of braille cells to the input handler.
453    * @param {string} cells Braille cells, encoded as described in
454    *     {@code cellsToArray}.
455    * @return {boolean} {@code true} iff all cells were sent successfully.
456    */
457   sendCells: function(cells) {
458     return cellsToArray(cells).reduce(function(prevResult, cell) {
459       var event = {command: cvox.BrailleKeyCommand.DOTS, brailleDots: cell};
460       return prevResult && this.inputHandler.onBrailleKeyEvent(event);
461     }.bind(this), true);
462   },
464   /**
465    * Sends a standard key event (such as backspace) to the braille input
466    * handler.
467    * @param {string} keyCode The key code name.
468    * @return {boolean} Whether the event was handled.
469    */
470   sendKeyEvent: function(keyCode) {
471     var event = {command: cvox.BrailleKeyCommand.STANDARD_KEY,
472                  standardKeyCode: keyCode};
473     return this.inputHandler.onBrailleKeyEvent(event);
474   },
476   /**
477    * Shortcut for asserting that the value expansion mode is {@code NONE}.
478    */
479   assertExpandingNone: function() {
480     assertEquals(cvox.ExpandingBrailleTranslator.ExpansionType.NONE,
481                  this.inputHandler.getExpansionType());
482   },
484   /**
485    * Shortcut for asserting that the value expansion mode is {@code SELECTION}.
486    */
487   assertExpandingSelection: function() {
488     assertEquals(cvox.ExpandingBrailleTranslator.ExpansionType.SELECTION,
489                  this.inputHandler.getExpansionType());
490   },
492   /**
493    * Shortcut for asserting that the value expansion mode is {@code ALL}.
494    */
495   assertExpandingAll: function() {
496     assertEquals(cvox.ExpandingBrailleTranslator.ExpansionType.ALL,
497                  this.inputHandler.getExpansionType());
498   },
500   storeKeyEvent: function(event, opt_callback) {
501     var storedCopy = {keyCode: event.keyCode, keyName: event.keyName,
502                       charValue: event.charValue};
503     if (event.type == 'keydown') {
504       this.keyEvents.push(storedCopy);
505     } else {
506       assertEquals('keyup', event.type);
507       assertTrue(this.keyEvents.length > 0);
508       assertEqualsJSON(storedCopy, this.keyEvents[this.keyEvents.length - 1]);
509     }
510     if (goog.isDef(opt_callback)) {
511       callback();
512     }
513   },
515   /** @override */
516   setUp: function() {
517     chrome.runtime.onConnectExternal = new FakeChromeEvent();
518     this.port = new FakePort();
519     this.translatorManager = new FakeTranslatorManager();
520     this.inputHandler = new cvox.BrailleInputHandler(this.translatorManager);
521     this.inputHandler.init();
522     this.uncontractedTranslator = new FakeTranslator(UNCONTRACTED_TABLE);
523     this.contractedTranslator = new FakeTranslator(CONTRACTED_TABLE, true);
524     chrome.virtualKeyboardPrivate.sendKeyEvent =
525       this.storeKeyEvent.bind(this);
526     this.keyEvents = [];
527   }
530 TEST_F('CvoxBrailleInputHandlerUnitTest', 'ConnectFromUnknownExtension',
531   function() {
532   this.port.sender.id = 'your unknown friend';
533   chrome.runtime.onConnectExternal.getListener()(this.port);
534   this.port.onMessage.assertNoListener();
538 TEST_F('CvoxBrailleInputHandlerUnitTest', 'NoTranslator', function() {
539   var editor = this.createEditor();
540   editor.setContent('blah', 0);
541   editor.setActive(true);
542   editor.focus('email');
543   assertFalse(this.sendCells('145 135 125'));
544   editor.setActive(false);
545   editor.blur();
546   editor.assertContentIs('blah', 0);
550 TEST_F('CvoxBrailleInputHandlerUnitTest', 'InputUncontracted', function() {
551   this.translatorManager.setTranslators(this.uncontractedTranslator, null);
552   var editor = this.createEditor();
553   editor.setActive(true);
555   // Focus and type in a text field.
556   editor.focus('text');
557   assertTrue(this.sendCells('125 15 123 123 135'));  // hello
558   editor.assertContentIs('hello', 'hello'.length);
559   this.assertExpandingNone();
561   // Move the cursor and type in the middle.
562   editor.select(2);
563   assertTrue(this.sendCells('0 2345 125 15 1235 15 0'));  // ' there '
564   editor.assertContentIs('he there llo', 'he there '.length);
566   // Field changes by some other means.
567   editor.insert('you!');
568   // Then type on the braille keyboard again.
569   assertTrue(this.sendCells('0 125 15'));  // ' he'
570   editor.assertContentIs('he there you! hello', 'he there you! he'.length);
572   editor.blur();
573   editor.setActive(false);
577 TEST_F('CvoxBrailleInputHandlerUnitTest', 'InputContracted', function() {
578   var editor = this.createEditor();
579   this.translatorManager.setTranslators(this.contractedTranslator,
580                                         this.uncontractedTranslator);
581   editor.setContent('', 0);
582   editor.setActive(true);
583   editor.focus('text');
584   this.assertExpandingSelection();
586   // First, type a 'b'.
587   assertTrue(this.sendCells('12'));
588   editor.assertContentIs('', 0);
589   // Remember that the contracted translator produces uppercase.
590   editor.assertUncommittedTextIs('BUT');
591   editor.assertExtraCellsAre('12');
592   this.assertExpandingNone();
594   // Typing 'rl' changes to a different contraction.
595   assertTrue(this.sendCells('1235 123'));
596   editor.assertUncommittedTextIs('BRAILLE');
597   editor.assertContentIs('', 0);
598   editor.assertExtraCellsAre('12 1235 123');
599   this.assertExpandingNone();
601   // Now, finish the word.
602   assertTrue(this.sendCells('0'));
603   editor.assertContentIs('BRAILLE ', 'BRAILLE '.length);
604   editor.assertUncommittedTextIs('');
605   editor.assertExtraCellsAre('');
606   this.assertExpandingSelection();
608   // Move the cursor to the beginning.
609   editor.select(0);
610   this.assertExpandingSelection();
612   // Typing now uses the uncontracted table.
613   assertTrue(this.sendCells('12'));  // 'b'
614   editor.assertContentIs('bBRAILLE ', 1);
615   this.assertExpandingSelection();
616   editor.select('bBRAILLE'.length);
617   this.assertExpandingSelection();
618   assertTrue(this.sendCells('12')); // 'b'
619   editor.assertContentIs('bBRAILLEb ', 'bBRAILLEb'.length);
620   // Move to the end, where contracted typing should work.
621   editor.select('bBRAILLEb '.length);
622   assertTrue(this.sendCells('1456 0'));  // Symbol for 'this', then space.
623   this.assertExpandingSelection();
624   editor.assertContentIs('bBRAILLEb THIS ', 'bBRAILLEb THIS '.length);
626   // Move to between the two words.
627   editor.select('bBRAILLEb'.length);
628   this.assertExpandingSelection();
629   assertTrue(this.sendCells('0 12'));  // Space plus 'b' for 'but'
630   editor.assertUncommittedTextIs('BUT');
631   editor.assertExtraCellsAre('12');
632   editor.assertContentIs('bBRAILLEb  THIS ', 'bBRAILLEb '.length);
633   this.assertExpandingNone();
637 TEST_F('CvoxBrailleInputHandlerUnitTest', 'TypingUrlWithContracted',
638        function() {
639   var editor = this.createEditor();
640   this.translatorManager.setTranslators(this.contractedTranslator,
641                                         this.uncontractedTranslator);
642   editor.setActive(true);
643   editor.focus('url');
644   this.assertExpandingAll();
645   assertTrue(this.sendCells('1245'));  // 'g'
646   editor.insert('oogle.com', true /*select*/);
647   editor.assertContentIs('google.com', 1, 'google.com'.length);
648   this.assertExpandingAll();
649   this.sendCells('135');  // 'o'
650   editor.insert('ogle.com', true /*select*/);
651   editor.assertContentIs('google.com', 2, 'google.com'.length);
652   this.assertExpandingAll();
653   this.sendCells('0');
654   editor.assertContentIs('go ', 'go '.length);
655   // In a URL, even when the cursor is in whitespace, all of the value
656   // is expanded to uncontracted braille.
657   this.assertExpandingAll();
661 TEST_F('CvoxBrailleInputHandlerUnitTest', 'Backspace', function() {
662   var editor = this.createEditor();
663   this.translatorManager.setTranslators(this.contractedTranslator,
664                                         this.uncontractedTranslator);
665   editor.setActive(true);
666   editor.focus('text');
668   // Add some text that we can delete later.
669   editor.setContent('Text ', 'Text '.length);
671   // Type 'brl' to make sure replacement works when deleting text.
672   assertTrue(this.sendCells('12 1235 123'));
673   editor.assertUncommittedTextIs('BRAILLE');
675   // Delete what we just typed, one cell at a time.
676   this.sendKeyEvent('Backspace');
677   editor.assertUncommittedTextIs('BR');
678   this.sendKeyEvent('Backspace');
679   editor.assertUncommittedTextIs('BUT');
680   this.sendKeyEvent('Backspace');
681   editor.assertUncommittedTextIs('');
683   // Now, backspace should be handled as usual, synthetizing key events.
684   assertEquals(0, this.keyEvents.length);
685   this.sendKeyEvent('Backspace');
686   assertEqualsJSON([{keyCode: 8, keyName: 'Backspace', charValue: 8}],
687                    this.keyEvents);
691 TEST_F('CvoxBrailleInputHandlerUnitTest', 'KeysImeNotActive', function() {
692   var editor = this.createEditor();
693   this.sendKeyEvent('Enter');
694   this.sendKeyEvent('ArrowUp');
695   assertEqualsJSON([{keyCode: 13, keyName: 'Enter', charValue: 0x0A},
696                     {keyCode: 38, keyName: 'ArrowUp', charValue: 0x41}],
697                    this.keyEvents);