1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Namespace for keyboard utility functions.
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var keyboard = {};
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Swallows keypress and keyup events of arrow keys.
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Event} event Raised event.
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @private
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)keyboard.onKeyIgnore_ = function(event) {
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  event = /** @type {KeyboardEvent} */(event);
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey)
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (event.keyIdentifier == 'Left' ||
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      event.keyIdentifier == 'Right' ||
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      event.keyIdentifier == 'Up' ||
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      event.keyIdentifier == 'Down') {
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    event.stopPropagation();
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    event.preventDefault();
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Converts arrow keys into tab/shift-tab key events.
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Event} event Raised event.
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @private
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)keyboard.onKeyDown_ = function(event) {
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  event = /** @type {KeyboardEvent} */(event);
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (event.ctrlKey || event.shiftKey || event.altKey || event.metaKey)
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  var needsUpDownKeys = event.target.classList.contains('needs-up-down-keys');
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (event.keyIdentifier == 'Left' ||
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      (!needsUpDownKeys && event.keyIdentifier == 'Up')) {
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    keyboard.raiseKeyFocusPrevious(document.activeElement);
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    event.stopPropagation();
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    event.preventDefault();
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else if (event.keyIdentifier == 'Right' ||
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             (!needsUpDownKeys && event.keyIdentifier == 'Down')) {
501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    keyboard.raiseKeyFocusNext(document.activeElement);
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    event.stopPropagation();
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    event.preventDefault();
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Raises tab/shift-tab keyboard events.
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {HTMLElement} element Element that should receive the event.
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {string} eventType Keyboard event type.
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {boolean} shift True if shift should be on.
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @private
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)keyboard.raiseTabKeyEvent_ = function(element, eventType, shift) {
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  var event = document.createEvent('KeyboardEvent');
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  event.initKeyboardEvent(
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      eventType,
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      true,  // canBubble
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      true,  // cancelable
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      window,
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      'U+0009',
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      0,  // keyLocation
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      false,  // ctrl
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      false,  // alt
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      shift,  // shift
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      false);  // meta
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  element.dispatchEvent(event);
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Raises shift+tab keyboard events to focus previous element.
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {HTMLElement} element Element that should receive the event.
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)keyboard.raiseKeyFocusPrevious = function(element) {
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  keyboard.raiseTabKeyEvent_(element, 'keydown', true);
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  keyboard.raiseTabKeyEvent_(element, 'keypress', true);
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  keyboard.raiseTabKeyEvent_(element, 'keyup', true);
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Raises tab keyboard events to focus next element.
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {HTMLElement} element Element that should receive the event.
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)keyboard.raiseKeyFocusNext = function(element) {
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  keyboard.raiseTabKeyEvent_(element, 'keydown', false);
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  keyboard.raiseTabKeyEvent_(element, 'keypress', false);
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  keyboard.raiseTabKeyEvent_(element, 'keyup', false);
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Initializes event handling for arrow keys driven focus flow.
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)keyboard.initializeKeyboardFlow = function() {
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  document.addEventListener('keydown',
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      keyboard.onKeyDown_, true);
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  document.addEventListener('keypress',
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      keyboard.onKeyIgnore_, true);
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  document.addEventListener('keyup',
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      keyboard.onKeyIgnore_, true);
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
110