| Index: chrome/browser/resources/access_chromevox/chromevox/injected/navigation_manager.js
|
| ===================================================================
|
| --- chrome/browser/resources/access_chromevox/chromevox/injected/navigation_manager.js (revision 0)
|
| +++ chrome/browser/resources/access_chromevox/chromevox/injected/navigation_manager.js (revision 0)
|
| @@ -0,0 +1,516 @@
|
| +// 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 Manages navigation within a page.
|
| + * This unifies navigation by the DOM walker and by WebKit selection.
|
| + */
|
| +
|
| +goog.provide('cvox.ChromeVoxNavigationManager');
|
| +
|
| +goog.require('cvox.ChromeVoxChoiceWidget');
|
| +goog.require('cvox.DomUtil');
|
| +goog.require('cvox.LinearDomWalker');
|
| +goog.require('cvox.SelectionUtil');
|
| +goog.require('cvox.SelectionWalker');
|
| +
|
| +/**
|
| + * @constructor
|
| + */
|
| +cvox.ChromeVoxNavigationManager = function() {
|
| + this.currentNode = null;
|
| + this.nodeInformationArray = new Array();
|
| + this.currentNavStrategy = 2;
|
| + this.lastUsedNavStrategy = 2;
|
| + this.linearDomWalker = new cvox.LinearDomWalker();
|
| + this.selectionWalker = new cvox.SelectionWalker();
|
| + this.customWalker = null;
|
| + this.selectionUniqueAncestors = [];
|
| + this.choiceWidget = new cvox.ChromeVoxChoiceWidget();
|
| +};
|
| +
|
| +/**
|
| + * @type {Object.<string, number>}
|
| + */
|
| +cvox.ChromeVoxNavigationManager.STRATEGIES =
|
| + {'SELECTION' : 0, 'LINEARDOM' : 1, 'SMART' : 2, 'CUSTOM' : 3};
|
| +
|
| +/**
|
| + * @type {Array.<string>}
|
| + */
|
| +cvox.ChromeVoxNavigationManager.STRATEGY_NAMES =
|
| + ['SELECTION', 'OBJECT', 'GROUP', 'CUSTOM'];
|
| +
|
| +/**
|
| + * Moves forward using the current navigation strategy.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.next = function() {
|
| + switch (this.currentNavStrategy) {
|
| + default:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
|
| + this.customWalker.next();
|
| + break;
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
|
| + var node = this.linearDomWalker.next();
|
| + if (node) {
|
| + cvox.SelectionUtil.selectAllTextInNode(node);
|
| + this.currentNode = node;
|
| + }
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
|
| + this.selectionUniqueAncestors = [];
|
| + var movedOk = this.selectionWalker.next();
|
| + if (!movedOk) {
|
| + var selectionNode = this.linearDomWalker.next();
|
| + this.selectionUniqueAncestors =
|
| + this.linearDomWalker.getUniqueAncestors();
|
| + if (selectionNode) {
|
| + this.currentNode = selectionNode;
|
| + this.selectionWalker.setCurrentNode(this.currentNode);
|
| + this.selectionWalker.next();
|
| + }
|
| + }
|
| + break;
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Moves backward using the current navigation strategy.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.previous = function() {
|
| + switch (this.currentNavStrategy) {
|
| + default:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
|
| + this.customWalker.previous();
|
| + break;
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
|
| + var node = this.linearDomWalker.previous();
|
| + if (node) {
|
| + cvox.SelectionUtil.selectAllTextInNode(node);
|
| + this.currentNode = node;
|
| + }
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
|
| + this.selectionUniqueAncestors = [];
|
| + var movedOk = this.selectionWalker.previous();
|
| + if (!movedOk) {
|
| + var selectionNode = this.linearDomWalker.previous();
|
| + this.selectionUniqueAncestors =
|
| + this.linearDomWalker.getUniqueAncestors();
|
| + if (selectionNode) {
|
| + this.currentNode = selectionNode;
|
| + this.selectionWalker.setCurrentNode(this.currentNode);
|
| + cvox.SelectionUtil.selectAllTextInNode(this.currentNode);
|
| + window.getSelection().collapseToEnd();
|
| + this.selectionWalker.previous();
|
| + }
|
| + }
|
| + break;
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Moves up a level of granularity.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.up = function() {
|
| + switch (this.currentNavStrategy) {
|
| + default:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
|
| + break;
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
|
| + if (!!this.customWalker) {
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| + this.currentNavStrategy = 3;
|
| + this.customWalker.setCurrentNode(this.currentNode);
|
| + this.customWalker.goToCurrentItem();
|
| + }
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| + this.currentNavStrategy = 2;
|
| + this.linearDomWalker.setSmartNavEnabled(true);
|
| + var node = this.currentNode;
|
| + while (this.linearDomWalker.isLeafNode(node)) {
|
| + this.currentNode = node;
|
| + node = node.parentNode;
|
| + }
|
| + this.linearDomWalker.setCurrentNode(this.currentNode);
|
| + if (this.currentNode !== null) {
|
| + this.previous();
|
| + }
|
| + this.next();
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
|
| + var changed = this.selectionWalker.lessGranular();
|
| + if (!changed) {
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| + this.currentNavStrategy = 1;
|
| + cvox.SelectionUtil.selectAllTextInNode(this.currentNode);
|
| + } else {
|
| + this.selectionWalker.previous();
|
| + this.selectionWalker.next();
|
| + }
|
| + break;
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Moves down a level of granularity.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.down = function() {
|
| + switch (this.currentNavStrategy) {
|
| + default:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| + this.currentNavStrategy = 2;
|
| + this.linearDomWalker.setSmartNavEnabled(true);
|
| + if (this.customWalker.getCurrentNode() != null) {
|
| + this.currentNode = this.customWalker.getCurrentNode();
|
| + this.linearDomWalker.setCurrentNode(this.currentNode);
|
| + }
|
| + break;
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| + this.currentNavStrategy = 1;
|
| + this.linearDomWalker.setSmartNavEnabled(false);
|
| + if (this.currentNode !== null) {
|
| + this.previous();
|
| + }
|
| + this.next();
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| + this.currentNavStrategy = 0;
|
| + this.selectionWalker.setCurrentNode(this.currentNode);
|
| + this.selectionWalker.next();
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
|
| + var changed = this.selectionWalker.moreGranular();
|
| + if (changed) {
|
| + this.selectionWalker.previous();
|
| + this.selectionWalker.next();
|
| + }
|
| + break;
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Moves to the next occurrence of a node that matches the given predicate,
|
| + * if one exists, using the linearDomWalker.
|
| + * @param {function(Array.<Node>)} predicate A function taking an array
|
| + * of unique ancestor nodes as a parameter and returning true if it's
|
| + * what to search for.
|
| + * @return {boolean} True if a match was found.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.findNext = function(predicate) {
|
| + this.syncPosition();
|
| + var node = undefined;
|
| + while (true) {
|
| + node = this.linearDomWalker.next();
|
| + if (!node) {
|
| + break;
|
| + }
|
| +
|
| + if (predicate(this.linearDomWalker.getUniqueAncestors())) {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (node) {
|
| + cvox.SelectionUtil.selectAllTextInNode(node);
|
| + this.currentNode = node;
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +};
|
| +
|
| +/**
|
| + * Moves to the previous occurrence of a node that matches the given predicate,
|
| + * if one exists, using the linearDomWalker.
|
| + * @param {function(Array.<Node>)} predicate A function taking an array
|
| + * of unique ancestor nodes as a parameter and returning true if it's
|
| + * what to search for.
|
| + * @return {boolean} True if a match was found.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.findPrevious = function(predicate) {
|
| + this.syncPosition();
|
| + var node = undefined;
|
| + while (true) {
|
| + node = this.linearDomWalker.previous();
|
| + if (!node) {
|
| + break;
|
| + }
|
| +
|
| + if (predicate(this.linearDomWalker.getUniqueAncestors())) {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (node) {
|
| + cvox.SelectionUtil.selectAllTextInNode(node);
|
| + this.currentNode = node;
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +};
|
| +
|
| +/**
|
| + * Returns the current navigation strategy.
|
| + *
|
| + * @return {string} The strategy that is being used.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.getStrategy = function() {
|
| + return cvox.ChromeVoxNavigationManager.STRATEGY_NAMES[
|
| + this.currentNavStrategy];
|
| +};
|
| +
|
| +/**
|
| + * Returns the current selection granularity.
|
| + *
|
| + * @return {string} The selection granularity that is being used.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.getGranularity = function() {
|
| + return this.selectionWalker.getGranularity();
|
| +};
|
| +
|
| +/**
|
| + * Synchronizes the current position between the different navigation
|
| + * strategies.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.syncPosition = function() {
|
| + if (!this.currentNode) {
|
| + this.currentNode = document.body;
|
| + }
|
| + if (this.currentNavStrategy != this.lastUsedNavStrategy) {
|
| + if ((this.lastUsedNavStrategy ==
|
| + cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM) ||
|
| + (this.lastUsedNavStrategy ==
|
| + cvox.ChromeVoxNavigationManager.STRATEGIES.SMART)) {
|
| + this.syncToNode(this.currentNode);
|
| + } else {
|
| + this.syncToSelection();
|
| + }
|
| + }
|
| + this.lastUsedNavStrategy = this.currentNavStrategy;
|
| +};
|
| +
|
| +/**
|
| + * Synchronizes the navigation strategies to the current selection.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.syncToSelection = function() {
|
| + if (window.getSelection() && window.getSelection().anchorNode) {
|
| + this.currentNode = window.getSelection().anchorNode;
|
| + this.linearDomWalker.setCurrentNode(this.currentNode);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Synchronizes the navigation strategies to the targetNode.
|
| + *
|
| + * @param {Node} targetNode The node that the navigation strategies should be
|
| + * synced to.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.syncToNode = function(targetNode) {
|
| + if (cvox.DomUtil.isDescendantOfNode(this.currentNode, targetNode)) {
|
| + // User is already synced at a more specific level than the target;
|
| + // therefore ignore the sync request.
|
| + return;
|
| + }
|
| + this.currentNode = targetNode;
|
| + this.linearDomWalker.setCurrentNode(targetNode);
|
| + var range = document.createRange();
|
| + range.selectNode(this.currentNode);
|
| + window.getSelection().removeAllRanges();
|
| + window.getSelection().addRange(range);
|
| + window.getSelection().collapseToStart();
|
| +};
|
| +
|
| +/**
|
| + * Returns only the text content for the current position.
|
| + *
|
| + * @return {string} The current text content.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.getCurrentContent = function() {
|
| + switch (this.currentNavStrategy) {
|
| + default:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
|
| + return this.customWalker.getCurrentContent();
|
| + break;
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
|
| + return cvox.DomUtil.getText(this.currentNode);
|
| + break;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
|
| + return this.selectionWalker.getCurrentContent();
|
| + break;
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Returns a complete description of the current position, including
|
| + * the text content and annotations such as "link", "button", etc.
|
| + *
|
| + * @return {string} The summary of the current position.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.getCurrentDescription = function() {
|
| + switch (this.currentNavStrategy) {
|
| + default:
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM:
|
| + return this.customWalker.getCurrentDescription();
|
| + break;
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SMART:
|
| + // Use a linear DOM walker in non-smart mode to traverse all of the
|
| + // nodes inside the current smart node and append all of their
|
| + // descriptions.
|
| + var description = '';
|
| + var walker = new cvox.LinearDomWalker();
|
| + walker.currentNode = this.linearDomWalker.currentNode;
|
| + walker.useSmartNav = false;
|
| + walker.previous();
|
| + walker.next();
|
| + while (cvox.DomUtil.isDescendantOfNode(
|
| + walker.currentNode, this.linearDomWalker.currentNode)) {
|
| + description = description + ' ' +
|
| + cvox.DomUtil.getText(walker.currentNode) + ' ' +
|
| + cvox.DomUtil.getInformationFromAncestors(
|
| + walker.getUniqueAncestors());
|
| + walker.next();
|
| + }
|
| + return description;
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.LINEARDOM:
|
| + return this.getCurrentContent() + ' ' +
|
| + cvox.DomUtil.getInformationFromAncestors(this.getChangedAncestors());
|
| +
|
| + case cvox.ChromeVoxNavigationManager.STRATEGIES.SELECTION:
|
| + return this.getCurrentContent() + ' ' +
|
| + cvox.DomUtil.getInformationFromAncestors(
|
| + this.selectionUniqueAncestors);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Returns an array of ancestor nodes that have been changed between the
|
| + * previous position and the current current position.
|
| + *
|
| + * @return {Array.<Node>} The current content.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.getChangedAncestors = function() {
|
| + return this.linearDomWalker.getUniqueAncestors();
|
| +};
|
| +
|
| +/**
|
| + * Sets the browser's focus to the current node.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.setFocus = function() {
|
| + cvox.DomUtil.setFocus(this.linearDomWalker.getCurrentNode());
|
| +};
|
| +
|
| +/**
|
| + * Acts on the current item and displays a disambiguation dialog
|
| + * if more than one action is possible.
|
| + *
|
| + * @return {boolean} True if an action was taken.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.actOnCurrentItem = function() {
|
| + if (this.currentNavStrategy ==
|
| + cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM) {
|
| + return this.customWalker.actOnCurrentItem();
|
| + } else if (this.currentNavStrategy ==
|
| + cvox.ChromeVoxNavigationManager.STRATEGIES.SMART) {
|
| + if (this.currentNode && this.currentNode.tagName &&
|
| + (this.currentNode.tagName == 'A')) {
|
| + cvox.DomUtil.clickElem(this.currentNode, false);
|
| + return true;
|
| + } else {
|
| + var aNodes = this.currentNode.getElementsByTagName('A');
|
| + if (aNodes.length == 1) {
|
| + cvox.DomUtil.clickElem(aNodes[0], false);
|
| + return true;
|
| + } else if (aNodes.length > 1) {
|
| + var descriptions = new Array();
|
| + var functions = new Array();
|
| + for (var i = 0, link; link = aNodes[i]; i++) {
|
| + if (cvox.DomUtil.hasContent(link)) {
|
| + descriptions.push(cvox.DomUtil.getText(link));
|
| + functions.push(cvox.ChromeVoxNavigationManager
|
| + .createSimpleClickFunction(link));
|
| + }
|
| + }
|
| + this.choiceWidget.show(descriptions, functions,
|
| + descriptions.toString());
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + return false;
|
| +};
|
| +
|
| +/**
|
| + * Checks if the navigation manager is able to act on the current item.
|
| + *
|
| + * @return {boolean} True if some action is possible.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.canActOnCurrentItem = function() {
|
| + if (this.currentNavStrategy ==
|
| + cvox.ChromeVoxNavigationManager.STRATEGIES.CUSTOM) {
|
| + return this.customWalker.canActOnCurrentItem();
|
| + }
|
| + if (this.currentNavStrategy ==
|
| + cvox.ChromeVoxNavigationManager.STRATEGIES.SMART) {
|
| + if (this.currentNode && this.currentNode.tagName &&
|
| + (this.currentNode.tagName == 'A')) {
|
| + return true;
|
| + } else {
|
| + var aNodes = this.currentNode.getElementsByTagName('A');
|
| + if (aNodes.length > 0) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + // Anything that is DOM level or lower will always be handled by the browser.
|
| + return false;
|
| +};
|
| +
|
| +/**
|
| + * Creates a simple function that will click on the given targetNode when
|
| + * invoked.
|
| + * Note that we are using this function because functions created inside a loop
|
| + * have to be created by another function and not within the loop directly.
|
| + *
|
| + * See: http://joust.kano.net/weblog/archive/2005/08/08/
|
| + * a-huge-gotcha-with-javascript-closures/
|
| + * @param {Node} targetNode The target node to click on.
|
| + * @return {function()} A function that will click on the given targetNode.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.createSimpleClickFunction = function(
|
| + targetNode) {
|
| + var target = targetNode.cloneNode(true);
|
| + return function() { cvox.DomUtil.clickElem(target, false); };
|
| +};
|
| +
|
| +/**
|
| + * Sets the custom walker to use for the current site.
|
| + *
|
| + * @param {Object} customWalkerObj The custom walker to use.
|
| + */
|
| +cvox.ChromeVoxNavigationManager.prototype.setCustomWalker =
|
| + function(customWalkerObj) {
|
| + this.customWalker = customWalkerObj;
|
| + this.currentNavStrategy = 3;
|
| + this.lastUsedNavStrategy = 3;
|
| +};
|
| +
|
|
|
| Property changes on: chrome/browser/resources/access_chromevox/chromevox/injected/navigation_manager.js
|
| ___________________________________________________________________
|
| Added: svn:executable
|
| + *
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|