OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview A collection of JavaScript utilities used to improve selection | 6 * @fileoverview A collection of JavaScript utilities used to improve selection |
7 * at different granularities. | 7 * at different granularities. |
8 */ | 8 */ |
9 | 9 |
10 | 10 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 * @param {Selection} sel The sentence-length selection. | 86 * @param {Selection} sel The sentence-length selection. |
87 * @return {boolean} True if the selection has been cleaned. | 87 * @return {boolean} True if the selection has been cleaned. |
88 * False if the selection cannot be cleaned without invalid extension. | 88 * False if the selection cannot be cleaned without invalid extension. |
89 */ | 89 */ |
90 cvox.SelectionUtil.cleanUpSentence = function(sel) { | 90 cvox.SelectionUtil.cleanUpSentence = function(sel) { |
91 var expand = true; | 91 var expand = true; |
92 var lastSelection; | 92 var lastSelection; |
93 var lastSelectionOffset; | 93 var lastSelectionOffset; |
94 | 94 |
95 while (expand) { | 95 while (expand) { |
96 | |
97 // nodeType:3 == TEXT_NODE | 96 // nodeType:3 == TEXT_NODE |
98 if (sel.focusNode.nodeType == 3) { | 97 if (sel.focusNode.nodeType == 3) { |
99 // The focus node is of type text, check end for period | 98 // The focus node is of type text, check end for period |
100 | 99 |
101 var fnode = sel.focusNode; | 100 var fnode = sel.focusNode; |
102 var foffset = sel.focusOffset; | 101 var foffset = sel.focusOffset; |
103 | 102 |
104 if (sel.rangeCount > 0 && sel.getRangeAt(0).endOffset > 0) { | 103 if (sel.rangeCount > 0 && sel.getRangeAt(0).endOffset > 0) { |
105 if (fnode.substringData(sel.getRangeAt(0).endOffset - 1, 1) == '.') { | 104 if (fnode.substringData(sel.getRangeAt(0).endOffset - 1, 1) == '.') { |
106 // Text node ends with period. | 105 // Text node ends with period. |
107 return true; | 106 return true; |
108 } else if (fnode.substringData(sel.getRangeAt(0).endOffset - 1, 1) == | 107 } else if ( |
109 ' ') { | 108 fnode.substringData(sel.getRangeAt(0).endOffset - 1, 1) == ' ') { |
110 // Text node ends with space. | 109 // Text node ends with space. |
111 return true; | 110 return true; |
112 } else { | 111 } else { |
113 // Text node does not end with period or space. Extend forward. | 112 // Text node does not end with period or space. Extend forward. |
114 sel.modify('extend', 'forward', 'sentence'); | 113 sel.modify('extend', 'forward', 'sentence'); |
115 | 114 |
116 if ((fnode == sel.focusNode) && (foffset == sel.focusOffset)) { | 115 if ((fnode == sel.focusNode) && (foffset == sel.focusOffset)) { |
117 // Nothing more to be done, cannot extend forward any further. | 116 // Nothing more to be done, cannot extend forward any further. |
118 return false; | 117 return false; |
119 } | 118 } |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 | 178 |
180 | 179 |
181 /** | 180 /** |
182 * Checks the contents of a selection for meaningful content. | 181 * Checks the contents of a selection for meaningful content. |
183 * @param {Selection} sel The selection. | 182 * @param {Selection} sel The selection. |
184 * @return {boolean} True if the selection is valid. False if the selection | 183 * @return {boolean} True if the selection is valid. False if the selection |
185 * contains only whitespace or is an empty string. | 184 * contains only whitespace or is an empty string. |
186 */ | 185 */ |
187 cvox.SelectionUtil.isSelectionValid = function(sel) { | 186 cvox.SelectionUtil.isSelectionValid = function(sel) { |
188 var regExpWhiteSpace = new RegExp(/^\s+$/); | 187 var regExpWhiteSpace = new RegExp(/^\s+$/); |
189 return (! ((regExpWhiteSpace.test(sel.toString())) || | 188 return (!((regExpWhiteSpace.test(sel.toString())) || (sel.toString() == ''))); |
190 (sel.toString() == ''))); | |
191 }; | 189 }; |
192 | 190 |
193 /** | 191 /** |
194 * Checks the contents of a range for meaningful content. | 192 * Checks the contents of a range for meaningful content. |
195 * @param {Range} range The range. | 193 * @param {Range} range The range. |
196 * @return {boolean} True if the range is valid. False if the range | 194 * @return {boolean} True if the range is valid. False if the range |
197 * contains only whitespace or is an empty string. | 195 * contains only whitespace or is an empty string. |
198 */ | 196 */ |
199 cvox.SelectionUtil.isRangeValid = function(range) { | 197 cvox.SelectionUtil.isRangeValid = function(range) { |
200 var text = range.cloneContents().textContent; | 198 var text = range.cloneContents().textContent; |
201 var regExpWhiteSpace = new RegExp(/^\s+$/); | 199 var regExpWhiteSpace = new RegExp(/^\s+$/); |
202 return (! ((regExpWhiteSpace.test(text)) || | 200 return (!((regExpWhiteSpace.test(text)) || (text == ''))); |
203 (text == ''))); | |
204 }; | 201 }; |
205 | 202 |
206 /** | 203 /** |
207 * Returns absolute top and left positions of an element. | 204 * Returns absolute top and left positions of an element. |
208 * | 205 * |
209 * @param {!Node} node The element for which to compute the position. | 206 * @param {!Node} node The element for which to compute the position. |
210 * @return {Array<number>} Index 0 is the left; index 1 is the top. | 207 * @return {Array<number>} Index 0 is the left; index 1 is the top. |
211 * @private | 208 * @private |
212 */ | 209 */ |
213 cvox.SelectionUtil.findPos_ = function(node) { | 210 cvox.SelectionUtil.findPos_ = function(node) { |
(...skipping 26 matching lines...) Expand all Loading... |
240 var parentNode = node.parentElement; | 237 var parentNode = node.parentElement; |
241 while (node != document.body && parentNode) { | 238 while (node != document.body && parentNode) { |
242 node.scrollTop = node.offsetTop; | 239 node.scrollTop = node.offsetTop; |
243 node.scrollLeft = node.offsetLeft; | 240 node.scrollLeft = node.offsetLeft; |
244 node = parentNode; | 241 node = parentNode; |
245 parentNode = node.parentElement; | 242 parentNode = node.parentElement; |
246 } | 243 } |
247 | 244 |
248 // Center the active element on the page once we know it's visible. | 245 // Center the active element on the page once we know it's visible. |
249 var pos = cvox.SelectionUtil.findPos_(focusNode); | 246 var pos = cvox.SelectionUtil.findPos_(focusNode); |
250 window.scrollTo(pos[0] - window.innerWidth / 2, | 247 window.scrollTo( |
251 pos[1] - window.innerHeight / 2); | 248 pos[0] - window.innerWidth / 2, pos[1] - window.innerHeight / 2); |
252 }; | 249 }; |
253 | 250 |
254 /** | 251 /** |
255 * Scrolls the selection into view if it is out of view in the current window. | 252 * Scrolls the selection into view if it is out of view in the current window. |
256 * Inspired by workaround for already-on-screen elements @ | 253 * Inspired by workaround for already-on-screen elements @ |
257 * http:// | 254 * http:// |
258 * www.performantdesign.com/2009/08/26/scrollintoview-but-only-if-out-of-view/ | 255 * www.performantdesign.com/2009/08/26/scrollintoview-but-only-if-out-of-view/ |
259 * @param {Selection} sel The selection to be scrolled into view. | 256 * @param {Selection} sel The selection to be scrolled into view. |
260 */ | 257 */ |
261 cvox.SelectionUtil.scrollToSelection = function(sel) { | 258 cvox.SelectionUtil.scrollToSelection = function(sel) { |
262 if (sel.rangeCount == 0) { | 259 if (sel.rangeCount == 0) { |
263 return; | 260 return; |
264 } | 261 } |
265 | 262 |
266 // First, scroll all parent elements into view. Later, move the body | 263 // First, scroll all parent elements into view. Later, move the body |
267 // which works slightly differently. | 264 // which works slightly differently. |
268 | 265 |
269 cvox.SelectionUtil.scrollElementsToView(sel.focusNode); | 266 cvox.SelectionUtil.scrollElementsToView(sel.focusNode); |
270 | 267 |
271 var pos = cvox.SelectionUtil.findSelPosition(sel); | 268 var pos = cvox.SelectionUtil.findSelPosition(sel); |
272 var top = pos[0]; | 269 var top = pos[0]; |
273 var left = pos[1]; | 270 var left = pos[1]; |
274 | 271 |
275 var scrolledVertically = window.pageYOffset || | 272 var scrolledVertically = window.pageYOffset || |
276 document.documentElement.scrollTop || | 273 document.documentElement.scrollTop || document.body.scrollTop; |
277 document.body.scrollTop; | |
278 var pageHeight = window.innerHeight || | 274 var pageHeight = window.innerHeight || |
279 document.documentElement.clientHeight || document.body.clientHeight; | 275 document.documentElement.clientHeight || document.body.clientHeight; |
280 var pageWidth = window.innerWidth || | 276 var pageWidth = window.innerWidth || document.documentElement.innerWidth || |
281 document.documentElement.innerWidth || document.body.clientWidth; | 277 document.body.clientWidth; |
282 | 278 |
283 if (left < pageWidth) { | 279 if (left < pageWidth) { |
284 left = 0; | 280 left = 0; |
285 } | 281 } |
286 | 282 |
287 // window.scroll puts specified pixel in upper left of window | 283 // window.scroll puts specified pixel in upper left of window |
288 if ((scrolledVertically + pageHeight) < top) { | 284 if ((scrolledVertically + pageHeight) < top) { |
289 // Align with bottom of page | 285 // Align with bottom of page |
290 var diff = top - pageHeight; | 286 var diff = top - pageHeight; |
291 window.scroll(left, diff + 100); | 287 window.scroll(left, diff + 100); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 * Determine if a node should be ignored by the iterator functions. | 320 * Determine if a node should be ignored by the iterator functions. |
325 * | 321 * |
326 * @param {Node} node An object implementing the DOM1 |Node| interface. | 322 * @param {Node} node An object implementing the DOM1 |Node| interface. |
327 * @return {boolean} True if the node is: | 323 * @return {boolean} True if the node is: |
328 * 1) A |Text| node that is all whitespace | 324 * 1) A |Text| node that is all whitespace |
329 * 2) A |Comment| node | 325 * 2) A |Comment| node |
330 * and otherwise false. | 326 * and otherwise false. |
331 */ | 327 */ |
332 | 328 |
333 cvox.SelectionUtil.isIgnorable = function(node) { | 329 cvox.SelectionUtil.isIgnorable = function(node) { |
334 return (node.nodeType == 8) || // A comment node | 330 return (node.nodeType == 8) || // A comment node |
335 ((node.nodeType == 3) && | 331 ((node.nodeType == 3) && |
336 cvox.SelectionUtil.isAllWs(node)); // a text node, all ws | 332 cvox.SelectionUtil.isAllWs(node)); // a text node, all ws |
337 }; | 333 }; |
338 | 334 |
339 /** | 335 /** |
340 * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM | 336 * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM |
341 * Version of |previousSibling| that skips nodes that are entirely | 337 * Version of |previousSibling| that skips nodes that are entirely |
342 * whitespace or comments. (Normally |previousSibling| is a property | 338 * whitespace or comments. (Normally |previousSibling| is a property |
343 * of all DOM nodes that gives the sibling node, the node that is | 339 * of all DOM nodes that gives the sibling node, the node that is |
344 * a child of the same parent, that occurs immediately before the | 340 * a child of the same parent, that occurs immediately before the |
345 * reference node.) | 341 * reference node.) |
346 * | 342 * |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 * | 598 * |
603 * @param {Range} range A range. | 599 * @param {Range} range A range. |
604 * @return {string} The text. | 600 * @return {string} The text. |
605 */ | 601 */ |
606 cvox.SelectionUtil.getRangeText = function(range) { | 602 cvox.SelectionUtil.getRangeText = function(range) { |
607 if (range) | 603 if (range) |
608 return range.cloneContents().textContent.replace(/\s+/g, ' '); | 604 return range.cloneContents().textContent.replace(/\s+/g, ' '); |
609 else | 605 else |
610 return ''; | 606 return ''; |
611 }; | 607 }; |
OLD | NEW |