Commit ec44cf30 authored by Katie D's avatar Katie D Committed by Commit Bot

Move Select to Speak utility functions into respective namespaces.

This is a refactor-only change that cleans up namespaces in
Select-to-Speak, for NodeUtils, ParagraphUtils, WordUtils, and RectUtils.

Cq-Include-Trybots: luci.chromium.try:closure_compilation
Change-Id: Iff4adaf10a2bee1f6c777505f1e0acadb1ab60d0
Reviewed-on: https://chromium-review.googlesource.com/1140893Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Commit-Queue: Katie Dektar <katie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576153}
parent ac69bd05
...@@ -72,9 +72,7 @@ let InputHandler = function(callbacks) { ...@@ -72,9 +72,7 @@ let InputHandler = function(callbacks) {
if (!this.trackingMouse_) if (!this.trackingMouse_)
return false; return false;
// Uses RectUtils. var rect = RectUtils.rectFromPoints(
// TODO(katie): Move RectUtils functions into the RectUtils namespace.
var rect = rectFromPoints(
this.mouseStart_.x, this.mouseStart_.y, evt.screenX, evt.screenY); this.mouseStart_.x, this.mouseStart_.y, evt.screenX, evt.screenY);
this.callbacks_.onSelectionChanged(rect); this.callbacks_.onSelectionChanged(rect);
return false; return false;
...@@ -162,7 +160,7 @@ InputHandler.prototype = { ...@@ -162,7 +160,7 @@ InputHandler.prototype = {
* @public * @public
*/ */
getMouseRect: function() { getMouseRect: function() {
return rectFromPoints( return RectUtils.rectFromPoints(
this.mouseStart_.x, this.mouseStart_.y, this.mouseEnd_.x, this.mouseStart_.x, this.mouseStart_.y, this.mouseEnd_.x,
this.mouseEnd_.y); this.mouseEnd_.y);
}, },
......
...@@ -5,13 +5,18 @@ ...@@ -5,13 +5,18 @@
var AutomationNode = chrome.automation.AutomationNode; var AutomationNode = chrome.automation.AutomationNode;
var RoleType = chrome.automation.RoleType; var RoleType = chrome.automation.RoleType;
/**
* @constructor
*/
let ParagraphUtils = function() {};
/** /**
* Gets the first ancestor of a node which is a paragraph or is not inline, * Gets the first ancestor of a node which is a paragraph or is not inline,
* or get the root node if none is found. * or get the root node if none is found.
* @param { AutomationNode } node The node to get the parent for. * @param { AutomationNode } node The node to get the parent for.
* @return { ?AutomationNode } the parent paragraph or null if there is none. * @return { ?AutomationNode } the parent paragraph or null if there is none.
*/ */
function getFirstBlockAncestor(node) { ParagraphUtils.getFirstBlockAncestor = function(node) {
let parent = node.parent; let parent = node.parent;
let root = node.root; let root = node.root;
while (parent != null) { while (parent != null) {
...@@ -28,7 +33,7 @@ function getFirstBlockAncestor(node) { ...@@ -28,7 +33,7 @@ function getFirstBlockAncestor(node) {
parent = parent.parent; parent = parent.parent;
} }
return null; return null;
} };
/** /**
* Determines whether two nodes are in the same block-like ancestor, i.e. * Determines whether two nodes are in the same block-like ancestor, i.e.
...@@ -37,7 +42,7 @@ function getFirstBlockAncestor(node) { ...@@ -37,7 +42,7 @@ function getFirstBlockAncestor(node) {
* @param { AutomationNode|undefined } second The second node to compare. * @param { AutomationNode|undefined } second The second node to compare.
* @return { boolean } whether two nodes are in the same paragraph. * @return { boolean } whether two nodes are in the same paragraph.
*/ */
function inSameParagraph(first, second) { ParagraphUtils.inSameParagraph = function(first, second) {
if (first === undefined || second === undefined) { if (first === undefined || second === undefined) {
return false; return false;
} }
...@@ -52,24 +57,24 @@ function inSameParagraph(first, second) { ...@@ -52,24 +57,24 @@ function inSameParagraph(first, second) {
// 'block' or 'inline-block' elements cannot be in the same paragraph. // 'block' or 'inline-block' elements cannot be in the same paragraph.
return false; return false;
} }
let firstBlock = getFirstBlockAncestor(first); let firstBlock = ParagraphUtils.getFirstBlockAncestor(first);
let secondBlock = getFirstBlockAncestor(second); let secondBlock = ParagraphUtils.getFirstBlockAncestor(second);
return firstBlock != undefined && firstBlock == secondBlock; return firstBlock != undefined && firstBlock == secondBlock;
} };
/** /**
* Determines whether a string is only whitespace. * Determines whether a string is only whitespace.
* @param { string } name A string to test * @param { string } name A string to test
* @return { boolean } whether the string is only whitespace * @return { boolean } whether the string is only whitespace
*/ */
function isWhitespace(name) { ParagraphUtils.isWhitespace = function(name) {
if (name.length == 0) { if (name.length == 0) {
return true; return true;
} }
// Search for one or more whitespace characters // Search for one or more whitespace characters
let re = /^\s+$/; let re = /^\s+$/;
return re.exec(name) != null; return re.exec(name) != null;
} };
/** /**
* Determines the index into the parent name at which the inlineTextBox * Determines the index into the parent name at which the inlineTextBox
...@@ -78,13 +83,13 @@ function isWhitespace(name) { ...@@ -78,13 +83,13 @@ function isWhitespace(name) {
* @return {number} The character index into the parent node at which * @return {number} The character index into the parent node at which
* this node begins. * this node begins.
*/ */
function getStartCharIndexInParent(inlineTextNode) { ParagraphUtils.getStartCharIndexInParent = function(inlineTextNode) {
let result = 0; let result = 0;
for (let i = 0; i < inlineTextNode.indexInParent; i++) { for (let i = 0; i < inlineTextNode.indexInParent; i++) {
result += inlineTextNode.parent.children[i].name.length; result += inlineTextNode.parent.children[i].name.length;
} }
return result; return result;
} };
/** /**
* Determines the inlineTextBox child of a staticText node that appears * Determines the inlineTextBox child of a staticText node that appears
...@@ -100,7 +105,8 @@ function getStartCharIndexInParent(inlineTextNode) { ...@@ -100,7 +105,8 @@ function getStartCharIndexInParent(inlineTextNode) {
* the last inlineTextBox in the staticText node if the index is too * the last inlineTextBox in the staticText node if the index is too
* large. * large.
*/ */
function findInlineTextNodeByCharacterIndex(staticTextNode, index) { ParagraphUtils.findInlineTextNodeByCharacterIndex = function(
staticTextNode, index) {
if (staticTextNode.children.length == 0) { if (staticTextNode.children.length == 0) {
return null; return null;
} }
...@@ -113,20 +119,21 @@ function findInlineTextNodeByCharacterIndex(staticTextNode, index) { ...@@ -113,20 +119,21 @@ function findInlineTextNodeByCharacterIndex(staticTextNode, index) {
textLength += node.name.length; textLength += node.name.length;
} }
return staticTextNode.children[staticTextNode.children.length - 1]; return staticTextNode.children[staticTextNode.children.length - 1];
} };
/** /**
* Builds information about nodes in a group until it reaches the end of the * Builds information about nodes in a group until it reaches the end of the
* group. It may return a NodeGroup with a single node, or a large group * group. It may return a NodeGroup with a single node, or a large group
* representing a paragraph of inline nodes. * representing a paragraph of inline nodes.
* @param { Array<AutomationNode> } nodes List of automation nodes to use. * @param {Array<AutomationNode>} nodes List of automation nodes to use.
* @param { number } index The index into nodes at which to start. * @param {number} index The index into nodes at which to start.
* @return { NodeGroup } info about the node group * @return {ParagraphUtils.NodeGroup} info about the node group
*/ */
function buildNodeGroup(nodes, index) { ParagraphUtils.buildNodeGroup = function(nodes, index) {
let node = nodes[index]; let node = nodes[index];
let next = nodes[index + 1]; let next = nodes[index + 1];
let result = new NodeGroup(getFirstBlockAncestor(nodes[index])); let result = new ParagraphUtils.NodeGroup(
ParagraphUtils.getFirstBlockAncestor(nodes[index]));
let staticTextParent = null; let staticTextParent = null;
// TODO: Don't skip nodes. Instead, go through every node in // TODO: Don't skip nodes. Instead, go through every node in
// this paragraph from the first to the last in the nodes list. // this paragraph from the first to the last in the nodes list.
...@@ -136,7 +143,7 @@ function buildNodeGroup(nodes, index) { ...@@ -136,7 +143,7 @@ function buildNodeGroup(nodes, index) {
// While next node is in the same paragraph as this node AND is // While next node is in the same paragraph as this node AND is
// a text type node, continue building the paragraph. // a text type node, continue building the paragraph.
while (index < nodes.length) { while (index < nodes.length) {
if ((node.name !== undefined && !isWhitespace(node.name)) || if ((node.name !== undefined && !ParagraphUtils.isWhitespace(node.name)) ||
(node.role == RoleType.TEXT_FIELD && node.value !== undefined)) { (node.role == RoleType.TEXT_FIELD && node.value !== undefined)) {
let newNode; let newNode;
if (node.role == RoleType.INLINE_TEXT_BOX && node.parent !== undefined) { if (node.role == RoleType.INLINE_TEXT_BOX && node.parent !== undefined) {
...@@ -149,17 +156,19 @@ function buildNodeGroup(nodes, index) { ...@@ -149,17 +156,19 @@ function buildNodeGroup(nodes, index) {
staticTextParent = null; staticTextParent = null;
} }
if (staticTextParent === null) { if (staticTextParent === null) {
staticTextParent = staticTextParent = new ParagraphUtils.NodeGroupItem(
new NodeGroupItem(node.parent, result.text.length, true); node.parent, result.text.length, true);
newNode = staticTextParent; newNode = staticTextParent;
} }
} else { } else {
// Not an staticText parent node. Add it directly. // Not an staticText parent node. Add it directly.
newNode = new NodeGroupItem(node, result.text.length, false); newNode =
new ParagraphUtils.NodeGroupItem(node, result.text.length, false);
} }
} else { } else {
// Not an inlineTextBox node. Add it directly. // Not an inlineTextBox node. Add it directly.
newNode = new NodeGroupItem(node, result.text.length, false); newNode =
new ParagraphUtils.NodeGroupItem(node, result.text.length, false);
} }
if (newNode) { if (newNode) {
if (newNode.node.role == RoleType.TEXT_FIELD && if (newNode.node.role == RoleType.TEXT_FIELD &&
...@@ -174,7 +183,7 @@ function buildNodeGroup(nodes, index) { ...@@ -174,7 +183,7 @@ function buildNodeGroup(nodes, index) {
result.nodes.push(newNode); result.nodes.push(newNode);
} }
} }
if (!inSameParagraph(node, next)) { if (!ParagraphUtils.inSameParagraph(node, next)) {
break; break;
} }
index += 1; index += 1;
...@@ -183,7 +192,7 @@ function buildNodeGroup(nodes, index) { ...@@ -183,7 +192,7 @@ function buildNodeGroup(nodes, index) {
} }
result.endIndex = index; result.endIndex = index;
return result; return result;
} };
/** /**
* Class representing a node group, which may be a single node or a * Class representing a node group, which may be a single node or a
...@@ -193,7 +202,7 @@ function buildNodeGroup(nodes, index) { ...@@ -193,7 +202,7 @@ function buildNodeGroup(nodes, index) {
* this group. This may be the paragraph parent, for example. * this group. This may be the paragraph parent, for example.
* @constructor * @constructor
*/ */
function NodeGroup(blockParent) { ParagraphUtils.NodeGroup = function(blockParent) {
/** /**
* Full text of this paragraph. * Full text of this paragraph.
* @type {string} * @type {string}
...@@ -202,7 +211,7 @@ function NodeGroup(blockParent) { ...@@ -202,7 +211,7 @@ function NodeGroup(blockParent) {
/** /**
* List of nodes in this paragraph in order. * List of nodes in this paragraph in order.
* @type {Array<NodeGroupItem>} * @type {Array<ParagraphUtils.NodeGroupItem>}
*/ */
this.nodes = []; this.nodes = [];
...@@ -221,7 +230,7 @@ function NodeGroup(blockParent) { ...@@ -221,7 +230,7 @@ function NodeGroup(blockParent) {
* @type {number} * @type {number}
*/ */
this.endIndex = -1; this.endIndex = -1;
} };
/** /**
* Class representing an automation node within a block of text, like * Class representing an automation node within a block of text, like
...@@ -236,7 +245,7 @@ function NodeGroup(blockParent) { ...@@ -236,7 +245,7 @@ function NodeGroup(blockParent) {
* children. * children.
* @constructor * @constructor
*/ */
function NodeGroupItem(node, startChar, opt_hasInlineText) { ParagraphUtils.NodeGroupItem = function(node, startChar, opt_hasInlineText) {
/** /**
* @type {AutomationNode} * @type {AutomationNode}
*/ */
...@@ -257,4 +266,4 @@ function NodeGroupItem(node, startChar, opt_hasInlineText) { ...@@ -257,4 +266,4 @@ function NodeGroupItem(node, startChar, opt_hasInlineText) {
*/ */
this.hasInlineText = this.hasInlineText =
opt_hasInlineText !== undefined ? opt_hasInlineText : false; opt_hasInlineText !== undefined ? opt_hasInlineText : false;
} };
...@@ -31,10 +31,10 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'GetFirstBlockAncestor', function() { ...@@ -31,10 +31,10 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'GetFirstBlockAncestor', function() {
let div = {role: 'genericContainer', parent: paragraph, display: 'block', let div = {role: 'genericContainer', parent: paragraph, display: 'block',
root: root}; root: root};
let text4 = {role: 'staticText', parent: div, root: root}; let text4 = {role: 'staticText', parent: div, root: root};
assertEquals(paragraph, getFirstBlockAncestor(text1)); assertEquals(paragraph, ParagraphUtils.getFirstBlockAncestor(text1));
assertEquals(root, getFirstBlockAncestor(text2)); assertEquals(root, ParagraphUtils.getFirstBlockAncestor(text2));
assertEquals(paragraph, getFirstBlockAncestor(text3)); assertEquals(paragraph, ParagraphUtils.getFirstBlockAncestor(text3));
assertEquals(div, getFirstBlockAncestor(text4)); assertEquals(div, ParagraphUtils.getFirstBlockAncestor(text4));
}); });
TEST_F('SelectToSpeakParagraphUnitTest', 'InSameParagraph', function() { TEST_F('SelectToSpeakParagraphUnitTest', 'InSameParagraph', function() {
...@@ -46,8 +46,8 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'InSameParagraph', function() { ...@@ -46,8 +46,8 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'InSameParagraph', function() {
let paragraph2 = {role: 'paragraph', display: 'block', parent: 'rootWebArea', let paragraph2 = {role: 'paragraph', display: 'block', parent: 'rootWebArea',
root: root}; root: root};
let text3 = {role: 'staticText', parent: paragraph2, root: root}; let text3 = {role: 'staticText', parent: paragraph2, root: root};
assertTrue(inSameParagraph(text1, text2)); assertTrue(ParagraphUtils.inSameParagraph(text1, text2));
assertFalse(inSameParagraph(text1, text3)); assertFalse(ParagraphUtils.inSameParagraph(text1, text3));
}); });
TEST_F('SelectToSpeakParagraphUnitTest', 'BlockDivBreaksSameParagraph', TEST_F('SelectToSpeakParagraphUnitTest', 'BlockDivBreaksSameParagraph',
...@@ -59,17 +59,17 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BlockDivBreaksSameParagraph', ...@@ -59,17 +59,17 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BlockDivBreaksSameParagraph',
let text2 = {role: 'image', parent: paragraph1, display: 'block', root: root}; let text2 = {role: 'image', parent: paragraph1, display: 'block', root: root};
let text3 = {role: 'image', parent: paragraph1, display: 'inline', root: root}; let text3 = {role: 'image', parent: paragraph1, display: 'inline', root: root};
let text4 = {role: 'staticText', parent: paragraph1, root: root}; let text4 = {role: 'staticText', parent: paragraph1, root: root};
assertFalse(inSameParagraph(text1, text2)); assertFalse(ParagraphUtils.inSameParagraph(text1, text2));
assertFalse(inSameParagraph(text2, text3)); assertFalse(ParagraphUtils.inSameParagraph(text2, text3));
assertTrue(inSameParagraph(text3, text4)); assertTrue(ParagraphUtils.inSameParagraph(text3, text4));
}); });
TEST_F('SelectToSpeakParagraphUnitTest', 'IsWhitespace', function() { TEST_F('SelectToSpeakParagraphUnitTest', 'IsWhitespace', function() {
assertTrue(isWhitespace('')); assertTrue(ParagraphUtils.isWhitespace(''));
assertTrue(isWhitespace(' ')); assertTrue(ParagraphUtils.isWhitespace(' '));
assertTrue(isWhitespace(' \n \t ')); assertTrue(ParagraphUtils.isWhitespace(' \n \t '));
assertFalse(isWhitespace('cats')); assertFalse(ParagraphUtils.isWhitespace('cats'));
assertFalse(isWhitespace(' cats ')); assertFalse(ParagraphUtils.isWhitespace(' cats '));
}); });
TEST_F('SelectToSpeakParagraphUnitTest', 'GetStartCharIndexInParent', TEST_F('SelectToSpeakParagraphUnitTest', 'GetStartCharIndexInParent',
...@@ -82,9 +82,9 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'GetStartCharIndexInParent', ...@@ -82,9 +82,9 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'GetStartCharIndexInParent',
let inline3 = {role: 'inlineTextBox', name: 'James Bond', indexInParent: 2, let inline3 = {role: 'inlineTextBox', name: 'James Bond', indexInParent: 2,
parent: staticText}; parent: staticText};
staticText.children = [inline1, inline2, inline3]; staticText.children = [inline1, inline2, inline3];
assertEquals(getStartCharIndexInParent(inline1), 0); assertEquals(ParagraphUtils.getStartCharIndexInParent(inline1), 0);
assertEquals(getStartCharIndexInParent(inline2), 11); assertEquals(ParagraphUtils.getStartCharIndexInParent(inline2), 11);
assertEquals(getStartCharIndexInParent(inline3), 17); assertEquals(ParagraphUtils.getStartCharIndexInParent(inline3), 17);
}); });
TEST_F('SelectToSpeakParagraphUnitTest', 'FindInlineTextNodeByCharIndex', TEST_F('SelectToSpeakParagraphUnitTest', 'FindInlineTextNodeByCharIndex',
...@@ -94,14 +94,21 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'FindInlineTextNodeByCharIndex', ...@@ -94,14 +94,21 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'FindInlineTextNodeByCharIndex',
let inline2 = {role: 'inlineTextBox', name: 'Bond, '}; let inline2 = {role: 'inlineTextBox', name: 'Bond, '};
let inline3 = {role: 'inlineTextBox', name: 'James Bond'}; let inline3 = {role: 'inlineTextBox', name: 'James Bond'};
staticText.children = [inline1, inline2, inline3]; staticText.children = [inline1, inline2, inline3];
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 0), inline1); assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 10), inline1); staticText, 0), inline1);
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 11), inline2); assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 16), inline2); staticText, 10), inline1);
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 17), inline3); assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 50), inline3); staticText, 11), inline2);
assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
staticText, 16), inline2);
assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
staticText, 17), inline3);
assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
staticText, 50), inline3);
staticText.children = []; staticText.children = [];
assertEquals(findInlineTextNodeByCharacterIndex(staticText, 10), null); assertEquals(ParagraphUtils.findInlineTextNodeByCharacterIndex(
staticText, 10), null);
}); });
TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupStopsAtNewParagraph', TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupStopsAtNewParagraph',
...@@ -117,7 +124,7 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupStopsAtNewParagraph', ...@@ -117,7 +124,7 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupStopsAtNewParagraph',
root: root}; root: root};
let text3 = {role: 'staticText', parent: paragraph2, name: 'text3', let text3 = {role: 'staticText', parent: paragraph2, name: 'text3',
root: root}; root: root};
let result = buildNodeGroup([text1, text2, text3], 0); let result = ParagraphUtils.buildNodeGroup([text1, text2, text3], 0);
assertEquals('text1 text2 ', result.text); assertEquals('text1 text2 ', result.text);
assertEquals(1, result.endIndex); assertEquals(1, result.endIndex);
assertEquals(2, result.nodes.length); assertEquals(2, result.nodes.length);
...@@ -141,7 +148,7 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupIncludesLinks', ...@@ -141,7 +148,7 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupIncludesLinks',
let link = {role: 'link', parent: paragraph1, root: root}; let link = {role: 'link', parent: paragraph1, root: root};
let linkText = {role: 'staticText', parent: link, name: 'linkText', let linkText = {role: 'staticText', parent: link, name: 'linkText',
root: root}; root: root};
let result = buildNodeGroup([text1, text2, linkText], 0); let result = ParagraphUtils.buildNodeGroup([text1, text2, linkText], 0);
assertEquals('text1 linkText ', result.text); assertEquals('text1 linkText ', result.text);
assertEquals(2, result.endIndex); assertEquals(2, result.endIndex);
assertEquals(2, result.nodes.length); assertEquals(2, result.nodes.length);
...@@ -158,11 +165,11 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupNativeTextBox', ...@@ -158,11 +165,11 @@ TEST_F('SelectToSpeakParagraphUnitTest', 'BuildNodeGroupNativeTextBox',
let parent = {role: 'pane', parent: root, root: root}; let parent = {role: 'pane', parent: root, root: root};
let searchBar = {role: 'textField', name: 'Address and search bar', let searchBar = {role: 'textField', name: 'Address and search bar',
value: 'http://www.google.com', children: []}; value: 'http://www.google.com', children: []};
let result = buildNodeGroup([searchBar], 0); let result = ParagraphUtils.buildNodeGroup([searchBar], 0);
assertEquals('http://www.google.com ', result.text); assertEquals('http://www.google.com ', result.text);
// If there is no value, it should use the name. // If there is no value, it should use the name.
searchBar.value = ''; searchBar.value = '';
result = buildNodeGroup([searchBar], 0); result = ParagraphUtils.buildNodeGroup([searchBar], 0);
assertEquals('Address and search bar ', result.text); assertEquals('Address and search bar ', result.text);
}); });
\ No newline at end of file
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Utility functions for processing rectangles and coordinates. // Utilities for processing rectangles and coordinates.
/**
* @constructor
*/
let RectUtils = function() {};
/** /**
* Return the rect that encloses two points. * Return the rect that encloses two points.
...@@ -12,13 +17,13 @@ ...@@ -12,13 +17,13 @@
* @param {number} y2 The second x coordinate. * @param {number} y2 The second x coordinate.
* @return {{left: number, top: number, width: number, height: number}} * @return {{left: number, top: number, width: number, height: number}}
*/ */
function rectFromPoints(x1, y1, x2, y2) { RectUtils.rectFromPoints = function(x1, y1, x2, y2) {
var left = Math.min(x1, x2); var left = Math.min(x1, x2);
var right = Math.max(x1, x2); var right = Math.max(x1, x2);
var top = Math.min(y1, y2); var top = Math.min(y1, y2);
var bottom = Math.max(y1, y2); var bottom = Math.max(y1, y2);
return {left: left, top: top, width: right - left, height: bottom - top}; return {left: left, top: top, width: right - left, height: bottom - top};
} };
/** /**
* Returns true if |rect1| and |rect2| overlap. The rects must define * Returns true if |rect1| and |rect2| overlap. The rects must define
...@@ -27,7 +32,7 @@ function rectFromPoints(x1, y1, x2, y2) { ...@@ -27,7 +32,7 @@ function rectFromPoints(x1, y1, x2, y2) {
* @param {{left: number, top: number, width: number, height: number}} rect2 * @param {{left: number, top: number, width: number, height: number}} rect2
* @return {boolean} True if the rects overlap. * @return {boolean} True if the rects overlap.
*/ */
function overlaps(rect1, rect2) { RectUtils.overlaps = function(rect1, rect2) {
var l1 = rect1.left; var l1 = rect1.left;
var r1 = rect1.left + rect1.width; var r1 = rect1.left + rect1.width;
var t1 = rect1.top; var t1 = rect1.top;
...@@ -37,4 +42,4 @@ function overlaps(rect1, rect2) { ...@@ -37,4 +42,4 @@ function overlaps(rect1, rect2) {
var t2 = rect2.top; var t2 = rect2.top;
var b2 = rect2.top + rect2.height; var b2 = rect2.top + rect2.height;
return (l1 < r2 && r1 > l2 && t1 < b2 && b1 > t2); return (l1 < r2 && r1 > l2 && t1 < b2 && b1 > t2);
} };
...@@ -25,12 +25,12 @@ TEST_F('SelectToSpeakRectUtilsUnitTest', 'Overlaps', function() { ...@@ -25,12 +25,12 @@ TEST_F('SelectToSpeakRectUtilsUnitTest', 'Overlaps', function() {
var rect2 = {left: 80, top: 0, width: 100, height: 20}; var rect2 = {left: 80, top: 0, width: 100, height: 20};
var rect3 = {left: 0, top: 80, width: 20, height: 100}; var rect3 = {left: 0, top: 80, width: 20, height: 100};
assertTrue(overlaps(rect1, rect1)); assertTrue(RectUtils.overlaps(rect1, rect1));
assertTrue(overlaps(rect2, rect2)); assertTrue(RectUtils.overlaps(rect2, rect2));
assertTrue(overlaps(rect3, rect3)); assertTrue(RectUtils.overlaps(rect3, rect3));
assertTrue(overlaps(rect1, rect2)); assertTrue(RectUtils.overlaps(rect1, rect2));
assertTrue(overlaps(rect1, rect3)); assertTrue(RectUtils.overlaps(rect1, rect3));
assertFalse(overlaps(rect2, rect3)); assertFalse(RectUtils.overlaps(rect2, rect3));
}); });
TEST_F('SelectToSpeakRectUtilsUnitTest', 'RectFromPoints', function() { TEST_F('SelectToSpeakRectUtilsUnitTest', 'RectFromPoints', function() {
...@@ -38,17 +38,17 @@ TEST_F('SelectToSpeakRectUtilsUnitTest', 'RectFromPoints', function() { ...@@ -38,17 +38,17 @@ TEST_F('SelectToSpeakRectUtilsUnitTest', 'RectFromPoints', function() {
assertNotEquals( assertNotEquals(
JSON.stringify(rect), JSON.stringify(rect),
JSON.stringify(rectFromPoints(0, 0, 10, 10))); JSON.stringify(RectUtils.rectFromPoints(0, 0, 10, 10)));
assertEquals( assertEquals(
JSON.stringify(rect), JSON.stringify(rect),
JSON.stringify(rectFromPoints(10, 20, 60, 80))); JSON.stringify(RectUtils.rectFromPoints(10, 20, 60, 80)));
assertEquals( assertEquals(
JSON.stringify(rect), JSON.stringify(rect),
JSON.stringify(rectFromPoints(60, 20, 10, 80))); JSON.stringify(RectUtils.rectFromPoints(60, 20, 10, 80)));
assertEquals( assertEquals(
JSON.stringify(rect), JSON.stringify(rect),
JSON.stringify(rectFromPoints(10, 80, 60, 20))); JSON.stringify(RectUtils.rectFromPoints(10, 80, 60, 20)));
assertEquals( assertEquals(
JSON.stringify(rect), JSON.stringify(rect),
JSON.stringify(rectFromPoints(60, 80, 10, 20))); JSON.stringify(RectUtils.rectFromPoints(60, 80, 10, 20)));
}); });
\ No newline at end of file
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Utility functions for processing words within strings and nodes. // Utilities for processing words within strings and nodes.
/**
* @constructor
*/
let WordUtils = function() {};
/** /**
* Regular expression to find the start of the next word after a word boundary. * Regular expression to find the start of the next word after a word boundary.
...@@ -10,7 +15,7 @@ ...@@ -10,7 +15,7 @@
* unicode characters. * unicode characters.
* @type {RegExp} * @type {RegExp}
*/ */
const WORD_START_REGEXP = /\b\S/; WordUtils.WORD_START_REGEXP = /\b\S/;
/** /**
* Regular expression to find the end of the next word, which is followed by * Regular expression to find the end of the next word, which is followed by
...@@ -18,7 +23,7 @@ const WORD_START_REGEXP = /\b\S/; ...@@ -18,7 +23,7 @@ const WORD_START_REGEXP = /\b\S/;
* \w does not know about many unicode characters. * \w does not know about many unicode characters.
* @type {RegExp} * @type {RegExp}
*/ */
const WORD_END_REGEXP = /\S\s/; WordUtils.WORD_END_REGEXP = /\S\s/;
/** /**
* Searches through text starting at an index to find the next word's * Searches through text starting at an index to find the next word's
...@@ -26,15 +31,15 @@ const WORD_END_REGEXP = /\S\s/; ...@@ -26,15 +31,15 @@ const WORD_END_REGEXP = /\S\s/;
* @param {string|undefined} text The string to search through * @param {string|undefined} text The string to search through
* @param {number} indexAfter The index into text at which to start * @param {number} indexAfter The index into text at which to start
* searching. * searching.
* @param {NodeGroupItem} nodeGroupItem The node whose name we are * @param {ParagraphUtils.NodeGroupItem} nodeGroupItem The node whose name we
* searching through. * are searching through.
* @return {number} The index of the next word's start * @return {number} The index of the next word's start
*/ */
function getNextWordStart(text, indexAfter, nodeGroupItem) { WordUtils.getNextWordStart = function(text, indexAfter, nodeGroupItem) {
if (nodeGroupItem.hasInlineText && nodeGroupItem.node.children.length > 0) { if (nodeGroupItem.hasInlineText && nodeGroupItem.node.children.length > 0) {
let node = findInlineTextNodeByCharacterIndex( let node = ParagraphUtils.findInlineTextNodeByCharacterIndex(
nodeGroupItem.node, indexAfter - nodeGroupItem.startChar); nodeGroupItem.node, indexAfter - nodeGroupItem.startChar);
let startCharInParent = getStartCharIndexInParent(node); let startCharInParent = ParagraphUtils.getStartCharIndexInParent(node);
for (var i = 0; i < node.wordStarts.length; i++) { for (var i = 0; i < node.wordStarts.length; i++) {
if (node.wordStarts[i] + nodeGroupItem.startChar + startCharInParent < if (node.wordStarts[i] + nodeGroupItem.startChar + startCharInParent <
indexAfter) { indexAfter) {
...@@ -47,9 +52,10 @@ function getNextWordStart(text, indexAfter, nodeGroupItem) { ...@@ -47,9 +52,10 @@ function getNextWordStart(text, indexAfter, nodeGroupItem) {
} else { } else {
// Try to parse using a regex, which is imperfect. // Try to parse using a regex, which is imperfect.
// Fall back to the given index if we can't find a match. // Fall back to the given index if we can't find a match.
return nextWordHelper(text, indexAfter, WORD_START_REGEXP, indexAfter); return WordUtils.nextWordHelper(
text, indexAfter, WordUtils.WORD_START_REGEXP, indexAfter);
} }
} };
/** /**
* Searches through text starting at an index to find the next word's * Searches through text starting at an index to find the next word's
...@@ -57,15 +63,15 @@ function getNextWordStart(text, indexAfter, nodeGroupItem) { ...@@ -57,15 +63,15 @@ function getNextWordStart(text, indexAfter, nodeGroupItem) {
* @param {string|undefined} text The string to search through * @param {string|undefined} text The string to search through
* @param {number} indexAfter The index into text at which to start * @param {number} indexAfter The index into text at which to start
* searching. * searching.
* @param {NodeGroupItem} nodeGroupItem The node whose name we are * @param {ParagraphUtils.NodeGroupItem} nodeGroupItem The node whose name we
* searching through. * are searching through.
* @return {number} The index of the next word's end * @return {number} The index of the next word's end
*/ */
function getNextWordEnd(text, indexAfter, nodeGroupItem) { WordUtils.getNextWordEnd = function(text, indexAfter, nodeGroupItem) {
if (nodeGroupItem.hasInlineText && nodeGroupItem.node.children.length > 0) { if (nodeGroupItem.hasInlineText && nodeGroupItem.node.children.length > 0) {
let node = findInlineTextNodeByCharacterIndex( let node = ParagraphUtils.findInlineTextNodeByCharacterIndex(
nodeGroupItem.node, indexAfter - nodeGroupItem.startChar + 1); nodeGroupItem.node, indexAfter - nodeGroupItem.startChar + 1);
let startCharInParent = getStartCharIndexInParent(node); let startCharInParent = ParagraphUtils.getStartCharIndexInParent(node);
for (var i = 0; i < node.wordEnds.length; i++) { for (var i = 0; i < node.wordEnds.length; i++) {
if (node.wordEnds[i] + nodeGroupItem.startChar + startCharInParent - 1 < if (node.wordEnds[i] + nodeGroupItem.startChar + startCharInParent - 1 <
indexAfter) { indexAfter) {
...@@ -80,10 +86,11 @@ function getNextWordEnd(text, indexAfter, nodeGroupItem) { ...@@ -80,10 +86,11 @@ function getNextWordEnd(text, indexAfter, nodeGroupItem) {
} else { } else {
// Try to parse using a regex, which is imperfect. // Try to parse using a regex, which is imperfect.
// Fall back to the full length of the text if we can't find a match. // Fall back to the full length of the text if we can't find a match.
return nextWordHelper(text, indexAfter, WORD_END_REGEXP, text.length - 1) + return WordUtils.nextWordHelper(
text, indexAfter, WordUtils.WORD_END_REGEXP, text.length - 1) +
1; 1;
} }
} };
/** /**
* Searches through text to find the first index of a regular expression * Searches through text to find the first index of a regular expression
...@@ -97,7 +104,7 @@ function getNextWordEnd(text, indexAfter, nodeGroupItem) { ...@@ -97,7 +104,7 @@ function getNextWordEnd(text, indexAfter, nodeGroupItem) {
* @return {number} The index found by the regular expression, or -1 * @return {number} The index found by the regular expression, or -1
* if none found. * if none found.
*/ */
function nextWordHelper(text, indexAfter, re, defaultValue) { WordUtils.nextWordHelper = function(text, indexAfter, re, defaultValue) {
if (text === undefined) { if (text === undefined) {
return defaultValue; return defaultValue;
} }
...@@ -106,4 +113,4 @@ function nextWordHelper(text, indexAfter, re, defaultValue) { ...@@ -106,4 +113,4 @@ function nextWordHelper(text, indexAfter, re, defaultValue) {
return indexAfter + result.index; return indexAfter + result.index;
} }
return defaultValue; return defaultValue;
} };
...@@ -25,37 +25,37 @@ SelectToSpeakWordUtilsUnitTest.prototype = { ...@@ -25,37 +25,37 @@ SelectToSpeakWordUtilsUnitTest.prototype = {
TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStartWithoutWordStarts', TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStartWithoutWordStarts',
function() { function() {
let node = {node: {}}; let node = {node: {}};
assertEquals(0, getNextWordStart('kitty cat', 0, node)); assertEquals(0, WordUtils.getNextWordStart('kitty cat', 0, node));
assertEquals(1, getNextWordStart(' kitty cat', 0, node)); assertEquals(1, WordUtils.getNextWordStart(' kitty cat', 0, node));
assertEquals(6, getNextWordStart('kitty cat', 5, node)); assertEquals(6, WordUtils.getNextWordStart('kitty cat', 5, node));
assertEquals(6, getNextWordStart('kitty cat', 6, node)); assertEquals(6, WordUtils.getNextWordStart('kitty cat', 6, node));
assertEquals(7, getNextWordStart('kitty "cat"', 5, node)); assertEquals(7, WordUtils.getNextWordStart('kitty "cat"', 5, node));
}); });
TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEndWithoutWordEnds', TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEndWithoutWordEnds',
function() { function() {
let node = {node: {}}; let node = {node: {}};
assertEquals(5, getNextWordEnd('kitty cat', 0, node)); assertEquals(5, WordUtils.getNextWordEnd('kitty cat', 0, node));
assertEquals(6, getNextWordEnd(' kitty cat', 0, node)); assertEquals(6, WordUtils.getNextWordEnd(' kitty cat', 0, node));
assertEquals(9, getNextWordEnd('kitty cat', 6, node)); assertEquals(9, WordUtils.getNextWordEnd('kitty cat', 6, node));
assertEquals(9, getNextWordEnd('kitty cat', 7, node)); assertEquals(9, WordUtils.getNextWordEnd('kitty cat', 7, node));
}); });
TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStart', function() { TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStart', function() {
let inlineText = {wordStarts: [0, 6], name: 'kitty cat'}; let inlineText = {wordStarts: [0, 6], name: 'kitty cat'};
let staticText = {children: [inlineText], name: 'kitty cat'}; let staticText = {children: [inlineText], name: 'kitty cat'};
let node = {node: staticText, startChar: 0, hasInlineText: true}; let node = {node: staticText, startChar: 0, hasInlineText: true};
assertEquals(0, getNextWordStart('kitty cat', 0, node)); assertEquals(0, WordUtils.getNextWordStart('kitty cat', 0, node));
assertEquals(6, getNextWordStart('kitty cat', 5, node)); assertEquals(6, WordUtils.getNextWordStart('kitty cat', 5, node));
assertEquals(6, getNextWordStart('kitty cat', 6, node)); assertEquals(6, WordUtils.getNextWordStart('kitty cat', 6, node));
node.startChar = 10; node.startChar = 10;
assertEquals(10, getNextWordStart('once upon kitty cat', 9, node)); assertEquals(10, WordUtils.getNextWordStart('once upon kitty cat', 9, node));
assertEquals(16, getNextWordStart('once upon kitty cat', 15, node)); assertEquals(16, WordUtils.getNextWordStart('once upon kitty cat', 15, node));
// Should return the default if the inlineText children are missing. // Should return the default if the inlineText children are missing.
staticText.children = []; staticText.children = [];
assertEquals(10, getNextWordStart('once upon a kitty cat', 10, node)); assertEquals(10, WordUtils.getNextWordStart('once upon a kitty cat', 10, node));
}); });
TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStartMultipleChildren', TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStartMultipleChildren',
...@@ -67,26 +67,26 @@ TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStartMultipleChildren', ...@@ -67,26 +67,26 @@ TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordStartMultipleChildren',
inlineText1.parent = staticText; inlineText1.parent = staticText;
inlineText2.parent = staticText; inlineText2.parent = staticText;
let node = {node: staticText, startChar: 0, hasInlineText: true}; let node = {node: staticText, startChar: 0, hasInlineText: true};
assertEquals(10, getNextWordStart('kitty cat is cute', 7, node)); assertEquals(10, WordUtils.getNextWordStart('kitty cat is cute', 7, node));
assertEquals(13, getNextWordStart('kitty cat is cute', 11, node)); assertEquals(13, WordUtils.getNextWordStart('kitty cat is cute', 11, node));
}); });
TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEnd', function() { TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEnd', function() {
let inlineText = {wordEnds: [5, 9], name: 'kitty cat'}; let inlineText = {wordEnds: [5, 9], name: 'kitty cat'};
let staticText = {children: [inlineText], name: 'kitty cat'}; let staticText = {children: [inlineText], name: 'kitty cat'};
let node = {node: staticText, startChar: 0, hasInlineText: true}; let node = {node: staticText, startChar: 0, hasInlineText: true};
assertEquals(5, getNextWordEnd('kitty cat', 0, node)); assertEquals(5, WordUtils.getNextWordEnd('kitty cat', 0, node));
assertEquals(5, getNextWordEnd('kitty cat', 4, node)); assertEquals(5, WordUtils.getNextWordEnd('kitty cat', 4, node));
assertEquals(9, getNextWordEnd('kitty cat', 5, node)); assertEquals(9, WordUtils.getNextWordEnd('kitty cat', 5, node));
assertEquals(9, getNextWordEnd('kitty cat', 6, node)); assertEquals(9, WordUtils.getNextWordEnd('kitty cat', 6, node));
node.startChar = 10; node.startChar = 10;
assertEquals(15, getNextWordEnd('once upon kitty cat', 9, node)); assertEquals(15, WordUtils.getNextWordEnd('once upon kitty cat', 9, node));
assertEquals(19, getNextWordEnd('once upon kitty cat', 17, node)); assertEquals(19, WordUtils.getNextWordEnd('once upon kitty cat', 17, node));
// Should return the default if the inlineText children are missing. // Should return the default if the inlineText children are missing.
staticText.children = []; staticText.children = [];
assertEquals(5, getNextWordEnd('kitty cat', 4, node)); assertEquals(5, WordUtils.getNextWordEnd('kitty cat', 4, node));
}); });
TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEndMultipleChildren', TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEndMultipleChildren',
...@@ -98,6 +98,6 @@ TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEndMultipleChildren', ...@@ -98,6 +98,6 @@ TEST_F('SelectToSpeakWordUtilsUnitTest', 'getNextWordEndMultipleChildren',
inlineText1.parent = staticText; inlineText1.parent = staticText;
inlineText2.parent = staticText; inlineText2.parent = staticText;
let node = {node: staticText, startChar: 0, hasInlineText: true}; let node = {node: staticText, startChar: 0, hasInlineText: true};
assertEquals(12, getNextWordEnd('kitty cat is cute', 10, node)); assertEquals(12, WordUtils.getNextWordEnd('kitty cat is cute', 10, node));
assertEquals(17, getNextWordEnd('kitty cat is cute', 13, node)); assertEquals(17, WordUtils.getNextWordEnd('kitty cat is cute', 13, node));
}); });
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment