| Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
|
| diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
|
| index c8b65e50d1d17e82be781b78f637347c875ec219..5339a636ed7c2911b81e209022ffddac726c0f0d 100644
|
| --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
|
| +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
|
| @@ -66,19 +66,44 @@ var Unit = cursors.Unit;
|
| * pointed to and covers the case where the accessible text is empty.
|
| */
|
| cursors.Cursor = function(node, index) {
|
| - /** @type {!AutomationNode} */
|
| - this.node = node;
|
| - /** @type {number} */
|
| - this.index = index;
|
| + /** @type {!AutomationNode} @private */
|
| + this.node_ = node;
|
| + /** @type {number} @private */
|
| + this.index_ = index;
|
| +};
|
| +
|
| +/**
|
| + * Convenience method to construct a Cursor from a node.
|
| + * @param {!AutomationNode} node
|
| + * @return {!cursors.Cursor}
|
| + */
|
| +cursors.Cursor.fromNode = function(node) {
|
| + return new cursors.Cursor(node, cursors.NODE_INDEX);
|
| };
|
|
|
| cursors.Cursor.prototype = {
|
| /**
|
| - * Returns a copy of this cursor.
|
| - * @return {cursors.Cursor}
|
| + * Returns true if |rhs| is equal to this cursor.
|
| + * @param {!cursors.Cursor} rhs
|
| + * @return {boolean}
|
| + */
|
| + equals: function(rhs) {
|
| + return this.node_ === rhs.getNode() &&
|
| + this.index_ === rhs.getIndex();
|
| + },
|
| +
|
| + /**
|
| + * @return {!AutomationNode}
|
| */
|
| - clone: function() {
|
| - return new cursors.Cursor(this.node, this.index);
|
| + getNode: function() {
|
| + return this.node_;
|
| + },
|
| +
|
| + /**
|
| + * @return {number}
|
| + */
|
| + getIndex: function() {
|
| + return this.index_;
|
| },
|
|
|
| /**
|
| @@ -92,23 +117,25 @@ cursors.Cursor.prototype = {
|
| * @return {string}
|
| */
|
| getText: function(opt_node) {
|
| - var node = opt_node || this.node;
|
| + var node = opt_node || this.node_;
|
| return node.attributes.name || node.attributes.value || '';
|
| },
|
|
|
| /**
|
| - * Moves this cursor by the unit in the given direction using the given
|
| - * movement type.
|
| + * Makes a Cursor which has been moved from this cursor by the unit in the
|
| + * given direction using the given movement type.
|
| * @param {Unit} unit
|
| * @param {Movement} movement
|
| * @param {Dir} dir
|
| + * @return {!cursors.Cursor} The moved cursor.
|
| */
|
| move: function(unit, movement, dir) {
|
| + var newNode, newIndex;
|
| switch (unit) {
|
| case Unit.CHARACTER:
|
| // BOUND and DIRECTIONAL are the same for characters.
|
| - var node = this.node;
|
| - var nextIndex = dir == Dir.FORWARD ? this.index + 1 : this.index - 1;
|
| + var node = this.node_;
|
| + var nextIndex = dir == Dir.FORWARD ? this.index_ + 1 : this.index_ - 1;
|
| if (nextIndex < 0 || nextIndex >= this.getText().length) {
|
| node = AutomationUtil.findNextNode(
|
| node, dir, AutomationPredicate.leaf);
|
| @@ -116,57 +143,67 @@ cursors.Cursor.prototype = {
|
| nextIndex =
|
| dir == Dir.FORWARD ? 0 : this.getText(node).length - 1;
|
| } else {
|
| - node = this.node;
|
| - nextIndex = this.index;
|
| + node = this.node_;
|
| + nextIndex = this.index_;
|
| }
|
| }
|
| - this.node = node;
|
| - this.index = nextIndex;
|
| + newNode = node;
|
| + newIndex = nextIndex;
|
| break;
|
| case Unit.WORD:
|
| switch (movement) {
|
| case Movement.BOUND:
|
| - if (this.node.role == Role.inlineTextBox) {
|
| + if (this.node_.role == Role.inlineTextBox) {
|
| var start, end;
|
| - for (var i = 0; i < this.node.attributes.wordStarts.length; i++) {
|
| - if (this.index >= this.node.attributes.wordStarts[i] &&
|
| - this.index <= this.node.attributes.wordEnds[i]) {
|
| - start = this.node.attributes.wordStarts[i];
|
| - end = this.node.attributes.wordEnds[i];
|
| + for (var i = 0;
|
| + i < this.node_.attributes.wordStarts.length;
|
| + i++) {
|
| + if (this.index_ >= this.node_.attributes.wordStarts[i] &&
|
| + this.index_ <= this.node_.attributes.wordEnds[i]) {
|
| + start = this.node_.attributes.wordStarts[i];
|
| + end = this.node_.attributes.wordEnds[i];
|
| break;
|
| }
|
| }
|
| if (goog.isDef(start) && goog.isDef(end))
|
| - this.index = dir == Dir.FORWARD ? end : start;
|
| + newIndex = dir == Dir.FORWARD ? end : start;
|
| } else {
|
| // TODO(dtseng): Figure out what to do in this case.
|
| }
|
| break;
|
| case Movement.DIRECTIONAL:
|
| - if (this.node.role == Role.inlineTextBox) {
|
| + if (this.node_.role == Role.inlineTextBox) {
|
| var start, end;
|
| - for (var i = 0; i < this.node.attributes.wordStarts.length; i++) {
|
| - if (this.index >= this.node.attributes.wordStarts[i] &&
|
| - this.index <= this.node.attributes.wordEnds[i]) {
|
| + for (var i = 0;
|
| + i < this.node_.attributes.wordStarts.length;
|
| + i++) {
|
| + if (this.index_ >= this.node_.attributes.wordStarts[i] &&
|
| + this.index_ <= this.node_.attributes.wordEnds[i]) {
|
| var nextIndex = dir == Dir.FORWARD ? i + 1 : i - 1;
|
| - start = this.node.attributes.wordStarts[nextIndex];
|
| - end = this.node.attributes.wordEnds[nextIndex];
|
| + start = this.node_.attributes.wordStarts[nextIndex];
|
| + end = this.node_.attributes.wordEnds[nextIndex];
|
| break;
|
| }
|
| }
|
| if (goog.isDef(start)) {
|
| - this.index = start;
|
| + newIndex = start;
|
| } else {
|
| - var node = AutomationUtil.findNextNode(this.node, dir,
|
| - AutomationPredicate.leaf);
|
| - if (node) {
|
| - this.node = node;
|
| - this.index = 0;
|
| - if (dir == Dir.BACKWARD && node.role == Role.inlineTextBox) {
|
| - var starts = node.attributes.wordStarts;
|
| - this.index = starts[starts.length - 1] || 0;
|
| - } else {
|
| - // TODO(dtseng): Figure out what to do for general nodes.
|
| + // The backward case is special at the beginning of nodes.
|
| + if (dir == Dir.BACKWARD && this.index_ != 0) {
|
| + this.index_ = 0;
|
| + } else {
|
| + var node = AutomationUtil.findNextNode(this.node_, dir,
|
| + AutomationPredicate.leaf);
|
| + if (node) {
|
| + newNode = node;
|
| + newIndex = 0;
|
| + if (dir == Dir.BACKWARD &&
|
| + node.role == Role.inlineTextBox) {
|
| + var starts = node.attributes.wordStarts;
|
| + newIndex = starts[starts.length - 1] || 0;
|
| + } else {
|
| + // TODO(dtseng): Figure out what to do for general nodes.
|
| + }
|
| }
|
| }
|
| }
|
| @@ -178,39 +215,133 @@ cursors.Cursor.prototype = {
|
| case Unit.NODE:
|
| switch (movement) {
|
| case Movement.BOUND:
|
| - this.index = dir == Dir.FORWARD ? this.getText().length - 1 : 0;
|
| + newIndex = dir == Dir.FORWARD ? this.getText().length - 1 : 0;
|
| break;
|
| case Movement.DIRECTIONAL:
|
| - this.node = AutomationUtil.findNextNode(
|
| - this.node, dir, AutomationPredicate.leaf) || this.node;
|
| - this.index = cursors.NODE_INDEX;
|
| + newNode = AutomationUtil.findNextNode(
|
| + this.node_, dir, AutomationPredicate.leaf) || this.node_;
|
| + newIndex = cursors.NODE_INDEX;
|
| break;
|
| }
|
| break;
|
| case Unit.LINE:
|
| - this.index = 0;
|
| + newIndex = 0;
|
| switch (movement) {
|
| case Movement.BOUND:
|
| - var node = this.node;
|
| - node = AutomationUtil.findNodeUntil(node, dir,
|
| + newNode = AutomationUtil.findNodeUntil(this.node_, dir,
|
| AutomationPredicate.linebreak, {before: true});
|
| - this.node = node || this.node;
|
| + newNode = newNode || this.node_;
|
| + newIndex =
|
| + dir == Dir.FORWARD ? this.getText(newNode).length - 1 : 0;
|
| break;
|
| case Movement.DIRECTIONAL:
|
| - var node = this.node;
|
| - node = AutomationUtil.findNodeUntil(
|
| - node, dir, AutomationPredicate.linebreak);
|
| + newNode = AutomationUtil.findNodeUntil(
|
| + this.node_, dir, AutomationPredicate.linebreak);
|
|
|
| // We stick to the beginning of lines out of convention.
|
| - if (node && dir == Dir.BACKWARD) {
|
| - node = AutomationUtil.findNodeUntil(node, dir,
|
| + if (newNode && dir == Dir.BACKWARD) {
|
| + newNode = AutomationUtil.findNodeUntil(newNode, dir,
|
| AutomationPredicate.linebreak, {before: true}) || node;
|
| }
|
| - this.node = node || this.node;
|
| break;
|
| }
|
| + break;
|
| + default:
|
| + throw 'Unrecognized unit: ' + unit;
|
| + }
|
| + newNode = newNode || this.node_;
|
| + newIndex = goog.isDef(newIndex) ? newIndex : this.index_;
|
| + return new cursors.Cursor(newNode, newIndex);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Represents a range in the automation tree. There is no visible selection on
|
| + * the page caused by usage of this object.
|
| + * It is assumed that the caller provides |start| and |end| in document order.
|
| + * @param {!cursors.Cursor} start
|
| + * @param {!cursors.Cursor} end
|
| + * @constructor
|
| + */
|
| +cursors.Range = function(start, end) {
|
| + /** @type {!cursors.Cursor} @private */
|
| + this.start_ = start;
|
| + /** @type {!cursors.Cursor} @private */
|
| + this.end_ = end;
|
| +};
|
| +
|
| +/**
|
| + * Convenience method to construct a Range surrounding one node.
|
| + * @param {!AutomationNode} node
|
| + * @return {!cursors.Range}
|
| + */
|
| +cursors.Range.fromNode = function(node) {
|
| + var cursor = cursors.Cursor.fromNode(node);
|
| + return new cursors.Range(cursor, cursor);
|
| +};
|
| +
|
| +cursors.Range.prototype = {
|
| + /**
|
| + * Returns true if |rhs| is equal to this range.
|
| + * @param {!cursors.Range} rhs
|
| + * @return {boolean}
|
| + */
|
| + equals: function(rhs) {
|
| + return this.start_.equals(rhs.getStart()) &&
|
| + this.end_.equals(rhs.getEnd());
|
| + },
|
| +
|
| + /**
|
| + * Gets a cursor bounding this range.
|
| + * @param {Dir} dir Which endpoint cursor to return; Dir.FORWARD for end,
|
| + * Dir.BACKWARD for start.
|
| + * @return {!cursors.Cursor}
|
| + */
|
| + getBound: function(dir) {
|
| + return dir == Dir.FORWARD ? this.end_ : this.start_;
|
| + },
|
| +
|
| + /**
|
| + * @return {!cursors.Cursor}
|
| + */
|
| + getStart: function() {
|
| + return this.start_;
|
| + },
|
| +
|
| + /**
|
| + * @return {!cursors.Cursor}
|
| + */
|
| + getEnd: function() {
|
| + return this.end_;
|
| + },
|
| +
|
| + /**
|
| + * Makes a Range which has been moved from this range by the given unit and
|
| + * direction.
|
| + * @param {Unit} unit
|
| + * @param {Dir} dir
|
| + * @return {cursors.Range}
|
| + */
|
| + move: function(unit, dir) {
|
| + var newStart = this.start_;
|
| + var newEnd = newStart;
|
| + switch (unit) {
|
| + case Unit.CHARACTER:
|
| + newStart = newStart.move(unit, Movement.BOUND, dir);
|
| + newEnd = newStart.move(unit, Movement.BOUND, Dir.FORWARD);
|
| + // Character crossed a node; collapses to the end of the node.
|
| + if (newStart.getNode() !== newEnd.getNode())
|
| + newEnd = newStart;
|
| + break;
|
| + case Unit.WORD:
|
| + case Unit.LINE:
|
| + case Unit.NODE:
|
| + newEnd = newEnd.move(unit, Movement.DIRECTIONAL, dir);
|
| + newStart = newEnd;
|
| + newEnd = newEnd.move(unit, Movement.BOUND, Dir.FORWARD);
|
| break;
|
| }
|
| + return new cursors.Range(newStart, newEnd);
|
| }
|
| };
|
|
|
|
|