| Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| index 4d40067b035e09515e8516578ac052ec11485667..9755994bdb06fad64d096a77883a1ad637a2d641 100644
|
| --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
|
| @@ -7,6 +7,8 @@
|
| * appropriate spoken and braille feedback.
|
| */
|
|
|
| +goog.provide('editing.EditableLine');
|
| +
|
| goog.provide('editing.TextEditHandler');
|
|
|
| goog.require('AutomationTreeWalker');
|
| @@ -227,9 +229,7 @@ function AutomationRichEditableText(node) {
|
| if (!root || !root.anchorObject || !root.focusObject)
|
| return;
|
|
|
| - this.range = new cursors.Range(
|
| - new cursors.Cursor(root.anchorObject, root.anchorOffset || 0),
|
| - new cursors.Cursor(root.focusObject, root.focusOffset || 0));
|
| + this.line_ = new editing.EditableLine(root.anchorObject, root.anchorOffset, root.focusObject, root.focusOffset);
|
| }
|
|
|
| AutomationRichEditableText.prototype = {
|
| @@ -241,64 +241,28 @@ AutomationRichEditableText.prototype = {
|
| if (!root.anchorObject || !root.focusObject)
|
| return;
|
|
|
| - var cur = new cursors.Range(
|
| - new cursors.Cursor(root.anchorObject, root.anchorOffset || 0),
|
| - new cursors.Cursor(root.focusObject, root.focusOffset || 0));
|
| - var prev = this.range;
|
| -
|
| - this.range = cur;
|
| + var cur = new editing.EditableLine(
|
| + root.anchorObject, root.anchorOffset || 0,
|
| + root.focusObject, root.focusOffset || 0);
|
| + var prev = this.line_;
|
| + this.line_ = cur;
|
|
|
| - if (prev.start.node == cur.start.node &&
|
| - prev.end.node == cur.end.node &&
|
| - cur.start.node == cur.end.node) {
|
| - // Plain text: diff the two positions.
|
| + if (prev.equals(cur)) {
|
| + // Collapsed cursor.
|
| this.changed(new cvox.TextChangeEvent(
|
| - root.anchorObject.name || '',
|
| - root.anchorOffset || 0,
|
| - root.focusOffset || 0,
|
| + cur.text || '',
|
| + cur.startOffset || 0,
|
| + cur.endOffset || 0,
|
| true));
|
|
|
| - var lineIndex = this.getLineIndex(this.start);
|
| - var brailleLineStart = this.getLineStart(lineIndex);
|
| - var brailleLineEnd = this.getLineEnd(lineIndex);
|
| - var buff = new Spannable(this.value);
|
| - buff.setSpan(new cvox.ValueSpan(0), brailleLineStart, brailleLineEnd);
|
| -
|
| - var selStart = this.start - brailleLineStart;
|
| - var selEnd = this.end - brailleLineStart;
|
| - buff.setSpan(new cvox.ValueSelectionSpan(), selStart, selEnd);
|
| - cvox.ChromeVox.braille.write(new cvox.NavBraille({text: buff,
|
| - startIndex: selStart,
|
| - endIndex: selEnd}));
|
| + cvox.ChromeVox.braille.write(new cvox.NavBraille({text: cur.value_,
|
| + startIndex: 0,
|
| + endIndex: 0}));
|
| return;
|
| - } else {
|
| - // Rich text:
|
| - // If the position is collapsed, expand to the current line.
|
| - var start = cur.start;
|
| - var end = cur.end;
|
| - if (start.equals(end)) {
|
| - start = start.move(Unit.LINE, Movement.BOUND, Dir.BACKWARD);
|
| - end = end.move(Unit.LINE, Movement.BOUND, Dir.FORWARD);
|
| - }
|
| - var range = new cursors.Range(start, end);
|
| - var output = new Output().withRichSpeechAndBraille(
|
| - range, prev, Output.EventType.NAVIGATE);
|
| -
|
| - // This position is not describable.
|
| - if (!output.hasSpeech) {
|
| - cvox.ChromeVox.tts.speak('blank', cvox.QueueMode.CATEGORY_FLUSH);
|
| - cvox.ChromeVox.braille.write(
|
| - new cvox.NavBraille({text: '', startIndex: 0, endIndex: 0}));
|
| - } else {
|
| - output.go();
|
| - }
|
| }
|
|
|
| - // Keep the other members in sync for any future editable text base state
|
| - // machine changes.
|
| - this.value = cur.start.node.name || '';
|
| - this.start = cur.start.index;
|
| - this.end = cur.start.index;
|
| + // Just output the current line.
|
| + new Output().withString(cur.text).go();
|
| },
|
|
|
| /** @override */
|
| @@ -313,34 +277,22 @@ AutomationRichEditableText.prototype = {
|
|
|
| /** @override */
|
| getLineIndex: function(charIndex) {
|
| - var breaks = this.getLineBreaks_();
|
| - var index = 0;
|
| - while (index < breaks.length && breaks[index] <= charIndex)
|
| - ++index;
|
| - return index;
|
| + return 0;
|
| },
|
|
|
| /** @override */
|
| getLineStart: function(lineIndex) {
|
| - if (lineIndex == 0)
|
| - return 0;
|
| - var breaks = this.getLineBreaks_();
|
| - return breaks[lineIndex - 1] ||
|
| - this.node_.root.focusObject.value.length;
|
| + return 0;
|
| },
|
|
|
| /** @override */
|
| getLineEnd: function(lineIndex) {
|
| - var breaks = this.getLineBreaks_();
|
| - var value = this.node_.root.focusObject.name;
|
| - if (lineIndex >= breaks.length)
|
| - return value.length;
|
| - return breaks[lineIndex];
|
| + return this.value.length;
|
| },
|
|
|
| /** @override */
|
| getLineBreaks_: function() {
|
| - return this.node_.root.focusObject.lineStartOffsets || [];
|
| + return [];
|
| }
|
| };
|
|
|
| @@ -396,4 +348,104 @@ editing.EditingChromeVoxStateObserver.prototype = {
|
| */
|
| editing.observer_ = new editing.EditingChromeVoxStateObserver();
|
|
|
| +/**
|
| + * An EditableLine encapsulates all data concerning a line in the automation
|
| + * tree necessary to provide output.
|
| + * @constructor
|
| + */
|
| +editing.EditableLine = function(startNode, startIndex, endNode, endIndex) {
|
| + /** @private {!Cursor} */
|
| + this.start_ = new Cursor(startNode, startIndex).deepEquivalent;
|
| + /** @private {!Cursor} */
|
| + this.end_ = new Cursor(endNode, endIndex).deepEquivalent;
|
| +
|
| + // Computed members.
|
| + /** @private {Spannable} */
|
| + this.value_;
|
| + /** @private {AutomationNode} */
|
| + this.lineStart_;
|
| + /** @private {AutomationNode} */
|
| + this.lineEnd_;
|
| + /** @private {AutomationNode} */
|
| + this.lineContainer_;
|
| +
|
| + this.computeLineData_();
|
| +};
|
| +
|
| +editing.EditableLine.prototype = {
|
| + /** @private */
|
| + computeLineData_: function() {
|
| + this.value_ = new Spannable(this.start_.node.name, this.start_);
|
| + if (this.start_.node == this.end_.node)
|
| + this.value_.setSpan(this.end_, 0, this.start_.node.name.length);
|
| +
|
| + this.lineStart_ = this.start_.node;
|
| + this.lineEnd_ = this.lineStart_;
|
| + this.lineContainer_ = this.lineStart_.parent;
|
| +
|
| + // Annotate each chunk with its associated node.
|
| + this.value_.setSpan(this.lineStart_, 0, this.lineStart_.name.length);
|
| +
|
| + while (this.lineStart_.previousOnLine) {
|
| + this.lineStart_ = this.lineStart_.previousOnLine;
|
| + var oldValue = this.value_;
|
| + var prepend = new Spannable(this.lineStart_.name, this.lineStart_);
|
| + prepend.append(this.value_);
|
| + this.value_ = prepend;
|
| + }
|
| +
|
| + while (this.lineEnd_.nextOnLine) {
|
| + this.lineEnd_ = this.lineEnd_.nextOnLine;
|
| +
|
| + var annotation = this.lineEnd_;
|
| + if (this.lineEnd_ == this.end_.node)
|
| + annotation = this.end_;
|
| +
|
| + this.value_.append(new Spannable(this.lineEnd_.name, annotation));
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Gets the selection offset based on the text content of this line.
|
| + * @return {number}
|
| + */
|
| + get startOffset() {
|
| + return this.value_.getSpanStart(this.start_) + this.start_.index;
|
| + },
|
| +
|
| + /**
|
| + * Gets the selection offset based on the text content of this line.
|
| + * @return {number}
|
| + */
|
| + get endOffset() {
|
| + return this.value_.getSpanStart(this.end_) + this.end_.index;
|
| + },
|
| +
|
| + /**
|
| + * Gets the selection offset based on the text content of this line.
|
| + * @return {number}
|
| + */
|
| + get lineStartOffset() {
|
| + return this.value_.getSpanStart(this.lineStart_);
|
| + },
|
| +
|
| + /**
|
| + * @return {string} The text of this line.
|
| + */
|
| + get text() {
|
| + return this.value_.toString();
|
| + },
|
| +
|
| + /**
|
| + * Returns true if |otherLine| surrounds the same line as |this|. Note that
|
| + * the contents of the line might be different.
|
| + */
|
| + equals: function(otherLine) {
|
| + // Equality is intentionally loose here as any of the state nodes can be invalidated at any time.
|
| + return (otherLine.lineStart_ == this.lineStart_ && otherLine.lineEnd_ == this.lineEnd_) ||
|
| + (otherLine.lineContainer_ == this.lineContainer_ &&
|
| + otherLine.lineStartOffset == this.lineStartOffset);
|
| + }
|
| +};
|
| +
|
| });
|
|
|