Index: chrome/browser/resources/access_chromevox/chromevox/injected/event_watcher.js |
=================================================================== |
--- chrome/browser/resources/access_chromevox/chromevox/injected/event_watcher.js (revision 0) |
+++ chrome/browser/resources/access_chromevox/chromevox/injected/event_watcher.js (revision 0) |
@@ -0,0 +1,447 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+/** |
+ * @fileoverview Watches for events in the browser such as focus changes. |
+ */ |
+ |
+goog.provide('cvox.ChromeVoxEventWatcher'); |
+ |
+goog.require('cvox.ChromeVox'); |
+goog.require('cvox.ChromeVoxEditableTextBase'); |
+goog.require('cvox.ChromeVoxKbHandler'); |
+goog.require('cvox.ChromeVoxUserCommands'); |
+goog.require('cvox.DomUtil'); |
+ |
+/** |
+ * @constructor |
+ */ |
+cvox.ChromeVoxEventWatcher = function() { |
+}; |
+ |
+/** |
+ * @type {Object} |
+ */ |
+cvox.ChromeVoxEventWatcher.lastFocusedNode = null; |
+ |
+/** |
+ * @type {string?} |
+ */ |
+cvox.ChromeVoxEventWatcher.lastFocusedNodeValue = null; |
+ |
+/** |
+ * @type {Object} |
+ */ |
+cvox.ChromeVoxEventWatcher.eventToEat = null; |
+ |
+/** |
+ * @type {Element} |
+ */ |
+cvox.ChromeVoxEventWatcher.currentTextControl = null; |
+ |
+/** |
+ * @type {cvox.ChromeVoxEditableTextBase} |
+ */ |
+cvox.ChromeVoxEventWatcher.currentTextHandler = null; |
+ |
+/** |
+ * @type {Object} |
+ */ |
+cvox.ChromeVoxEventWatcher.previousTextHandlerState = null; |
+ |
+/** |
+ * The last timestamp for the last keypress; that helps us separate |
+ * user-triggered events from other events. |
+ * @type {number} |
+ */ |
+cvox.ChromeVoxEventWatcher.lastKeypressTime = 0; |
+ |
+/** |
+ * The delay before the timer function is first called to check on a |
+ * focused text control, to see if it's been modified without an event |
+ * being generated. |
+ * @const |
+ * @type {number} |
+ */ |
+cvox.ChromeVoxEventWatcher.TEXT_TIMER_INITIAL_DELAY_MS = 10; |
+ |
+/** |
+ * The delay between subsequent calls to the timer function to check |
+ * focused text controls. |
+ * @const |
+ * @type {number} |
+ */ |
+cvox.ChromeVoxEventWatcher.TEXT_TIMER_DELAY_MS = 250; |
+ |
+/** |
+ * Add all of our event listeners to the document. |
+ */ |
+cvox.ChromeVoxEventWatcher.addEventListeners = function() { |
+ document.addEventListener( |
+ 'keypress', cvox.ChromeVoxEventWatcher.keyPressEventWatcher, true); |
+ document.addEventListener( |
+ 'keydown', cvox.ChromeVoxEventWatcher.keyDownEventWatcher, true); |
+ document.addEventListener( |
+ 'keyup', cvox.ChromeVoxEventWatcher.keyUpEventWatcher, true); |
+ document.addEventListener( |
+ 'focus', cvox.ChromeVoxEventWatcher.focusEventWatcher, true); |
+ document.addEventListener( |
+ 'blur', cvox.ChromeVoxEventWatcher.blurEventWatcher, true); |
+ document.addEventListener( |
+ 'change', cvox.ChromeVoxEventWatcher.changeEventWatcher, true); |
+ document.addEventListener( |
+ 'select', cvox.ChromeVoxEventWatcher.selectEventWatcher, true); |
+}; |
+ |
+/** |
+ * Return the last focused node. |
+ * @return {Object} The last node that was focused. |
+ */ |
+cvox.ChromeVoxEventWatcher.getLastFocusedNode = function() { |
+ return cvox.ChromeVoxEventWatcher.lastFocusedNode; |
+}; |
+ |
+/** |
+ * Handles focus events. |
+ * |
+ * @param {Event} evt The focus event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.focusEventWatcher = function(evt) { |
+ cvox.ChromeVoxEventWatcher.lastFocusedNode = evt.target; |
+ if (evt.target) { |
+ if (cvox.DomUtil.isControl(evt.target)) { |
+ cvox.ChromeVoxEventWatcher.lastFocusedNodeValue = |
+ cvox.ChromeVoxEventWatcher.getControlValueAndStateString( |
+ /** @type {Element} */(evt.target)); |
+ cvox.ChromeVox.tts.speak(cvox.ChromeVoxEventWatcher.lastFocusedNodeValue, |
+ 0, null); |
+ } |
+ if (evt.target.tagName == 'A' && |
+ cvox.ChromeVoxUserCommands.silenceLevel == 0) { |
+ cvox.ChromeVox.tts.speak(cvox.DomUtil.getText(evt.target), 0, null); |
+ } |
+ cvox.ChromeVox.navigationManager.syncToNode(evt.target); |
+ } else { |
+ cvox.ChromeVoxEventWatcher.lastFocusedNodeValue = null; |
+ } |
+ cvox.ChromeVoxEventWatcher.handleTextChanged(false); |
+ return true; |
+}; |
+ |
+/** |
+ * Handles blur events. |
+ * |
+ * @param {Object} evt The blur event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.blurEventWatcher = function(evt) { |
+ cvox.ChromeVoxEventWatcher.lastFocusedNode = null; |
+ cvox.ChromeVoxEventWatcher.handleTextChanged(false); |
+ return true; |
+}; |
+ |
+/** |
+ * Handles key down events. |
+ * |
+ * @param {Object} evt The event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.keyDownEventWatcher = function(evt) { |
+ if (cvox.ChromeVoxEventWatcher.currentTextHandler) { |
+ cvox.ChromeVoxEventWatcher.previousTextHandlerState = |
+ cvox.ChromeVoxEventWatcher.currentTextHandler.saveState(); |
+ } |
+ cvox.ChromeVoxEventWatcher.lastKeypressTime = new Date().getTime(); |
+ |
+ cvox.ChromeVoxEventWatcher.eventToEat = null; |
+ if (!cvox.ChromeVoxKbHandler.basicKeyDownActionsListener(evt) || |
+ cvox.ChromeVoxEventWatcher.handleControlAction(evt)) { |
+ // Swallow the event immediately to prevent the arrow keys |
+ // from driving controls on the web page. |
+ evt.preventDefault(); |
+ evt.stopPropagation(); |
+ // Also mark this as something to be swallowed when the followup |
+ // keypress/keyup counterparts to this event show up later. |
+ cvox.ChromeVoxEventWatcher.eventToEat = evt; |
+ return false; |
+ } |
+ cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
+ setTimeout(function() { |
+ cvox.ChromeVoxEventWatcher.handleControlChanged(evt.target); |
+ }, 0); |
+ return true; |
+}; |
+ |
+/** |
+ * Handles key press events. |
+ * |
+ * @param {Object} evt The event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.keyPressEventWatcher = function(evt) { |
+ cvox.ChromeVoxEventWatcher.handleTextChanged(false); |
+ |
+ if (cvox.ChromeVoxEventWatcher.eventToEat && |
+ evt.keyCode == cvox.ChromeVoxEventWatcher.eventToEat.keyCode) { |
+ evt.preventDefault(); |
+ evt.stopPropagation(); |
+ return false; |
+ } |
+ return true; |
+}; |
+ |
+/** |
+ * Handles key up events. |
+ * |
+ * @param {Object} evt The event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.keyUpEventWatcher = function(evt) { |
+ if (cvox.ChromeVoxEventWatcher.eventToEat && |
+ evt.keyCode == cvox.ChromeVoxEventWatcher.eventToEat.keyCode) { |
+ evt.stopPropagation(); |
+ evt.preventDefault(); |
+ return false; |
+ } |
+ return true; |
+}; |
+ |
+/** |
+ * Handles change events. |
+ * |
+ * @param {Object} evt The event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.changeEventWatcher = function(evt) { |
+ if (cvox.ChromeVoxEventWatcher.handleTextChanged(false)) { |
+ return true; |
+ } |
+ return true; |
+}; |
+ |
+/** |
+ * Handles select events. |
+ * |
+ * @param {Object} evt The event to process. |
+ * @return {boolean} True if the default action should be performed. |
+ */ |
+cvox.ChromeVoxEventWatcher.selectEventWatcher = function(evt) { |
+ if (cvox.ChromeVoxEventWatcher.handleTextChanged(false)) { |
+ return true; |
+ } |
+ return true; |
+}; |
+ |
+/** |
+ * Speaks updates to editable text controls as needed. |
+ * @param {boolean} isKeypress Was this change triggered by a keypress? |
+ * @return {boolean} True if an editable text control has focus. |
+ */ |
+cvox.ChromeVoxEventWatcher.handleTextChanged = function(isKeypress) { |
+ var currentFocus = document.activeElement; |
+ |
+ if (currentFocus != cvox.ChromeVoxEventWatcher.currentTextControl) { |
+ if (cvox.ChromeVoxEventWatcher.currentTextControl) { |
+ cvox.ChromeVoxEventWatcher.currentTextControl.removeEventListener( |
+ 'input', cvox.ChromeVoxEventWatcher.changeEventWatcher, false); |
+ cvox.ChromeVoxEventWatcher.currentTextControl.removeEventListener( |
+ 'click', cvox.ChromeVoxEventWatcher.changeEventWatcher, false); |
+ } |
+ cvox.ChromeVoxEventWatcher.currentTextControl = null; |
+ cvox.ChromeVoxEventWatcher.currentTextHandler = null; |
+ cvox.ChromeVoxEventWatcher.previousTextHandlerState = null; |
+ |
+ if (currentFocus == null) { |
+ return false; |
+ } |
+ |
+ if (currentFocus.constructor == HTMLInputElement && |
+ cvox.DomUtil.isInputTypeText(currentFocus)) { |
+ cvox.ChromeVoxEventWatcher.currentTextControl = currentFocus; |
+ cvox.ChromeVoxEventWatcher.currentTextHandler = |
+ new cvox.ChromeVoxEditableHTMLInput(currentFocus, cvox.ChromeVox.tts); |
+ } else if (currentFocus.constructor == HTMLTextAreaElement) { |
+ cvox.ChromeVoxEventWatcher.currentTextControl = currentFocus; |
+ cvox.ChromeVoxEventWatcher.currentTextHandler = |
+ new cvox.ChromeVoxEditableTextArea(currentFocus, cvox.ChromeVox.tts); |
+ } |
+ |
+ if (cvox.ChromeVoxEventWatcher.currentTextControl) { |
+ cvox.ChromeVoxEventWatcher.currentTextControl.addEventListener( |
+ 'input', cvox.ChromeVoxEventWatcher.changeEventWatcher, false); |
+ cvox.ChromeVoxEventWatcher.currentTextControl.addEventListener( |
+ 'click', cvox.ChromeVoxEventWatcher.changeEventWatcher, false); |
+ cvox.ChromeVoxEventWatcher.currentTextHandler.describe(); |
+ window.setTimeout(cvox.ChromeVoxEventWatcher.textTimer, |
+ cvox.ChromeVoxEventWatcher.TEXT_TIMER_INITIAL_DELAY_MS); |
+ cvox.ChromeVox.navigationManager.syncToNode( |
+ cvox.ChromeVoxEventWatcher.currentTextControl); |
+ } |
+ |
+ return (null != cvox.ChromeVoxEventWatcher.currentTextHandler); |
+ } |
+ |
+ if (cvox.ChromeVoxEventWatcher.currentTextHandler) { |
+ var handler = cvox.ChromeVoxEventWatcher.currentTextHandler; |
+ window.setTimeout(function() { |
+ // If this update was not triggered by an explicit user keypress, |
+ // and we already started speaking an update to this text control |
+ // very recently (less than 50 ms ago), restore the control to its |
+ // previous state and then speak the new update (interrupting any |
+ // ongoing speech). That way, if the user presses a key and the |
+ // page's javascript causes a few more characters to be inserted, |
+ // we'll speak it as one big update. |
+ var now = new Date().getTime(); |
+ if (!isKeypress && |
+ handler.needsUpdate() && |
+ cvox.ChromeVoxEventWatcher.previousTextHandlerState && |
+ now - cvox.ChromeVoxEventWatcher.lastKeypressTime < 50) { |
+ handler.restoreState( |
+ cvox.ChromeVoxEventWatcher.previousTextHandlerState); |
+ } |
+ |
+ handler.update(); |
+ }, 0); |
+ return true; |
+ } else { |
+ } |
+ |
+ return false; |
+}; |
+ |
+/** |
+ * Called repeatedly while a text box has focus, because many changes |
+ * to a text box don't ever generate events - e.g. if the page's javascript |
+ * changes the contents of the text box after some delay. |
+ */ |
+cvox.ChromeVoxEventWatcher.textTimer = function() { |
+ if (cvox.ChromeVoxEventWatcher.currentTextHandler && |
+ cvox.ChromeVoxEventWatcher.currentTextHandler.needsUpdate()) { |
+ cvox.ChromeVoxEventWatcher.handleTextChanged(false); |
+ } |
+ |
+ if (cvox.ChromeVoxEventWatcher.currentTextControl) { |
+ window.setTimeout(cvox.ChromeVoxEventWatcher.textTimer, |
+ cvox.ChromeVoxEventWatcher.TEXT_TIMER_DELAY_MS); |
+ } |
+}; |
+ |
+/** |
+ * Speaks updates to other form controls as needed. |
+ * @param {Element} control The target control. |
+ */ |
+cvox.ChromeVoxEventWatcher.handleControlChanged = function(control) { |
+ var newValue = cvox.ChromeVoxEventWatcher.getControlValueAndStateString( |
+ control); |
+ |
+ if (control != cvox.ChromeVoxEventWatcher.lastFocusedNode) { |
+ cvox.ChromeVoxEventWatcher.lastFocusedNode = control; |
+ cvox.ChromeVoxEventWatcher.lastFocusedNodeValue = newValue; |
+ return; |
+ } |
+ |
+ if (newValue == cvox.ChromeVoxEventWatcher.lastFocusedNodeValue) { |
+ return; |
+ } |
+ |
+ cvox.ChromeVoxEventWatcher.lastFocusedNodeValue = newValue; |
+ |
+ var announceChange = false; |
+ |
+ if (control.tagName == 'SELECT') { |
+ announceChange = true; |
+ } |
+ |
+ if (control.tagName == 'INPUT') { |
+ switch (control.type) { |
+ case 'checkbox': |
+ case 'color': |
+ case 'datetime': |
+ case 'datetime-local': |
+ case 'date': |
+ case 'month': |
+ case 'radio': |
+ case 'range': |
+ case 'week': |
+ announceChange = true; |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ if (announceChange && cvox.ChromeVoxUserCommands.silenceLevel == 0) { |
+ cvox.ChromeVox.tts.speak(newValue, 0, null); |
+ } |
+}; |
+ |
+/** |
+ * Handle actions on form controls triggered by key presses. |
+ * @param {Object} evt The event. |
+ * @return {boolean} True if this key event was handled. |
+ */ |
+cvox.ChromeVoxEventWatcher.handleControlAction = function(evt) { |
+ var control = evt.target; |
+ |
+ if (control.tagName == 'SELECT' && |
+ (evt.keyCode == 13 || evt.keyCode == 32)) { // Enter or Space |
+ evt.preventDefault(); |
+ evt.stopPropagation(); |
+ // Do nothing, but eat this keystroke |
+ return true; |
+ } |
+ |
+ if (control.tagName == 'INPUT' && control.type == 'range') { |
+ var value = parseFloat(control.value); |
+ var step; |
+ if (control.step && control.step > 0.0) { |
+ step = control.step; |
+ } else if (control.min && control.max) { |
+ var range = (control.max - control.min); |
+ if (range > 2 && range < 31) { |
+ step = 1; |
+ } else { |
+ step = (control.max - control.min) / 10; |
+ } |
+ } else { |
+ step = 1; |
+ } |
+ |
+ if (evt.keyCode == 37 || evt.keyCode == 38) { // left or up |
+ value -= step; |
+ } else if (evt.keyCode == 39 || evt.keyCode == 40) { // right or down |
+ value += step; |
+ } |
+ |
+ if (control.max && value > control.max) { |
+ value = control.max; |
+ } |
+ if (control.min && value < control.min) { |
+ value = control.min; |
+ } |
+ |
+ control.value = value; |
+ } |
+ |
+ return false; |
+}; |
+ |
+/** |
+ * Get a string representing a control's value and state. |
+ * @param {Element} control A control. |
+ * @return {string} A string representing a control's value and state. |
+ */ |
+cvox.ChromeVoxEventWatcher.getControlValueAndStateString = function( |
+ control) { |
+ var controlValue = cvox.DomUtil.getValue(control); |
+ var controlState = cvox.DomUtil.getBasicNodeState(control); |
+ var controlTitle = cvox.DomUtil.getTitle(control); |
+ var controlLabel = cvox.DomUtil.getLabel(control, false); |
+ if ((controlTitle.length < 1) && (controlLabel.length < 1)) { |
+ controlLabel = cvox.DomUtil.getLabel(control, true); |
+ } |
+ return controlLabel + ' ' + controlTitle + ' ' + controlValue + ' ' + |
+ controlState; |
+}; |
Property changes on: chrome/browser/resources/access_chromevox/chromevox/injected/event_watcher.js |
___________________________________________________________________ |
Added: svn:executable |
+ * |
Added: svn:eol-style |
+ LF |