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 87 // Widgets do not respond to sticky key. 88 cvox.ChromeVox.stickyOverride = false; 89 90 if (this.getNameMsg() && this.getHelpMsg()) { 91 cvox.$m(this.getNameMsg()). 92 andPause(). 93 andMessage(this.getHelpMsg()). 94 speakFlush(); 95 } 96 cvox.ChromeVox.earcons.playEarcon(cvox.AbstractEarcons.OBJECT_OPEN); 97 98 this.active = true; 99}; 100 101 102/** 103 * Visual/aural hide of this widget. 104 * @param {boolean=} opt_noSync Whether to attempt to sync to the node before 105 * this widget was first shown. If left unspecified or false, an attempt to sync 106 * will be made. 107 */ 108cvox.Widget.prototype.hide = function(opt_noSync) { 109 window.removeEventListener('keypress', this.onKeyPress, true); 110 window.removeEventListener('keydown', this.onKeyDown, true); 111 cvox.ChromeVox.stickyOverride = null; 112 113 cvox.ChromeVox.earcons.playEarcon(cvox.AbstractEarcons.OBJECT_CLOSE); 114 if (!opt_noSync) { 115 this.initialNode = this.initialNode.nodeType == 1 ? 116 this.initialNode : this.initialNode.parentNode; 117 cvox.ApiImplementation.syncToNode(this.initialNode, 118 true, 119 cvox.AbstractTts.QUEUE_MODE_QUEUE); 120 } 121 122 this.active = false; 123}; 124 125 126/** 127 * Toggle between showing and hiding. 128 */ 129cvox.Widget.prototype.toggle = function() { 130 if (this.isActive()) { 131 this.hide(); 132 } else { 133 this.show(); 134 } 135}; 136 137 138/** 139 * The name of the widget. 140 * @return {!Array} The message id referencing the name of the widget in an 141 * array argument form passable to cvox.ChromeVox.msgs.getMsg.apply. 142 */ 143cvox.Widget.prototype.getNameMsg = goog.abstractMethod; 144 145 146/** 147 * Gets the help message for the widget. 148 * The help message succintly describes how to use the widget. 149 * @return {string} The message id referencing the help for the widget. 150 */ 151cvox.Widget.prototype.getHelpMsg = goog.abstractMethod; 152 153 154/** 155 * The default widget key down handler. 156 * 157 * @param {Event} evt The keyDown event. 158 * @return {boolean} Whether or not the event was handled. 159 * 160 * @protected 161 */ 162cvox.Widget.prototype.onKeyDown = function(evt) { 163 if (evt.keyCode == 27) { // Escape 164 this.hide(); 165 evt.preventDefault(); 166 return true; 167 } else if (evt.keyCode == 9) { // Tab 168 this.hide(); 169 return true; 170 } else if (evt.keyCode == 17) { 171 cvox.ChromeVox.tts.stop(); 172 } 173 174 evt.stopPropagation(); 175 return true; 176}; 177 178 179/** 180 * The default widget key press handler. 181 * 182 * @param {Event} evt The keyPress event. 183 * @return {boolean} Whether or not the event was handled. 184 * 185 * @protected 186 */ 187cvox.Widget.prototype.onKeyPress = function(evt) { 188 return false; 189}; 190/** 191 * @return {boolean} True if any widget is currently active. 192 */ 193cvox.Widget.isActive = function() { 194 return (cvox.Widget.ref_ && cvox.Widget.ref_.isActive()) || false; 195}; 196