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.
6 * @fileoverview Defines the ContentEditableExtractor class.
9 goog
.provide('cvox.ContentEditableExtractor');
11 goog
.require('cvox.Cursor');
12 goog
.require('cvox.TraverseUtil');
15 * Extracts the text and line break information from a contenteditable region.
18 cvox
.ContentEditableExtractor = function() {
20 * The extracted, flattened, text.
27 * The start cursor/selection index.
34 * The end cursor/selection index.
41 * Map from line index to a data structure containing the start
42 * and end index within the line.
43 * @type {Object<number, {startIndex: number, endIndex: number}>}
49 * Map from 0-based character index to 0-based line index.
50 * @type {Array<number>}
53 this.characterToLineMap_
= [];
57 * Update the metadata.
58 * @param {Element} element The DOM element that's contentEditable.
60 cvox
.ContentEditableExtractor
.prototype.update = function(element
) {
62 * Map from line index to a data structure containing the start
63 * and end index within the line.
64 * @type {Object<number, {startIndex: number, endIndex: number}>}
66 var lines
= {0: {startIndex
: 0, endIndex
: 0}};
67 var startCursor
= new cvox
.Cursor(element
, 0, '');
68 var endCursor
= startCursor
.clone();
69 var range
= document
.createRange();
72 var lastBottom
= null;
75 var selectionStartIndex
= -1;
76 var selectionEndIndex
= -1;
77 var sel
= window
.getSelection();
78 var selectionStart
= new cvox
.Cursor(sel
.baseNode
, sel
.baseOffset
, '');
79 var selectionEnd
= new cvox
.Cursor(sel
.extentNode
, sel
.extentOffset
, '');
85 var c
= cvox
.TraverseUtil
.forwardsChar(endCursor
, entered
, left
);
90 for (var i
= 0; i
< left
.length
&& !done
; i
++) {
91 if (left
[i
] == element
) {
99 range
.setStart(startCursor
.node
, startCursor
.index
);
100 range
.setEnd(endCursor
.node
, endCursor
.index
);
101 rect
= range
.getBoundingClientRect();
102 if (!rect
|| rect
.width
== 0 || rect
.height
== 0) {
106 if (lastBottom
!== null &&
107 rect
.bottom
!= lastBottom
&&
109 text
.substr(-1).match(/\S/) &&
115 if (startCursor
.node
!= endCursor
.node
&& endCursor
.index
> 0) {
116 range
.setStart(endCursor
.node
, endCursor
.index
- 1);
117 rect
= range
.getBoundingClientRect();
118 if (!rect
|| rect
.width
== 0 || rect
.height
== 0) {
124 selectionStartIndex
== -1 &&
125 endCursor
.node
== selectionStart
.node
&&
126 endCursor
.index
>= selectionStart
.index
) {
127 if (endCursor
.index
> selectionStart
.index
) {
128 selectionStartIndex
= textSize
;
134 selectionEndIndex
== -1 &&
135 endCursor
.node
== selectionEnd
.node
&&
136 endCursor
.index
>= selectionEnd
.index
) {
137 if (endCursor
.index
> selectionEnd
.index
) {
138 selectionEndIndex
= textSize
;
144 if (lastBottom
=== null) {
145 // This is the first character we've successfully measured on this
146 // line. Save the vertical position but don't do anything else.
147 lastBottom
= rect
.bottom
;
148 } else if (rect
.bottom
!= lastBottom
) {
149 lines
[lineIndex
].endIndex
= textSize
;
151 lines
[lineIndex
] = {startIndex
: textSize
, endIndex
: textSize
};
152 lastBottom
= rect
.bottom
;
156 startCursor
= endCursor
.clone();
159 selectionStartIndex
= textSize
;
163 selectionEndIndex
= textSize
;
168 // Finish up the last line.
169 lines
[lineIndex
].endIndex
= textSize
;
171 // Create a map from character index to line number.
172 var characterToLineMap
= [];
173 for (var i
= 0; i
<= lineIndex
; i
++) {
174 for (var j
= lines
[i
].startIndex
; j
<= lines
[i
].endIndex
; j
++) {
175 characterToLineMap
[j
] = i
;
179 // Finish updating fields.
181 this.characterToLineMap_
= characterToLineMap
;
184 this.start_
= selectionStartIndex
>= 0 ? selectionStartIndex
: text
.length
;
185 this.end_
= selectionEndIndex
>= 0 ? selectionEndIndex
: text
.length
;
190 * @return {string} The extracted, flattened, text.
192 cvox
.ContentEditableExtractor
.prototype.getText = function() {
197 * @return {number} The start cursor/selection index.
199 cvox
.ContentEditableExtractor
.prototype.getStartIndex = function() {
204 * @return {number} The end cursor/selection index.
206 cvox
.ContentEditableExtractor
.prototype.getEndIndex = function() {
211 * Get the line number corresponding to a particular index.
212 * @param {number} index The 0-based character index.
213 * @return {number} The 0-based line number corresponding to that character.
215 cvox
.ContentEditableExtractor
.prototype.getLineIndex = function(index
) {
216 return this.characterToLineMap_
[index
];
220 * Get the start character index of a line.
221 * @param {number} index The 0-based line index.
222 * @return {number} The 0-based index of the first character in this line.
224 cvox
.ContentEditableExtractor
.prototype.getLineStart = function(index
) {
225 return this.lines_
[index
].startIndex
;
229 * Get the end character index of a line.
230 * @param {number} index The 0-based line index.
231 * @return {number} The 0-based index of the end of this line.
233 cvox
.ContentEditableExtractor
.prototype.getLineEnd = function(index
) {
234 return this.lines_
[index
].endIndex
;