Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Unified Diff: chrome/browser/resources/access_chromevox/powerkey-bundle.js

Issue 6254007: Adding ChromeVox as a component extensions (enabled only for ChromeOS, for no... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/access_chromevox/powerkey-bundle.js
===================================================================
--- chrome/browser/resources/access_chromevox/powerkey-bundle.js (revision 0)
+++ chrome/browser/resources/access_chromevox/powerkey-bundle.js (revision 0)
@@ -0,0 +1,1434 @@
+// 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.
+
+/**
+ * Javascript class for providing keyboard interface enhancements for
+ * Web 2.0 applications.
+ * @param {string} context The user specified string value, which is the
+ * starting context of the application. This can be changed later.
+ * @param {AxsJAX} axsJAX The AxsJAX object provided by the user.
+ * @constructor
+ */
+var PowerKey = function(context, axsJAX) {
+ /**
+ * Holds the current application context.
+ * @type {string}
+ */
+ this.context = context;
+
+ /**
+ * The div element holding the completion text field.
+ * @type {Element?}
+ */
+ this.cmpFloatElement = null;
+
+ /**
+ * The completion text field element.
+ * @type {Element?}
+ */
+ this.cmpTextElement = null;
+
+ /**
+ * The div element for the page-wide background of the completion field.
+ * @type {Element?}
+ */
+ this.backgroundDivElement = null;
+
+ /**
+ * The div element which holds the text of the selected list item.
+ * @type {Element?}
+ * @private
+ */
+ this.listElement_ = null;
+
+ /**
+ * The div element inside the completion div, holding the selected list item.
+ * @type {Element?}
+ * @private
+ */
+ this.cmpDivElement_ = null;
+
+ /**
+ * Variable to hold the completion mode prompt string.
+ * @type {string}
+ * @private
+ */
+ this.completionPromptStr_ = 'Enter Completion';
+
+ /**
+ * Variable to hold the completion mode prompt string when the completion
+ * list is empty.
+ * @type {string}
+ * @private
+ */
+ this.noCompletionStr_ = 'No completions found';
+
+ /**
+ * AxsJAX object
+ * @type {AxsJAX?}
+ * @private
+ */
+ this.axsJAX_ = null;
+
+ /**
+ * The list of completions to select from.
+ * @type {Array}
+ * @private
+ */
+ this.cmpList_ = [];
+
+ /**
+ * The navigation position in the list.
+ * @type {number}
+ * @private
+ */
+ this.listPos_ = -1;
+
+ /**
+ * A collection of completion lists.
+ * @type {Object}
+ * @private
+ */
+ this.managedCmpLists_ = new Array();
+
+ /**
+ * The current position in the managed completion lists.
+ * @type {number}
+ * @private
+ */
+ this.managedCmpListsPos_ = -1;
+
+ /**
+ * Whether to hide completion field on blur.
+ * @type {boolean}
+ * @private
+ */
+ this.hideCmdFieldOnBlur_ = false;
+
+ /**
+ * Whether to show the page-wide background.
+ * @type {boolean}
+ * @private
+ */
+ this.showBackgroundDiv_ = false;
+
+ /**
+ * Color of the page-wide background.
+ * @type {string}
+ * @private
+ */
+ this.backgroundColor_ = '#000000';
+
+ /**
+ * Transparency of the page-wide background 100 being fully
+ * opaque and 0 being fully transparent.
+ * @type {number}
+ * @private
+ */
+ this.backgroundTransparency_ = 0;
+
+ /**
+ * Function to callback on browsing or filtering.
+ * @type {?function(string, string)}
+ * @private
+ */
+ this.browseCallback_ = null;
+
+ /**
+ * The HashMap which provides a context-based mapping
+ * from keys to functions.
+ * @type {?Object}
+ * @private
+ */
+ this.completionActionMap_ = null;
+
+ /**
+ * The completion handler.
+ * handler = function(completion, index, elementId, args) {}.
+ * @type {?function(string, string, Node, Array)}
+ * @private
+ */
+ this.completionHandler_ = null;
+
+ /**
+ * Function to callback on changing the managed completion list.
+ * @type {?function(string)}
+ * @private
+ */
+ this.managedCompletionListCallback_ = null;
+
+ this.nodeMap = {};
+ this.indexList_ = {};
+ if (axsJAX && PowerKey.isGecko) {
+ this.axsJAX_ = axsJAX;
+ }
+ var self = this;
+ this.attachHandlerAndListen(window, PowerKey.Event.RESIZE,
+ function(evt) {
+ self.onPageResize_.call(self, evt);
+ }, null);
+};
+
+
+/**
+ * The reg exp indicating the pattern of the parameter to a completion. It
+ * should start with '<', end wit '>' and contain only characters and numbers.
+ * @type {RegExp}
+ */
+PowerKey.CMD_PARAM = /^\<[A-Z|a-z|0-9|\-|\_]+\>$/;
+
+
+/**
+ * The reg exp to check if there are spaces, new lines or carriage returns at
+ * the beginning of a string.
+ * @type {RegExp}
+ */
+PowerKey.LEFT_TRIMMABLE = /^(\s|\r|\n)+/;
+
+
+/**
+ * The reg exp to check if there are spaces, new lines or carriage returns at
+ * the end of a string.
+ * @type {RegExp}
+ */
+PowerKey.RIGHT_TRIMMABLE = /(\s|\r|\n)+$/;
+
+
+/**
+ * Attaches event listener and sets the user specified handler or the
+ * default handler if the action map is provided.
+ * @param {EventTarget?} target The element to attach the event listerner to.
+ * @param {string} event The event to listen for.
+ * @notypecheck {Function?} handler.
+ * @param {Function?} handler The event handler.
+ * @param {Object?} actionMap The HashMap which provides a context-based
+ * mapping from keys to functions.
+ */
+PowerKey.prototype.attachHandlerAndListen = function(target,
+ event,
+ handler,
+ actionMap) {
+ // Firefox
+ if (PowerKey.isGecko && handler) {
+ target.addEventListener(event, handler, true);
+ } else if (PowerKey.isIE && handler) { // IE
+ target.attachEvent(event, function(event) {
+ handler(event);});
+ }
+ // Use default handler if the action map is provided.
+ if (actionMap) {
+ actionMap = this.expandActionMap(actionMap);
+ var handlerObj = new PowerKey.DefaultHandler(actionMap);
+ var pkObj = this;
+ this.attachHandlerAndListen(target, event, function(evt) {
+ handlerObj.handler(evt, handlerObj, pkObj);
+ }, null);
+ }
+};
+
+
+/**
+ * Expand the action map by splitting multiple keys specified in a single
+ * mapping separated by commas.
+ * @param {Object} map The action map.
+ * @return {Object} Returns the updated action map.
+ */
+PowerKey.prototype.expandActionMap = function(map) {
+ for (var key in map) {
+ var toks = key.split(',');
+ if (toks.length > 1) {
+ for (var i in toks) {
+ map[toks[i]] = map[key];
+ }
+ map[key] = null;
+ }
+ }
+ return map;
+};
+
+
+/**
+ * Detaches event listener with the user specified event and handler.
+ * @param {Element} target The element to detach the event listerner from.
+ * @param {string} event The event to detach.
+ * @param {Function} handler The event handler to stop calling.
+ */
+PowerKey.prototype.detachHandler = function(target, event, handler) {
+ // Firefox
+ if (PowerKey.isGecko) {
+ target.removeEventListener(event, handler, true);
+ } else if (PowerKey.isIE) { // IE
+ target.detachEvent(event, handler);
+ }
+};
+
+
+/**
+ * Creates a floating element for holding the completion shell's text field.
+ * @param {Element} parent The element whose child this element will be.
+ * @param {number} size The size of the completion text field in # of
+ * characters.
+ * @param {Function?} handler The completion handler.
+ * handler = function(completion, index, node, args) {}.
+ * @param {Object?} actionMap The object consisting of completion strings as
+ * keys and functions as values.
+ * @param {Array?} completionList The array of completions.
+ * @param {boolean} browseOnly Whether the completion list is browse-only.
+ */
+PowerKey.prototype.createCompletionField = function(parent,
+ size,
+ handler,
+ actionMap,
+ completionList,
+ browseOnly) {
+ var self = this;
+ var floatId, fieldId, oldCmdNode, divId, bgDivId;
+ // If the completion field already exists, remove it and create a new one.
+ if (this.cmpFloatElement) {
+ // TODO: remove the handlers attached to cmpTextElement before removing
+ // the completion field. The inline handler needs to be moved outside.
+ this.cmpFloatElement.parentNode.removeChild(this.cmpFloatElement);
+ }
+ do {
+ floatId = 'completionField_' + Math.floor(Math.random() * 1001);
+ fieldId = 'txt_' + floatId;
+ divId = 'div_' + floatId;
+ bgDivId = 'bgdiv_' + floatId;
+ oldCmdNode = document.getElementById(floatId);
+ } while (oldCmdNode);
+
+ if (this.backgroundDivElement) {
+ this.backgroundDivElement.parentNode.removeChild(
+ this.backgroundDivElement);
+ }
+
+ // The background element
+ var bgNode = document.createElement('div');
+ bgNode.id = bgDivId;
+ bgNode.style.display = 'none';
+
+ // create completion field element
+ var cmpNode = document.createElement('div');
+ cmpNode.id = floatId;
+ cmpNode.style.position = 'absolute';
+
+ // text field in which the user types the completion.
+ var txtNode = document.createElement('input');
+ txtNode.type = 'text';
+ txtNode.id = fieldId;
+ txtNode.size = size;
+ txtNode.value = '';
+ txtNode.setAttribute('aria-owns', divId);
+ txtNode.onkeypress =
+ function(evt) {
+ evt.stopPropagation();
+ if (evt.keyCode == PowerKey.keyCodes.TAB) {
+ return false;
+ }
+ };
+ txtNode.readOnly = browseOnly;
+
+ if (browseOnly) {
+ txtNode.style.fontSize = 0;
+ }
+
+ // This div element holds the currently selected completion list item.
+ var divNode = document.createElement('div');
+ divNode.id = divId;
+ divNode.setAttribute('tabindex', 0);
+ divNode.setAttribute('role', 'row');
+
+ cmpNode.appendChild(divNode);
+ cmpNode.appendChild(txtNode);
+ parent.appendChild(bgNode);
+ parent.appendChild(cmpNode);
+
+ this.cmpFloatElement = cmpNode;
+ this.cmpTextElement = txtNode;
+ this.backgroundDivElement = bgNode;
+ this.cmpDivElement_ = divNode;
+ this.listElement_ = null;
+
+ this.cmpFloatElement.className = 'pkHiddenStatus';
+ this.cmpTextElement.className = 'pkOpaqueCompletionText';
+ this.backgroundDivElement.className = 'pkBackgroundHide';
+
+ this.completionActionMap_ = actionMap;
+ this.completionHandler_ = handler;
+
+ // Initialize completion list
+ if (completionList) {
+ this.addCompletionListByName(this.context, completionList,
+ this.completionPromptStr_);
+ this.setCompletionListByName(this.context);
+ }
+
+ // filter the completion list on keyup if it is not UP or DOWN arrow.
+ this.attachHandlerAndListen(this.cmpTextElement, PowerKey.Event.KEYUP,
+ function(evt) {
+ self.handleCompletionKeyUp_.call(self, evt);
+ }, null);
+
+ this.attachHandlerAndListen(this.cmpTextElement, PowerKey.Event.KEYDOWN,
+ function(evt) {
+ self.handleCompletionKeyDown_.call(self, evt);
+ }, null);
+
+ this.attachHandlerAndListen(this.cmpTextElement, PowerKey.Event.BLUR,
+ function(evt) {
+ if (self.hideCmdFieldOnBlur_) {
+ self.updateCompletionField(PowerKey.status.HIDDEN);
+ }
+ }, null);
+};
+
+
+/**
+ * Sets if the completion field is browse only.
+ * @param {boolean} browseOnly If the completion field is browse only.
+ */
+PowerKey.prototype.setBrowseOnly = function(browseOnly) {
+ if (this.cmpTextElement) {
+ this.cmpTextElement.readOnly = browseOnly;
+ }
+};
+
+
+/**
+ * Tells whether to hide the completion field when focus is lost.
+ * @param {boolean} hide If true, hide the completion field on blur.
+ */
+PowerKey.prototype.setAutoHideCompletionField = function(hide) {
+ this.hideCmdFieldOnBlur_ = hide;
+};
+
+
+/**
+ * Sets the function to be called back when browsing/filtering the list.
+ * @param {Function} callback The callback function.
+ */
+PowerKey.prototype.setBrowseCallback = function(callback) {
+ this.browseCallback_ = callback;
+};
+
+
+/**
+ * Sets the map which provides a context-based mapping
+ * from completion values (keys) to functions. Note that
+ * setting this map implies use of the default completion
+ * handler and setting you custom one will have no effect.
+ * A mapping is triggered after the user has selected an
+ * action form the auto-completion list via pressing
+ * ENTER.
+ * @param {Object} completionActionMap The map instance.
+ */
+PowerKey.prototype.setCompletionActionMap = function(completionActionMap) {
+ this.completionActionMap_ = completionActionMap;
+};
+
+
+/**
+ * Sets the handler which provides custom actions for completion
+ * values. Note that setting a completion map implies use of the
+ * default completion handler therefore setting the completion
+ * handler will have no effect. The handler signature is as follows:
+ *
+ * handler = function(completion, index, elementId, args) {}.
+ *
+ * @param {Function} completionHandler The map instance.
+ */
+PowerKey.prototype.setCompletionHandler = function(completionHandler) {
+ this.completionHandler_ = completionHandler;
+};
+
+
+/**
+ * Sets the function to be called back when changing the managed
+ * (named) completion list.
+ * @param {Function} callback The callback function.
+ */
+PowerKey.prototype.setManagedCompletionListCallback = function(callback) {
+ this.managedCompletionListCallback_ = callback;
+};
+
+
+/**
+ * Sets the label to be displayed and spoken, when the
+ * completion field is made visible.
+ * @param {string} str The string to display.
+ */
+PowerKey.prototype.setCompletionPromptStr = function(str) {
+ this.completionPromptStr_ = str;
+};
+
+
+/**
+ * Sets the label to be displayed and spoken, when there are no completions
+ * in the completion list.
+ * @param {string} str The string to display.
+ */
+PowerKey.prototype.setNoCompletionStr = function(str) {
+ this.noCompletionStr_ = str;
+};
+
+
+/**
+ * Sets the completion list.
+ * @param {Array} list The array to be used as the completion list.
+ */
+PowerKey.prototype.setCompletionList = function(list) {
+ if (!list) {
+ return;
+ }
+ this.managedCmpListsPos_ = -1;
+ this.cmpList_ = list;
+ this.filterList_ = this.cmpList_;
+ this.indexList_ = {};
+ for (var i = 0, cmp; cmp = this.cmpList_[i]; i++) {
+ this.indexList_[cmp.toLowerCase()] = i;
+ }
+ this.listPos_ = -1;
+};
+
+
+/**
+ * Adds a completion list. A completion list is identified by its
+ * name. If a completion list by that name already exists,
+ * then the Ids of the strings in the existing list are removed.
+ * @param {string} name The name of the completion list.
+ * @param {Array} list The array to be used as the completion list.
+ * @param {string} prompt The prompt for this completion list.
+ */
+PowerKey.prototype.addCompletionListByName = function(name, list, prompt) {
+ var oldManagedCmpList = this.getManagedCompletionListByName_(name);
+ if (oldManagedCmpList) {
+ for (var i = 0, item; item = oldManagedCmpList.values[i]; i++) {
+ this.nodeMap[item] = null;
+ }
+ oldManagedCmpList.list = list;
+ oldManagedCmpList.completionPromptStr = prompt;
+ } else {
+ var newManagedCmpList = new Object();
+ newManagedCmpList.values = list;
+ newManagedCmpList.name = name;
+ newManagedCmpList.completionPromptStr = prompt;
+ newManagedCmpList.index = this.managedCmpLists_.length;
+ this.managedCmpLists_.push(newManagedCmpList);
+ }
+};
+
+
+/**
+ * Sets the completion list.
+ * @param {string} name The name of the list to be used for completion.
+ */
+PowerKey.prototype.setCompletionListByName = function(name) {
+ var cmpList = this.getManagedCompletionListByName_(name);
+ if (!cmpList) {
+ return;
+ }
+ this.setManagedCompletionList_(cmpList.index);
+};
+
+
+/**
+ * Gets a completion list given its name.
+ * @param {string} name The name of the completion list.
+ * @return {Object?} The list with the given name.
+ * @private
+ */
+PowerKey.prototype.getManagedCompletionListByName_ = function(name) {
+ for (var i = 0, list; list = this.managedCmpLists_[i]; i++) {
+ if (list.name == name) {
+ return list;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * Sets the color and transparency of the background.
+ * @param {string?} listName Name of the completion list for which to set
+ * the background property.
+ * @param {boolean} show Whether to show the background div when completion
+ * field is made visible.
+ * @param {string?} color The color of the background.
+ * @param {number?} transparency The transparency level, 100 being fully
+ * opaque and 0 being fully transparent.
+ */
+PowerKey.prototype.setBackgroundStyle = function(listName,
+ show,
+ color,
+ transparency) {
+ if (listName) {
+ var managedCmpList = this.getManagedCompletionListByName_(listName);
+ if (managedCmpList) {
+ managedCmpList.backgroundShow = show;
+ managedCmpList.backgroundColor = color;
+ managedCmpList.backgroundTransparency = transparency;
+ }
+ } else {
+ this.showBackgroundDiv_ = show;
+ if (color) {
+ this.backgroundColor_ = color;
+ }
+ if (transparency) {
+ this.backgroundTransparency_ = transparency;
+ }
+ }
+};
+
+
+/**
+ * Sets the style of the completion field.
+ * @param {string} textColor Color of the completion text.
+ * @param {number} textSize Size of the completion text.
+ * @param {string} bgColor Background color of the completion field.
+ * @param {number} transparency Background tranparency, 100 being fully
+ * opaque and 0 being fully transparent.
+ * @param {string} fontStyle Font style of the completion text.
+ * @param {string} fontFamily Font family of the completion text.
+ */
+PowerKey.prototype.setCompletionFieldStyle = function(textColor,
+ textSize,
+ bgColor,
+ transparency,
+ fontStyle,
+ fontFamily) {
+ if (textSize) {
+ this.cmpFloatElement.style.fontSize = textSize + 'px';
+ this.cmpTextElement.style.fontSize = textSize + 'px';
+ this.cmpTextElement.style.height = (textSize + 5) + 'px';
+ }
+ if (textColor) {
+ this.cmpFloatElement.style.color = textColor;
+ this.cmpTextElement.style.color = textColor;
+ }
+ if (bgColor) {
+ this.cmpFloatElement.style.backgroundColor = bgColor;
+ this.cmpTextElement.style.backgroundColor = bgColor;
+ }
+ if (transparency) {
+ this.cmpFloatElement.style.setProperty('-moz-opacity',
+ '' + (transparency / 100), '');
+ }
+ if (fontStyle) {
+ this.cmpFloatElement.style.fontStyle = fontStyle;
+ this.cmpTextElement.style.fontStyle = fontStyle;
+ }
+ if (fontFamily) {
+ this.cmpFloatElement.style.fontFamily = fontFamily;
+ this.cmpTextElement.style.fontFamily = fontFamily;
+ }
+};
+
+
+/**
+ * Updates the completion field element with the new visibility status
+ * and location parameters.
+ * @param {string} status Indicates whether the completion field should be
+ * made PowerKey.status.VISIBLE or PowerKey.status.HIDDEN.
+ * @param {boolean} opt_resize Indicates whether resizing is necessary.
+ * @param {number} opt_top The y-coordinate pixel location of the top
+ * border of the element.
+ * @param {number} opt_left The x-coordinate pixel location of the left
+ * border of the element.
+ */
+PowerKey.prototype.updateCompletionField = function(status,
+ opt_resize,
+ opt_top,
+ opt_left) {
+ if (!this.cmpFloatElement) {
+ return;
+ }
+ var backgoundShow = this.showBackgroundDiv_;
+ var backgroundColor = this.backgroundColor_;
+ var backgroundTransparency = this.backgroundTransparency_;
+ var managedCmpList = undefined;
+ if (this.managedCmpListsPos_ > -1) {
+ managedCmpList = this.managedCmpLists_[this.managedCmpListsPos_];
+ if (managedCmpList.backgoundShow) {
+ backgoundShow = managedCmpList.backgoundShow;
+ }
+ if (managedCmpList.backgroundColor) {
+ backgroundColor = managedCmpList.backgroundColor;
+ }
+ if (managedCmpList.backgroundTransparency) {
+ managedCmpList.backgroundTransparency = backgroundTransparency;
+ }
+ }
+ if (status == PowerKey.status.VISIBLE) {
+ if (this.cmpFloatElement.className == 'pkHiddenStatus' ||
+ this.listPos_ < 0) {
+ if (managedCmpList) {
+ this.setListElement_(managedCmpList.completionPromptStr);
+ } else {
+ this.setListElement_(this.completionPromptStr_);
+ }
+ }
+ this.showBackground_(backgoundShow, backgroundColor,
+ backgroundTransparency);
+ this.cmpFloatElement.className = 'pkVisibleStatus';
+ // Need to do this for IE. Setting focus immediately after making it
+ // visible generates an error. Hence have to set the timeout.
+ var elem = this.cmpTextElement;
+ window.setTimeout(function() {elem.focus();}, 0);
+ } else if (status == PowerKey.status.HIDDEN) {
+ if (PowerKey.isIE && this.listElement_) {
+ this.listElement_.innerText = '';
+ } else if (this.listElement_) {
+ this.listElement_.textContent = '';
+ }
+ this.showBackground_(false, backgroundColor, backgroundTransparency);
+ this.cmpFloatElement.className = 'pkHiddenStatus';
+ this.cmpTextElement.value = '';
+ this.listPos_ = -1;
+ }
+ if (opt_resize) {
+ var viewportSz = PowerKey.getViewportSize();
+ if (!opt_top) {
+ opt_top = viewportSz.height - this.cmpFloatElement.offsetHeight;
+ }
+ if (!opt_left) {
+ opt_left = 0;
+ }
+ this.cmpFloatElement.style.top = opt_top;
+ this.cmpFloatElement.style.left = opt_left;
+ }
+};
+
+
+/**
+ * Creates the list of completions from the text content of the elements
+ * obtained from the xpath which satisfy the function func.
+ * @param {string} tags The tags to be selected.
+ * @param {Function} func Only those elements are considered for which
+ * this function returns true.
+ * @param {Function?} getText Function to get text for this list element.
+ * @param {boolean} newList If this is true, all entries in idMap
+ * are erased, and a new mapping of completions and IDs is created.
+ * @return {Array} The array of completion strings.
+ */
+PowerKey.prototype.createCompletionList = function(tags,
+ func,
+ getText,
+ newList) {
+ var cmpList = new Array();
+ var tagArray = tags.split(/\s+/);
+ if (newList) {
+ delete this.nodeMap;
+ this.nodeMap = new Object();
+ }
+ for (var j = 0, tag; tag = tagArray[j]; j++) {
+ var nodeArray = document.getElementsByTagName(tag);
+ for (var i = 0, node; node = nodeArray[i]; i++) {
+ if (func(node)) {
+ if (getText) {
+ var label = getText(node);
+ } else {
+ label = PowerKey.isIE ?
+ node.innerText : node.textContent;
+ }
+ if (label) {
+ label = PowerKey.rightTrim(PowerKey.leftTrim(label));
+ label = label.replace(/\n/g, '');
+ if (label.toLowerCase().indexOf('ctrl+') === 0) {
+ label = label.substring(6);
+ }
+ cmpList.push(label);
+ if (String(this.nodeMap[label.toLowerCase()]) == 'undefined') {
+ this.nodeMap[label.toLowerCase()] = node;
+ }
+ }
+ }
+ }
+ }
+ return cmpList;
+};
+
+
+/**
+ * Resizes the background upon page resize if the background is
+ * currently showing.
+ * @param {Event} evt The resize event.
+ * @private
+ */
+PowerKey.prototype.onPageResize_ = function(evt) {
+ // wait for the resize to complete so that the new
+ // viewport size is available.
+ var self = this;
+ window.setTimeout(function() {
+ if (self.showBackgroundDiv_ &&
+ self.backgroundDivElement.style.display == 'block') {
+ self.showBackground_(self.showBackgroundDiv_, self.backgroundColor_,
+ self.backgroundTransparency_);
+ }
+ }, 0);
+};
+
+
+/**
+ * Handle keyup events. If the key is not an arrow key, filter the list by the
+ * contents of the completion text field.
+ * @param {Object} evt The key event object.
+ * @private
+ */
+PowerKey.prototype.handleCompletionKeyUp_ = function(evt) {
+ if (this.cmpTextElement.value.length === 0) {
+ this.filterList_ = this.cmpList_;
+ }
+ if (evt.keyCode != PowerKey.keyCodes.ARROWUP &&
+ evt.keyCode != PowerKey.keyCodes.ARROWDOWN &&
+ evt.keyCode != PowerKey.keyCodes.ARROWLEFT &&
+ evt.keyCode != PowerKey.keyCodes.ARROWRIGHT &&
+ evt.keyCode != PowerKey.keyCodes.ENTER &&
+ evt.keyCode != PowerKey.keyCodes.TAB &&
+ evt.keyCode != PowerKey.keyCodes.ESC) {
+
+ if (this.cmpTextElement.value.length) {
+ this.filterList_ = this.getWordFilterMatches_(this.cmpList_,
+ this.cmpTextElement.value, 50);
+ this.listPos_ = -1;
+ if (this.filterList_.length > 0) {
+ this.setListElement_(this.filterList_[0]);
+ this.listPos_ = 0;
+ } else {
+ this.setListElement_(this.noCompletionStr_);
+ }
+ } else {
+ if (this.managedCmpListsPos_ > -1) {
+ var managedCmpList = this.managedCmpLists_[this.managedCmpListsPos_];
+ this.setListElement_(managedCmpList.completionPromptStr);
+ } else {
+ this.setListElement_(this.completionPromptStr_);
+ }
+ }
+ }
+ // Handle ENTER key pressed in the completion field.
+ if (evt.keyCode == PowerKey.keyCodes.ENTER) {
+ if (this.cmpTextElement.readOnly) {
+ return;
+ }
+ // Select current filtered list item.
+ if (this.filterList_ &&
+ this.filterList_.length > 0 &&
+ this.filterList_[this.listPos_] &&
+ this.cmpTextElement.value != this.filterList_[this.listPos_] &&
+ // Does not have parameters or is incomplete
+ (this.filterList_[this.listPos_].indexOf('<') < 0 ||
+ (this.filterList_[this.listPos_].indexOf('<') >= 0 &&
+ this.filterList_[this.listPos_].split(' ').length >
+ this.cmpTextElement.value.split(' ').length))) {
+ this.selectCurrentListItem_();
+ }
+ var str = this.cmpTextElement.value;
+ var originalCmd = (PowerKey.isIE ? this.listElement_.innerText :
+ this.listElement_.textContent).toLowerCase();
+ // Change only the basic portion (non-argument) of the selection to lower
+ // case. For ex: In 'Watch Video funnySeries', only 'Watch Video' is
+ // changed to lower case.
+ var pos = originalCmd.indexOf('<');
+ var baseCmd;
+ if (pos >= 0) {
+ baseCmd = str.substr(0, pos - 1).toLowerCase();
+ str = baseCmd + ' ' + str.substr(pos);
+ } else {
+ str = str.toLowerCase();
+ baseCmd = str;
+ }
+ var handled = false;
+ if (this.completionActionMap_) {
+ handled = this.actionHandler_.call(this, str,
+ originalCmd, this.completionActionMap_);
+ }
+ if (this.completionHandler_ && !handled) {
+ var args = this.getArguments_(str, originalCmd);
+ this.completionHandler_(baseCmd, this.indexList_[originalCmd],
+ this.nodeMap[originalCmd], args);
+ }
+ this.cmpTextElement.value = '';
+ } else if (evt.keyCode == PowerKey.keyCodes.ESC) {
+ this.cmpTextElement.blur();
+ this.updateCompletionField(PowerKey.status.HIDDEN);
+ }
+};
+
+
+/**
+ * Handle keydown events. If the key is an UP/DOWN arrow key, navigates to the
+ * previous and next item in the filtered list.
+ * @param {Object} evt The key event object.
+ * @private
+ */
+PowerKey.prototype.handleCompletionKeyDown_ = function(evt) {
+ // Handle UP arrow key
+ if (evt.keyCode == PowerKey.keyCodes.ARROWUP &&
+ this.filterList_ &&
+ this.filterList_.length > 0) {
+ this.prevListItem_();
+ }
+ // Handle DOWN arrow key
+ else if (evt.keyCode == PowerKey.keyCodes.ARROWDOWN &&
+ this.filterList_ &&
+ this.filterList_.length > 0) {
+ this.nextListItem_();
+ if (evt.preventDefault) {
+ evt.preventDefault();
+ }
+ }
+ // Handle LEFT arrow key
+ if (evt.keyCode == PowerKey.keyCodes.ARROWLEFT &&
+ this.cmpTextElement.readOnly &&
+ this.managedCmpLists_.length > 0) {
+ this.prevManagedCompletionList_();
+ }
+ // Handle RIGHT arrow key
+ else if (evt.keyCode == PowerKey.keyCodes.ARROWRIGHT &&
+ this.cmpTextElement.readOnly &&
+ this.managedCmpLists_.length > 0) {
+ this.nextManagedCompletionList_();
+ }
+ // On TAB, keep the focus in the completion field.
+ else if (evt.keyCode == PowerKey.keyCodes.TAB) {
+ if (this.filterList_ && this.filterList_.length > 0) {
+ this.selectCurrentListItem_();
+ }
+ if (evt.preventDefault) {
+ evt.preventDefault();
+ }
+ }
+};
+
+
+/**
+ * Shows or hides the page-wide background div. Used internally by
+ * updateCompletionField().
+ * @param {boolean} show Whether to show the background or not.
+ * @param {string} color The color of the background.
+ * @param {number} transparency The transparency level, 100 being fully opaque
+ * and 0 being fully transparent.
+ * @private
+ */
+PowerKey.prototype.showBackground_ = function(show, color, transparency) {
+ if (show) {
+ this.backgroundDivElement.className = 'pkBackgroundShow';
+ this.backgroundDivElement.style.display = 'block';
+ var viewportSz = PowerKey.getViewportSize();
+ this.backgroundDivElement.style.width = viewportSz.width + 'px';
+ this.backgroundDivElement.style.height = viewportSz.height + 'px';
+ if (color) {
+ this.backgroundDivElement.style.backgroundColor = color;
+ }
+ if (transparency) {
+ this.backgroundDivElement.style.setProperty('-moz-opacity',
+ '' + (transparency / 100), '');
+ }
+ } else {
+ this.backgroundDivElement.style.display = 'none';
+ }
+};
+
+
+/**
+ * Splits token into words and filters the rows by these words.
+ * @param {Array} list An array of all completions.
+ * @param {string} token Token to match.
+ * @param {number} maxMatches Max number of matches to return.
+ * @return {Array} matches Returns the array of matching rows.
+ * @private
+ */
+PowerKey.prototype.getWordFilterMatches_ = function(list, token, maxMatches) {
+ var matches = list;
+ var rows = list;
+ var words = token.split(' ');
+ for (var i = 0, word; word = words[i]; i++) {
+ rows = matches;
+ matches = [];
+ if (word !== '') {
+ var escapedToken = PowerKey.regExpEscape(word);
+ var matcher = new RegExp('(^|\\W+)' + escapedToken, 'i');
+ for (var j = 0, row; row = rows[j]; j++) {
+ if (String(row).match(matcher)) {
+ matches.push(row);
+ }
+ }
+ }
+ }
+ rows = list;
+ for (j = 0; row = rows[j]; j++) {
+ var parts = row.split(' ');
+ var cmpArray = [];
+ var part;
+ for (i = 0; part = parts[i]; i++) {
+ if (part.charAt(0) == '<') {
+ break;
+ }
+ cmpArray.push(part);
+ }
+ var cmp = cmpArray.join(' ');
+ if (token.indexOf(cmp) === 0) {
+ matches.push(row);
+ }
+ }
+ if (matches.length > maxMatches) {
+ matches.slice(0, maxMatches - 1);
+ }
+ return matches;
+};
+
+
+/**
+ * Compares the original and user-entered completion and returns the array
+ * of arguments if any, otherwise returns null.
+ * @param {string} str The string to parse for arguments.
+ * @param {string} originalCmd The original completion.
+ * @return {Array} Returns an array of completion arguments.
+ * @private
+ */
+PowerKey.prototype.getArguments_ = function(str, originalCmd) {
+ str = str.replace(/\s+/g, ' ');
+ originalCmd = originalCmd.replace(/\s+/g, ' ');
+ var pos = originalCmd.indexOf('<');
+ if (pos < 0) {
+ return [];
+ }
+ originalCmd = originalCmd.substr(pos);
+ str = str.substr(pos);
+ var strTokens = str.split(',');
+ var ostrTokens = originalCmd.split(',');
+ if (strTokens.length != ostrTokens.length) {
+ return [];
+ }
+ var args = [];
+ for (var i = 0, j = 0, token1, token2;
+ (token1 = strTokens[i]) && (token2 = ostrTokens[i]);
+ i++) {
+ token1 = PowerKey.leftTrim(PowerKey.rightTrim(token1));
+ token2 = PowerKey.leftTrim(PowerKey.rightTrim(token2));
+ if (token2.match(PowerKey.CMD_PARAM)) {
+ args.push(token1);
+ }
+ }
+ return args;
+};
+
+
+/**
+ * The default completion handler: executes the appropriate functions
+ * by looking at the action map.
+ * @param {string} act The action/selection to be handled.
+ * @param {string} originalCmd The original format of the completion without
+ * the final parameter values.
+ * @param {Object} actionMap The HashMap consisting of completion strings
+ * as keys and functions as values.
+ * @return {boolean} Whether the completion was successfully handled.
+ * @private
+ */
+PowerKey.prototype.actionHandler_ = function(act,
+ originalCmd,
+ actionMap) {
+ var actionObj = actionMap[originalCmd];
+ if (actionObj && actionObj[this.context]) {
+ var func = actionObj[this.context];
+ var args = this.getArguments_(act, originalCmd);
+ if (func instanceof Function) {
+ window.setTimeout(func(args), 0);
+ } else {
+ //TODO: Remove this if it is not used. Prefer the above instead.
+ window.setTimeout(func + '(args)', 0);
+ }
+ return true;
+ } else {
+ return false;
+ }
+};
+
+/**
+ * Displays the previous item in the filtered list.
+ * @private
+ */
+PowerKey.prototype.prevListItem_ = function() {
+ if (this.listPos_ < 0) {
+ this.listPos_ = 0;
+ }
+ this.listPos_ = (this.listPos_ || this.filterList_.length) - 1;
+ if (this.listPos_ >= 0) {
+ this.setListElement_(this.filterList_[this.listPos_]);
+ }
+};
+
+
+/**
+ * Displays the next item in the filtered list.
+ * @private
+ */
+PowerKey.prototype.nextListItem_ = function() {
+ this.listPos_ = (this.listPos_ + 1) % this.filterList_.length;
+ if (this.listPos_ < this.filterList_.length) {
+ this.setListElement_(this.filterList_[this.listPos_]);
+ }
+};
+
+
+/**
+ * Displays the previous managed (named) completion list.
+ * @private
+ */
+PowerKey.prototype.prevManagedCompletionList_ = function() {
+ var completionListsPos = this.managedCmpListsPos_ - 1;
+ if (completionListsPos < 0) {
+ completionListsPos = this.managedCmpLists_.length - 1;
+ }
+ this.setManagedCompletionList_(completionListsPos);
+};
+
+
+/**
+ * Displays the next managed (named) completion list.
+ * @private
+ */
+PowerKey.prototype.nextManagedCompletionList_ = function() {
+ var completionListsPos = this.managedCmpListsPos_ + 1;
+ if (completionListsPos >= this.managedCmpLists_.length) {
+ completionListsPos = 0;
+ }
+ this.setManagedCompletionList_(completionListsPos);
+};
+
+
+/**
+ * Sets the managed (named) completion list given its position.
+ * @param {number} managedCmpListsPos Always valid list position.
+ * @private
+ */
+PowerKey.prototype.setManagedCompletionList_ = function(managedCmpListsPos) {
+ var managedCmpList = this.managedCmpLists_[managedCmpListsPos];
+ this.context = managedCmpList.name;
+ this.setCompletionList(managedCmpList.values);
+ this.managedCmpListsPos_ = managedCmpListsPos;
+ var status = (this.cmpFloatElement.className == 'pkVisibleStatus') ?
+ PowerKey.status.VISIBLE : PowerKey.status.HIDDEN;
+ this.updateCompletionField(status);
+ if (this.managedCompletionListCallback_) {
+ this.managedCompletionListCallback_(managedCmpList.name);
+ }
+};
+
+
+/**
+ * Selects the current completion from the list, displays it in the completion
+ * text field and speaks it.
+ * @private
+ */
+PowerKey.prototype.selectCurrentListItem_ = function() {
+ this.cmpTextElement.value =
+ this.filterList_[this.listPos_ >= 0 ? this.listPos_ : 0];
+ this.filterList_ = this.getWordFilterMatches_(this.cmpList_,
+ this.cmpTextElement.value, 50);
+ if (this.axsJAX_ && PowerKey.isGecko) {
+ this.axsJAX_.speakTextViaNode(this.cmpTextElement.value);
+ }
+ this.listPos_ = 0;
+};
+
+
+/**
+ * Speaks the element of the filtered list which is currently selected.
+ * @param {string} text The text to be displayed as the completion field's
+ * label (usually the selected list element itself).
+ * @private
+ */
+PowerKey.prototype.setListElement_ = function(text) {
+ if (!this.listElement_) {
+ this.listElement_ = document.createElement('div');
+ this.listElement_.id = 'listElem_' + Math.floor(Math.random() * 1001);
+ this.cmpDivElement_.appendChild(this.listElement_);
+ }
+ if (PowerKey.isIE) {
+ this.listElement_.innerText = text;
+ } else {
+ this.listElement_.textContent = text;
+ }
+ if (this.browseCallback_) {
+ this.browseCallback_(text, this.indexList_[text.toLowerCase()]);
+ }
+ if (this.axsJAX_ && PowerKey.isGecko) {
+ this.axsJAX_.speakNode(this.listElement_, false);
+ }
+};
+
+/**
+ * Calls the class level setDefaultCSSStyle function.
+ * This allows PowerKey objects to be used without causing
+ * an additional dependency if they are only used optionally.
+ */
+PowerKey.prototype.setDefaultCSSStyle = function() {
+ PowerKey.setDefaultCSSStyle();
+};
+
+// Methods in the PowerKey class end here.
+
+
+/**
+ * A class which creates an event handler with a pre-specified action map.
+ * @param {Object} map A HashMap which holds key-function bindings.
+ * @constructor
+ */
+PowerKey.DefaultHandler = function(map) {
+ /**
+ * HashMap holding the key-function bindings.
+ * @type {Object}
+ */
+ this.actionMap = map;
+};
+
+
+/**
+ * The event handler to be called called inside the original event handler.
+ * @param {Object} evt The event object passed to the event handler.
+ * @param {PowerKey.DefaultHandler} handlerObj A reference to this.
+ * @param {PowerKey} pkObj An object of the PowerKey class.
+ */
+PowerKey.DefaultHandler.prototype.handler =
+ function(evt, handlerObj, pkObj) {
+ if (!handlerObj.actionMap) {
+ return;
+ }
+ if (evt.keyCode) {
+ var mapkeyCode = '' + evt.keyCode;
+ var mapkeyChar = String.fromCharCode(evt.keyCode).toLowerCase();
+ if (evt.ctrlKey) {
+ mapkeyCode = 'Ctrl+' + mapkeyCode;
+ mapkeyChar = 'Ctrl+' + mapkeyChar;
+ }
+ if (evt.altKey) {
+ mapkeyCode = 'Alt+' + mapkeyCode;
+ mapkeyChar = 'Alt+' + mapkeyChar;
+ }
+ if (evt.shiftKey) {
+ mapkeyCode = 'Shift+' + mapkeyCode;
+ mapkeyChar = 'Shift+' + mapkeyChar;
+ }
+ var actionObj = null;
+ actionObj = handlerObj.actionMap[mapkeyChar];
+ if (!actionObj) {
+ actionObj = handlerObj.actionMap[mapkeyCode];
+ }
+ if (actionObj) {
+ // If there is no action for the current context, try '*'
+ var funcObj = actionObj[pkObj.context] ?
+ actionObj[pkObj.context] : actionObj['*'];
+ if (funcObj) {
+ var func = funcObj[1];
+ if (func) {
+ func(evt);
+ if (funcObj[0] != '*') {
+ pkObj.context = funcObj[0];
+ }
+ }
+ }
+ }
+ }
+};
+
+
+/**
+ * A class to store the height and width of the viewport.
+ * @param {number} width The width of the browser viewport.
+ * @param {number} height The height of the browser viewport.
+ * @constructor
+ */
+PowerKey.ViewportSize = function(width, height) {
+ this.width = width ? width : undefined;
+ this.height = height ? height : undefined;
+};
+
+
+/**
+ * Trims spaces and new lines from the left end of the string.
+ * @param {string} str String to trim.
+ * @return {string} The left-trimmed string.
+ */
+PowerKey.leftTrim = function(str) {
+ return str.replace(PowerKey.LEFT_TRIMMABLE, '');
+};
+
+
+/**
+ * Trims spaces and new lines from the right end of the string.
+ * @param {string} str String to trim.
+ * @return {string} The right-trimmed string.
+ */
+PowerKey.rightTrim = function(str) {
+ return str.replace(PowerKey.RIGHT_TRIMMABLE, '');
+};
+
+
+/**
+ * Escapes special characters in the string so that it can be matched against
+ * a regular expression.
+ * @param {string} s String from which to escape characters.
+ * @return {string} Returns the escaped string.
+ */
+PowerKey.regExpEscape = function(s) {
+ return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
+ replace(/\x08/g, '\\x08');
+};
+
+
+/**
+ * Gets the size of the browser viewport.
+ * @return {PowerKey.ViewportSize} Returns the size of the viewport.
+ */
+PowerKey.getViewportSize = function() {
+ var myWidth = 0, myHeight = 0;
+ if (typeof(window.innerWidth) == 'number') {
+ //Non-IE
+ myWidth = window.innerWidth;
+ myHeight = window.innerHeight;
+ } else if (document.documentElement &&
+ (document.documentElement.clientWidth ||
+ document.documentElement.clientHeight)) {
+ //IE 6+ in 'standards compliant mode'
+ myWidth = document.documentElement.clientWidth;
+ myHeight = document.documentElement.clientHeight;
+ } else if (document.body &&
+ (document.body.clientWidth ||
+ document.body.clientHeight)) {
+ //IE 4 compatible
+ myWidth = document.body.clientWidth;
+ myHeight = document.body.clientHeight;
+ }
+ return new PowerKey.ViewportSize(myWidth, myHeight);
+};
+
+
+/**
+ * Is the user agent Internet Explorer?
+ * @type {boolean}
+ */
+PowerKey.isIE = false;
+
+
+/**
+ * Is the user agent Firefox?
+ * @type {boolean}
+ */
+PowerKey.isGecko = false;
+
+
+/**
+ * Detects the browser type and version.
+ */
+PowerKey.setBrowser = function() {
+ var agt = navigator.userAgent.toLowerCase();
+ PowerKey.isGecko = (agt.indexOf('gecko') != -1);
+ PowerKey.isIE = ((agt.indexOf('msie') != -1) &&
+ (agt.indexOf('opera') == -1));
+};
+// Set browser type
+PowerKey.setBrowser();
+
+
+/**
+ * Enumeration for events.
+ * @enum {string}
+ */
+PowerKey.Event = {
+ KEYUP: PowerKey.isIE ? 'onkeyup' : 'keyup',
+ KEYDOWN: PowerKey.isIE ? 'onkeydown' : 'keydown',
+ KEYPRESS: PowerKey.isIE ? 'onkeypress' : 'keypress',
+ CLICK: PowerKey.isIE ? 'onclick' : 'click',
+ RESIZE: PowerKey.isIE ? 'onresize' : 'resize',
+ FOCUS: PowerKey.isIE ? 'onfocus' : 'focus',
+ BLUR: PowerKey.isIE ? 'onblur' : 'blur'
+};
+
+
+/**
+ * CSS styles.
+ * @type {string}
+ */
+PowerKey.cssStr =
+'.pkHiddenStatus {display: none; position: absolute;}' +
+'.pkVisibleStatus {display: block; position: absolute; left: 2px; top: 2px; ' +
+ 'line-height: 1.2em; z-index: 10001; background-color: #000000; ' +
+ 'padding: 2px; color: #fff; font-family: Arial, Sans-serif; ' +
+ 'font-size: 20px; filter: alpha(opacity=80); -moz-opacity: .80;}' +
+'.pkOpaqueCompletionText {border-style: none; background-color:transparent; ' +
+ 'font-family: Arial, Helvetica, sans-serif; font-size: 35px; ' +
+ 'font-weight: bold; color: #fff; width: 1000px; height: 50px;}' +
+'.pkBackgroundShow {position: absolute; width: 0px;' +
+ 'height: 0px; background-color: #000000; filter: alpha(opacity=70); ' +
+ ' -moz-opacity: .70; left: 0px; top: 0px; z-index: 10000;}';
+
+
+/**
+ * Adds the PowerKey CSS to the page.
+ */
+PowerKey.setDefaultCSSStyle = function() {
+ var head, style;
+ head = document.getElementsByTagName('head')[0];
+ if (!head) {
+ return;
+ }
+ style = document.createElement('style');
+ style.type = 'text/css';
+ if (PowerKey.isIE) {
+ style.innerhtml = PowerKey.cssStr;
+ } else if (PowerKey.isGecko) {
+ style.innerHTML = PowerKey.cssStr;
+ }
+ head.appendChild(style);
+};
+
+
+/**
+ * Adds the CSS tag to he DOM with href as the specified CSS file.
+ * @param {string} cssFile The CSS file to include.
+ */
+PowerKey.addCSSStyle = function(cssFile) {
+ var headID = document.getElementsByTagName('head')[0];
+ var cssNode = document.createElement('link');
+ cssNode.type = 'text/css';
+ cssNode.rel = 'stylesheet';
+ cssNode.href = cssFile;
+ headID.appendChild(cssNode);
+};
+
+
+/**
+ * Constants for key codes.
+ * @enum {number}
+ */
+PowerKey.keyCodes = {
+ ARROWUP: 38,
+ ARROWDOWN: 40,
+ ARROWLEFT: 37,
+ ARROWRIGHT: 39,
+ ENTER: 13,
+ TAB: 9,
+ ESC: 27
+};
+
+/**
+ * PowerKey string constants.
+ * @enum {string}
+ */
+PowerKey.str = {
+ DEFAULT_COMPLETION_LIST_NAME: 'Completion list',
+ DEFAULT_COMPLETION_PROMPT: 'Enter Completion',
+ DEFAULT_NO_COMPLETION: 'No completions found'
+};
+
+/**
+ * Visibility status of the completion field.
+ * @enum {string}
+ */
+PowerKey.status = {
+ VISIBLE: 'visible',
+ HIDDEN: 'hidden'
+};
+
Property changes on: chrome/browser/resources/access_chromevox/powerkey-bundle.js
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:eol-style
+ LF

Powered by Google App Engine
This is Rietveld 408576698