widget.js revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 Base class for all ChromeVox widgets. 7 * 8 * Widgets are keyboard driven and modal mediums for ChromeVox to expose 9 * additional features such as lists, interactive search, or grids. 10 */ 11 12goog.provide('cvox.Widget'); 13 14goog.require('cvox.AbstractEarcons'); 15goog.require('cvox.ApiImplementation'); 16goog.require('cvox.ChromeVox'); 17goog.require('cvox.SpokenMessages'); 18 19/** 20 * Keeps a reference to a currently or formerly active widget. This enforces 21 * the singleton nature of widgets. 22 * @type {cvox.Widget} 23 * @private 24 */ 25cvox.Widget.ref_; 26 27 28/** 29 * @constructor 30 */ 31cvox.Widget = function() { 32 /** 33 * @type {boolean} 34 * @protected 35 */ 36 this.active = false; 37 38 39 /** 40 * Keeps a reference to a node which should receive focus once a widget hides. 41 * @type {Node} 42 * @protected 43 */ 44 this.initialFocus = null; 45 46 /** 47 * Keeps a reference to a node which should receive selection once a widget 48 * hides. 49 * @type {Node} 50 * @protected 51 */ 52 this.initialNode = null; 53 54 // Checks to see if there is a current widget in use. 55 if (!cvox.Widget.ref_ || !cvox.Widget.ref_.isActive()) { 56 cvox.Widget.ref_ = this; 57 } 58}; 59 60 61/** 62 * Returns whether or not the widget is active. 63 * @return {boolean} Whether the widget is active. 64 */ 65cvox.Widget.prototype.isActive = function() { 66 return this.active; 67}; 68 69 70/** 71 * Visual/aural display of this widget. 72 */ 73cvox.Widget.prototype.show = function() { 74 if (this.isActive()) { 75 // Only one widget should be shown at any given time. 76 this.hide(true); 77 } 78 this.onKeyDown = goog.bind(this.onKeyDown, this); 79 this.onKeyPress = goog.bind(this.onKeyPress, this); 80 window.addEventListener('keydown', this.onKeyDown, true); 81 window.addEventListener('keypress', this.onKeyPress, true); 82 83 this.initialNode = 84 cvox.ChromeVox.navigationManager.getCurrentNode(); 85 this.initialFocus = document.activeElement; 86 this.isStickyOn = cvox.ChromeVox.isStickyOn; 87 88 // Widgets do not respond to sticky key. 89 cvox.ChromeVox.host.sendToBackgroundPage({ 90 'target': 'Prefs', 91 'action': 'setPref', 92 'pref': 'sticky', 93 'value': false, 94 'announce': false}); 95 96 if (this.getNameMsg() && this.getHelpMsg()) { 97 cvox.$m(this.getNameMsg()). 98 andPause(). 99 andMessage(this.getHelpMsg()). 100 speakFlush(); 101 } 102 cvox.ChromeVox.earcons.playEarcon(cvox.AbstractEarcons.OBJECT_OPEN); 103 104 this.active = true; 105}; 106 107 108/** 109 * Visual/aural hide of this widget. 110 * @param {boolean=} opt_noSync Whether to attempt to sync to the node before 111 * this widget was first shown. If left unspecified or false, an attempt to sync 112 * will be made. 113 */ 114cvox.Widget.prototype.hide = function(opt_noSync) { 115 window.removeEventListener('keypress', this.onKeyPress, true); 116 window.removeEventListener('keydown', this.onKeyDown, true); 117 118 cvox.ChromeVox.host.sendToBackgroundPage({ 119 'target': 'Prefs', 120 'action': 'setPref', 121 'pref': 'sticky', 122 'value': this.isStickyOn, 123 'announce': false}); 124 125 cvox.ChromeVox.earcons.playEarcon(cvox.AbstractEarcons.OBJECT_CLOSE); 126 if (!opt_noSync) { 127 this.initialNode = this.initialNode.nodeType == 1 ? 128 this.initialNode : this.initialNode.parentNode; 129 cvox.ApiImplementation.syncToNode(this.initialNode, 130 true, 131 cvox.AbstractTts.QUEUE_MODE_QUEUE); 132 } 133 134 this.active = false; 135}; 136 137 138/** 139 * Toggle between showing and hiding. 140 */ 141cvox.Widget.prototype.toggle = function() { 142 if (this.isActive()) { 143 this.hide(); 144 } else { 145 this.show(); 146 } 147}; 148 149 150/** 151 * The name of the widget. 152 * @return {!Array} The message id referencing the name of the widget in an 153 * array argument form passable to cvox.ChromeVox.msgs.getMsg.apply. 154 */ 155cvox.Widget.prototype.getNameMsg = goog.abstractMethod; 156 157 158/** 159 * Gets the help message for the widget. 160 * The help message succintly describes how to use the widget. 161 * @return {string} The message id referencing the help for the widget. 162 */ 163cvox.Widget.prototype.getHelpMsg = goog.abstractMethod; 164 165 166/** 167 * The default widget key down handler. 168 * 169 * @param {Event} evt The keyDown event. 170 * @return {boolean} Whether or not the event was handled. 171 * 172 * @protected 173 */ 174cvox.Widget.prototype.onKeyDown = function(evt) { 175 if (evt.keyCode == 27) { // Escape 176 this.hide(); 177 evt.preventDefault(); 178 return true; 179 } else if (evt.keyCode == 9) { // Tab 180 this.hide(); 181 return true; 182 } else if (evt.keyCode == 17) { 183 cvox.ChromeVox.tts.stop(); 184 } 185 186 evt.stopPropagation(); 187 return true; 188}; 189 190 191/** 192 * The default widget key press handler. 193 * 194 * @param {Event} evt The keyPress event. 195 * @return {boolean} Whether or not the event was handled. 196 * 197 * @protected 198 */ 199cvox.Widget.prototype.onKeyPress = function(evt) { 200 return false; 201}; 202/** 203 * @return {boolean} True if any widget is currently active. 204 */ 205cvox.Widget.isActive = function() { 206 return (cvox.Widget.ref_ && cvox.Widget.ref_.isActive()) || false; 207}; 208