[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / common / content_editable_extractor.js
blobce0d9415f0d71d17deea72a6387e59747a217f59
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.
5 /**
6  * @fileoverview Defines the ContentEditableExtractor class.
7  */
9 goog.provide('cvox.ContentEditableExtractor');
11 goog.require('cvox.Cursor');
12 goog.require('cvox.TraverseUtil');
14 /**
15  * Extracts the text and line break information from a contenteditable region.
16  * @constructor
17  */
18 cvox.ContentEditableExtractor = function() {
19   /**
20    * The extracted, flattened, text.
21    * @type {string}
22    * @private
23    */
24   this.text_ = '';
26   /**
27    * The start cursor/selection index.
28    * @type {number}
29    * @private
30    */
31   this.start_ = 0;
33   /**
34    * The end cursor/selection index.
35    * @type {number}
36    * @private
37    */
38   this.end_ = 0;
40   /**
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}>}
44    * @private
45    */
46   this.lines_ = {};
48   /**
49    * Map from 0-based character index to 0-based line index.
50    * @type {Array<number>}
51    * @private
52    */
53   this.characterToLineMap_ = [];
56 /**
57  * Update the metadata.
58  * @param {Element} element The DOM element that's contentEditable.
59  */
60 cvox.ContentEditableExtractor.prototype.update = function(element) {
61   /**
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}>}
65    */
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();
70   var rect;
71   var lineIndex = 0;
72   var lastBottom = null;
73   var text = '';
74   var textSize = 0;
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, '');
80   var setStart = false;
81   var setEnd = false;
82   while (true) {
83     var entered = [];
84     var left = [];
85     var c = cvox.TraverseUtil.forwardsChar(endCursor, entered, left);
86     var done = false;
87     if (!c) {
88       done = true;
89     }
90     for (var i = 0; i < left.length && !done; i++) {
91       if (left[i] == element) {
92         done = true;
93       }
94     }
95     if (done) {
96       break;
97     }
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) {
103       continue;
104     }
106     if (lastBottom !== null &&
107         rect.bottom != lastBottom &&
108         textSize > 0 &&
109         text.substr(-1).match(/\S/) &&
110         c.match(/\S/)) {
111       text += '\n';
112       textSize++;
113     }
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) {
119         continue;
120       }
121     }
123     if (!setStart &&
124         selectionStartIndex == -1 &&
125         endCursor.node == selectionStart.node &&
126         endCursor.index >= selectionStart.index) {
127       if (endCursor.index > selectionStart.index) {
128         selectionStartIndex = textSize;
129       } else {
130         setStart = true;
131       }
132     }
133     if (!setEnd &&
134         selectionEndIndex == -1 &&
135         endCursor.node == selectionEnd.node &&
136         endCursor.index >= selectionEnd.index) {
137       if (endCursor.index > selectionEnd.index) {
138         selectionEndIndex = textSize;
139       } else {
140         setEnd = true;
141       }
142     }
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;
150       lineIndex++;
151       lines[lineIndex] = {startIndex: textSize, endIndex: textSize};
152       lastBottom = rect.bottom;
153     }
154     text += c;
155     textSize++;
156     startCursor = endCursor.clone();
158     if (setStart) {
159       selectionStartIndex = textSize;
160       setStart = false;
161     }
162     if (setEnd) {
163       selectionEndIndex = textSize;
164       setEnd = false;
165     }
166   }
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;
176     }
177   }
179   // Finish updating fields.
180   this.text_ = text;
181   this.characterToLineMap_ = characterToLineMap;
182   this.lines_ = lines;
184   this.start_ = selectionStartIndex >= 0 ? selectionStartIndex : text.length;
185   this.end_ = selectionEndIndex >= 0 ? selectionEndIndex : text.length;
189  * Get the text.
190  * @return {string} The extracted, flattened, text.
191  */
192 cvox.ContentEditableExtractor.prototype.getText = function() {
193   return this.text_;
197  * @return {number} The start cursor/selection index.
198  */
199 cvox.ContentEditableExtractor.prototype.getStartIndex = function() {
200   return this.start_;
204  * @return {number} The end cursor/selection index.
205  */
206 cvox.ContentEditableExtractor.prototype.getEndIndex = function() {
207   return this.end_;
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.
214  */
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.
223  */
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.
232  */
233 cvox.ContentEditableExtractor.prototype.getLineEnd = function(index) {
234   return this.lines_[index].endIndex;